Table of Contents

Custom drawing

When appearance properties are not enough — a per-row badge, a rating, a progress indicator, a custom icon layout — the list box exposes drawing events that hand you the TTMSFNCGraphics context and the item rectangle. You can draw after the default rendering (the common case, to add decoration on top) or before it (to replace or suppress the default drawing). Owner drawing pairs naturally with the per-item data fields from Items: store a value on each item, then read it back in the draw handler.

Drawing on top of items

OnAfterDrawItem runs once the default item has been painted, so anything you draw appears above it. The snippet below seeds each item with a 1–5 rating in DataInteger, then draws that many gold dots at the right edge of every row:

procedure TForm1.SeedRatings;
var
  I: Integer;
begin
  // Store a 1..5 rating on each item for the drawing handler to read.
  for I := 0 to ListBox1.Items.Count - 1 do
    ListBox1.Items[I].DataInteger := (I mod 5) + 1;
end;

procedure TForm1.ListBox1AfterDrawItem(Sender: TObject;
  AGraphics: TTMSFNCGraphics; ARect: TRectF; AItem: TTMSFNCListBoxItem);
var
  Rating, I: Integer;
  Dot: TRectF;
  X: Single;
const
  DotSize = 10;
  Gap = 4;
begin
  Rating := AItem.DataInteger;
  AGraphics.Fill.Color := gcGold;
  AGraphics.Stroke.Color := gcGoldenrod;

  X := ARect.Right - Gap;
  for I := 1 to Rating do
  begin
    X := X - DotSize - Gap;
    Dot := RectF(X, ARect.Top + (ARect.Height - DotSize) / 2,
      X + DotSize, ARect.Top + (ARect.Height - DotSize) / 2 + DotSize);
    AGraphics.DrawEllipse(Dot);
  end;
end;
List box rows with owner-drawn gold rating dots at the right edge List box rows with owner-drawn gold rating dots at the right edge

Drawing hooks

Event Runs Use it to
OnBeforeDrawItem / OnAfterDrawItem Around the whole item Replace or decorate the row (ADefaultDraw, AAllow)
OnBeforeDrawItemText / OnAfterDrawItemText Around the item text Recolor or replace text rendering
OnBeforeDrawItemIcon / OnAfterDrawItemIcon Around the item icon Swap or annotate the icon
OnBeforeDrawSortIndicator / OnAfterDrawSortIndicator Around the header sort triangle Customize the sort indicator

The OnBefore… drawing events expose a var ADefaultDraw (and AAllow) parameter — set ADefaultDraw := False to take over the rendering entirely, or leave it True to draw alongside the default.

Suppressing the default text

OnBeforeDrawItemText can suppress the built-in text drawing for specific rows — for example to skip rendering for a sentinel "separator" row:

procedure TForm1.ListBox1BeforeDrawItemText(Sender: TObject;
  AGraphics: TTMSFNCGraphics; ARect: TRectF; AItem: TTMSFNCListBoxItem;
  AText: String; var AAllow: Boolean);
begin
  AAllow := AItem.DataString <> 'separator';   // skip text for separators
end;

Combining owner drawing with item data

Because the draw handlers read straight from the item, you can combine the rating dots with a data-driven text color: store the rating once, draw the dots in OnAfterDrawItem, and tint high-rated rows in OnBeforeDrawItemText — both handlers read the same DataInteger:

procedure TForm1.ListBox1AfterDrawItem(Sender: TObject;
  AGraphics: TTMSFNCGraphics; ARect: TRectF; AItem: TTMSFNCListBoxItem);
var
  I: Integer;
  X: Single;
  Dot: TRectF;
begin
  AGraphics.Fill.Color := gcGold;
  AGraphics.Stroke.Color := gcGoldenrod;
  X := ARect.Right - 4;
  for I := 1 to AItem.DataInteger do
  begin
    X := X - 14;
    Dot := RectF(X, ARect.Top + (ARect.Height - 10) / 2,
      X + 10, ARect.Top + (ARect.Height - 10) / 2 + 10);
    AGraphics.DrawEllipse(Dot);
  end;
end;

procedure TForm1.ListBox1BeforeDrawItemText(Sender: TObject;
  AGraphics: TTMSFNCGraphics; ARect: TRectF; AItem: TTMSFNCListBoxItem;
  AText: String; var AAllow: Boolean);
begin
  // Tint top-rated rows; the same DataInteger drives both handlers.
  if AItem.DataInteger >= 4 then
    AItem.TextColor := gcGoldenrod
  else
    AItem.TextColor := gcBlack;
end;

Keep drawing handlers fast: they run for every visible row on each repaint, so avoid allocations and file access inside them.

Pitfalls

  • OnAfterDrawItem draws over the default content; OnBeforeDrawItem draws under it. Pick the event that matches whether your decoration should sit on top.
  • Drawing handlers fire on every repaint and for every visible item — keep them allocation-free.
  • Construct colors with the graphics color constants (gcGold, gcGoldenrod, …) so they render identically on every platform.
  • TTMSFNCListBoxOnBeforeDrawItem, OnAfterDrawItem, OnBeforeDrawItemText, OnAfterDrawItemText, OnBeforeDrawItemIcon, OnAfterDrawItemIcon

See also

  • Items — the data fields the draw handlers read
  • Appearance — property-based styling that avoids owner drawing
  • Events — the full event catalog