i have a problem with the VCL-Styles and MDI-Form. I want to use the VCL Styles, but i also want to draw the background (image) of my MainForm (MDI) by myself. This worked fine without VCL Styles, but when a Style is active the background image of the MainForm isn't shown.
I checked out the StyleElements for the MainForm, but exclude the seClient is ignoerd and the background image isn't shown.
When i exclude the seClient and seBoarder the image is shown again. Obviously the Form Boarder lost the Style, which is also not that what i want.
The image is drawn at the Canvas in the ClientWndProc by the messages WM_ERASEBKGND, WM_VSCROLL and WM_HSCROLL. With the Styles, it looks like this events didn't raise. Is there any way the get the image at the form background with VCL Styles active?
The main point to realize here is that form styled fsMDIForm
is a very special TWinControl
that manages two window handles instead of one - TWinControl.Handle
and TForm.ClientHandle
. While the first handle is the form window itself the second is MDI client window (container-like for MDI child windows inside MDI parent).
TFormStyleHook
hooks both window procedures and introduces new method TFormStyleHook.MDIClientWndProc
, which processes messages sent to MDI client. This method luckily virtual. It does some pre-processing of messages and then calls the original hooked procedure. The sad part is that it prevents calling the old procedure for WM_NCACTIVATE
, WM_NCCALCSIZE
, WM_NCPAINT
and WM_ERASEBKGND
. Even worse is that on WM_ERASEBKGND
it paints the client area background directly using StyleServices
.
Thanks to the above the subclassing of TFormStyleHook
for MDI forms a PITA. I see multiple design flaws here:
TFormStyleHook.PaintMDIClientBackground
similar to TFormStyleHook.PaintBackground
.FMDIPrevClientProc
).TForm.StyleElements
(as noted by OP).So what is the workaround? The easiest I can see is creating a custom style hook:
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;
and applying it to your MDI parent:
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;
Note that you still need to keep painting background in MDI parent form in case the VCL styles are disabled, so it's worth creating method TMainForm.PaintMDICLientBackground(DC: HDC)
and call it from both places.
I would argue that this is a bug in VCL. How about you guys?