Delphi: RichEdit Text Background Color

Something I needed in a project recently was to put some makeup into a richedit control. This is pretty easy with the SelAttributes field in the class. However, to my disappointment I found that the TTextAttributes class (SelAttributes) does not contain a field to set the background color of your text even though it is supported by the control. So I set out to find a solution to the problem on Google, but everything I could find required you to externally use the windows api to force this property onto the window.

I didn’t quite like this solution, so I came up with a cleaner one. I created a class helper for the TTextAttributes class. I first had a look at this class and noticed that the way the Color property is set and read does not require much work. Since I “can’t” access any of the class’ private methods I simply copied the few ones I needed to my class helper, being SetAttributes and GetAttributes. However, I couldn’t do the same with the 2 private fields in the class which I needed, so I made a small workaround for this. Note that this workaround will only work as long as VCL is changed. I created a small class with the same private fields. When casting the original object to this class, you can use it to access those private fields. If the name of the fields is changed in VCL, this small workaround has to be updated as well.

Here’s the header code:

uses
  ComCtrls, Graphics, RichEdit;

type
  __TTextAttributes = class
  private
    RichEdit: TCustomRichEdit;
    FType: TAttributeType;
  end;

  TTextAttributesH = class helper for TTextAttributes
  private
    function GetBackColor: TColor;
    procedure SetBackColor(const Value: TColor);
    procedure GetAttributes(var Format: TCharFormat2);
    procedure SetAttributes(var Format: TCharFormat2);
  public
    property BackColor: TColor read GetBackColor write SetBackColor;
  end;

And the body:

{ TTextAttributesH }

procedure TTextAttributesH.GetAttributes(var Format: TCharFormat2);
var
  RichEdit: TCustomRichEdit;
begin
  RichEdit := __TTextAttributes(Self).RichEdit;
  InitFormat(Format);
  if RichEdit.HandleAllocated then
    SendGetStructMessage(RichEdit.Handle, EM_GETCHARFORMAT,
      WPARAM(__TTextAttributes(Self).FType = atSelected), Format, True);
end;

function TTextAttributesH.GetBackColor: TColor;
var
  Format: TCharFormat2;
begin
  GetAttributes(Format);
  with Format do
    if (dwEffects and CFE_AUTOBACKCOLOR) <> 0 then
      Result := clWindow
    else
      Result := crBackColor;
end;

procedure TTextAttributesH.SetAttributes(var Format: TCharFormat2);
var
  Flag: Longint;
  RichEdit: TCustomRichEdit;
begin
  RichEdit := __TTextAttributes(Self).RichEdit;
  if __TTextAttributes(Self).FType = atSelected then 
    Flag := SCF_SELECTION
  else
    Flag := 0;
  if RichEdit.HandleAllocated then
    SendStructMessage(RichEdit.Handle, EM_SETCHARFORMAT, Flag, Format);
end;

procedure TTextAttributesH.SetBackColor(const Value: TColor);
var
  Format: TCharFormat2;
begin
  InitFormat(Format);
  with Format do
  begin
    dwMask := CFM_BACKCOLOR;
    if Value = clWindowText then
      dwEffects := CFE_AUTOBACKCOLOR
    else
      crBackColor := ColorToRGB(Value);
  end;
  SetAttributes(Format);
end;

Note that this only works in versions of Delphi that support class helpers. You can also use this method to add other missing properties.

Read More