MDI-Form игнорирует стиль StyleCleent - PullRequest
1 голос
/ 04 июня 2019

У меня проблема с VCL-стилями и MDI-формой.Я хочу использовать стили VCL, но я также хочу нарисовать фон (изображение) моей MainForm (MDI) самостоятельно.Это работало нормально без VCL Styles, но когда активен стиль, фоновое изображение MainForm не отображается.

Without Style

Я проверил StyleElementsдля MainForm, но исключить seClient игнорируется и фоновое изображение не отображается.

With Style

Когда я исключаю seClient и seBoarder, изображение отображается снова.Очевидно, что граница формы потеряла стиль, что тоже не то, что я хочу.

Exclude seClient/seBoarder

Изображение рисуется на холсте в ClientWndProc с помощьюсообщения WM_ERASEBKGND, WM_VSCROLL и WM_HSCROLL.Со стилями, похоже, эти события не возникли.Есть ли способ получить изображение на фоне формы с активными стилями VCL?

1 Ответ

0 голосов
/ 10 июня 2019

Здесь важно понять, что форма в стиле 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.Здесь я вижу несколько недостатков дизайна:

  1. Отсутствует виртуальный TFormStyleHook.PaintMDIClientBackground аналогично TFormStyleHook.PaintBackground.
  2. Нет способа контролировать / получать доступ к исходному клиентскому процессу MDI без взлома (скрыто вприватное поле FMDIPrevClientProc).
  3. Отключение управления стилем окна клиента 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.Как насчет вас, ребята?

...