开发的过程中,经常要使用到 TListview,为了界面美观,需要自绘 TListview,下面就 TListview 自绘的三个方面,总结一下。
1、显示千万条数据记录;
2、表格自绘;
3、表头自绘;
1、显示千万条数据记录;
A:OwnerData 设为 True, OwnerDraw 暂时设为 False, 不进行自绘,由系统来完成绘制。
我们的重点放在显示千万条记录上面;
B:数据来源,数据库查询:
ADOQuery1.SQL.Clear;
ADOQuery1.Close;
ADOQuery1.SQL.Text := 'select ROW_NUMBER() over(order by ID) as RowNum, * from TableName';
ADOQuery1.Open;
这里:
ID :数据库表中自增长字段(换成你要查询的数据库表中的自增长字段);
TableName :数据库表名;
* :数据库中所有字段名称;
实际使用中,最好不要用 * ,需要显示哪些字段就填入字段名称 ,效率会大大提高。
注:
ID 是必须的,为最后一列,不显示的一列,用于记录标识;当选中/双击时,根据此列值,可找到数据库对应记录。
无论你的查询语句如何写,select ROW_NUMBER() over(order by ID) as RowNum 是必不可少的。
ADOQuery1 查询结束后,添加代码:
lvData.Items.Count := ADOQuery1.RecordCount;
将触发 OnData 事件:
C:双击 OnData 事件,进入代码界面:
假设 ADOQuery1 查询结果中包含千万条记录
显示数据前,列名先准备好。动态创建也可以。
注:
列名一般使用字段名,但字段名一般为英文。实际使用肯定是需要中文的。
我们可以使用字段的说明属性,来当作列名。
建立字段时,顺手给字段的列属性中的说明属性加个中文说明,
程序中获取字段对应的说明属性来当作列名,就OK了。
procedure TfrmListViewData.lvDataData(Sender: TObject; Item: TListItem);
var
I: Integer;
begin
ADOQuery1.RecNo := Item.Index + 1;
Item.Caption := ADOQuery1.Fields[1].AsString;
for I := 2 to ADOQuery1.Fields.Count - 1 do
begin
if ADOQuery1.Fields[I].DataType = ftBlob then
Item.SubItems.Add('')
else
Item.SubItems.Add(ADOQuery1.Fields[I].AsString);
end;
end;
OK,大功告成。上面代码只是示例,细节根据自己需要,自己把控。
2、表格自绘;
这一步比较容易;
A:设置 OwnerDraw 为 True ,就会触发自绘事件;
B:双击 OnCustomDrawItem 事件,在事件中输入自绘代码。
有画布 Canvas,TListview(Sender).Canvas,有 Item ,想绘制成啥样,就绘制成啥样。
划过/选中/动态,等等效果都可以在这里实现。
3、表头自绘;
想绘制表头,就需要知道表头句柄。
想得到表头句柄,就需要拦截 ListView 的 WMParentNotify 消息,因为表头的创建是由它完成的。
类定义:
type
TListView = Class(ComCtrls.TListView)
private
FHeaderHandle: Integer;
{ 表头的回调过程 }
FOldHeaderProc, FHeaderInstance: Pointer;
protected
procedure WMParentNotify(var Message: TWMParentNotify); message WM_PARENTNOTIFY;
procedure HeaderWndProc(var Message: TMessage);
end;
实现:
procedure TListView.WMParentNotify(var Message: TWMParentNotify);
begin
if (Message.Event = WM_CREATE) then
begin
FHeaderHandle := message.ChildWnd; // 获取到表头句柄
FOldHeaderProc := Pointer(GetWindowLong(FHeaderHandle, GWL_WNDPROC)); // 保存好表头原有的回调过程
SetWindowLong(FHeaderHandle, GWL_WNDPROC, Integer(FHeaderInstance)); // 指向表头新的回调过程,用于自绘
end;
inherited;
end;
注:
这里还有一些细节,比如:
WINXP、WIN7、WIN10下创建表头句柄是有点差别的;
创建时,需要经过几次才会真正得到句柄;
上面的代码只是核心代码,细化没有写出,自己动手试试,也就能理解了。
事前要设置一下表头新的回调过程:
FHeaderInstance := MakeObjectInstance(HeaderWndProc);
{ 自绘表头 }
procedure TListView.HeaderWndProc(var Message: TMessage);
begin
with Message do
begin
{ 自绘表头 }
if msg = WM_PAINT then
begin
{ 有句柄 FHeaderHandle,就可以得到画布 Canvas ,有画布就可以自绘了 }
end;
end;
end;
注:
还有其它消息可以处理:
WM_SETCURSOR :当鼠标移动到表头分割线时;
WM_NCHITTEST :调整表头宽度;
HDM_LAYOUT :调整表头高度;
等等,绘制表头悉数掌控。
OK,绘制完毕。
效果图:
滚动条的绘制比较好玩。以后在写吧。不在 TListview 的自绘范围内。
它是一个通用的绘制类,可以给任意控件来用(除了IE控件)。