【问题标题】:Make owner-drawn TPageControl tabs look nicer, like without owner-draw使所有者绘制的 TPageControl 选项卡看起来更好,就像没有所有者绘制一样
【发布时间】:2013-08-16 21:59:07
【问题描述】:

我使用 Delphi7,PageControl 与所有者绘制。正如我在非所有者绘制的 PageControls 上看到的那样,我无法获得如此简单和漂亮的选项卡外观。有什么不好: 使用自绘时,我无法在“整个”选项卡标题区域上绘制,OS 会在选项卡标题周围绘制 1-2 像素的小框。

1)Delphi不是owner-draw,看起来也可以(用XPMan):

2) Delphi owner-draw,你看到不是整个标签页头都可以着色(使用 XPMan):

我在这里用蓝色绘制当前标签,用白色绘制其他标签。仅举例。 代码:

procedure TForm1.PageControl1DrawTab(Control: TCustomTabControl;
  TabIndex: Integer; const Rect: TRect; Active: Boolean);
var
  c: TCanvas;
begin
  c:= (Control as TPageControl).Canvas;
  if Active then
    c.Brush.Color:= clBlue
  else
    c.Brush.Color:= clWhite;
  c.FillRect(Rect);    
end;

2b) 真实应用中的 Delphi 所有者绘制(使用 XPMan):

为什么我需要使用自绘?简单的。在选项卡标题上绘制 X 按钮,使用自定义颜色绘制上线,从图像列表绘制图标。

我正在寻找一种方法来绘制选项卡标题的整个矩形,而不是减少 PageControl 所有者绘制事件的矩形。我试图增加所有者绘制事件给出的矩形,但这无济于事,操作系统无论如何都会在标签标题周围重新绘制这个 1-2 像素的薄框架。

【问题讨论】:

  • 您可以首先将您的 Delphi 版本更新为不是为 Windows 95 设计的版本。另外,如果您自己绘制标签,并且不包含正在绘制的代码他们,我们不可能建议如何使它画得更好。
  • 第一张图片中的选项卡控件看起来很像普通的选项卡控件,没有迹象表明它是所有者绘制的。包含“xpman”以使用并将“OwnerDraw”设置为 false,您的选项卡控件应该看起来完全一样(在 W7 和可能的 Vista 上)。
  • 有人能解释一下新的 Delphi 版本有什么帮助吗?它是“样式”的意思吗?程序员可以定义自己的风格吗?
  • @Ken - 我认为您的意思是“支持主题化”的“vcl 样式”。我不知道它是否允许自定义主题,如果不允许,它对 OP 没有帮助。此外,对操作系统主题的内置支持与这个问题无关,这不是 OP 想要的。至于代码,你不可能不在事件处理程序中调用inherited,DrawTab(准确地说是OnDrawTab)不是消息处理程序。无论如何,不​​幸的是,即使你完全超越了WM|CN_DRAWITEM,而不是调用inherited,3D 边框仍然由操作系统绘制——显然是在绘制周期的后期。
  • @TLama - 我也是这么理解的。我认为前一张照片只是对 OP 的一些误解。但亚历克斯必须参与澄清,要么接受答案,要么尝试更好地解释。

标签: delphi delphi-7 tpagecontrol


【解决方案1】:

所有者绘制的原生“选项卡控件”的选项卡(在 VCL 中为TPageControl,虽然它的上层被恰当地命名为TCustomTabControl - 这是任何人都猜想为什么创意命名..),预计将由其绘制处理WM_DRAWITEM消息时的父控件,如documented here。

VCL 通过将消息更改为CN_DRAWITEM 消息并将其发送到控件本身来承担父级的负担。在这个过程中,VCL 没有进一步的干预。它只是调用OnDrawTab 消息处理程序,如果它是由用户代码分配的,并传递适当的参数。

因此,不是 VCL 在标签周围绘制边框,而是操作系统本身。此外,显然,它不会在处理WM_DRAWITEM 消息期间执行此操作,而是在稍后的绘制过程中执行此操作。您可以通过在页面控件的父级上放置一个空的WM_DRAWITEM 处理程序来验证这一点。结果是,无论我们在事件处理程序中绘制什么,它稍后都会被操作系统获取边框。

