【问题标题】:Delphi TListview OwnerDraw SubItems - change default font (it's bold somehow after you Draw on the canvas)Delphi TListview OwnerDraw SubItems - 更改默认字体(在画布上绘制后以某种方式变粗)
【发布时间】:2012-11-09 06:16:29
【问题描述】:

如果您将 ownerdraw 与 TListView 一起使用,则子项默认情况下都是 BOLD 字体样式,即使 listview font.style 设置为 [],对于自定义绘制的子项之后的所有子项。

我发现的一种解决方法是强制在 CustomDrawSubItem 事件中设置样式:

ListView2.Canvas.Font.Style := [fsItalic];
ListView2.Canvas.Font.Style := [];

(除非默认样式设置为 [] 以外的其他内容,否则使用 [] 的简单调用将不起作用,因为 SetStyle 调用不认为样式已更改)

然而,这是一个丑陋的修复,需要额外的处理时间。有没有更好的解决方案?

演示项目:http://www.mediafire.com/?v8bsdpvpfqy47vn

【问题讨论】:

  • 我只是好奇它所涉及的额外处理时间有多少时间?虽然我同意你的看法,但这很丑陋,但我认为处理时间不是这种情况下的一个因素。
  • 在普通笔记本电脑上似乎每个子项大约需要 3 微秒,因此显示 10 个子项和 30 个项的 LV 需要重绘时需要 0.3 毫秒的额外处理时间。显然是的,这没什么,但它仍然是一个丑陋的修复,我想知道这个错误的根源(我假设它是,因为 JEDI 版本没有这个问题)。
  • 在我看来像一个错误。如果你 touch 画布,它就会一团糟。所有其他绘图都是一团糟,例如将鼠标悬停在任何项目上。另一方面,它(错误)在 XE 和 XE2 中的功能相同。我现在没有其他 Delphi 可以检查。
  • 这是另一个问题,我用 SetBkMode(TListView(Sender).Canvas.Handle, TRANSPARENT); ListView_SetTextBkColor(TListView(Sender).Handle, CLR_NONE); ListView_SetBKColor(TListView(Sender).Handle, CLR_NONE);
  • @hikari,你真是个天才!感谢 SetBkMode(...TRANSPARENT)... 这救了我!

标签: delphi ownerdrawn tlistview


【解决方案1】:

我同意 jachguate 的 comment 的观点,即 VCL 控件似乎存在问题; TCustomListView.CNNotify 可能存在设计问题。但要遵循那里的逻辑并不容易。

一种解决方案是在 DefaultDraw 为 true 时强制更改控件的画布,以便 VCL 在自定义绘图通知返回之前再次创建并选择控件的字体到传递的 DC。示例:

procedure TForm1.LVCustomDrawSubItem(Sender: TCustomListView; Item: TListItem;
  SubItem: Integer; State: TCustomDrawState; var DefaultDraw: Boolean);
Var R: TRect;
    bmp: TBitmap;
    x: Integer;
begin
  DefaultDraw := True;

  if SubItem = 1 then begin
    DefaultDraw := False;
    ...
      ...       
      Sender.Canvas.Draw(R.Left - 2, R.Top, Bmp);
      Bmp.Free;
    end;
  end;

  if DefaultDraw then
    Sender.Canvas.Brush.Color := ColorToRGB(clWindow);     // <--
end;


我希望尽可能避免使用控件的画布。您可以为您的情况使用临时 DC,这也避免了问题中 cmets 中提到的黑色背景问题。

uses
  commctrl;

  ...

procedure TForm1.LVCustomDrawSubItem(Sender: TCustomListView; Item: TListItem;
  SubItem: Integer; State: TCustomDrawState; var DefaultDraw: Boolean);
Var R: TRect;
    bmp: TBitmap;
    x: Integer;
    DC: HDC;
begin
  DefaultDraw := True;

  if SubItem = 1 then begin
    DefaultDraw := False;
    ...
      ...

      DC := GetDC(Sender.Handle);
      ImageList_Draw(TypeImages.Handle, 0, DC, R.Left - 2, R.Top, ILD_NORMAL);
      ReleaseDC(Sender.Handle, DC);

      Bmp.Free;
    end;
  end;
end;

【讨论】:

    【解决方案2】:

    我没有遇到您描述的确切情况,但我遇到了类似的问题。当我使用所有者绘制的TListView 和分配OnAdvancedCustomDrawSubItem 事件以在每个子项的基础上更改Canvas.Font 时,我发现在我为一个子项更改Sender.Canvas.Font 后,后续子项将被绘制为即使我为他们更改了Sender.Canvas.Font,也会出现错误的设置。我的解决方法是在OnAdvancedCustomDrawSubItem 事件处理程序的末尾手动调用Sender.Canvas.Font.OnChange 事件处理程序。这表示TListView 向Windows 报告CDRF_NEWFONT,然后一切都会正确绘制。就好像Sender.Canvas.Font.OnChange 事件没有正确连接,而TListView 正在被所有者绘制,因此它不会检测到字体更改,因此不会正确地向Windows 报告。

    【讨论】:

    • 这似乎比更改画布画笔等更快,在我的测试应用程序中为 0 微秒,而其他方法涉及 1-3 微秒。 (使用 queryperformancecounter 等进行测试)
    • Canvas.Font 不使用其Sender 事件的Sender 参数,因此它可以是您想要的任何东西,甚至是nil。但是,为了更好地衡量,我建议传递 Canvas.Font 本身,以防将来发生变化:Sender.Canvas.Font.OnChange(Sender.Canvas.Font);
    • @hikari - 你是否超时不使用画布,即 GetDC/ReleaseDC?我没有,但是查看代码路径,如果它需要更多时间然后重置画布字体,我会感到惊讶。作为奖励,您还可以摆脱与位图相关的代码。 :)
    • 两种方法都计时,都显示 0 微秒,但我会切换到你的代码,看起来比使用位图更干净。 (但仍然需要字体修复)。
    • 实际上由于几个绘图子项,我的完整应用程序中的代码存在问题,释放 DC 导致图像消失。这可以很好地代替 ImageList_Draw(TypeImages.Handle, 0, Sender.Canvas.Handle, R.Left - 2, R.Top, ILD_NORMAL);
    猜你喜欢
    • 2011-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多