I am trying to implement my own drawing on a TEdit
control when it does not have focus (show ellipsis in TEdit
when the editor doesn’t fully display its text). So I starאed with this code:
type
TEdit = class(StdCtrls.TEdit)
private
FEllipsis: Boolean;
FCanvas: TCanvas;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
constructor TEdit.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FEllipsis := False;
FCanvas := TControlCanvas.Create;
TControlCanvas(FCanvas).Control := Self;
end;
destructor TEdit.Destroy;
begin
FCanvas.Free;
inherited;
end;
procedure TEdit.WMPaint(var Message: TWMPaint);
begin
if FEllipsis and (not Focused) then
begin
// Message.Result := 0;
// TODO...
end
else
inherited;
end;
Notice that when FEllipsis and (not Focused)
the message handler does nothing.
Now I dropped a TButton
and 2 TEdit
controls on the form, and added form OnCreate
:
procedure TForm1.FormCreate(Sender: TObject);
begin
Edit2.FEllipsis := True;
end;
I expected Edit1
to draw normally, and Edit2
not to draw anything inside the edit control.
Instead the message handler was processed endlessly, Edit1
was not drawn also, and the entire application was choking (with 25% CPU usage!). I have also tried returning Message.Result := 0
- same effect.
Now, for the "strange" part: When I obtain the canvas handle with BeginPaint
, everything works as expected.
procedure TEdit.WMPaint(var Message: TWMPaint);
var
PS: TPaintStruct;
begin
if FEllipsis and (not Focused) then
begin
if Message.DC = 0 then
FCanvas.Handle := BeginPaint(Handle, PS)
else
FCanvas.Handle := Message.DC;
try
// paint on FCanvas...
finally
FCanvas.Handle := 0;
if Message.DC = 0 then EndPaint(Handle, PS);
end;
end
else
inherited;
end;
Notice I did not call inherited
either.
How to explain this behavior? Thanks.
When a window is invalidated, it is asked to make itself valid at the next paint cycle. Typically that happens in the main thread message loop when GetMessage
finds that the queue is empty. At that point WM_PAINT
messages are synthesised and dispatched to the window.
When the window receives these messages, its task is to paint itself. That is typically done with calls to BeginPaint
and then EndPaint
. The call to BeginPaint
validates the window's client rect. This is the critical piece of information that you are lacking.
Now, in your code, you did not call inherited
and so did not paint anything, and did not call BeginPaint
/ EndPaint
. Because you did not call BeginPaint
, the window remains invalid. And so an endless stream of WM_PAINT
messages are generated.
The relevant documentation can be found here:
The BeginPaint function automatically validates the entire client area.