Здесь важно понять, что форма в стиле fsMDIForm
- это очень особенное TWinControl
, которое управляет двумя оконными дескрипторами вместо одной - TWinControl.Handle
и TForm.ClientHandle
.В то время как первый дескриптор - это само окно формы, второй - окно клиента MDI (похожее на контейнер для дочерних окон MDI внутри родительского MDI).
TFormStyleHook
перехватывает обе оконные процедуры и вводит новый метод TFormStyleHook.MDIClientWndProc
, которыйобрабатывает сообщения, отправленные клиенту MDI.Этот метод к счастью виртуальный.Он выполняет некоторую предварительную обработку сообщений, а затем вызывает оригинальную подключенную процедуру.Грустная часть заключается в том, что он предотвращает вызов старой процедуры для WM_NCACTIVATE
, WM_NCCALCSIZE
, WM_NCPAINT
и WM_ERASEBKGND
.Еще хуже то, что на WM_ERASEBKGND
он рисует фон клиентской области напрямую, используя StyleServices
.
Благодаря вышеизложенному, подкласс TFormStyleHook
для MDI образует PITA.Здесь я вижу несколько недостатков дизайна:
- Отсутствует виртуальный
TFormStyleHook.PaintMDIClientBackground
аналогично TFormStyleHook.PaintBackground
. - Нет способа контролировать / получать доступ к исходному клиентскому процессу MDI без взлома (скрыто вприватное поле
FMDIPrevClientProc
). - Отключение управления стилем окна клиента MDI с помощью
TForm.StyleElements
(как указано в 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;
Обратите внимание, что вам все еще нужно сохранить фоновый рисунок в родительском MDIФорма на случай, если стили VCL отключены, поэтому стоит создать метод TMainForm.PaintMDICLientBackground(DC: HDC)
и вызывать его из обоих мест.
Я бы сказал, что это ошибка в VCL.Как насчет вас, ребята?