среда, 30 января 2013 г.

Наполняем базовую форму полезным кодом

Предыдущие заметки по теме: номер раз, номер два.

Для начала я наполню базовую форму довольно простыми, но полезными вещами. Это будут обработчики сообщений, а потому нам не нужно будет переустанавливать пакет с визардом. Затем добавлю пару published-свойств, и, переустановив пакет, мы эти свойства увидим в инспекторе объектов.

Решение проблемы с иконкой в Aero

Проблема наблюдается при включённой теме Aero в Windows: если закрыть окно, то видно, как иконка приложения меняется на иконку по умолчанию.

Это заметно как раз в тот момент, когда окно начинает уменьшаться и плавно исчезает с экрана (верхняя часть картинки):

 image

А происходит это потому, что в момент уничтожения окна VCL посылает сообщение WM_SETICON со значением HICON = 0.

Проблему я решил таким образом:

..
    procedure WMSetIcon(var Message: TWMSetIcon); message WM_SETICON;
..
procedure TBaseForm.WMSetIcon(var Message: TWMSetIcon);
begin
  // В момент уничтожения окна игнорируем установку иконки (иначе это заметно при анимации в Windows Aero)
  if (csDesigning in ComponentState) or not (csDestroying in ComponentState) then
    inherited;
end;

Решение проблемы при сворачивании модального окна

Если пользователь свернёт модальную форму, то модальная форма свернётся не в панель задач, а в нижнюю часть рабочего стола. Причём родительская форма останется на экране и одновременно не доступной для пользователя. Такое поведение вводит пользователя в недоумение, а в последствии приучает программиста отключать возможность сворачивать окно. Я же предлагаю кнопку [Minimize] в модальных формах не отключать, оставив пользователю возможность свернуть приложение целиком. Для этого отлавливаем сообщение WM_SYSCOMMAND:

..
    procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
..
procedure TBaseForm.WMSysCommand(var Message: TWMSysCommand);
begin
  // Если окно открыто в модальном режиме, то, вместо сворачивания окна, сворачиваем приложение
  if (Message.CmdType = SC_MINIMIZE) and (fsModal in FormState) then
    Application.Minimize
  else
    inherited;
end;

Однако, при разворачивании приложения из Панели задач VCL (почему-то) устанавливает фокус ввода в главную форму (а не последнюю модальную форму), что может привести к непредсказуемому результату. Поэтому дополнительно обрабатываем сообщение WM_ACTIVATE.

..
    procedure WMActivate(var Message: TWMActivate); message WM_ACTIVATE;
..
var
  IsRestoringPositions: Boolean;

procedure RestoreFormsPositions;
var
  List: TList;
  i: Integer;
  F: TCustomForm;
begin
  if IsRestoringPositions then
    Exit;
  IsRestoringPositions := True;
  try
    List := TList.Create;
    try
      // сначала сохраняем список окон в том порядке, в хотором они были отображены
      List.Capacity := Screen.CustomFormCount;
      for i := 0 to Screen.CustomFormCount - 1 do
        List.Add(Screen.CustomForms[i]);

      // теперь восстанавливаем этот порядок
      for i := List.Count - 1 downto 0 do
      begin
        F := TCustomForm(List[i]);
        BringWindowToTop(F.Handle);
      end;
    finally
      List.Free;
    end;
  finally
    IsRestoringPositions := False;
  end;
end;
..
procedure TBaseForm.WMActivate(var Message: TWMActivate);
begin
  if not (csDesigning in ComponentState) and (Message.Active > 0) and not IsWindowEnabled(Handle) then
    // здесь: наше окно отключено, но каким-то образом активируется
    // скорее всего есть модальные окна, если такие есть - то надо их показать
    RestoreFormsPositions;
  inherited;
end;

Автоматическое закрытие окна по клавише Escape

Об этом я уже писал ранее. Единственное, для удобства, добавим свойство CloseByEscape, которое по умолчанию выставлено в True.

