我试图自定义在Delphi TListView中绘制一个进度条,如NGLN’s answer to another SO question所示.除了使用Vista中引入的新的资源管理器主题绘制时,与热跟踪的交互作用很好.
热跟踪绘画和Delphi自定义绘图事件似乎互相干扰.例如,我看到的输出类型如下所示:
列1中的文本应该读取项3,但是被删除.它看起来像Delphi包装器中的列表视图控件的错误,但同样可能的是我做错了事情!
虽然我在XE2中一直在开发这个过程,但2010年也是这样,也就是XE.
以下是重现此行为的代码:
Pascal文件
unit Unit1; interface uses Windows,Classes,Controls,Forms,CommCtrl,ComCtrls; type TForm1 = class(TForm) ListView: TListView; procedure FormCreate(Sender: TObject); procedure ListViewCustomDrawSubItem(Sender: TCustomListView; Item: TListItem; SubItem: Integer; State: TCustomDrawState; var DefaultDraw: Boolean); end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin ListView.RowSelect := True; ListView.Items.Add.Caption := 'Item 1'; ListView.Items.Add.Caption := 'Item 2'; ListView.Items.Add.Caption := 'Item 3'; end; procedure TForm1.ListViewCustomDrawSubItem(Sender: TCustomListView; Item: TListItem; SubItem: Integer; State: TCustomDrawState; var DefaultDraw: Boolean); var R: TRect; begin DefaultDraw := False; ListView_GetSubItemRect(Sender.Handle,Item.Index,SubItem,LVIR_BOUNDS,@R); Sender.Canvas.MoveTo(R.Left,R.Top); Sender.Canvas.LineTo(R.Right-1,R.Bottom-1); end; end.
表单文件
object Form1: TForm1 Caption = 'Custom Draw List View Bug' ClientHeight = 290 ClientWidth = 554 OnCreate = FormCreate object ListView: TListView Align = alClient Columns = < item Caption = 'Column 1' Width = 250 end item Caption = 'Column 2' Width = 250 end> ViewStyle = vsReport OnCustomDrawSubItem = ListViewCustomDrawSubItem end end
解决方法
解决办法是在进行自定义绘图之后,将项目绘画的公共控件分配给设备上下文的背景模式设置为透明:
procedure TForm1.ListViewCustomDrawSubItem(Sender: TCustomListView; Item: TListItem; SubItem: Integer; State: TCustomDrawState; var DefaultDraw: Boolean); var R: TRect; begin if not [CustomDrawing] then // <- If we're not gonna do anything do not Exit; // fiddle with the DC in any way DefaultDraw := False; ListView_GetSubItemRect(Sender.Handle,R.Bottom-1); SetBkMode(Sender.Canvas.Handle,TRANSPARENT); // <- will effect the next [sub]item end;
在[子]项目绘画循环中,绘画始终以自顶向下的方式完成,具有较低索引的项目在具有较高索引的项目之前发送NM_CUSTOMDRAW通知.当鼠标从一行移动到另一行时,需要重新绘制两行 – 一个放弃热状态,一个获取它.当自定义绘图有效时,绘制掉热状态的行会使DC处于不良状态.当向上移动鼠标时,这不是问题,因为该项目最后被绘制.
自定义绘图ListView和TreeView控件与自定义绘制其他控件不同,有些复杂(参见:Custom Draw With List-View and Tree-View Controls).但是你可以完全控制整个过程. VCL的“comctrls.pas”中的TC_CANOMDRAW案例TCustomListView.CNNotify中的代码同样复杂.但是尽管提供了一堆自定义绘图处理程序(其中一半是高级的),但是您无法控制VCL的工作.例如,您不能返回您想要的CDRF_xxx,或者您无法设置所需的clrTextBk.我有偏见的是,Delphi列表视图控件中存在一个错误/设计问题,但是我没有比找到解决方法更直观的直觉.