Page 1 of 1

ListBox items with different color

PostPosted: Tue Oct 31, 2017 5:10 am
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]);
        }
}
//---------------------------------------------------------------------------

Re: ListBox items with different color

PostPosted: Tue Oct 31, 2017 8:34 am
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]);
}

Re: ListBox items with different color

PostPosted: Tue Oct 31, 2017 9:38 am
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]);
}

Re: ListBox items with different color

PostPosted: Tue Oct 31, 2017 1:45 pm
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] );
}

Re: ListBox items with different color

PostPosted: Tue Oct 31, 2017 2:02 pm
by mark_c
Thank you Remy Lebeau