You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi 10.2下VCL自定义TCustomListbox迁移FMX的绘图问题

从VCL自定义ListBox转FMX的最佳实践

刚从VCL转FMX确实会遇到不少API逻辑差异的问题,我之前也踩过类似的坑,完全没必要去重写PaintChildren(那会把滚动、布局这些底层逻辑全丢给你,工作量陡增),FMX其实有更贴合VCL DrawItem逻辑的方案,给你两个最实用的方向:

1. 直接用TListBox的OnDrawItem事件(最接近VCL的DrawItem)

FMX的TListBox内置了OnDrawItem事件,专门用来自定义每个列表项的绘制,ListBox会自动帮你处理滚动、选中状态、布局这些底层细节,你只需要专注于每个项的绘制逻辑,和VCL的DrawItem用法几乎一致。

举个贴合你需求的代码示例(实现按索引缩进+自定义文本颜色):

procedure TYourForm.YourListBoxDrawItem(Sender: TCustomListBox; Index: Integer;
  const ARect: TRectF; State: TListItemDrawStates);
var
  LItemText: string;
  LIndentOffset: Single;
  LCanvas: TCanvas;
begin
  LCanvas := Sender.Canvas;
  LItemText := Sender.Items[Index];
  
  // 1. 处理背景绘制(根据选中状态区分)
  if TListItemDrawState.Selected in State then
    LCanvas.Fill.Color := TAlphaColors.LightSteelBlue
  else
    LCanvas.Fill.Color := TAlphaColors.White;
  LCanvas.FillRect(ARect, 0, 0, [], 1); // 填充Item区域

  // 2. 设置缩进(比如每级索引缩进20像素)
  LIndentOffset := Index * 20;

  // 3. 设置文本颜色(比如奇数索引用红色,偶数用黑色)
  if Index mod 2 = 0 then
    LCanvas.Fill.Color := TAlphaColors.Black
  else
    LCanvas.Fill.Color := TAlphaColors.DarkRed;

  // 4. 绘制带缩进的文本
  LCanvas.FillText(
    RectF(ARect.Left + LIndentOffset, ARect.Top, ARect.Right, ARect.Bottom),
    LItemText,
    False, // 是否换行
    1, // 透明度
    [], // 字间距
    TTextAlign.Leading, // 水平对齐
    TTextAlign.Center // 垂直居中
  );
end;

只需要给你的ListBox绑定这个事件,就能实现和VCL DrawItem一样的效果,完全不用管滚动、Item复用这些细节。

2. 封装成自定义ListBox组件(适合复用场景)

如果这个自定义逻辑需要在多个地方复用,你可以创建一个继承自TListBox的自定义组件,重写它的DoDrawItem方法,把绘制逻辑封装进去:

type
  TMyCustomListBox = class(TListBox)
  protected
    procedure DoDrawItem(Index: Integer; const ARect: TRectF; State: TListItemDrawStates); override;
  end;

procedure TMyCustomListBox.DoDrawItem(Index: Integer; const ARect: TRectF; State: TListItemDrawStates);
var
  LItemText: string;
  LIndentOffset: Single;
  LCanvas: TCanvas;
begin
  LCanvas := Self.Canvas;
  LItemText := Self.Items[Index];

  // 这里写和上面事件里一样的绘制逻辑
  // 处理背景、缩进、文本颜色...

  // 如果需要保留FMX默认的部分绘制逻辑,可以调用inherited;
  // inherited;
end;

这样你在设计时直接拖这个自定义组件就能用,不用每次都绑定事件。

为什么不用PaintChildren?

PaintChildren是用来绘制所有子控件的,FMX的ListBox内部已经用它来管理Item的绘制和布局了,你重写它等于要自己实现Item的定位、滚动时的重绘、选中状态的维护等等,工作量巨大,完全是舍近求远。

内容的提问来源于stack exchange,提问作者maaartinus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:05:40