..
  private
    FCloseByEscape: Boolean;
    procedure CMDialogKey(var Message: TCMDialogKey); message CM_DIALOGKEY;
  protected
    procedure InitializeNewForm; override;
  published
    property CloseByEscape: Boolean read FCloseByEscape write FCloseByEscape default True;
..
procedure TBaseForm.InitializeNewForm;
begin
  inherited InitializeNewForm;
  FCloseByEscape := True;
end;

procedure TBaseForm.CMDialogKey(var Message: TCMDialogKey);
begin
  // обработка CloseByEscape
  if CloseByEscape then
    with Message do
      if (CharCode = VK_ESCAPE) and (KeyDataToShiftState(KeyData) = []) then
      begin
        // для модального режима - говорим Cancel
        if fsModal in FormState then
        begin
          ModalResult := mrCancel;
          Result := 1;
        end else
          // для обычного - посылаем команду на закрытие
          Result := Integer(PostMessage(Handle, WM_CLOSE, 0, 0));

        if Result <> 0 then
          Exit;
      end;

  inherited;
end;

Автоматическое уничтожение окна при закрытии

Это очень полезно при использовании MDI-стиля приложения – MDIChild-окна, как правило, при закрытии надо сразу уничтожать (иначе, по умолчанию, они просто сворачиваются). Тоже бывает полезно и для не модальных диалогов. По-умолчанию свойство FreeOnClose установлено в False, но Вы можете установить его в True, заменив значение после слова default и добавив инициализацию переменной в методе InitializeNewForm.

..
  private
    FFreeOnClose: Boolean;
  protected
    procedure DoClose(var Action: TCloseAction); override;
  published
    property FreeOnClose: Boolean read FFreeOnClose write FFreeOnClose default False;
..
procedure TBaseForm.DoClose(var Action: TCloseAction);
begin
  if FreeOnClose then
    Action := caFree;
  inherited DoClose(Action);  // <- Form.OnClose
end;

Сразу замечу, что на самом деле это свойство работает не как FreeOnClose, а как ReleaseOnClose. Потому что. И на самом деле, это свойство не имеет значения, если у формы в обработчике OnClose переопределяется параметр Action.

Автоматическое уничтожение объектов, созданных в коде модуля формы

Об этом я писал ранее. Единственным отличием, от приведённого ранее кода будет то, что списков для уничтожения объектов будет два: один список будет уничтожать объекты при уничтожении формы (после обработчика OnDestroy), а другой – при её скрытии (после обработчика OnHide). Эти события являются зеркальными к событиям OnCreate и OnShow соответственно.

..
type
  TAutoFreeOnEvent = (afDefault, afHide, afDestroy);
..
  protected
    FAutoFreeOnHide: TObjectList;
    FAutoFreeOnDestroy: TObjectList;
    procedure DoHide; override;
    procedure DoDestroy; override;
  public
    function AutoFree(AObject: TObject; OnEvent: TAutoFreeOnEvent = afDefault): Pointer;
..
procedure TBaseForm.DoHide;
begin
  inherited DoHide;           // <- Form.OnHide
  // destory auto free objects
  FreeAndNil(FAutoFreeOnHide);
end;

procedure TBaseForm.DoDestroy;
begin
  inherited DoDestroy;        // <- Form.OnDestroy
  // destory auto free objects
  FreeAndNil(FAutoFreeOnHide);
  FreeAndNil(FAutoFreeOnDestroy);
end;

function TBaseForm.AutoFree(AObject: TObject; OnEvent: TAutoFreeOnEvent = afDefault): Pointer;
begin
  if OnEvent = afDefault then
    if fsShowing in FormState
      then OnEvent := afHide
      else OnEvent := afDestroy;

  case OnEvent of
    afHide:
      begin
        if not Assigned(FAutoFreeOnHide) then
          FAutoFreeOnHide := TObjectList.Create;
        FAutoFreeOnHide.Add(AObject);
      end;
    afDestroy:
      begin
        if not Assigned(FAutoFreeOnDestroy) then
          FAutoFreeOnDestroy := TObjectList.Create;
        FAutoFreeOnDestroy.Add(AObject);
      end;
  else
    Assert(False);
  end;
  Result := AObject;
