TEdit 和 WM_PAINT 消息处理程序的奇怪行为
TEdit and WM_PAINT message handler strange behavior
我正在尝试在没有焦点的 TEdit
控件上实现我自己的绘图(当编辑器未完全显示其文本时,在 TEdit
中显示省略号)。所以我用这段代码加注星标:
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;
请注意,当 FEllipsis and (not Focused)
消息处理程序不执行任何操作。
现在我在表单上删除了一个 TButton
和 2 个 TEdit
控件,并添加了表单 OnCreate
:
procedure TForm1.FormCreate(Sender: TObject);
begin
Edit2.FEllipsis := True;
end;
我希望 Edit1
可以正常绘制,并且 Edit2
不会在编辑控件内绘制任何东西。
取而代之的是,消息处理程序被无休止地处理,Edit1
也没有被绘制,整个应用程序令人窒息(25% CPU 使用率!)。我也试过返回 Message.Result := 0
- 同样的效果。
现在,对于 "strange" 部分:当我使用 BeginPaint
获得 canvas 句柄时,一切都按预期工作。
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;
注意我也没有打电话给 inherited
。
如何解释这种行为?谢谢。
当一个window无效时,它会被要求在下一个绘制周期使自己有效。当 GetMessage
发现队列为空时,通常会在主线程消息循环中发生这种情况。那时 WM_PAINT
消息被合成并发送到 window.
当window收到这些消息后,它的任务就是画自己。这通常通过调用 BeginPaint
然后调用 EndPaint
来完成。对 BeginPaint
的调用验证了 window 的客户端 rect。这是您缺少的关键信息。
现在,在您的代码中,您没有调用 inherited
,因此没有绘制任何东西,也没有调用 BeginPaint
/ EndPaint
。因为您没有调用 BeginPaint
,所以 window 仍然无效。就这样,源源不断的WM_PAINT
消息产生了。
可以找到相关文档here:
The BeginPaint function automatically validates the entire client area.
我正在尝试在没有焦点的 TEdit
控件上实现我自己的绘图(当编辑器未完全显示其文本时,在 TEdit
中显示省略号)。所以我用这段代码加注星标:
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;
请注意,当 FEllipsis and (not Focused)
消息处理程序不执行任何操作。
现在我在表单上删除了一个 TButton
和 2 个 TEdit
控件,并添加了表单 OnCreate
:
procedure TForm1.FormCreate(Sender: TObject);
begin
Edit2.FEllipsis := True;
end;
我希望 Edit1
可以正常绘制,并且 Edit2
不会在编辑控件内绘制任何东西。
取而代之的是,消息处理程序被无休止地处理,Edit1
也没有被绘制,整个应用程序令人窒息(25% CPU 使用率!)。我也试过返回 Message.Result := 0
- 同样的效果。
现在,对于 "strange" 部分:当我使用 BeginPaint
获得 canvas 句柄时,一切都按预期工作。
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;
注意我也没有打电话给 inherited
。
如何解释这种行为?谢谢。
当一个window无效时,它会被要求在下一个绘制周期使自己有效。当 GetMessage
发现队列为空时,通常会在主线程消息循环中发生这种情况。那时 WM_PAINT
消息被合成并发送到 window.
当window收到这些消息后,它的任务就是画自己。这通常通过调用 BeginPaint
然后调用 EndPaint
来完成。对 BeginPaint
的调用验证了 window 的客户端 rect。这是您缺少的关键信息。
现在,在您的代码中,您没有调用 inherited
,因此没有绘制任何东西,也没有调用 BeginPaint
/ EndPaint
。因为您没有调用 BeginPaint
,所以 window 仍然无效。就这样,源源不断的WM_PAINT
消息产生了。
可以找到相关文档here:
The BeginPaint function automatically validates the entire client area.