Delphi – How do i custom draw of TEdit control text

delphivcl

I'd like to draw a piece of TEdit.Text using Font.Color different from the default. Are there any examples how to do that?

I'm attempting to do something like this:

NOTE: what this screenshot pictures is merely a hairy draft, but it convinces me what problem solvable.

Best Solution

Edit controls do not have owner-draw support, but you can custom-draw it by sub-classing it and handling WM_PAINT (among many other messages). It's doable, but it would be a world of pain to actually implement 100% correctly. From the docs: Developing Custom Draw Controls in Visual C++:

Note that owner-draw will work for most controls. However, it doesn't work for edit controls; and with regards to the list control, it works only for report-view style

I was also interested to find out how deep the rabbit hole goes, so,
Here is a code sample using an interposer class (still needs to implement selection but the custom drawing works when the caret is in the control):

type
  TEdit = class(StdCtrls.TEdit)
  private
    FCanvas: TCanvas;
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  protected
    procedure WndProc(var Message: TMessage); override;
    procedure Paint; virtual;
    procedure PaintWindow(DC: HDC); override;
    property Canvas: TCanvas read FCanvas;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

...

constructor TEdit.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FCanvas := TControlCanvas.Create;
  TControlCanvas(FCanvas).Control := Self;
end;

destructor TEdit.Destroy;
begin
  FCanvas.Free;
  inherited Destroy;
end;

procedure TEdit.Paint;
var
  R: TRect;
  I: Integer;
  S: String;
begin
  R := ClientRect;
  Inc(R.Left, 1);
  Inc(R.Top, 1);
  Canvas.Brush.Assign(Self.Brush);
  Canvas.Font.Assign(Self.Font);
  for I := 1 to Length(Text) do
  begin
    if Text[I] in ['0'..'9'] then
      Canvas.Font.Color := clRed
    else
      Canvas.Font.Color := clGreen;
    S := Text[I];
    DrawText(Canvas.Handle, PChar(S), -1, R, DT_LEFT or DT_NOPREFIX or
      DT_WORDBREAK or DrawTextBiDiModeFlagsReadingOnly);
    Inc(R.Left,Canvas.TextWidth(S));
  end;
end;

procedure TEdit.PaintWindow(DC: HDC);
begin
  FCanvas.Lock;
  try
    FCanvas.Handle := DC;
    try
      TControlCanvas(FCanvas).UpdateTextFlags;
      Paint;
    finally
      FCanvas.Handle := 0;
    end;
  finally
    FCanvas.Unlock;
  end;
end;

procedure TEdit.WMPaint(var Message: TWMPaint);
begin
  ControlState := ControlState+[csCustomPaint];
  inherited;
  ControlState := ControlState-[csCustomPaint];
end;

procedure TEdit.WndProc(var Message: TMessage);
begin
  inherited WndProc(Message);
  with Message do
    case Msg of
      CM_MOUSEENTER, CM_MOUSELEAVE, WM_LBUTTONUP, WM_LBUTTONDOWN,
      WM_KEYDOWN, WM_KEYUP,
      WM_SETFOCUS, WM_KILLFOCUS,
      CM_FONTCHANGED, CM_TEXTCHANGED:
      begin
        Invalidate;
      end;
   end; 
end;

enter image description here

Related Question