The Road to Delphi

Delphi – Free Pascal – Oxygene

Changing the color of Edit Controls with VCL Styles Enabled

42 Comments

The Issue

The last weeks I found a couple questions and QC reports(100645, 101822, 102984) regarding to how change the color of a Edit Control (TEdit, TMaskEdit, TMemo and so on) when the VCL Styles are enabled. So today I will show you how you can change the colors of these controls even with the VCL Styles activated.

In a Standard VCL Form application you can change the Color property of Edit controls

But when the Vcl Styles Are applied these colors are not used, and the controls are painted using the colors of the current VCl Style.

The Solution

So what is the solution? well the answer is : Create a new TStyleHook (read this article to learn more about Style Hooks) . All the TWinControls uses a TStyleHook to paint the control when the vcl styles are actived, so you can modify the TStyleHook of a particular control to modify the way of how the component is painted.

Implementation

Before to implement a Custom Style hook you must be aware which this new TStyleHook will affect to all the controls of the same type used in the RegisterStyleHook method, because the Style hooks are implemented for a particular class type and not for an instance.

First let’s create a style hook for the TCustomEdit descendents, using the TEditStyleHook class, this new style hook can be used with controls like TEdit, TMaskEdit, TLabeledEdit and so on.

Check the next commented code

  TEditStyleHookColor = class(TEditStyleHook)
  private
    procedure UpdateColors;
  protected
    procedure WndProc(var Message: TMessage); override;
  public
    constructor Create(AControl: TWinControl); override;
  end;

uses
  Vcl.Styles;

type
 TWinControlH= class(TWinControl);

constructor TEditStyleHookColor.Create(AControl: TWinControl);
begin
  inherited;
  //call the UpdateColors method to use the custom colors
  UpdateColors;
end;

//Here you set the colors of the style hook
procedure TEditStyleHookColor.UpdateColors;
var
  LStyle: TCustomStyleServices;
begin
 if Control.Enabled then
 begin
  Brush.Color := TWinControlH(Control).Color; //use the Control color
  FontColor   := TWinControlH(Control).Font.Color;//use the Control font color
 end
 else
 begin
  //if the control is disabled use the colors of the style
  LStyle := StyleServices;
  Brush.Color := LStyle.GetStyleColor(scEditDisabled);
  FontColor := LStyle.GetStyleFontColor(sfEditBoxTextDisabled);
 end;
end;

//Handle the messages of the control
procedure TEditStyleHookColor.WndProc(var Message: TMessage);
begin
  case Message.Msg of
    CN_CTLCOLORMSGBOX..CN_CTLCOLORSTATIC:
      begin
        //Get the colors
        UpdateColors;
        SetTextColor(Message.WParam, ColorToRGB(FontColor));
        SetBkColor(Message.WParam, ColorToRGB(Brush.Color));
        Message.Result := LRESULT(Brush.Handle);
        Handled := True;
      end;
    CM_ENABLEDCHANGED:
      begin
        //Get the colors
        UpdateColors;
        Handled := False;
      end
  else
    inherited WndProc(Message);
  end;
end;

Now a style hook for the TCustomMemo using the TMemoStyleHook class.


  TMemoStyleHookColor = class(TMemoStyleHook)
  private
    procedure UpdateColors;
  protected
    procedure WndProc(var Message: TMessage); override;
  public
    constructor Create(AControl: TWinControl); override;
  end;

constructor TMemoStyleHookColor.Create(AControl: TWinControl);
begin
  inherited;
  //call the UpdateColors method to use the custom colors
  UpdateColors;
end;

//Set the colors to be used by the Style hook
procedure TMemoStyleHookColor.UpdateColors;
var
  LStyle: TCustomStyleServices;
begin
 if Control.Enabled then
 begin
  Brush.Color := TWinControlH(Control).Color;
  FontColor   := TWinControlH(Control).Font.Color;
 end
 else
 begin
  //if the control is disabled use the current style colors
  LStyle := StyleServices;
  Brush.Color := LStyle.GetStyleColor(scEditDisabled);
  FontColor := LStyle.GetStyleFontColor(sfEditBoxTextDisabled);
 end;
end;

