使用自定义绘图时,Delphi列表视图控件中是否存在错误?

时间:2011-11-19 08:56:29

标签: delphi delphi-xe2

QC#101189

我正在尝试按照NGLN's answer to another SO question的建议在Delphi TListView中自定义绘制进度条。除了使用Vista中引入的新浏览器主题绘制时与热跟踪的交互之外,这种方法很好。

热跟踪绘画和Delphi自定义绘图事件似乎相互干扰。例如,我看到的那种输出看起来像这样:

enter image description here

第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

2 个答案:

答案 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自定义绘制第一列。