【问题标题】:Can Delphi themed toolbars have dividers that are centred between their tool buttons?Delphi 主题工具栏是否可以在其工具按钮之间具有居中的分隔符?
【发布时间】:2012-09-20 15:47:15
【问题描述】:

我注意到 Delphi 工具栏有一个相当烦人的奇怪之处。我有一个TToolbar,它有逻辑的图标组。为了使分组脱颖而出,我想使用分隔按钮,样式为tbsDivider

当我这样做时,它看起来像这样:

注意每个图标之间的双垂直线。右手是在分隔工具按钮的中间。左边的在分隔工具按钮的左边缘。

所以,我可以切换到 tbsSeparator 删除中间线:

但我并不热衷于此,因为我希望分隔线位于中间。

我查看了我的真实应用的一个古老版本,发现它有居中的分隔符。当 Windows 主题被禁用时,这似乎是可能的。以下是 tbsDivider 和删除应用程序清单后的样子:

这就是我正在寻找的布局。当主题活跃时,这是否可以实现?

我确实在 Embarcadero 论坛上找到了有关该问题的讨论,但没有有用的见解:https://forums.embarcadero.com/message.jspa?messageID=467842

为了完整起见,这里是 .dfm 文件的相关摘录

object ToolButton1: TToolButton
  Left = 0
  Top = 0
  ImageIndex = 0
end
object ToolButton2: TToolButton
  Left = 23
  Top = 0
  Width = 16
  ImageIndex = 1
  Style = tbsDivider
end
object ToolButton3: TToolButton
  Left = 39
  Top = 0
  ImageIndex = 1
end
object ToolButton4: TToolButton
  Left = 62
  Top = 0
  Width = 16
  ImageIndex = 2
  Style = tbsDivider
end
object ToolButton5: TToolButton
  Left = 78
  Top = 0
  ImageIndex = 2
end

【问题讨论】:

  • 我可以确认这个问题也出现在我的机器上(Windows 7 Home Premium 64 位,Delphi 2009,主题已启用)。问题(如果对每个人都不是很明显)是最左边的线不应该在那里。分隔符应该完全空白,并且分隔符中间应该有一行。
  • 你知道是VCL还是Windows问题?
  • 这既是一个 MS 通用控件问题,也是一个 VCL 缺陷。

标签: windows delphi winapi


【解决方案1】:

对我来说,这看起来像是 VCL 中的一个缺陷。 tbsDivider 样式不映射到 Win32 中的等效样式。具有该样式的工具按钮在 VCL 中的实现方式与 tbsSeparator 样式相同,但使用自定义绘制方法。这是从TToolButton.Paint中提取的:

if Style = tbsDivider then
  with Canvas do
  begin
    R := Rect(Width div 2 - 1, 0, Width, Height);
    if StyleServices.Enabled then
    begin
      Details := StyleServices.GetElementDetails(ttbSeparatorNormal);
      StyleServices.DrawElement(Handle, Details, R);
    end
    else
      DrawEdge(Handle, R, EDGE_ETCHED, BF_LEFT)
  end;

在 v6 之前的 comctl32 的旧时代,tbsSeparator 样式映射到 Win32 TBSTYLE_SEP 样式工具按钮。在 v6 之前的 comctl32 中,它只是呈现为没有垂直线的空间。 VCL 设计人员显然想做得更多,并在上面的自定义绘画中添加了tbsDivider

快进到 v6 comctl32。现在公共控件在所有TBSTYLE_SEP 分隔符的左侧边缘绘制一条垂直线。所以上面的代码只是在分隔符的中间多加了一条垂直线。

我们可以尝试通过像这样修改代码来摆脱tbsDivider的左侧垂直线:

if Style = tbsDivider then
  with Canvas do
  begin
    if StyleServices.Enabled then
    begin
      //re-paint the background to remove the vertical line drawn
      //for the standard separator button
      R := Rect(0, 0, Width, Height);
      StyleServices.DrawParentBackground(FToolBar.Handle, Handle, nil, False, R);
    end;

    R := Rect(Width div 2 - 1, 0, Width, Height);
    if StyleServices.Enabled then
    begin
      Details := StyleServices.GetElementDetails(ttbSeparatorNormal);
      StyleServices.DrawElement(Handle, Details, R);
    end
    else
      DrawEdge(Handle, R, EDGE_ETCHED, BF_LEFT)
  end;

