Search code examples
listboxfiremonkey

Firemonkey TListBox changing background color of item rows at runtime


Is there a way, at runtime, other than using styles, to change the background color of item rows of a TListBox? Can I use the OnPaint event?


Solution

  • "Can I use the OnPaint event?"

    Yes, you can. But maybe it is not considered the orthodox way, as it isn't made available and may fall apart in future Delphi releases. Anyway, if you want to try it, here goes:

    You want to use the OnPaint event of the TListBoxItem class. However, OnPaint event of the item is not made available in the designer, so you will need to add it manually to your form class.

    type
      TForm1 = class(TForm)
        Label1: TLabel;
        ListBox1: TListBox;
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
        // add following manually (copy & paste)
        procedure ListBoxItemPaint(Sender: TObject; Canvas: TCanvas; 
          const ARect: TRectF);
      private
        { Private declarations }
        procedure PopulateListBox;
      public
        { Public declarations }
      end;
    

    Then, when you add items to ListBox1, it is important to assign ListBoxItemPaint to each items OnPaint event (see end of procedure):

    procedure TForm1.PopulateListBox;
    var
      i: integer;
      item: TListBoxItem;
    begin
      // Set default item height
      // Individual heights are not possible
      ListBox1.ItemHeight := 48;
      // Set nr of columns, items arranged horizontally first, then vertically
      ListBox1.Columns := 1; // 3;
      // Add items
      for i := 0 to 7 do
      begin
        item := TListBoxItem.Create(nil);
        item.Parent := ListBox1;
        item.Width := Listbox1.Width;
        item.StyledSettings := [];    // You are now responsible for corresponding
                                      // item.TextSettings, e.g. font, style, color etc.
        item.tag := i;
        item.Text := 'Item ' + IntToStr(i);
        item.OnPaint := ListBoxItemPaint;   // This links the OnPaint event to your code
      end;
    end;
    

    And this is the ListBoxItemPaint() procedure:

    procedure TForm1.ListBoxItemPaint(Sender: TObject; Canvas: TCanvas;
      const ARect: TRectF);
    var
      Brush: TBrush;
      Color: TAlphaColor;
      Txt: string;
      LocRect: TRectF;
    begin
      begin
        Txt := (Sender as TListBoxItem).Text;
        case (Sender as TListBoxItem).Tag of
          0: Color := TAlphaColors.Aliceblue;
          1: Color := TAlphaColors.Antiquewhite;
          2: Color := TAlphaColors.Aqua;
          3: Color := TAlphaColors.Aquamarine;
          4: Color := TAlphaColors.Azure;
          5: Color := TAlphaColors.Beige;
          6: Color := TAlphaColors.Bisque;
        else
             Color := TAlphaColorRec.Chocolate;
        end;
    
        // Item background, color and shape
        Brush := TBrush.Create(TBrushKind.Solid, Color);
        try
          Canvas.FillRect(ARect, 1, Brush); // rectangular item band
    //    alternatively use rounded corners
    //      Canvas.FillRect(ARect,11, 11, AllCorners, 1, Brush); // rounded corners
        finally
          Brush.Free;
        end;
    
        // Text color, font, style and size
        Canvas.Fill.Color := TAlphaColors.Red;
        Canvas.Font.Family := 'Segoe UI';
        Canvas.Font.Style := [TFontStyle.fsBold, TFontStyle.fsItalic];
        Canvas.Font.Size := 16;
    
        // Item text location and drawing
        LocRect := ARect;
        LocRect.Left := LocRect.Left + 10;
        Canvas.FillText(LocRect, Txt, False, 1, [], TTextAlign.Leading, TTextAlign.Leading);
      end;
    end;
    

    enter image description here

    Code is written and tested with Delphi 10.4 and demonstrates how you can have different bg colors for each item in a TListBox.

    To change the color of the items at run time you need to move the color selection out from the OnPaint to a lookup function with selection logic you want to use.