Delphi主题工具栏可以在工具按钮之间居中使用分隔线吗?

时间:2012-09-28 18:28:19

标签: windows delphi winapi

我注意到Delphi工具栏有些令人讨厌的怪异。我有一个TToolbar具有逻辑图标组。为了使分组脱颖而出,我想使用样式为tbsDivider的分隔符按钮。

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

enter image description here

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

所以,我可以切换到删除中间行的tbsSeparator

enter image description here

但是我并不热衷于此,因为我希望分频器处于中间位置。

我查看了我的真实应用程序的古老版本,发现它有居中的分隔符。当禁用Windows主题时,似乎可以实现。以下是使用tbsDivider并删除应用程序清单的情况:

enter image description here

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

我确实在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

3 个答案:

答案 0 :(得分: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样式映射到Win32 TBSTYLE_SEP样式工具按钮。在pre-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时错过了这个神秘的细节。

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

答案 1 :(得分:3)

当工具栏具有平面样式时,本机控件会为分隔符按钮绘制垂直线。因此,如果您删除平面样式,您将独自使用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的确切位置,我怀疑是否存在。

答案 2 :(得分: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时创建)拖放到您希望的位置。
  10. 最终结果 - 一个漂亮的应用程序,在你的图标旁边有分隔符。

    enter image description here