在 TCustomControl 后代调整大小时,子控件闪烁

Child controls flicker during resize of TCustomControl descendant

长期以来,我在我的应用程序中使用了 TCustomPanel class 的后代,称为 MyContainer。我可以说的其他视觉控件的典型容器。一切都很好。有一天我意识到我根本不使用面板功能,所以我可以直接从 TCustomControl 派生 MyContainer。

这样做之后,当使用鼠标调整 MyContainer 大小时,我遇到了子控件(例如对齐的 TMemo)可怕的闪烁。这太可怕了——看起来整个 TMemo 都消失了一会儿,所以我可以看到背景。 MyContainer 将自身描绘得很好——这只是子控件的问题。

MyContainer 派生自 TCustomPanel 时不会发生这种情况。我缺少什么,在哪里?子控件是双缓冲的,MyContainer 也是。我使用 Delphi 7 个人版,所以我没有 VCL 源代码,所以我无法比较 TCustomPanel 和 TCustomControl 实现。处理 WM_EXITSIZEMOVEWM_ENTERSIZEMOVE 消息(对 enable/disable 子对齐)没有帮助。

我相信我的问题与擦除控件背景有关。作为我 "migration" 到 TCustomControl 的一部分,我将以下代码添加到 Paint 方法:

Canvas.Font.Assign(Font);
Canvas.Brush.Style := bsSolid;
Canvas.Brush.Color := Color;
PatBlt(Canvas.Handle, Canvas.ClipRect.Left, Canvas.ClipRect.Top, Canvas.ClipRect.Right, Canvas.ClipRect.Bottom, PATCOPY);

没有这段代码,子控件不再闪烁,但父控件的绘制被破坏了。

影响此行为的 TCustomPanelTCustomControl 之间的区别是 TCustomPanel 添加了csAcceptControls 样式到构造函数中的 ControlStyle。这反过来会影响 TWinControl 基础 class 中的行为,它将 WS_CLIPCHILDREN 样式添加到 window 用于设置了该样式的控件。

因此,您可以通过以下两种方式之一获得相同的结果:

  1. 覆盖 构造函数 并将 csAcceptsControls 添加到容器控件的 ControlStyle

  1. 重写CreateParams直接在windowStyle中添加WS_CLIPCHILDREN标志 你的容器控件

代码

选项 1:

constructor TMyContainer.Create(Owner: TComponent);
begin
  inherited;
  ControlStyle := ControlStyle + [csAcceptsControls];
end;

请注意,这意味着您的容器控件现在可以接受在设计时 放置在其上的控件。即使没有此 ControlStyle,您也可以通过设置 Parent 属性 在 运行 时将控件添加到容器中。 =12=]

选项 2:

procedure TMyContainer.CreateParams(var aParams: TCreateParams);
begin
  inherited;
  aParams.Style := aParams.Style or WS_CLIPCHILDREN;
end;

这实现了您所追求的绘画行为的特定变化,但不影响控件在设计时接受控件的能力。