MDI-Form忽略StyleElements seClient

时间:2019-06-04 10:59:36

标签: delphi delphi-10.2-tokyo

我对VCL样式和MDI表单有疑问。我想使用VCL样式,但我也想自己绘制MainForm(MDI)的背景(图像)。在没有VCL样式的情况下,此方法可以正常工作,但是当“样式”处于活动状态时,不会显示MainForm的背景图像。

Without Style

我签出了MainForm的StyleElements,但排除了seClient被忽略并且不显示背景图像的情况。

With Style

当我排除seClient和seBoarder时,再次显示图像。显然,Form Boarder失去了样式,这也不是我想要的。

Exclude seClient/seBoarder

通过消息WM_ERASEBKGND,WM_VSCROLL和WM_HSCROLL在ClientWndProc中的画布上绘制图像。使用样式,看起来此事件没有发生。有什么方法可以在VCL样式处于活动状态时在窗体背景上获取图像?

1 个答案:

答案 0 :(得分:0)

这里要意识到的主要一点是,样式为fsMDIForm的表单是一种非常特殊的TWinControl,它管理两个窗口句柄,而不是一个-TWinControl.Handle和{ {3}}。第一个句柄是表单窗口本身,第二个句柄是MDI客户端窗口(对于MDI父内部的MDI子窗口,类似于容器)。

TFormStyleHook钩住了两个窗口过程,并引入了新方法TFormStyleHook.MDIClientWndProc,该方法处理发送到MDI客户端的消息。幸运的是,这种方法是虚拟的。它对消息进行一些预处理,然后调用原始的挂接过程。可悲的是,它阻止调用WM_NCACTIVATEWM_NCCALCSIZEWM_NCPAINTWM_ERASEBKGND的旧过程。更糟糕的是,它在WM_ERASEBKGND上直接使用TForm.ClientHandle绘制了客户区域背景。

由于上述原因,MDI的TFormStyleHook子类形成了PITA。我在这里看到了多个设计缺陷:

  1. 缺少类似于TFormStyleHook.PaintMDIClientBackground的虚拟TFormStyleHook.PaintBackground
  2. 没有被黑客入侵(隐藏在私有字段FMDIPrevClientProc中)的方法,无法控制/访问原始MDI客户端进程。
  3. 无法通过TForm.StyleElements控制MDI客户端窗口的样式(如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中的错误。你们呢?