Drawing the lines of a Listbox in different colors is related to two tasks:
- How to draw the listbox lines in a different way?
- How to tell the drawing procedure which color should be used for which line?
But first of all, there's the question: What exactly should be done? Should the text be drawn in different color? Or should be background be drawn differently? Or both? I'll be assuming the first case, drawing text in different colors.
Ad 1.
The listbox items normally are drawn by the operating system. But when the
Listbox.Style is switched to one of the "owner-draw" options (
lbOwnerDrawFixed or
lbOwnerDrawVariable), you tell the OS that you will take responsibility of drawing the lines. Then, in order to add the code that you want the listbox to execute for drawing its lines, you double-click on the
OnDrawItem event of the listbox. This inserts an empty code block in the source editor, and you must complete it with your own code:
procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
ARect: TRect; State: TOwnerDrawState);
begin
end;
This event handler has the parameters "Control", "Index", "ARect" and "AState":
- Control is the Control which wants to be redrawn by your code, well - the listbox (you can use the same handler for multiple listboxes, then Control tells you which one actually has to be painted). But note that Control is given in a general way as a TWinControl, you must type-cast it to TListbox to access the listbox properties!
- Index is the index of the item to be painted. Use it to get the text to be painted: Listbox1.Items[Index], or more general using the Control parameter: TListbox(Control).Items[Index]
- ARect tells you the rectangle in which the painted text will be expected
- AState tells you the state of the line: normal (set is empty), selected ([odSelected]), selected+focused ([odSelected, odFocused], disabled ([odDisabled]).
The code how to draw the text, finally, uses one of the Canvas methods, most often TCanvas.TextOut(x, y, text), where x and y are the coordinates of the top-left corner of the text to be drawn.
Here is an example assuming that you want to draw every odd line in bold type-face
procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
ARect: TRect; State: TOwnerDrawState);
begin
// Determine canvas properties to be used
Listbox1.Canvas.Font.Assing(Listbox1.Font);
if (odSelected in AState) then
begin
Listbox1.Canvas.Brush.Color := clRed;
Listbox1.Canvas.Font.Color := clYellow;
end else
begin
Listbox1.Canvas.Brush.Color := clWhite;
Listbox1.Canvas.Font.Color := clBlack;
end;
if odd(Index) then
Listbox1.Canvas.Font.Style := [fsBold]
else
Listbox1.Canvas.Font.style := [];
// Draw the background, using the canvas brush
Listbox1.Canvas.FillRect(ARect);
// Draw the text, using the canvas font
Listbox1.Canvas.TextOut(ARect.Left, ARect.Top, Listbox1.Items[Index]);
end;
Ad 2:
One simple possibility, although a bit hacky, to pass the requested colors to the OnDrawItem procedure is to add them to the Listbox along with the text. The Listbox items can store a pointer to an object in addition to the text: Listbox.Items.Strings[index], or ListBox.Items[index] in short, refers to the text, Listbox.Items.Objects[index] refers to the object at the given index. The object is passed by a special Items method:
AddObject. A color is not an object (just an integer data type), but the Listbox.Items do not check this and can be fooled by type-casting the TColor to an object. Basically an object is a pointer, and a typecast tells the compiler: Use the same bits as in the TColor variable but consider it to be a pointer - of course you cannot deference this pointer because it points to non-allocated memory. There is one more complication: TColor is declared as LongInt (32-bit), but a pointer has the same size in a 32-bit application only, in a 64-bit application is is 64 bit in size. Therefore, you must type-cast TColor to PtrInt before casting it to a pointer - PtrInt is an integer which has the same size as a pointer independent of the application's bitness. Without this type-cast your application will crash in 64-bit.
Example: Add the strings 'Carlo', 'Brubo', 'Walter', 'Giuseppe' to the listbox and define the color for Carlo to be red, for Brubo to be black, for Walter to be green and for Guiseppe to be blue:
procedure TForm1.PopulateListbox(Listbox: TListbox);
begin
Listbox.Items.Clear;
Listbox.Items.AddObject('Carlo', TObject(PtrInt(clRed)));
Listbox.Items.AddObject('Brubo', TObject(PtrInt(clBlack)));
Listbox.Items.AddObject('Walter', TObject(PtrInt(clGreen)));
Listbox.Items.AddObject('Guiseppe', TObject(PtrInt(clBlue)));
end;
The drawing code is essentially the same as in the example above, only color-definition is different:
Listbox1.Canvas.Font.Color := TColor(PtrInt(Listbox1.Items.Objects[Index]));
See details in attachment.