在 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_EXITSIZEMOVE
和 WM_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);
没有这段代码,子控件不再闪烁,但父控件的绘制被破坏了。
影响此行为的 TCustomPanel 和 TCustomControl 之间的区别是 TCustomPanel 添加了csAcceptControls 样式到构造函数中的 ControlStyle。这反过来会影响 TWinControl 基础 class 中的行为,它将 WS_CLIPCHILDREN 样式添加到 window 用于设置了该样式的控件。
因此,您可以通过以下两种方式之一获得相同的结果:
- 覆盖 构造函数 并将 csAcceptsControls 添加到容器控件的 ControlStyle
或
- 重写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;
这实现了您所追求的绘画行为的特定变化,但不影响控件在设计时接受控件的能力。
长期以来,我在我的应用程序中使用了 TCustomPanel
class 的后代,称为 MyContainer。我可以说的其他视觉控件的典型容器。一切都很好。有一天我意识到我根本不使用面板功能,所以我可以直接从 TCustomControl
派生 MyContainer。
这样做之后,当使用鼠标调整 MyContainer 大小时,我遇到了子控件(例如对齐的 TMemo)可怕的闪烁。这太可怕了——看起来整个 TMemo 都消失了一会儿,所以我可以看到背景。 MyContainer 将自身描绘得很好——这只是子控件的问题。
MyContainer 派生自 TCustomPanel 时不会发生这种情况。我缺少什么,在哪里?子控件是双缓冲的,MyContainer 也是。我使用 Delphi 7 个人版,所以我没有 VCL 源代码,所以我无法比较 TCustomPanel 和 TCustomControl 实现。处理 WM_EXITSIZEMOVE
和 WM_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);
没有这段代码,子控件不再闪烁,但父控件的绘制被破坏了。
影响此行为的 TCustomPanel 和 TCustomControl 之间的区别是 TCustomPanel 添加了csAcceptControls 样式到构造函数中的 ControlStyle。这反过来会影响 TWinControl 基础 class 中的行为,它将 WS_CLIPCHILDREN 样式添加到 window 用于设置了该样式的控件。
因此,您可以通过以下两种方式之一获得相同的结果:
- 覆盖 构造函数 并将 csAcceptsControls 添加到容器控件的 ControlStyle
或
- 重写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;
这实现了您所追求的绘画行为的特定变化,但不影响控件在设计时接受控件的能力。