Секреты Delphi. Работа с модальными формами

Секреты Delphi. Работа с модальными формами

Модальные формы отличаются от других форм тем, что при выводе на экран доступ к родительской форме, а также к ранее созданным объектам приложения запрещается до момента закрытия модальной формы. Свойство блокировки родительской формы очень удобно использовать для формирования различных диалогов и справочников, корректировка которых должна исключать изменение зависимых данных. К сожалению, в оригинальной реализации данного типа форм в Delphi при создании модальной формы, ввиду того, что доступ к ранее созданным элементам управления запрещается, при сворачивании (иконофикации) модальной формы ранее созданные элементы приложения не сворачиваются, загромождая экран. Одним из вариантов решения этой проблемы может быть использование перехвата системных сообщений TWMACTIVATEAPP, TWMSYSCOMMAND и вызова 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. Приложение может воспринимать любое целочисленное значение в качестве модального результата. Для удобства использования, в системе определены несколько значений данного типа, приведенные в таблице.

Теперь настало время воспользоваться ранее созданной заготовкой. Добавим в проект новую форму, наследующую ранее созданный класс модальной формы. Для этого выберем пункт меню 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. Компилируем созданный проект и запускаем его на выполнение. По нажатию на кнопку вызывается наша модальная форма. Причем по нажатию на кнопке "свернуть" сворачивается не только модальная форма, но и главная форма приложения (см. рисунок).

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

Сергей Бердачук, Berdachuk@tochka.by


Компьютерная газета. Статья была опубликована в номере 32 за 2003 год в рубрике программирование :: delphi

©1997-2024 Компьютерная газета