我们可能会尝试阻止操作系统绘制的内容生效,毕竟我们拥有设备上下文(如 Canvas.Handle)。不幸的是,这条路线也是一条死胡同,因为在事件处理程序返回后,VCL 会恢复设备上下文的状态。

那么,我们唯一的办法就是完全放弃处理OnDrawTab 事件,并根据CN_DRAWITEM 消息采取行动。下面的示例代码使用了一个 interposer 类,但您可以以任何您喜欢的方式对控件进行子类化。确保设置了OwnerDrawn。

type
  TPageControl = class(comctrls.TPageControl)
  protected
    procedure CNDrawitem(var Message: TWMDrawItem); message CN_DRAWITEM;
  end;

  TForm1 = class(TForm)
    ..

..

procedure TPageControl.CNDrawitem(var Message: TWMDrawItem);
var
  Color: TColor;
  Rect: TRect;
  Rgn: HRGN;
begin
  Color := 0;  
  // draw in different colors so we see where we've drawn
  case Message.DrawItemStruct.itemID of
    0: Color := $D0C0BF;
    1: Color := $D0C0DF;
    2: Color := $D0C0FF;
  end;
  SetDCBrushColor(Message.DrawItemStruct.hDC, Color);

  // we don't want to get clipped in the passed rectangle
  SelectClipRgn(Message.DrawItemStruct.hDC, 0);

  // magic numbers corresponding to where the OS draw the borders
  Rect := Message.DrawItemStruct.rcItem;
  if Bool(Message.DrawItemStruct.itemState and ODS_SELECTED) then begin
    Inc(Rect.Left, 2);
//    Inc(Rect.Top, 1);
    Dec(Rect.Right, 2);
    Dec(Rect.Bottom, 3);
  end else begin
    Dec(Rect.Left, 2);
    Dec(Rect.Top, 2);
    Inc(Rect.Right, 2);
    Inc(Rect.Bottom);
  end;
  FillRect(Message.DrawItemStruct.hDC, Rect,
      GetStockObject(DC_BRUSH));

  // just some indication for the active tab
  SetROP2(Message.DrawItemStruct.hDC, R2_NOTXORPEN);
  if Bool(Message.DrawItemStruct.itemState and ODS_SELECTED) then
    Ellipse(Message.DrawItemStruct.hDC, Rect.Left + 4, Rect.Top + 4,
      Rect.Left + 12, Rect.Top + 12);

  // we want to clip the DC so that the borders to be drawn are out of region
  Rgn := CreateRectRgn(0, 0, 0, 0);
  SelectClipRgn(Message.DrawItemStruct.hDC, Rgn);
  DeleteObject(Rgn);

  Message.Result := 1;
  inherited;
end;


这里是上面的样子:

【讨论】:

  • 不错!不过,问题是,这样的黑客绘图是否有意义。您可以隐藏选项卡并用于页面切换,例如放置在页面控件顶部的图像。
  • @TLama - 剪辑代码可以放在继承的处理程序周围,也许它看起来不那么hack-ish。无论如何,确实如此!
【解决方案2】:

据我所知,您只是希望对您的应用程序进行主题绘制。在 Delphi 7 中,您需要做的就是添加一个应用程序清单,指定使用 comctl32 版本 6。这样做的简单方法是将 TXPManifest 组件添加到您的表单或数据模块之一,或者只是在您的项目中引用 XPMan 单元。

由于您希望系统绘制您的页面控件,因此您不能进行任何所有者绘制。

【讨论】:

  • 只需将XPMan 添加到项目中任何位置的使用列表中就足够了。根本不需要将TXPManifest 组件放在任何地方。
  • @Jeroen 或者实际上只是链接资源
  • 一些非常好的 Delphi 7 相关清单答案在这里:stackoverflow.com/questions/4757533/…
  • @David,不,不是我想要的。我已经用过 XPMan。我更正了第 1 个帖子。
  • 好的,好的。您最初想要的所谓 .net 外观只是 Vista+ 外观。这就是答案。但显然你的问题现在有些不同了。
猜你喜欢
  • 2011-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多