end;

Итого

Имея базовую форму мы смогли (без дублирования кода):

  • обойти странности VCL (с иконкой и сворачиванием модального окна);
  • получили возможность скрывать форму по Escape и уничтожать её, изменяя лишь свойства через инспектор объектов;
  • мы научили форму автоматически уничтожать простые объекты (наследники от TObject).

Исходники доступны на этой странице. Ссылка для быстрого скачивания.

Также я добавил тестовый проект, который наглядно демонстрирует новые возможности.

28 коммент.:

Анонимный комментирует...

Мысли после прочтения.
1)Еще полезно, даже не в форму, а в файл проекта добавить функцию низкоуровневого перехвата клавиш, куда можно будет добавлять системные сочетания (альт+таб и.т.д)
2)Изредка встречалось предложение об эмуляции окна IDE (пользователями демо-версий компонентов), полезно, но вещь редкая...
3)Есть вещи, не слишком реализованные дефолтными методами - корректное сворачивание в трей с отрисовкой, или не реализованными в старых средах вообще - прогресс бар и превьюшка в Win7.
4)обычно имеется целый набор классов\функций навроде накопленного годами собственного api, было бы интересно иметь метод также вставлять автоматом ссылки на него.
Это не предложение, это просто мысли вслух, с уважением, dj vk.

Николай Зверев комментирует...

Спасибо, интересные мысли.

1) когда-то я делал в секции initialization базовой формы такое: создавался экземпляр вспомогательного класса (синглтон), который вешал хук на обработчик сообщений объекта Apllication, а дальше на те или иные сообщения можно было довольно легко подписаться/отписаться из любого модуля.
Но со временем я от этого отказался... возможно я продемонстрирую такое.
2) я уже вышел из этого возраста :)
3) Надо искать возможность переходить на новые версии IDE, чтобы не изобретать велосипед (хотя давно, в Delphi 5, я копировал модуль Forms и некоторые другие в папку с проектом и вносил правки прям в VCL, и это прекрасно работало!). К слову сказать, на работе я делал перевод наших приложений с Delphi 7 на Delphi 2010 -- запарно, но оно того стоит. Надо бы об этом написать..
4) или я не понял, или это делается экспертами вроде CnPack и GExperts

Анонимный комментирует...

Довольно интересно, имхо я бы лично добавил бы в базовую форму возможность ее работы как фрейма. Такой вариант, с двумя конструкторами описан в книге Пачеко.

Сергей комментирует...

Честно говоря, я не очень понял зачем понадобился флажок-свойство FreeOnClose.
Единственный вариант, к-рый мне приходит в голову - таким образом ты собираешься регулировать поведение деструктора.

Николай Зверев комментирует...

Анонимный, насколько я помню, в той книге описан довольно простой способ - форма встраивается в любую другую только в Run-Time. У фрейм есть преимущество - их можно встраивать в Design-Time.

Именно поэтому я в своё время не стал это делать, определив для себя: всё что можно встраивать - надо делать в фрейме.

Хотя мысль интересная и не сложна в реализации - думаю, что добавлю это, спасибо за напоминание.

Сергей, FreeOnClose не регулирует поведение деструктора, деструктор вызывается автоматически, когда форме говорят Free (либо явно, либо неявно через Release).
Флаг FreeOnClose эквивалентен обработчику

