我正在尝试按照NGLN's answer to another SO question的建议在Delphi TListView
中自定义绘制进度条。除了使用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
答案 0 :(得分:13)
对于有缺陷的行为,这是一个解决方法,而不是如果VCL中存在错误,以及一些想法,那么就是对问题的回答。
解决方法是在执行自定义绘图后,将项目绘制cyle的公共控件指定的设备上下文的背景模式设置为透明:
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, Item.Index, SubItem, LVIR_BOUNDS, @R);
Sender.Canvas.MoveTo(R.Left, R.Top);
Sender.Canvas.LineTo(R.Right-1, R.Bottom-1);
SetBkMode(Sender.Canvas.Handle, TRANSPARENT); // <- will effect the next [sub]item
end;
在[子]项目绘画循环中,绘画总是以自上而下的方式完成,具有较低索引的项目会在具有较高索引的项目之前发送NM_CUSTOMDRAW
通知索引。当鼠标从一行移动到另一行时,需要重新绘制两行 - 一行失去 hot 状态,一行获得它。看起来,当自定义绘图生效时,绘制失去热状态的行会使DC处于不合需要的状态。向上移动鼠标时不会出现问题,因为该项目最后被绘制。
自定义绘图ListView和TreeView控件与自定义绘制其他控件不同,有些复杂(请参阅:Custom Draw With List-View and Tree-View Controls)。但是你可以完全控制整个过程。 VCL的'comctrls.pas'中NM_CUSTOMDRAW
TCustomListView.CNNotify
案例中的代码同样复杂。但是,尽管提供了一堆自定义绘图处理程序(其中一半是高级),但您无法控制VCL的功能。例如,您无法返回您想要的CDRF_xxx
,或者您无法设置所需的clrTextBk
。我的偏见是,在Delphi列表视图控件中存在一个错误/设计问题,但我找到一个解决方法没有什么比直觉更具体。
答案 1 :(得分:0)
我对文本位置的黑色矩形没有线索,但缺少热跟踪是由于代码中的DefaultDraw := False;
。 OnCustomDrawSubItem
仅针对subitem <> 0
调用,因此第一列绘制为默认值,而第二列使用您的代码。可以使用OnCustomDrawItem
自定义绘制第一列。