【问题标题】:Flat toolbar buttons with Delphi VCL Styles enabled?启用了 Delphi VCL 样式的平面工具栏按钮?
【发布时间】:2013-05-29 03:49:23
【问题描述】:

没有启用 VCL 样式,我的 TActionToolbar(s) 看起来像平面工具栏。但是,如果我启用几乎任何 VCL 样式,突然间所有工具栏按钮看起来都像 3d 按钮。

VCL Style Viewer 应用程序显示具有扁平和类似按钮外观的工具栏按钮:

当我启用 VCL 样式时,如何使我的 TActionToolbar 具有平面工具栏按钮样式而不是看起来像一堆按钮?

【问题讨论】:

    标签: delphi vcl-styles


    【解决方案1】:

    所有与TActionManager 相关的控件使用的绘制方法都由TPlatformDefaultStyleActionBars 类处理,用于绘制控件的类取决于windows 版本,如果启用了vcl 样式等在。在这种情况下,csThemed TActionControlStyle 被选中并使用 Vcl.ThemedActnCtrls 单元中定义的类。

    所以要修改按钮的外观,您需要创建一个TActionBarStyleEx 后代类,然后覆盖Vcl.ThemedActnCtrls 单元中定义的类和方法。幸运的是,这项工作已经在Vcl.PlatformVclStylesActnCtrls 单元中完成,该单元是Vcl Styles Utils 项目的一部分。因此,您只需进行一些小的修改即可获得所需的结果。

    试试这个示例(这是 Vcl.PlatformVclStylesActnCtrls 单元的修改版本)我添加了一些 cmets 以显示必须修改代码的位置。

    unit Vcl.PlatformVclStylesActnCtrls;
    
    interface
    
    uses
       Vcl.ActnMan,
       Vcl.Buttons,
       Vcl.PlatformDefaultStyleActnCtrls;
    
    type
      TPlatformVclStylesStyle = class(TPlatformDefaultStyleActionBars)
      public
        function GetControlClass(ActionBar: TCustomActionBar; AnItem: TActionClientItem): TCustomActionControlClass; override;
        function GetStyleName: string; override;
      end;
    
    var
      PlatformVclStylesStyle: TPlatformVclStylesStyle;
    
    implementation
    
    uses
      Vcl.Menus,
      Winapi.Windows,
      System.SysUtils,
      Vcl.ActnMenus,
      Vcl.ActnCtrls,
      Vcl.ThemedActnCtrls,
      Vcl.Forms,
      Vcl.ListActns,
      Vcl.ActnColorMaps,
      Vcl.Themes,
      Vcl.XPActnCtrls,
      Vcl.StdActnMenus,
      Vcl.Graphics;
    
    type
      TActionControlStyle = (csStandard, csXPStyle, csThemed);
    
      TThemedMenuItemEx = class(Vcl.ThemedActnCtrls.TThemedMenuItem)
      private
        procedure NativeDrawText(DC: HDC; const Text: string; var Rect: TRect; Flags: Longint);
      protected
        procedure DrawText(var Rect: TRect; var Flags: Cardinal; Text: string); override;
      end;
    
      TThemedMenuButtonEx = class(Vcl.ThemedActnCtrls.TThemedMenuButton)
      private
        procedure NativeDrawText(const Text: string; var Rect: TRect; Flags: Longint);
      protected
        procedure DrawText(var ARect: TRect; var Flags: Cardinal;
          Text: string); override;
      end;
    
      TThemedMenuItemHelper = class Helper for TThemedMenuItem
      private
       function GetPaintRect: TRect;
       property PaintRect: TRect read GetPaintRect;
      end;
    
      TThemedButtonControlEx = class(TThemedButtonControl)
      protected
        procedure DrawBackground(var PaintRect: TRect); override;
      end;
    
    
    { TThemedMenuItemHelper }
    function TThemedMenuItemHelper.GetPaintRect: TRect;
    begin
     Result:=Self.FPaintRect;
    end;
    
    function GetActionControlStyle: TActionControlStyle;
    begin
      if TStyleManager.IsCustomStyleActive then
        Result := csThemed
      else
      if TOSVersion.Check(6) then
      begin
        if StyleServices.Theme[teMenu] <> 0 then
          Result := csThemed
        else
          Result := csXPStyle;
      end
      else
      if TOSVersion.Check(5, 1) then
        Result := csXPStyle
      else
        Result := csStandard;
    end;
    
    { TPlatformDefaultStyleActionBarsStyle }
    
    function TPlatformVclStylesStyle.GetControlClass(ActionBar: TCustomActionBar;
      AnItem: TActionClientItem): TCustomActionControlClass;
    begin
      if ActionBar is TCustomActionToolBar then
      begin
        if AnItem.HasItems then
          case GetActionControlStyle of
            csStandard: Result := TStandardDropDownButton;
            csXPStyle: Result := TXPStyleDropDownBtn;
          else
            Result := TThemedDropDownButton;
          end
        else
        if (AnItem.Action is TStaticListAction) or (AnItem.Action is TVirtualListAction) then
          Result := TCustomComboControl
        else
        case GetActionControlStyle of
          csStandard: Result := TStandardButtonControl;
          csXPStyle: Result := TXPStyleButton;
        else
          Result := TThemedButtonControlEx;//this is the class used to draw the buttons of the TActionToolbar
        end
      end
      else
      if ActionBar is TCustomActionMainMenuBar then
        case GetActionControlStyle of
          csStandard: Result := TStandardMenuButton;
          csXPStyle: Result := TXPStyleMenuButton;
        else
          Result := TThemedMenuButtonEx;
        end
      else
      if ActionBar is TCustomizeActionToolBar then
      begin
        with TCustomizeActionToolbar(ActionBar) do
          if not Assigned(RootMenu) or (AnItem.ParentItem <> TCustomizeActionToolBar(RootMenu).AdditionalItem) then
            case GetActionControlStyle of
              csStandard: Result := TStandardMenuItem;
              csXPStyle: Result := TXPStyleMenuItem;
            else
              Result := TThemedMenuItemEx;
            end
          else
          case GetActionControlStyle of
              csStandard: Result := TStandardAddRemoveItem;
              csXPStyle: Result := TXPStyleAddRemoveItem;
          else
              Result := TThemedAddRemoveItem;
          end
      end
      else
      if ActionBar is TCustomActionPopupMenu then
        case GetActionControlStyle of
          csStandard: Result := TStandardMenuItem;
          csXPStyle: Result := TXPStyleMenuItem;
        else
          Result := TThemedMenuItemEx;
        end
      else
      case GetActionControlStyle of
        csStandard: Result := TStandardButtonControl;
        csXPStyle: Result := TXPStyleButton;
      else
        Result := TThemedButtonControl;
      end
    end;
    
    function TPlatformVclStylesStyle.GetStyleName: string;
    begin
      Result := 'Platform VclStyles Style';
    end;
    
    { TThemedMenuItemEx }
    
    procedure TThemedMenuItemEx.NativeDrawText(DC: HDC; const Text: string;
      var Rect: TRect; Flags: Integer);
    const
      MenuStates: array[Boolean] of TThemedMenu = (tmPopupItemDisabled, tmPopupItemNormal);
    var
      LCaption: string;
      LFormats: TTextFormat;
      LColor: TColor;
      LDetails: TThemedElementDetails;
      LNativeStyle : TCustomStyleServices;
    begin
      LNativeStyle:=TStyleManager.SystemStyle;
    
      LFormats := TTextFormatFlags(Flags);
      if Selected and Enabled then
      begin
        LDetails := StyleServices.GetElementDetails(tmPopupItemHot);
        if TOSVersion.Check(5, 1) then
         SetBkMode(DC, Winapi.Windows.TRANSPARENT);
      end
      else
        LDetails := StyleServices.GetElementDetails(MenuStates[Enabled or ActionBar.DesignMode]);
    
      if not StyleServices.GetElementColor(LDetails, ecTextColor, LColor) or (LColor = clNone) then
        LColor := ActionBar.ColorMap.FontColor;
    
      LCaption := Text;
      if (tfCalcRect in LFormats) and ( (LCaption = '') or (LCaption[1] = cHotkeyPrefix) and (LCaption[2] = #0) ) then
        LCaption := LCaption + ' ';
    
      LNativeStyle.DrawText(DC, LDetails, LCaption, Rect, LFormats, LColor);
    end;
    
    procedure TThemedMenuItemEx.DrawText(var Rect: TRect; var Flags: Cardinal;
      Text: string);
    var
      LRect: TRect;
    begin
      if Selected and Enabled then
        StyleServices.DrawElement(Canvas.Handle, StyleServices.GetElementDetails(tmPopupItemHot), PaintRect)
      else if Selected then
        StyleServices.DrawElement(Canvas.Handle, StyleServices.GetElementDetails(tmPopupItemDisabledHot), PaintRect);
    
      if (Parent is TCustomActionBar) and (not ActionBar.PersistentHotkeys) then
        Text := FNoPrefix;
      Canvas.Font := Screen.MenuFont;
    
      if ActionClient.Default then
        Canvas.Font.Style := Canvas.Font.Style + [fsBold];
    
      LRect := PaintRect;
      NativeDrawText(Canvas.Handle, Text, LRect, Flags or DT_CALCRECT or DT_NOCLIP);
      OffsetRect(LRect, Rect.Left,
        ((PaintRect.Bottom - PaintRect.Top) - (LRect.Bottom - LRect.Top)) div 2);
      NativeDrawText(Canvas.Handle, Text, LRect, Flags);
    
      if ShowShortCut and ((ActionClient <> nil) and not ActionClient.HasItems) then
      begin
        Flags := DrawTextBiDiModeFlags(DT_RIGHT);
        LRect := TRect.Create(ShortCutBounds.Left, LRect.Top, ShortCutBounds.Right, LRect.Bottom);
        LRect.Offset(Width, 0);
        NativeDrawText(Canvas.Handle, ActionClient.ShortCutText, LRect, Flags);
      end;
    end;
    
    { TThemedMenuButtonEx }
    procedure TThemedMenuButtonEx.NativeDrawText(const Text: string; var Rect: TRect;
      Flags: Integer);
    const
      MenuStates: array[Boolean] of TThemedMenu = (tmMenuBarItemNormal, tmMenuBarItemHot);
    var
      LCaption: string;
      LFormats: TTextFormat;
      LColor: TColor;
      LDetails: TThemedElementDetails;
      LNativeStyle : TCustomStyleServices;
    begin
      LNativeStyle:=TStyleManager.SystemStyle;
    
      LFormats := TTextFormatFlags(Flags);
      if Enabled then
        LDetails := StyleServices.GetElementDetails(MenuStates[Selected or MouseInControl or ActionBar.DesignMode])
      else
        LDetails := StyleServices.GetElementDetails(tmMenuBarItemDisabled);
    
      Canvas.Brush.Style := bsClear;
      if Selected then
        Canvas.Font.Color := clHighlightText
      else
        Canvas.Font.Color := clMenuText;
    
      if not StyleServices.GetElementColor(LDetails, ecTextColor, LColor) or (LColor = clNone) then
        LColor := ActionBar.ColorMap.FontColor;
    
      LCaption := Text;
      if (tfCalcRect in LFormats) and ( (LCaption = '') or (LCaption[1] = cHotkeyPrefix) and (LCaption[2] = #0) ) then
        LCaption := LCaption + ' ';
    
      if Enabled then
        LDetails := StyleServices.GetElementDetails(MenuStates[Selected or MouseInControl]);
    
      LNativeStyle.DrawText(Canvas.Handle, LDetails, LCaption, Rect, LFormats, LColor);
    end;
    
    procedure TThemedMenuButtonEx.DrawText(var ARect: TRect; var Flags: Cardinal;
      Text: string);
    var
      LRect: TRect;
    begin
      if Parent is TCustomActionMainMenuBar then
        if not TCustomActionMainMenuBar(Parent).PersistentHotkeys then
          Text := StripHotkey(Text);
    
      LRect := ARect;
      Inc(LRect.Left);
      Canvas.Font := Screen.MenuFont;
      NativeDrawText(Text, LRect, Flags or DT_CALCRECT or DT_NOCLIP);
      NativeDrawText(Text, LRect, Flags);
    end;
    
    { TThemedButtonControlEx }
    //Here you must modify the code to draw the buttons
    procedure TThemedButtonControlEx.DrawBackground(var PaintRect: TRect);
    const
      DisabledState: array[Boolean] of TThemedToolBar = (ttbButtonDisabled, ttbButtonPressed);
      CheckedState: array[Boolean] of TThemedToolBar = (ttbButtonHot, ttbButtonCheckedHot);
    var
      SaveIndex: Integer;
    begin
      if not StyleServices.IsSystemStyle and ActionClient.Separator then Exit;
    
      SaveIndex := SaveDC(Canvas.Handle);
      try
        if Enabled and not (ActionBar.DesignMode) then
        begin
          if (MouseInControl or IsChecked) and
             Assigned(ActionClient) {and not ActionClient.Separator)} then
          begin
            StyleServices.DrawElement(Canvas.Handle, StyleServices.GetElementDetails(CheckedState[IsChecked or (FState = bsDown)]), PaintRect);
    
            if not MouseInControl then
              StyleServices.DrawElement(Canvas.Handle, StyleServices.GetElementDetails(ttbButtonPressed), PaintRect);
          end
          else
            ;//StyleServices.DrawElement(Canvas.Handle, StyleServices.GetElementDetails(ttbButtonNormal), PaintRect);// the code to draw the button in normal state was commented to get the desired look and feel
        end
        else
          ;//StyleServices.DrawElement(Canvas.Handle, StyleServices.GetElementDetails(DisabledState[IsChecked]), PaintRect);// the code to draw the button in disabled state was commented to get the desired look and feel
    
      finally
        RestoreDC(Canvas.Handle, SaveIndex);
      end;
    end;
    
    initialization
      PlatformVclStylesStyle := TPlatformVclStylesStyle.Create;
      RegisterActnBarStyle(PlatformVclStylesStyle);
      DefaultActnBarStyle :=PlatformVclStylesStyle.GetStyleName;
    finalization
      UnregisterActnBarStyle(PlatformVclStylesStyle);
      PlatformVclStylesStyle.Free;
    end.
    

    要使用它,只需将 Vcl.PlatformVclStylesActnCtrls 单元添加到您的项目中,然后像这样设置 TActionManager 的样式:

      ActionManager1.Style:=PlatformVclStylesStyle;
    

    之前

    之后

    【讨论】:

    • 感谢这对所有常规按钮都非常有用。有一个工具栏按钮,它有一个带有附加项目的子菜单,它仍然显示为一个带边框的按钮。让带有子菜单的按钮也显得扁平化会不会很困难?
    • 在这种情况下,您必须使用 TThemedMenuButtonEx 类并覆盖 DrawBackground 方法。
    • 我只有XE4的入门版,不包含VCL源代码。我认为 DrawBackground 的内容将来自 VCL 源?它绝对不在 Vcl.PlatformVclStylesActnCtrls 源代码中;-)
    • @JessicaBrown,给我发一封包含示例项目的电子邮件到 gmail dot com 的 rodrigo dot ruz dot v,或者在 SO 中发布一个新问题。
    猜你喜欢
    • 2013-05-26
    • 2011-01-03
    • 2012-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-02
    相关资源
    最近更新 更多