//handle the messages
procedure TMemoStyleHookColor.WndProc(var Message: TMessage);
begin
  case Message.Msg of
    CN_CTLCOLORMSGBOX..CN_CTLCOLORSTATIC:
      begin
        //get the colors
        UpdateColors;
        SetTextColor(Message.WParam, ColorToRGB(FontColor));
        SetBkColor(Message.WParam, ColorToRGB(Brush.Color));
        Message.Result := LRESULT(Brush.Handle);
        Handled := True;
      end;

    CM_COLORCHANGED,
    CM_ENABLEDCHANGED:
      begin
        //get the colors
        UpdateColors;
        Handled := False;
      end
  else
    inherited WndProc(Message);
  end;
end;

Finally to apply the above code you must call the TStyleManager.Engine.RegisterStyleHook method in this way (ideally in the initialization part of you unit)

 TStyleManager.Engine.RegisterStyleHook(TEdit, TEditStyleHookColor);
 TStyleManager.Engine.RegisterStyleHook(TMaskEdit, TEditStyleHookColor);
 TStyleManager.Engine.RegisterStyleHook(TLabeledEdit, TEditStyleHookColor);
 TStyleManager.Engine.RegisterStyleHook(TButtonedEdit, TEditStyleHookColor);

 TStyleManager.Engine.RegisterStyleHook(TMemo, TMemoStyleHookColor);

Trick : If you only want apply these new hooks to the controls of a particular form, you can use a interposer class in the begining of your unit

type
  TEdit= Class (Vcl.StdCtrls.TEdit);
  TMemo= Class (Vcl.StdCtrls.TMemo);
  TButtonedEdit= Class (Vcl.ExtCtrls.TButtonedEdit);
  TLabeledEdit= Class (Vcl.ExtCtrls.TLabeledEdit);

The final result

Now look how the forms with the vcl styles can have custom colors in the Edit controls


Check the full source code on Github.

Author: Rodrigo

Just another Delphi guy.

