8

我注意到 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
4

3 回答 3

4

对我来说,这看起来像是 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样式映射到 Win32TBSTYLE_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 时错过了这个神秘的细节。

我会在适当的时候提交一份质量控制报告。

于 2012-09-28T19:06:28.160 回答
3

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

正如文档所述,人们会猜测取消设置该Flat属性会产生任何影响。但是在启用时无条件启用它。因此需要 API 调用;TToolBar.CreateParamsStyleServices

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,而该行大约在中间。我不知道是否有任何方法可以准确地告诉主题 api 在哪里绘制它,我怀疑是否存在。

于 2012-09-29T01:09:24.073 回答
3

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

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

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

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

在此处输入图像描述

于 2014-08-22T19:13:41.210 回答