ListBox items with different color

This is the forum for miscellaneous technical/programming questions.

Moderator: 2ffat

Post Reply
mark_c
BCBJ Master
BCBJ Master
Posts: 263
Joined: Thu Jun 21, 2012 1:13 am

ListBox items with different color

Post by mark_c »

Hello,
this is my very trivial example that should color differently the items in a ListBox when a directory exists or does not exist. Is there a better way without using the ListBox1DrawItem event and keeping the lbStandard Style?

thank you

Code: Select all

void __fastcall TForm1::Button3Click(TObject *Sender)
{

        TRect TheRect;
        //Rect(int ALeft, int ATop, int ARight, int ABottom);

        for(int i=0; i<ListBox1->Items->Count; i++)
        {
                TheRect = Rect(0,20*i,200,20*i);
                // eliminate artifacts
                ListBox1->Canvas->FillRect(TheRect);

                DWORD dwAttr = GetFileAttributes(ListBox1->Items->Strings[i].c_str());
                if(dwAttr == FILE_ATTRIBUTE_DIRECTORY)
                {
                        // color the line
                        ListBox1->Canvas->Brush->Color = clRed;
                        ListBox1->Canvas->FillRect(TheRect);
                }
                else
                {
                        ListBox1->Canvas->Brush->Color = clGreen;
                        ListBox1->Canvas->FillRect(TheRect);
                }

                // render the text
                ListBox1->Canvas->TextOut( TheRect.Left + 1, TheRect.Top, ListBox1->Items->Strings[i]);
        }
}
//---------------------------------------------------------------------------
mark_c
BCBJ Master
BCBJ Master
Posts: 263
Joined: Thu Jun 21, 2012 1:13 am

Re: ListBox items with different color

Post by mark_c »

another version using the DrwaItem event

Code: Select all

void __fastcall TForm1::ListBox1DrawItem(TWinControl *Control, int Index,
      TRect &Rect, TOwnerDrawState State)
{
        TRect TheRect = ::Rect(0,22,300,22);
        //Rect(int ALeft, int ATop, int ARight, int ABottom);

                ListBox1->Canvas->Font->Size=12;

                // eliminate artifacts
                ListBox1->Canvas->FillRect(TheRect);

                DWORD dwAttr = GetFileAttributes(ListBox1->Items->Strings[Index].c_str());
                if(dwAttr == FILE_ATTRIBUTE_DIRECTORY)
                {
                        // color the line
                        ListBox1->Canvas->Brush->Color = clRed;
                        ListBox1->Canvas->FillRect(TheRect);
                }
                else
                {
                        ListBox1->Canvas->Brush->Color = clGreen;
                        ListBox1->Canvas->FillRect(TheRect);
                }

                // render the text
                ListBox1->Canvas->TextOut( TheRect.Left, TheRect.Top, ListBox1->Items->Strings[Index]);
}
mark_c
BCBJ Master
BCBJ Master
Posts: 263
Joined: Thu Jun 21, 2012 1:13 am

Re: ListBox items with different color

Post by mark_c »

final version

Code: Select all

void __fastcall TForm1::ListBox1DrawItem(TWinControl *Control, int Index,
      TRect &Rect, TOwnerDrawState State)
{
                ListBox1->Canvas->Font->Size=12;
                ListBox1->Canvas->Font->Color=clBlack;

                // eliminate artifacts
                ListBox1->Canvas->FillRect(Rect);

                DWORD dwAttr = GetFileAttributes(ListBox1->Items->Strings[Index].c_str());
                if(dwAttr == FILE_ATTRIBUTE_DIRECTORY)
                {
                        // color the line
                        ListBox1->Canvas->Brush->Color = clRed;
                        ListBox1->Canvas->FillRect(Rect);
                }
                // check for selection rectangle
                else if (State.Contains(odSelected))
                {
                        ListBox1->Canvas->Font->Color = clHighlightText;
                }
                else
                {
                        ListBox1->Canvas->Brush->Color = clGreen;
                        //ListBox1->Canvas->FillRect(TheRect);
                }

                // render the text
                ListBox1->Canvas->TextOut( Rect.Left, Rect.Top, ListBox1->Items->Strings[Index]);
}
rlebeau
BCBJ Author
BCBJ Author
Posts: 1759
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA
Contact:

Re: ListBox items with different color

Post by rlebeau »

mark_c wrote:Is there a better way without using the ListBox1DrawItem event and keeping the lbStandard Style?
No, you need to use owner-drawing for what you are attempting.
mark_c wrote:

Code: Select all

DWORD dwAttr = GetFileAttributes(ListBox1->Items->Strings[Index].c_str());
if(dwAttr == FILE_ATTRIBUTE_DIRECTORY)
That is not the correct way to check the flag. The return value is a bitmask, and folders (and files) can have multiple flags enabled, so you must use the bitwise AND (&) operator to test individual bits.

Also, GetFileAttributes() returns INVALID_FILE_ATTRIBUTES when it can't retrieve the attributes (like, say, when the directory is not found), so you have to account for that, too.

You should be doing something more like this instead:

Code: Select all

void __fastcall TForm1::ListBox1DrawItem(TWinControl *Control, int Index,
    TRect &Rect, TOwnerDrawState State)
{
    if (State.Contains(odSelected))
    {
        ListBox1->Canvas->Brush->Color = clHighlight;
        ListBox1->Canvas->Font->Color = clHighlightText;
    }
    else
    {
        DWORD dwAttr = GetFileAttributes(ListBox1->Items->Strings[Index].c_str());
        if ((dwAttr != INVALID_FILE_ATTRIBUTES) &&
            (dwAttr & FILE_ATTRIBUTE_DIRECTORY))
        {
            ListBox1->Canvas->Brush->Color = clRed;
        }
        else
        {
            ListBox1->Canvas->Brush->Color = clGreen;
        }
        ListBox1->Canvas->Font->Color = clBlack;
    }

    ListBox1->Canvas->FillRect(Rect);

    ListBox1->Canvas->Font->Size = 12;
    ListBox1->Canvas->TextRect( Rect, Rect.Left, Rect.Top, ListBox1->Items->Strings[Index] );
}
Alternatively, the RTL has a DirectoryExists() function in the SysUtils unit:

Code: Select all

#include <SysUtils.hpp>

void __fastcall TForm1::ListBox1DrawItem(TWinControl *Control, int Index,
    TRect &Rect, TOwnerDrawState State)
{
    if (State.Contains(odSelected))
    {
        ListBox1->Canvas->Brush->Color = clHighlight;
        ListBox1->Canvas->Font->Color = clHighlightText;
    }
    else 
    {
        if( DirectoryExists(ListBox1->Items->Strings[Index]) )
            ListBox1->Canvas->Brush->Color = clRed;
        else
            ListBox1->Canvas->Brush->Color = clGreen;
        ListBox1->Canvas->Font->Color = clBlack;
    }

    ListBox1->Canvas->FillRect(Rect);

    ListBox1->Canvas->Font->Size = 12;
    ListBox1->Canvas->TextRect( Rect, Rect.Left, Rect.Top, ListBox1->Items->Strings[Index] );
}
Last edited by rlebeau on Wed Nov 01, 2017 6:44 pm, edited 1 time in total.
Remy Lebeau (TeamB)
Lebeau Software
mark_c
BCBJ Master
BCBJ Master
Posts: 263
Joined: Thu Jun 21, 2012 1:13 am

Re: ListBox items with different color

Post by mark_c »

Thank you Remy Lebeau
Post Reply