procedure TForm1.Close(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;

и просто позволяет его не писать. Это бывает полезно, когда за временем жизни формы не следит создающий форму код. (Ну т.е. пользователь закрыл форму, и она уничтожилась - типичная ситуация для MDI)

Сергей комментирует...

Николай, ну я об этом же. Только другими словами.
В таком подходе естьодин маленький минус
создаем форму в ран-тайм
затем что-то делаем
и забываем написать
FreeOnClose := True;
и забываем написать деструктор, ну или вызвать FreeAndNil

Сергей комментирует...

Т. е. с моей точки зрения этот флажок лишний.
А лучше всего всегда пользоваться FreeAndNil.
А там уж VCL само разбереться.

Николай Зверев комментирует...

Сергей, если мы забываем что-то уничтожить, то MemCheck, FastMM или EurekaLog эту утечку памяти покажут, на сегодня это не проблема.
Вообще, я приучил себя избавляться от генерируемых IDE переменных типа var Form1: TForm1 и все формы, в т.ч. и MainForm, я стараюсь создавать не в DPR-файле, а по мере необходимости.
Если использовать сценарий Create - ShowModal - Free, то флаг и не нужен.
Я этот флаг создавал для примерно такого сценария: FindOrCreateForm(TFormClass) - Show/BringToFront. Т.е. вызывающая сторона не заботится о времени жизни формы, её задача эту форму отобразить. При этом флаг я ставлю в True в RunTime.
... наверное Вы правы, в DesignTime этот флаг не очень-то и нужен, я просто хотел показать преимущества визарда.

IL комментирует...

Спасибо, попробую прикрутить к своим проектам первые 3 фичи. Хотелось бы понять, намеренно ли в модулях использующих BaseForms, таких как: BaseFormsTest/uMainForm - в .pas форма объявлена как TMainForm = class(TBaseForm), а в .dfm как object MainForm: TMainForm вместо *inherited* MainForm: TMainForm? Хотя в инспекторе объектов главной формы все равно видны свойства CloseByEscape и FreeOnClose, и IDE почему-то возвращает object вместо установленного вручную inherited в тексте формы (Alt-F12).

Мысли о развитии BaseForms вот какие:
1) была бы очень полезна проверка, что окно не вылетело целиком за границы экрана, что случается при смене разрешения экрана, и возврат его в видимую область или центр экрана
2) возможно ли его прикрутить к BaseForms модуль масштабирования элементов формы в зависимости от DPI текста scaling.pas http://www.delphikingdom.ru/asp/answer.asp?IDAnswer=48940?

Николай Зверев комментирует...

2All. У меня сейчас такой период, когда вдохновения что-то писать совсем нет (семья, работа, весна). Поэтому прошу прощения у всех за отсутствие активности.

IL, класс TBaseForm не имеет своего собственного DFM-файла. Отсюда следует, что визуальная часть TMainForm (т.е. та часть, которая редактируется в дизайнере) ни от чего не наследуется. Поэтому в DFM-файле главной формы подставляется слово object, как признак корневого ресурса формы (или фреймы, или датамодуля).

Если вы создадите наследник от TMainForm, то в DFM-файле наследника уже будет слово inherited, которое говорит о том, что при обработке DFM-файла наследника сначала надо обработать DFM-файл предка.

Когда вы руками прописываете слово inherited, среда видит, что у предка нет DFM-файла и исправляет это. Но если сделать наоборот, т.е. inherited заменить на object, можно заметить интересные побочные эффекты...

1) спасибо, учту (у меня такая проверка есть в момент создания формы и считывания её положения из реестра).
2) да, тот кусок кода на DelphiKingdom взят как раз из моей базовой формы. Я планировал его включить, но там есть один нюанс, который я реализовал грубо... я хочу попробовать найти более элегантное решение, а заодно проверить, как это отрабатывает в Delphi 2010 и старше.

IL комментирует...

