BERDAFLEX Software Systems
Главная > Delphi > Работа с модальными формами

Работа с модальными формами

История переиздания
Издание 1.0 Сентябрь 2003 г. Сергей Бердачук

Модальные формы отличаются от других форм тем, что при выводе ее на экран, доступ к родительской форме, а так же к ранее созданным объектам приложения запрещается до момента закрытия модальной формы. Свойство блокировки родительской формы очень удобно использовать для формирования различных диалогов, а так же справочников, корректировка которых должна исключать изменение зависимых данных. Но в базовой реализации данного типа форм в Delphi есть один существенный недостаток. При сворачивании (иконофикации) модальной формы, ранее созданные элементы приложения не сворачиваются.

Одним из вариантов решения этой проблемы может быть использование перехвата системных сообщений WM_ACTIVATEAPP, WM_SYSCOMMAND и вызова Win API функции ShowWindow для изменения состояния главной формы приложения.

Отличительной особенностью объектного программирования является возможность наследования классов. В неявном виде этот механизм повсеместно используется программистами при создании новых форм или при использовании визуальных компонент. Интересно отметить, что многие программисты, даже с солидным стажем мало уделяют внимания возможности повторного использования единожды написанного кода путем наследования ранее созданных типовых объектов. Я не рассматриваю при этом визуальные компоненты. Здесь имеется в виду собственные наработки программиста. Попробуем создать заготовку для наших модальных форм, реализующую решение ранее описанной проблемы управления сворачиванием и разворачиванием главной формы приложения из модальной формы. Создадим новую форму типа TfrmModal наследующую класс Tform. Текст модуля данной формы приведен ниже:

unit frmuModal;

interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
TfrmModal = class(TForm)
  private
    procedure WMSYSCOMMAND(var Msg: TWMSYSCOMMAND); message WM_SYSCOMMAND;
    procedure WMACTIVATEAPP(var Msg: TWMACTIVATEAPP); message WM_ACTIVATEAPP;
  end;

var
  frmModal: TfrmModal;

implementation

{$R *.DFM}
procedure TfrmModal.WMACTIVATEAPP(var Msg: TWMACTIVATEAPP);
 begin
   //Если приложение в свернутом состоянии,
   //то разворачиваем в нормальное
   if IsIconic(Application.Handle) then
     ShowWindow(Application.Handle, SW_RESTORE);
   inherited;
 end;

procedure TfrmModal.WMSYSCOMMAND(var Msg: TWMSYSCOMMAND);
 begin
   //Если сообщение "свернуть",
   // то сворачиваем главную форму приложения
   if Msg.CmdType = SC_MINIMIZE then
    ShowWindow(Application.Handle, SW_MINIMIZE)
   else
     inherited;
 end;
end.

Созданную заготовку желательно сохранить в отдельный каталог, доступный всем Вашим проектам. Для этого желательно создать каталог $(DELPHI)\Projects\common.

Для тестирования созданной заготовки создадим новый проект, например $(DELPHI)\Projects\TestModalForm\ TestModal.dpr. Вызвав меню «Project-Add to project» добавим в проект ранее созданную форму $(DELPHI)\Projects\common\frmuModal. При этом данную форму нужно удалить из списка автоматически создаваемых форм в меню «Project-Options» закладка «Forms».

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

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

if Not Assigned(frmNewModal) then
Application.CreateForm(TfrmNewModal,frmNewModal);
  try
    if (frmMyForm.ShowModal = mrOk) then
      //Выполняем некоторые действия
  finally
    frmMyForm.Free;
    frmMyForm :=nil;
  //Можно заменить функцией FreeAndNeel(frmMyForm);
end;

Хочется обратить внимание, что при закрытии модального диалога, возвращается результат типа TModalResult. Приложение может воспринимать любое целочисленное значение в качестве модального результата. Для удобства использования, в системе определены несколько значений данного типа приведенные в таблице:

Таблица 1. Значения результатов TModalResult

Константа Значение Описание
mrNone 0 Используется как значение по умолчанию
mrOk idOK Выход по нажатию кнопки типа OK
mrCancel idCancel Выход по нажатию кнопки типа CANCEL
mrAbort idAbort Выход по нажатию кнопки типа ABORT
mrRetry idRetry Выход по нажатию кнопки типа RETRY
mrIgnore idIgnore Выход по нажатию кнопки типа IGNORE
mrYes idYes Выход по нажатию кнопки типа YES
mrNo oidN Выход по нажатию кнопки типа NO
mrAll mrNo + 1 Используется для определения последней предустановленной константы

Теперь настало время воспользоваться ранее созданной заготовкой. Добавим в проект новую форму, наследующую ранее созданный класс модальной формы. Для этого выберем пункт меню File – New. В появившемся диалоге выберем закладку с именем проекта, и выберем в списке форм проекта форму frmModal. Данную форму так же уберем из списка автоматически создаваемых форм. Добавим метод создания новой формы:

//** Создаем экземпляр формы и выводим ее модально
procedure ShowNewModalForm;
begin
  // Предварительно проверяем, на наличие экземпляра
  // данного класса
  if Not Assigned(frmNewModal) then
    Application.CreateForm(TfrmNewModal,frmNewModal);
  try
    frmNewModal.ShowModal;
  finally
    frmNewModal.Free;
    frmNewModal:=nil;
  end;
end;

В главную форму приложения добавим ссылку на модуль новой модальной формы и кнопку вызова ранее созданного метода ShowNewModalForm. Компилируем созданный проект и запускаем его на выполнение. По нажатию на кнопку вызывается наша модальная форма. Причем по нажатию на кнопке «свернуть» сворачивается не только модальная форма, но и главная форма приложения.

В итоге этих нехитрых манипуляций мы получили новый класс, реализующий заданные функции и скрывающий реализацию этих функций.

Наследование классов позволяет упростить разработку программ, и при этом отпадает необходимость в многократном повторении одного и того же блока кода. Естественно при этом снижается количество ошибок в программе.

Rambler's Top100 Рейтинг@Mail.ru