但是,这行不通,因为在绘制左侧线然后涂上时会出现很多闪烁。

我怀疑 VCL 设计者只是在过渡到 v6 comctl32 时错过了这个神秘的细节。

我会在适当的时候提交一份 QC 报告。

【讨论】:

  • +1 我想微软沿着最左边画一条线的理由是分隔按钮非常薄,比如一两个像素宽?有趣的是MSDN 没有提到这条线。
  • @AndreasRejbrand 遗憾的是我的修复不起作用。在我们绘制左侧线时闪烁,然后将其擦除。所以唯一的办法就是以某种方式压制左手线........
  • 您是否尝试过创建带有工具栏的普通 Win32 应用程序(不使用 VCL)?我只是好奇那会是什么样子。
  • @Andreas 我没有,但从 VCL 代码中可以清楚地看到它的样子。
  • 左边 1 px 的线?但是,如果分隔按钮的宽度 > 1,这看起来会很糟糕,而且虽然常规工具栏在过去几年中有些“弃用”,但我很难相信标准的 Windows 控件存在这样的缺陷。
【解决方案2】:

当工具栏具有平面样式时,本机控件为分隔按钮绘制垂直线。因此,如果您删除平面样式,您将只剩下 VCL 的 divider 行。当应用程序为主题时,您可以安全地删除样式,主题工具栏按钮不考虑平面样式(为什么工具栏分隔符,我不知道)。但是,当主题被禁用时,将再次出现两行。在这种情况下,保留分隔符而不是分隔符似乎是更好的选择。

可能会猜测取消设置 Flat 属性会产生任何影响,因为 documentation 状态。但是TToolBar.CreateParams 在启用StyleServices 时无条件启用它。因此需要 API 调用;

procedure TForm1.FormCreate(Sender: TObject);
var
  TbStyle: DWORD;
begin
  if StyleServices.Enabled then begin
    TbStyle := SendMessage(ToolBar1.Handle, TB_GETSTYLE, 0, 0);
    SendMessage(Toolbar1.Handle, TB_SETSTYLE, 0, TbStyle and not TBSTYLE_FLAT);
  end;
end;


这消除了部分问题,剩下的部分是分隔线不在两个按钮之间的中心。 VCL 的问题是,它不想自己画线。所以它调用主题 api 将分隔线绘制到分隔符的左侧。为了规避,VCL 将分隔符矩形的右半部分传递给 api,并且该行在中间得到 about。我不知道是否有任何方法可以准确地告诉主题 api 在哪里绘制它,我怀疑是否存在。

【讨论】:

    【解决方案3】:

    如前所述,由于操作系统的变化,您无法使用 TToolBar 执行此操作。

    但是,通过使用 ActionToolBars,您可以实现您想要实现的目标 - 即使使用当前版本的 Delphi (XE6) 和 Windows 8.1/2012 R2 主题。步骤如下:

    1. 创建操作管理器
    2. 添加尽可能多的操作
    3. 添加一个 ImageList(以及另一个可选的禁用菜单项)
    4. 将您的每个操作与图像相关联
    5. 向表单添加 ActionToolBar
    6. 将您希望在工具栏上显示的每个操作拖放到工具栏上
    7. 将工具栏上的单个操作设置为 ShowCaption := False(如果需要)
    8. 拖放“拖动以创建分隔符”(从 ActionManager 编辑器)框以添加分隔符
    9. 将 ActionClientItems(当您将操作拖放到 ActionTooLBar 上时创建)拖放到您想要的位置。

    最终结果 - 一个漂亮的应用程序,图标之间有分隔线。

    【讨论】:

    • +1 感谢您花时间添加这个有用且有帮助的答案
    • 我们在这里互相帮助:)
    • 这个工具栏是如何实现的?是自定义 VCL 控件吗?
    • TActionToolBar 与 TActionManager 一起位于“附加”下。
    • 我手头没有源。我想知道它是由 VCL 包装的系统工具栏,还是用 VCL 代码实现的完全自定义控件。
    猜你喜欢
    • 1970-01-01
    • 2015-07-10
    • 1970-01-01
    • 2016-07-02
    • 1970-01-01
    • 2020-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多