Большое спасибо за разъяснение object/inherited. Я думал, что inherited обязателен для того, чтобы увидеть в дизайнере объекты и published свойства базовой формы. Оказалось нет, свойства отдельно, объекты отдельно :)
Побочный эффект от указания object вместо inherited состоит в скрытии объектов базовой формы в дизайнере формы наследника, при выполнении программы вроде бы разницы нет. Кажется, иногда вылазит AV-ошибка, и не совсем понятно как будет работать Revert to inherited. В общем, это распространенная ошибка, например,
http://wiert.me/2009/07/22/delphi-frames-as-visual-components-changing-your-inheritance/
http://delphi2010.ru/?p=71 (перевод этой статьи)
http://wiert.me/2009/08/20/delphi-tinterfaceddatamodule-revisted-inherited-in-your-dfm-files-when-your-datamodules-look-like-forms-in-the-designer/
http://stackoverflow.com/questions/190504/how-do-i-use-or-resolve-issues-with-visual-form-inheritance-in-delphi
С весной понятно, жизнь в онлайн замирает. Если б не релиз XE4, можно было бы до осени комп не включать :)
Готов помочь с всевозможным тестированием масштабирования и позиционирования окон на 2010-XE2, ибо задача ну очень востребована, как мне кажется. Я помню тот комментарий на DK, что можно сделать красивее. Честно говоря с трудом верится, что это будет работать не только со стандартными компонентами, но и при использовании компонент TMS, например.

Николай Зверев комментирует...

IL, спасибо за предложение помощи. Я вот посмотрел, чем отличаются исходники VCL в Delphi 2010 от Delphi 7 - похоже, что в Delphi 2010 проблемы масштабирования уже решены. Я попробовал воспроизвести баг (достаточно в контролах выставить якори, отличными от дефолтовых) - в Delphi 7 баг воспроизводится, в Delphi 2010 - уже нет, всё масштабируется корректно.
Единственное, что не сделано в Delphi 2010 - это автомасштабирование фрейм в случае их ручного создания из RunTime - это я и добавлю в BaseForms.pas.
Если у Вас есть какой-нибудь демо пример с проблемами масштабирования - с удовольствием посмотрю.


P.S.: обновил исходники - добавил базовый датамодуль и прописал default значения для свойств OldCreateOrder, Color, ParentFont, Left, Top -- чтобы в dfm файле не сохранялась всякая ерунда.

IL комментирует...

Что-то я уже не пойму, вы говорите о масштабировании формы/фрейма AutoSize или Anchor контролов при ресайзе формы?
Ну да, масштабирование фрейма при его положении в RunTime на форму/панель/pagecontrol интересно. Кажется, я этого пытался добиться с помощью
AutoSize=Off;
AutoSize=On;
Но вообще-то, я совсем о другом - масштабирование контролов ScaleControl http://pastebin.com/Gvp9hnAP при PixelsPerInch <> 96.

Николай Зверев комментирует...

не, AutoSize это не масштабирование. Масштабирование это именно пропорциональное изменение размеров формы и контролов, расположенных на ней, в зависимости от PixelsPerInch, установленном в Design-Time (и сохранённом в DFM) и от текущего значения PixelsPerInch.
Т.е. если Screen.PixelsPerInch <> DFM.PixelsPerInch - нужно делать масштабирование.

Так вот, это масштабирование делает VCL, причём в Delphi 7 это масштабирование работает довольно криво: оно не учитывает констрейнты, а Anchors контролов учитываются "лишний раз", из-за этого контролы могут уехать за пределы формы. Чтобы это воспроизвести, достаточно на форму кинуть кнопку (куда-нибудь в середину формы) и установить Anchors = [akRight, akBottom].

В Delphi 2010 масштабирование уже корректно обрабатывает Anchors и масштабирует констрейнты, и даже поля (Margins), поэтому тут вроде бы и фиксить нечего.
Но есть одно но: при сохраненнии фреймы в DFM значение PixelsPerInch не пишется. Поэтому если фрейму создать и встроить в форму в RunTime (а-ля TMyFrame.Create(Self).Parent := Self), то фрейма не промасштабируется. Вот это и надо фиксить.

