I need to change the color of a TPanel when the VCL styles are enabled. I tried using and modifying the code listed in the article Changing the color of Edit Controls with VCL Styles Enabled, but it is not working for a TPanel. How I can change the color of a TPanel with the VCL Styles enabled?
            Asked
            
        
        
            Active
            
        
            Viewed 1.5k times
        
    3 Answers
21
            The TPanel doesn't use a style hook to draw the control, so you can't use the technique described in the article. instead you must override the paint method.
Check this sample using a interposer class.
type
  TPanel=Class(Vcl.ExtCtrls.TPanel)
  protected
    procedure Paint; override;
  End;
Uses
  Vcl.Styles,
  Vcl.Themes;
{$R *.dfm}
{ TPanel }
procedure TPanel.Paint;
const
  Alignments: array[TAlignment] of Longint = (DT_LEFT, DT_RIGHT, DT_CENTER);
  VerticalAlignments: array[TVerticalAlignment] of Longint = (DT_TOP, DT_BOTTOM, DT_VCENTER);
var
  Rect: TRect;
  LColor: TColor;
  LStyle: TCustomStyleServices;
  LDetails: TThemedElementDetails;
  TopColor        : TColor;
  BottomColor     : TColor;
  LBaseColor      : TColor;
  LBaseTopColor   : TColor;
  LBaseBottomColor: TColor;
  Flags: Longint;
  procedure AdjustColors(Bevel: TPanelBevel);
  begin
    TopColor := LBaseTopColor;
    if Bevel = bvLowered then
      TopColor := LBaseBottomColor;
    BottomColor := LBaseBottomColor;
    if Bevel = bvLowered then
      BottomColor := LBaseTopColor;
  end;
begin
  Rect := GetClientRect;
  LBaseColor := Color;//use the color property value to get the background color.
  LBaseTopColor := clBtnHighlight;
  LBaseBottomColor := clBtnShadow;
  LStyle := StyleServices;
  if LStyle.Enabled then
  begin
    LDetails := LStyle.GetElementDetails(tpPanelBevel);
    if LStyle.GetElementColor(LDetails, ecEdgeHighLightColor, LColor) and (LColor <> clNone) then
      LBaseTopColor := LColor;
    if LStyle.GetElementColor(LDetails, ecEdgeShadowColor, LColor) and (LColor <> clNone) then
      LBaseBottomColor := LColor;
  end;
  if BevelOuter <> bvNone then
  begin
    AdjustColors(BevelOuter);
    Frame3D(Canvas, Rect, TopColor, BottomColor, BevelWidth);
  end;
  if not (LStyle.Enabled and (csParentBackground in ControlStyle)) then
    Frame3D(Canvas, Rect, LBaseColor, LBaseColor, BorderWidth)
  else
    InflateRect(Rect, -Integer(BorderWidth), -Integer(BorderWidth));
  if BevelInner <> bvNone then
  begin
    AdjustColors(BevelInner);
    Frame3D(Canvas, Rect, TopColor, BottomColor, BevelWidth);
  end;
  with Canvas do
  begin
    if not LStyle.Enabled or not ParentBackground then
    begin
      Brush.Color := LBaseColor;
      FillRect(Rect);
    end;
    if ShowCaption and (Caption <> '') then
    begin
      Brush.Style := bsClear;
      Font := Self.Font;
      Flags := DT_EXPANDTABS or DT_SINGLELINE or
        VerticalAlignments[VerticalAlignment] or Alignments[Alignment];
      Flags := DrawTextBiDiModeFlags(Flags);
      if LStyle.Enabled then
      begin
        LDetails := LStyle.GetElementDetails(tpPanelBackground);
        if not LStyle.GetElementColor(LDetails, ecTextColor, LColor) or (LColor = clNone) then
          LColor := Font.Color;
        LStyle.DrawText(Handle, LDetails, Caption, Rect, TTextFormatFlags(Flags), LColor)
      end
      else
        DrawText(Handle, Caption, -1, Rect, Flags);
    end;
  end;
end;

        RRUZ
        
- 134,889
 - 20
 - 356
 - 483
 
- 
                    1there seems to be a much easier way, see the other answer – George Birbilis Dec 18 '16 at 18:31
 - 
                    ecEdgeHighLightColor is close enough, but which would be the actual constant for getting the color used to paint the body of a TPanel? – Gabriel Jul 11 '20 at 16:17
 - 
                    I have found the answer: tpPanelBackground + ecFillColor. It will work with most themes but not with 'Auric'. But for Auric it will give a close-enough result. -------------------------- Update: This will always work: cl:= TStyleManager.ActiveStyle.GetSystemColor(clBtnFace); – Gabriel Jul 11 '20 at 16:25
 
20
            
            
        In XE5, if you turn off the seClient flag in the StyleElements property, then the Color property works again as expected.
        approxiblue
        
- 6,982
 - 16
 - 51
 - 59
 
        boggy
        
- 3,674
 - 3
 - 33
 - 56
 
- 
                    Thanks for the tip, was upgrading some old Delphi code of mine to Berlin 10.1 Update 2 and it wasn't painting the background of a TPanel control descendent I was using – George Birbilis Dec 18 '16 at 18:24
 - 
                    3@GeorgeBirbilis: Glad it helped you. The accepted answer seemed overkill. – boggy Dec 18 '16 at 22:13
 - 
                    Brilliant! playing around with style elements of a TSpeedButton in XE5, I was able to change its font color and style despite visual theme enabled. May help someone. – user30478 Jul 30 '18 at 18:12
 
4
            
            
        Based on @costa's answer, use:
StyleElements := StyleElements - [seClient];
in the constructor of your TPanel descendent class
or if you just have some TPanel (or descendent class) instance you can do:
with myPanel do StyleElements := StyleElements - [seClient];
The -[...] syntax is used since the StyleElements is a set
For more on StyleElements read this article:
Tuning VCL Styles for Forms and Controls - http://edn.embarcadero.com/article/42812
        George Birbilis
        
- 2,782
 - 2
 - 33
 - 35
 
- 
                    1This is a much simpler solution. Works on for Delphi 2007 as well using TControlStyle. { ControlStyle := MyPanel.ControlStyle; Exclude(ControlStyle, csParentBackground); MyPanel.ControlStyle := ControlStyle; } – Airs Mar 23 '18 at 18:42
 - 
                    1I was on the verge of switching to C# and XAML until I found this solution. – Phil Rogers Feb 20 '21 at 17:24