TEdit和WM_PAINT消息处理程序的奇怪行为

8

我正在尝试在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和(非聚焦)时,消息处理程序不会执行任何操作。
现在我在表单上添加了一个TButton和2个TEdit控件,并添加了表单OnCreate事件:
procedure TForm1.FormCreate(Sender: TObject);
begin
  Edit2.FEllipsis := True;
end;

我原本希望Edit1正常绘制,而且Edit2不在编辑控件内绘制任何东西。

相反,消息处理程序无休止地被处理,Edit1也没有被绘制,整个应用程序都处于崩溃状态(CPU使用率达到25%!)。 我还尝试返回Message.Result := 0 - 效果相同。

现在,关于“奇怪”的部分:当我使用BeginPaint获取画布句柄时,一切都按预期工作。

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

如何解释这种行为?谢谢。

1个回答

13

当窗口被无效化后,它会在下一次绘制周期被要求重新变为有效状态。通常情况下,在主线程消息循环中,当GetMessage发现队列为空时,就会发生这种情况。此时,WM_PAINT消息会被合成并分派到窗口。

当窗口接收到这些消息时,它的任务是绘制自身。通常使用调用BeginPaint,然后再调用EndPaint来完成此操作。调用BeginPaint会验证窗口的客户区矩形。这是您缺少的关键信息。

现在,在您的代码中,由于没有调用inherited,因此没有绘制任何内容,并且没有调用BeginPaint/EndPaint。因为没有调用BeginPaint,所以窗口仍然处于无效状态。因此会产生无休止的流WM_PAINT 消息。

相关文档可以在此处找到:

BeginPaint函数会自动验证整个客户区域。


1
谢谢!文档已经说明得很清楚了:“系统会继续生成 WM_PAINT 消息,直到当前的更新区域被验证。” - zig
1
这是我读过的关于窗口消息绘图过程的最好简介。 - Jerry Dodge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接