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.