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_NCACTIVATEWM_NCCALCSIZEWM_NCPAINTWM_ERASEBKGND 调用旧过程。更糟糕的是,在 WM_ERASEBKGND 上它直接使用 StyleServices.

绘制客户区背景

感谢上面的 TFormStyleHook MDI 的子类化形成了一个 PITA。我在这里看到多个设计缺陷:

  1. 缺少虚拟 TFormStyleHook.PaintMDIClientBackground 类似于 TFormStyleHook.PaintBackground
  2. 不通过黑客攻击就无法 control/access 原始 MDI 客户端程序(隐藏在私有字段 FMDIPrevClientProc 中)。
  3. 无法通过 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 中的错误。你们呢?