0

I have a number of files in a directory, where each file has two lines, line 1 is the string that I want to put into my ListBox, and line 2 is the background color that I want that ListBox item to have (represented as an 8-digit hex value).

The contents of each file looks like this:

string
14603481

This is my code so far:

for i := 0 to PathList.Count - 1 do
begin
  FileLines := TStringList.Create;
  try   
    FileLines.LoadFromFile(PathList.Strings[i]);
    s := FileLines[0]; { Loads string to add to ListBox1 }
    RGBColor := FileLines[1];
  finally
    FileLines.Free;
  end;
ListBox1.Items.AddObject(s, TObject(RGBColor)); { This code doesn't work, but hopefully you get what I'm }
end;                                            { trying to do                                           }

All other examples that do anything similar to this declare the color in the DrawItem procedure, but I need to set the color from within this for loop, since each entry will have a unique color.

How do I set the color of each item uniquely from within this loop?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • @KenWhite Please could you undelete this question? –  Feb 26 '20 at 00:08
  • It's not deleted, so I can't undelete it. It's closed, because there's already an existing answer to this question in the linked post I provided. The answer to that linked question shows you how to custom draw listbox items, which is what your question here asked. Where the color is declared is irrelevant - you can declare it in the item drawing procedure or read it from the listbox item's object where your code stored it. – Ken White Feb 26 '20 at 00:16
  • @KenWhite Yes, and I don't know how to do that, hence the question. –  Feb 26 '20 at 00:36
  • Your question as written asks how to write the DrawItem procedure. If what you're asking is different, then [edit] your post to make it more clear how it's not asking that and ask what you want to know instead, and I'll be more than happy to remove my close vote. – Ken White Feb 26 '20 at 00:38
  • @KenWhite Thank you very much - I have now edited the question to make it clearer what I am asking. –  Feb 26 '20 at 00:53
  • Divide your problem into specific parts, do not ask for a task. Additionally *"How to make a ListBox item a given color?"* pretty much implies you already have the color. – Sertac Akyuz Feb 26 '20 at 00:53
  • Your edit didn't help clarify what you're asking much, but I removed my close vote. Please [edit] your post again to remove the discussion about DrawItem, provide sample content of one of your text files that shows how the color is stored, and ask about how you would convert that stored string value to a color that can be stored in ListBox.Items.Objects. I keep trying to help, and you're not cooperating very much. :-) – Ken White Feb 26 '20 at 00:56
  • 1
    You can't set the color from within the loop because there is no color to set, list items do not have any color property. What you can do is to keep the color somewhere else to retrieve in OnDrawItem. Which you attempt to do the first part already. But convert the color to an integer before storing it to a list object. Then in the event handler you can convert it back to a color. – Sertac Akyuz Feb 26 '20 at 01:17

1 Answers1

2

The VCL's TListBox does not natively support any kind of per-item coloring. The TListBox.Font and TListBox.Color properties apply to all items equally.

To do what you are asking for, you will have to set the TListBox.Style property to lbOwnerDrawFixed and then use the TListBox.OnDrawItem event to custom-draw the items manually however you want, eg:

var
  ...
  s: string;
  RGBColor: Integer;
begin
  ...
  for i := 0 to PathList.Count - 1 do
  begin
    FileLines := TStringList.Create;
    try   
      FileLines.LoadFromFile(PathList[i]);
      s := FileLines[0];
      RGBColor := StrToInt(FileLines[1]);
    finally
      FileLines.Free;
    end;
    ListBox1.Items.AddObject(s, TObject(RGBColor));
  end;
  ...
end;

...

procedure TMyForm.ListBox1DrawItem(Control: TWinControl;
  Index: Integer; const Rect: TRect; State: TOwnerDrawState);
var
  LB: TListBox;
begin
  LB := TListBox(Control);

  if odSelected in State then
  begin
    LB.Canvas.Brush.Color := clHighlight;
    LB.Canvas.Font.Color := clHighlightText;
  end else
  begin
    LB.Canvas.Brush.Color := TColor(Integer(LB.Items.Objects[Index]));
    LB.Canvas.Font.Color := LB.Font.Color;
  end;

  LB.Canvas.FillRect(Rect);
  LB.Canvas.TextRect(Rect, Rect.Left + 2, Rect.Top + 2, LB.Items[Index]);

  if (odFocused in State) and not (odNoFocusRect in State) then
    LB.Canvas.DrawFocusRect(Rect);
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thank you very much, your solution works perfectly and I will study and learn from it. –  Feb 26 '20 at 01:31