MDI-Form 忽略 StyleElements seClient
MDI-Form ignores StyleElements seClient
我对 VCL 样式和 MDI 表单有疑问。我想使用 VCL 样式,但我也想自己绘制 MainForm (MDI) 的背景(图像)。这在没有 VCL 样式的情况下工作正常,但是当样式处于活动状态时,不会显示 MainForm 的背景图像。
我检查了 MainForm 的 StyleElements,但排除了 seClient 被忽略和背景图像未显示的情况。
当我排除 seClient 和 seBoarder 时,图像再次显示。显然Form Boarder失去了风格,这也不是我想要的。
图像由消息 WM_ERASEBKGND、WM_VSCROLL 和 WM_HSCROLL 在 ClientWndProc 中的 Canvas 绘制。使用 Styles,似乎没有引发此事件。有什么方法可以在激活 VCL 样式的情况下在表单背景中获取图像吗?
这里要认识到的要点是样式化的表单 fsMDIForm
是一个非常特殊的 TWinControl
管理 two window 句柄而不是一 - TWinControl.Handle
and TForm.ClientHandle
。虽然第一个句柄是 window 本身的形式,但第二个句柄是 MDI 客户端 window(container-like 用于 MDI 子 windows inside MDI parent)。
TFormStyleHook
挂钩两个 window 过程并引入新方法 TFormStyleHook.MDIClientWndProc
,该方法处理发送到 MDI 客户端的消息。此法幸虚。它执行一些 pre-processing 消息,然后调用原始的挂钩过程。可悲的是,它阻止了为 WM_NCACTIVATE
、WM_NCCALCSIZE
、WM_NCPAINT
和 WM_ERASEBKGND
调用旧过程。更糟糕的是,在 WM_ERASEBKGND
上它直接使用 StyleServices
.
绘制客户区背景
感谢上面的 TFormStyleHook
MDI 的子类化形成了一个 PITA。我在这里看到多个设计缺陷:
- 缺少虚拟
TFormStyleHook.PaintMDIClientBackground
类似于 TFormStyleHook.PaintBackground
。
- 不通过黑客攻击就无法 control/access 原始 MDI 客户端程序(隐藏在私有字段
FMDIPrevClientProc
中)。
- 无法通过
TForm.StyleElements
控制 MDI 客户端 window 的样式(如 OP 所述)。
那么解决方法是什么?我能看到的最简单的方法是创建自定义样式挂钩:
type
TMainFormStyleHook = class(TFormStyleHook)
public
procedure MDIClientWndProc(var Message: TMessage); override;
end;
{ TMainFormStyleHook }
procedure TMainFormStyleHook.MDIClientWndProc(var Message: TMessage);
begin
if Message.Msg = WM_ERASEBKGND then
begin
{ TODO: Paint background to TWMEraseBkgnd(Message).DC }
Message.Result := 1;
end
else
inherited;
end;
并将其应用于您的 MDI 父级:
type
TMainForm = class(TForm)
private
class constructor Create;
class destructor Destroy;
{ ... }
end;
{ TMainForm }
class constructor TMainForm.Create;
begin
TCustomStyleEngine.RegisterStyleHook(TMainForm, TMainFormStyleHook);
end;
class destructor TMainForm.Destroy;
begin
TCustomStyleEngine.UnRegisterStyleHook(TMainForm, TMainFormStyleHook);
end;
请注意,如果 VCL 样式被禁用,您仍然需要在 MDI 父窗体中绘制背景,因此值得创建方法 TMainForm.PaintMDICLientBackground(DC: HDC)
并从两个地方调用它。
我认为这是 VCL 中的错误。你们呢?
我对 VCL 样式和 MDI 表单有疑问。我想使用 VCL 样式,但我也想自己绘制 MainForm (MDI) 的背景(图像)。这在没有 VCL 样式的情况下工作正常,但是当样式处于活动状态时,不会显示 MainForm 的背景图像。
我检查了 MainForm 的 StyleElements,但排除了 seClient 被忽略和背景图像未显示的情况。
当我排除 seClient 和 seBoarder 时,图像再次显示。显然Form Boarder失去了风格,这也不是我想要的。
图像由消息 WM_ERASEBKGND、WM_VSCROLL 和 WM_HSCROLL 在 ClientWndProc 中的 Canvas 绘制。使用 Styles,似乎没有引发此事件。有什么方法可以在激活 VCL 样式的情况下在表单背景中获取图像吗?
这里要认识到的要点是样式化的表单 fsMDIForm
是一个非常特殊的 TWinControl
管理 two window 句柄而不是一 - TWinControl.Handle
and TForm.ClientHandle
。虽然第一个句柄是 window 本身的形式,但第二个句柄是 MDI 客户端 window(container-like 用于 MDI 子 windows inside MDI parent)。
TFormStyleHook
挂钩两个 window 过程并引入新方法 TFormStyleHook.MDIClientWndProc
,该方法处理发送到 MDI 客户端的消息。此法幸虚。它执行一些 pre-processing 消息,然后调用原始的挂钩过程。可悲的是,它阻止了为 WM_NCACTIVATE
、WM_NCCALCSIZE
、WM_NCPAINT
和 WM_ERASEBKGND
调用旧过程。更糟糕的是,在 WM_ERASEBKGND
上它直接使用 StyleServices
.
感谢上面的 TFormStyleHook
MDI 的子类化形成了一个 PITA。我在这里看到多个设计缺陷:
- 缺少虚拟
TFormStyleHook.PaintMDIClientBackground
类似于TFormStyleHook.PaintBackground
。 - 不通过黑客攻击就无法 control/access 原始 MDI 客户端程序(隐藏在私有字段
FMDIPrevClientProc
中)。 - 无法通过
TForm.StyleElements
控制 MDI 客户端 window 的样式(如 OP 所述)。
那么解决方法是什么?我能看到的最简单的方法是创建自定义样式挂钩:
type
TMainFormStyleHook = class(TFormStyleHook)
public
procedure MDIClientWndProc(var Message: TMessage); override;
end;
{ TMainFormStyleHook }
procedure TMainFormStyleHook.MDIClientWndProc(var Message: TMessage);
begin
if Message.Msg = WM_ERASEBKGND then
begin
{ TODO: Paint background to TWMEraseBkgnd(Message).DC }
Message.Result := 1;
end
else
inherited;
end;
并将其应用于您的 MDI 父级:
type
TMainForm = class(TForm)
private
class constructor Create;
class destructor Destroy;
{ ... }
end;
{ TMainForm }
class constructor TMainForm.Create;
begin
TCustomStyleEngine.RegisterStyleHook(TMainForm, TMainFormStyleHook);
end;
class destructor TMainForm.Destroy;
begin
TCustomStyleEngine.UnRegisterStyleHook(TMainForm, TMainFormStyleHook);
end;
请注意,如果 VCL 样式被禁用,您仍然需要在 MDI 父窗体中绘制背景,因此值得创建方法 TMainForm.PaintMDICLientBackground(DC: HDC)
并从两个地方调用它。
我认为这是 VCL 中的错误。你们呢?