我对VCL样式和MDI表单有疑问。我想使用VCL样式,但我也想自己绘制MainForm(MDI)的背景(图像)。在没有VCL样式的情况下,此方法可以正常工作,但是当“样式”处于活动状态时,不会显示MainForm的背景图像。
我签出了MainForm的StyleElements,但排除了seClient被忽略并且不显示背景图像的情况。
当我排除seClient和seBoarder时,再次显示图像。显然,Form Boarder失去了样式,这也不是我想要的。
通过消息WM_ERASEBKGND,WM_VSCROLL和WM_HSCROLL在ClientWndProc中的画布上绘制图像。使用样式,看起来此事件没有发生。有什么方法可以在VCL样式处于活动状态时在窗体背景上获取图像?
答案 0 :(得分:0)
这里要意识到的主要一点是,样式为fsMDIForm
的表单是一种非常特殊的TWinControl
,它管理两个窗口句柄,而不是一个-TWinControl.Handle
和{ {3}}。第一个句柄是表单窗口本身,第二个句柄是MDI客户端窗口(对于MDI父内部的MDI子窗口,类似于容器)。
TFormStyleHook
钩住了两个窗口过程,并引入了新方法TFormStyleHook.MDIClientWndProc
,该方法处理发送到MDI客户端的消息。幸运的是,这种方法是虚拟的。它对消息进行一些预处理,然后调用原始的挂接过程。可悲的是,它阻止调用WM_NCACTIVATE
,WM_NCCALCSIZE
,WM_NCPAINT
和WM_ERASEBKGND
的旧过程。更糟糕的是,它在WM_ERASEBKGND
上直接使用TForm.ClientHandle
绘制了客户区域背景。
由于上述原因,MDI的TFormStyleHook
子类形成了PITA。我在这里看到了多个设计缺陷:
TFormStyleHook.PaintMDIClientBackground
的虚拟TFormStyleHook.PaintBackground
。FMDIPrevClientProc
中)的方法,无法控制/访问原始MDI客户端进程。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中的错误。你们呢?