42 thoughts on “Changing the color of Edit Controls with VCL Styles Enabled

  1. Hello, Rodrigo!
    My comment is not related to this topic, but is related to the theme of VCL StyleHook’s.
    Next about the problem: I want the TForm’s borders and header (caption, close/minimize/maximize buttons, etc.) to be drawn in native Windows style (background and child elements must be drawn using theme style).
    I found this article http://blog.runbits.com/post/Fix-for-TRibbon-and-VCL-styles.aspx , but when I tried to do like there I got an error:

    —————————
    Debugger Exception Notification
    —————————
    Project Project1.exe raised exception class $C0000005 with message ‘access violation at 0x0061f521: read of address 0x00000000’.
    —————————
    Break Continue Help
    —————————

    Can you help me to resolve my problem?

  2. Pingback: RAD Studio XE2 정보 모음

  3. Hi Rodrigo,

    thanks for your support. You seem to be a master of styles ;)
    Could you please have a look at QC #99359 (standard controls are ignoring the clHighlight color as set in the theme but are using system color settings which gives a bad user experience for dark themes)?
    I’m waiting for some response for quite some time now from Embarcadero but nothing happens and I’m lost.

    Many thanks in advance,
    Fred

  4. Pingback: Delphi XE2 - Benutzerdefinierte Stile - Delphi-PRAXiS

  5. Hi,
    We have downloaded your demo and it works fine.
    We added a second button to change the color of Memo2.
    When we click the button, the memo is coloured, except the border and the entire last line.
    How can we solve this?

    TIA,
    Sonja

    • Hi Sonja, I just update the code of the TMemoStyleHookColor.WndProc method. try now and let me know the results :)

      • Hi, Now it works fine for a TMemo.

        We also like to use this for a TDBMemo.
        We registered this StyleHook for TDBMemo:
        TStyleManager.Engine.RegisterStyleHook(TDBMemo, TMemoStyleHookColor);

        Now the TDBMemo is coloured, but only the part where the text is displayed. When the TDBMemo is larger then the text, the bottom of the memo (where there is no text) isn’t coloured.

        When we click in the memo or leave the screen, the memo is fully coloured.

        Any idea?

        TIA
        Sonja

        • Sonja, are you sure about this? because I just test the updated Style hook (wtih the CM_COLORCHANGED message ) in a TDBMemo and works fine.

          • Hi,
            Yes, the TDBMemo reacts like described above.

            We have changed the code a little bit. If a memo is disabled, you can’t use the scrollbars anymore.
            And Control has no Read property.
            So in the program we set ReadOnly = True and Tag = 1.
            If Tag = 1 whe use the colors for disabled.
            Can this cause the problem?

            We have changed the code like this:

            { TMemoStyleHookColor }

            constructor TMemoStyleHookColor.Create(AControl: TWinControl);
            begin
            inherited;
            UpdateColors;
            end;

            procedure TMemoStyleHookColor.UpdateColors;
            var
            LStyle: TCustomStyleServices;
            begin
            // Tag = 1 -> ReadOnly -> same color as disabled
            if Control.Tag = 1 then
            begin
            LStyle := StyleServices;
            Brush.Color := LStyle.GetStyleColor(scEditDisabled);
            FontColor := LStyle.GetStyleFontColor(sfEditBoxTextDisabled);
            end
            else
            begin
            LStyle := StyleServices;
            Brush.Color := LStyle.GetStyleColor(scEdit);
            FontColor := LStyle.GetStyleFontColor(sfEditBoxTextNormal);
            end;
            end;

            procedure TMemoStyleHookColor.WndProc(var Message: TMessage);
            begin
            case Message.Msg of
            CN_CTLCOLORMSGBOX..CN_CTLCOLORSTATIC:
            begin
            UpdateColors;
            SetTextColor(Message.WParam, ColorToRGB(FontColor));
            SetBkColor(Message.WParam, ColorToRGB(Brush.Color));
            Message.Result := LRESULT(Brush.Handle);
            Handled := True;
            end;

            CM_COLORCHANGED,
            CM_ENABLEDCHANGED:
            begin
            UpdateColors;
            Handled := False;
            end
            else
            inherited WndProc(Message);
            end;
            end;

            initialization

            TStyleManager.Engine.RegisterStyleHook(TDBMemo, TMemoStyleHookColor);
            TStyleManager.Engine.RegisterStyleHook(TMemo, TMemoStyleHookColor);

            After the build of the program we also get this message:
            [DCC Hint] StyleHooks.pas(22): H2269 Overriding virtual method ‘TMemoStyleHookColor.Create’ has lower visibility (protected) than base class ‘TMemoStyleHook’ (public)
            Is that normal?

          • Sonja, send me a mail with a sample project with the issue. rodrigo.ruz.v at gmail.com

          • I’ll try to make a sample project. We use an IBM i-Series to store our data.

  6. Rodrigo, you have a similar solution for the DBGrid and their columns?

  7. Hello Rodrigo,

    I have a mixed component like

    TKHJCustomLabeledEditError = class(TCustomPanel, IUnknown, IJvErrorIndicatorClient)
    private
    FEdit: TKHJValidateEdit;
    FLabelDescription: TKHJCustomEditLabel;
    FLabelSymbol: TKHJCustomEditLabel;
    FLabelTecUnit: TKHJCustomEditLabel;

    how canI change the color of the Edit.Font during run time ?

  8. Hi. I have tried to do the same with a TCombBox but it’s not working. Any ideas? I need to set the background when no items have been selected so like the TEdit. I’m guessing something special is required for Combobox.

  9. Hi, I am looking for a similar solution for changing the color of TButton. Could you point me to a solution??
    Thanks in advance!

  10. There is a much, much simpler way to choose what will be styled by the vcl styles engine and what won’t be styled. Just uncheck on the Object Inspector the Style Elements property to bypass the style engine completely for the chosen component or, if you just want to “unstyle” the background of a component, just uncheck the seClient.

  11. i use raize component, i want to keep focus color property in edit or dbedit components but i want to keep the color vcl style too in edit/dbedit . in less word, i just want to focus color in vcl style, can you help me. thanks

  12. Pingback: Anonymous

  13. Pingback: [Delphi EX2] Cambiare colore ai componenti VCL - Delphi

  14. You can apply (or not) theme to single component using tag (or add your own propertie ?). For example, set tag of edit2 to 1, and at the very beginning of procedure TEditStyleHookColor.UpdateColors add “if Control.tag = 0 then”. That way you can control, to which component add style, to which not.

  15. Old post, but very fine one!
    I am using Delphi XE7 and found out from stackoverflow an easier method(Correct me if I am out of the way).
    By simply changing the propery seFont under StyleElements to false in the property of the control in question.
    Here is the link.
    http://stackoverflow.com/questions/20053523/delphi-change-metropolis-ui-font-color

  16. Pingback: Generic Solution to Coloring the Focused Entry Control in Delphi Applications | Žarko Gajić

  17. I’m not going to claim I understand this code, but it is awesome and so easy to use the control.

    I had been struggling for months to try and change colours of components (DBEdit, etc.) when VCL.Styles were implemented. Having read your code ad seen how you control the desired component it’s now very easily done.

    THANK YOU!

Leave a comment