IL комментирует...

Ух ты! Я и не знал, что Delphi 2010+ уже действительно масштабирует контролы. Все же мне кажется, что старые формы из D7 с растровым шрифтом MS Sans Serif даже откомпилированные в D2010+ при dpi>96 будут смотреться весьма криво. Новые формы имеют Tahoma по-умолчанию.

Николай Зверев комментирует...

Я тоже не знал, но предполагал, что там это уже пофикшено (ну не могли разработчики пройти мимо этого бага). Кстати я вот решил включить в BaseForms поддержку Delphi7, всё-таки есть у меня ещё несколько проектов, которые портировать на Delphi2010 не имеет смысла, да и у сообщества это пока ещё популярная среда. Да и на память код по масштабированию сохраню :)

> старые формы из D7 с растровым шрифтом MS Sans Serif .. будут смотреться весьма криво
Угу. Поэтому я сделал так (пока об этом не писал), что BaseForm.ParentFont = True по умолчанию, и в DFM шрифт не сохраняется, а используется Application.DefaultFont (или DefFontData в старых Delphi). Это позволяет централизовано устанавливать шрифт по-умолчанию. Правда при этом надо себя приучить не трогать свойство Font у контролов и изменять размер/начертание в RunTime.

С праздником!

IL комментирует...

Код масштабирования в D7 будет очень полезен. Его-то я и готов погонять под madexcept на разных унаследованных из D7 формах. Правда, хотелось бы все-таки это делать в XE, а не D7 :)
Почему все-таки не стоит трогать шрифт контролов в дизайнере, например, включить Bold, поменять размер или цвет, если использовать тот же базовый шрифт Tahoma, что и в BaseForms?

IL комментирует...

Пробую сделать такую цепочку наследования: ваша TBaseForm -> моя TMyBaseForm -> остальные мои формы новые/прежние формы, чтобы потом можно было поменять шрифт всех форм одним махом в MyBaseForm. В дизайнере MyBaseForm задаю шрифт формы MS Sans Serif,10,Bold и естественно ParentFont становится False. Дальше, например, новую форму наследую от MyBaseForm, шрифт формы ParentFont=False как и у MyBaseForm. При установке ParentFont=True новой формы шрифт меняется на стандартную Tahoma,8. Как же добиться того, чтобы ParentFont=True сбрасывал шрифт формы в шрифт MyBaseForm? Если не трудно загляните в исходник формы MyBaseForm http://pastebin.com/uN0pm1im

Николай Зверев комментирует...

IL
Дело в том, что если свойство ParentFont у формы выставлено в True, то подставляется дефолтовый шрифт приложения (а не шрифт, объявленный в родительском классе).
В Delphi 2010 - это Application.DefaultFont. В Delphi 7 - Graphics.DefFontData.

Вообще термин Parent в VCL относится не к наследованию, а к расположению WinControl'ов относительно друг друга. А для формы Parent'а как бы и нету.

(прошу прощения, что долго не отвечал)

Николай Зверев комментирует...

На вопрос "Почему все-таки не стоит трогать шрифт контролов в дизайнере" у меня такой ответ: если Вы в своём приложении хотите дать возможность пользователю менять шрифт, и при этом хотите обойтись малой кровью - то достаточно изменить Application.DefaultFont перед созданием главной формы. Если при этом все видоизменения шрифта делаются в коде, то больше ничего делать и не надо.

Если же шрифт задаётся в дизайнере, то тогда придётся делать больше телодвижений...

Николай Зверев комментирует...

Для всех
Я провёл много экспериментов с масштабированием форм в новых версиях Delphi - всё-таки проблемы масштабирования остались. VCL Form and Frame Scale Fix

IL комментирует...

