-1

I created a simple little unit that gives the basic TEdit component the ability to display a colorizable border. Essentially, it is:

type
  TEdit = class(StdCtrls.TEdit)
  private
    FBorderColor: TColor;
    FUseCustomColor: boolean;
    procedure SetUseCustomColor(const Value: boolean);
    procedure SetBorderColor(const Value: TColor);
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  public
    property BorderColor: TColor read FBorderColor write SetBorderColor;
    property UseCustomColor: boolean read FUseCustomColor write SetUseCustomColor;
  end;

implementation

procedure TEdit.SetBorderColor(const Value: TColor);
begin
  if FBorderColor <> Value then
  begin
    FBorderColor := Value;
    Self.Repaint;
  end;
end;

procedure TEdit.SetUseCustomColor(const Value: boolean);
begin
  if FUseCustomColor <> Value then
  begin
    FUseCustomColor := Value;
    Self.Repaint;
  end;
end;

procedure TEdit.WMPaint(var Message: TWMPaint);
var
  CC: TControlCanvas;
begin
  inherited;
  if FUseCustomColor then
  begin
    CC := TControlCanvas.Create;
    try
      CC.Control := Self;
      CC.Pen.Color := FBorderColor;
      CC.Pen.Width := 1;
      CC.Brush.Style := bsClear;
      CC.Rectangle(ClientRect);
    finally
      CC.Free;
    end;
  end;
end;

There is no problem with the TEdit on the form, but I have a special TEdit that adds extra functionality to the basic TEdit (password masking, when initialized it displays a random length text so that when opened it is not possible to deduce the password from the length of the password).

I replace the component with the following function:

type
  TPasswordEdit = class(TEdit)
  private
    FInitialText: string;
    procedure UpdatePasswordChar;
    function GetIsInitial: boolean;
  protected
    procedure CMFontChanged(var Msg: TMessage); message CM_FONTCHANGED;
  public
    constructor Create(AOwner: TComponent); override;
    procedure InitializeText(const bIsEmpty: boolean);
    procedure InitializeTextAgainst(const PasswordEdit: TPasswordEdit);
    property IsInitial: boolean read GetIsInitial;
    class function ReplaceEdit(const Replaced: TCustomEdit): TPasswordEdit;
    class function IsComplexPassword(const strPassword: WideString;
      const intMinLength: integer): boolean;
  end;

implementation

class function TPasswordEdit.ReplaceEdit(
  const Replaced: TCustomEdit): TPasswordEdit;
var
  intTabOrder: integer;
  i: integer;
begin
  intTabOrder := Replaced.TabOrder;
  Result := TPasswordEdit.Create(Replaced.Owner);
  try
    Result.Parent := Replaced.Parent;
    { copy relevant visual properties }
    Result.SetBounds(
      Replaced.Left, Replaced.Top, Replaced.Width, Replaced.Height);
    Result.Anchors := Replaced.Anchors;
    Result.TabOrder := intTabOrder;
    if Replaced is TEdit then
    begin
      Result.Font := (Replaced as TEdit).Font;
      Result.ParentFont := (Replaced as TEdit).ParentFont;
      Result.Color := (Replaced as TEdit).Color;
    end
    else
      raise Exception.Create(
        'Unsupported TCustomEdit to be replaced with a TPasswordEdit.');
    { change relevant references }
    for i := 0 to Replaced.Owner.ComponentCount - 1 do
      if Replaced.Owner.Components[i] is TLabel then
        if (Replaced.Owner.Components[i] as TLabel).FocusControl = Replaced then
          (Replaced.Owner.Components[i] as TLabel).FocusControl := Result;
    Replaced.Hide;
  except
    Result.Free;
    raise;
  end;
end;

The problem is that the WM_PAINT event is not called on such a replaced component. Of course I created the TPasswordEdit class inside the first unit with the same functionality.

One more additive... The development environment is Delphi 7, and due to the huge size of the whole project, it is not possible to port it to newer Delphi.

Anyone have any ideas?

As I read it, WM_PAINT needs the Form message handler to know about it, and from the descriptions, Invalidate was recommended everywhere. I tried InvalidateWindow, Form Invalidate and called it on the new component, but no way the message handler was called.

I changed the SetBorderColor() and SetUseCustomColor() functions to call the WMPaint() function and the border appears, but as soon as I drag the window out and then back in, the repaint does not happen.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Needback
  • 15
  • 2
  • Can you provide a [mcve] – David Heffernan Aug 09 '23 at 06:54
  • 2
    Have you made sure that TPasswordEdit is derived from your own TEdit and not from StdCtrls.TEdit? It may depend on the uses clause order. – Uwe Raabe Aug 09 '23 at 10:58
  • While in the design time components the element is created as an object of the classes I created, in the case of TPasswordEdit it is created inside the ReplaceEdit function, and moreover that class has no WM_PAINT event handler. I have modified the original TPasswordEdit class so that it can optionally be given the class of the object to create, and so that I can specify what type of object to create when the ReplaceEdit function is called. – Needback Aug 09 '23 at 11:27
  • The tumbleweed is caused by the lack of a [mcve] – David Heffernan Aug 10 '23 at 05:16
  • Seems that you are confused in namespaces. Try to rename your own type from `TEdit` into `TEditExtended` or something like this, or always use full name of class `MyUnit.TEdit`. In other way class what you will use depends on order of units in uses section of each unit. – Oleksandr Morozevych Aug 14 '23 at 11:11

0 Answers0