Ничего страшного, что не отвечали, у меня было время причесать формы. Насчет DefaultFont и ParentFont формы теперь понятно. Не думаю, что мне когда-либо понадобится дать пользователю шрифт бизнес-приложения, так как размер кнопок, к сожалению, не промасштабировать. И смысла в этом не вижу. Стандартных вариантов тоже не много: Tahoma,8 или Segoe,9 (Виста+).
Спасибо, пора попробовать ваш Scale Fix в действии :)

IL комментирует...

Для WinForms проблему использования стандартного шрифта ОС решают так:
1) http://www.codeproject.com/Articles/19403/Adjusting-font-and-layout-for-cross-Windows-compat
Windows Vista:
Has a new default font (Segoe UI vs. Tahoma)
Has a new default font size (9.25 vs. 8.25)
Font sysFont = SystemFonts.MessageBoxFont;
Решение:
this.Font = new Font(sysFont.Name, sysFont.SizeInPoints, sysFont.Style);

2) http://texhex.blogspot.ru/2008/02/setting-right-font-for-windows-forms.html
if (Environment.OSVersion.Version.Major < 6)
{
//Windows 2000 (5.0), Windows XP (5.1), Windows Server 2003 and XP Pro x64 Edtion v2003 (5.2)
_DefaultFont = SystemFonts.DialogFont; //Tahoma hopefully
}
else
{
//Vista and above
_DefaultFont = SystemFonts.MessageBoxFont; //should be SegoiUI
}
То есть почти аналогично.

Delphi по-умолчанию использует MessageBoxFont для формы?

Николай Зверев комментирует...

IL, за ссылки и исследования - спасибо.
Старые Delphi по-умолчанию используют тот шрифт, который зашит в исходниках VCL - см. Graphics.pas, DefFontData и InitDefFontData.
В новых Delphi хитрее - они смотрят реестр, параметр MS Shell Dlg 2 (в ветке HKLM), который равен Tahoma.
Есть ещё такой момент: при разработке новой формы, её шрифт сохраняется в DFM (т.к. ParentFont = False), поэтому на практике получается так, что шрифт всё-равно определяется разработчиком.

Ingword комментирует...

Для исходников под 2010 Delphi пришлось немного изменить путь к файлу inc
{$i ..\BaseForms.inc}
вместо
{$i BaseForms.inc}

Alex Kononov комментирует...

Под XE4 с трудом, но собралась.
Несколько мыслей:

1) Необходимо добавить Win64
2) Нельзя использовать SetWindowPos(Self.Handle, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE or SWP_SHOWWINDOW); в TBaseForm.DoClose(var Action: TCloseAction);
Это влечет за собой несолько негативных момнетов:

- Если форма была создана, но не показана, и из нее вызвыно Close то форма мигает на экране. Иногда в моем проекте она вообще остается на экране, любой клик по ней - AV
- Более серъезный баг - при вызове SetWindowPos в OnClose вызывается OnActivate.

Николай Зверев комментирует...

> 1) Необходимо добавить Win64
Возможно. Для Вас ведь это не проблема?

> 2) Нельзя использовать SetWindowPos
Простите, я Вас не понял. SetWindowPos я явно не дёргаю. Вероятно, дёргаете Вы. Без описания причин и кода, ничего не могу сказать.
В DoClose - вообще ничего не делается, кроме переопределения Action (и то, при условии, что установлено свойство FreeOnClose)

> форма была создана, но не показана, и из нее вызвыно Close
Зачем Вы так делаете?

> Более серъезный баг
Это проявляется с использованием BaseFroms? Попробуйте отключить (закомментировать) вызов RestoreFormsPositions в TBaseForm.WMActivate. Хотя я не уверен, что дело именно в этом.

Alex Kononov комментирует...

Прошу прощения, у меня каким то чудом смержились 2 BaseForms.pas файла :)

Насчет закрытия непоказанной формы - я сопровождаю написанный код. Пока код и логика не разделены, приходится терпеть.
Проблема с OnActivate именно из за SetWindowPos в DoClose (в моем BaseForms).

Отправить комментарий

.

.