О формах

Вступление

Как известно, в приложениях, написанных на Делфи, если два типа форм – создаваемые автоматически (AutoCreate form) и создаваемые вручную. По умолчанию, если, конечно, вы не лазили в настройки, создаются формы первого типа. Рассмотрим преимущества форм первого типа и второго. Пример использования смотрите в прилагаемом архиве в каталоге Project.

Автоформы

Для форм этого типа Делфи вставляет в файл проекта строки вида:

Application.CreateForm(TForm1, Form1);
Этот метод выполняет создание формы, плюс небольшой код, который проверяет, является ли данная форма главной (в таком случае он её отображает) или нет.

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

Во-первых, эти формы должны создаться. А это порой отнимает очень много времени – ведь должны отработать конструкторы для каждой формы. А сюда многие "запихивают" и подключение к базе, и поиск файлов на диске...

При сотнях форм это начинает раздражать – программа открывается с большой задержкой (иногда счёт идёт на минуты). А главная форма отобразится только тогда, когда будут созданы все формы. И начинается перенос кода в OnShow (Это событие возникает, когда форма отображается на экране), но так как форма может скрываться и отображаться снова, то вводятся дополнительные переменные флаги. В целом код стаёт просто неподъёмным. И проект "загибается".

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

Формы, создаваемые вручную

Противники Делфи ликуют – вот оно, для маленьких проектов может Делфи и подходит, но для серьезных – нет. А решение проблемы-то простое. Для начала нужно ответить на вопрос: "Зачем создавать форму, которая возможно не будет отображена?". Например, форма "О программе". Я более, чем уверен, что в каждом сеансе работы с программой пользователи её не открывают. А туда многие любят ставить красивые большие картинки, разные спецэффекты.

Давайте напишем более оптимальный код для окон типа "О программе".

About

Первым делом, сделаем форму свободной (это перевод делфийского Available form). Это можно сделать двумя способами. Можно просто открыть окно настроек проекта на вкладке Forms (меню Project – Options). Там увидите два списка. Правый скорее всего будет пуст – именно тут находятся свободные формы. А слева находятся автоматически создаваемые. Кликнем дважды мышкой по имени нашей формы, которое скорее всего будет называться AboutForm (У вас она имеет вид Form23 или что то в этом виде? Срочно переименовывайте, так жить нельзя, ведь в школе к ученикам не обращаются "ученик 3", "ученик 15"?). Она переместится в список справа.

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

Следующим этапом будет добавление метода класса для нашей формы. Методы класса – это такие методы, которые можно вызвать у класса, а не объекта. То есть не нужно создавать объект. В частности, конструктор класса является таким методом – а иначе создавать объекты было бы невозможно.

Итак, делаем.

1. Добавим в public-часть формы метод, вот такого вида:
class procedure MyShowForm;
2. Нажав Ctrl+Shift+C получаем заготовку обработчика.

3. В этой процедуре пишем следующее:
with Create(Application) do
  try
    ShowModal;
  finally
    Free;
  end;
Всё. Готово. Этот код делает следующее. Вначале создаст форму, потом отобразит как модальную и после закрытия формы (например методом Close) уничтожит её. То есть форма просуществует ровно столько, сколько нам она нужна. Осталось разобраться с тем, как её вызывать. А это очень просто. Вначале добавим в список uses вызывающей формы наш юнит с формой "О программе". А в обработчике соответствующего пункта меню напишем так:
TAboutBox.MyShowForm;
Стоит заметить одно маленько "но". В юните с формой "О программе" осталась переменная для формы (у меня она называется AboutBox: TAboutBox). К этой переменной обращаться нельзя – она не указывает ни на какую форму! Можно даже удалить это объявление, чтобы не мешалось перед глазами.

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

Попутные замечания

В нашем случае на форме нужна кнопка «Ок», но писать для неё обработчик с одной строкой Close как-то некрасиво. Но есть красивое решение – устанавливаем в кнопки свойство ModalResult равным mrOk. Теперь модальная форма будет закрываться сама.

Давайте чуть расширим нашу форму "О программе". Добавим возможность изменения надписи на этой форме (у меня это будет Label, содержащий имя продукта). Для этого к нашему методу дописываем параметр. Суммарно это будет выглядеть где-то так:
public
  { Public declarations }
  class procedure MyShowForm(AProductName: string);
...
class procedure TAboutBox.MyShowForm(AProductName: string);
begin
  with Create(Application) do
  try
    ProductName.Caption := AProductName;
    ShowModal;
  finally
    Free;
  end;
end;
и соответственно вызов:
TAboutBox.MyShowForm('My program');
Вся прелесть этого способа в том, что если вы вздумаете отображать имя продукта не в Label, а на навороченном компоненте, только что скачанном, вам не придется перелопачивать весь проект в поисках всех вызовов формы. И если вы работаете в команде, то для ваших коллег не нужно будет составлять объяснительные "писульки", как, где и что нужно поправлять.

Ленивые формы

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

Сделать такую форму bp обычной просто. Вначале делаем её свободной. Это мы уже проходили чуть выше. Следующим делом переписываем строку вида:
var
  LazyForm: TLazyForm;
на следующую:
function LazyForm: TLazyForm;
Под строкой {$R *.dfm} добавим такое:
var privateForm:TLazyForm;
 
function LazyForm: TLazyForm;
begin
  if not Assigned (privateForm) then
    privateForm := TLazyForm.Create(Application);
  Result := privateForm;
end;
В этим строках мы проверяем, если форма уже создана, то возвращаем ссылку на неё. Если нет – создаем.

Самое чудесное, что остальной код переделывать не надо. То есть, можно свободно написать LazyForm.Show; и не бояться, что она не появится, а вместо неё будет окно с сообщением об ошибке. Такую форму можно закрывать, открывать снова. Можно делать модальной.

Но у этой формы тоже есть один недостаток. Если где-то будет вызван деструктор, то последствия будут плачевны, если вы снова захотите увидеть форму. Но чтобы исправить это, достаточно добавить в событие OnDestroy всего одну строку - privateForm := nil;

Также эту форму можно научить автоматически вызывать деструктор, когда вы её закрываете. Для этого в событии OnClose вставим строку Action := caFree;

Можно заменить caFree на caHide – это обычное поведение формы – просто скрыться.

Есть также caMinimize – форма будет сворачиваться, но не скрываться. И последний вариант – caNone. Он как раз заставляет "ничего не делать". Этот вариант очень удобен, когда у вас на форме введены данные и перед закрытием формы вы хотите их проверить. Проверяете в этом событии, и если что то не нравится – просто выставляете это значение и форма не закрывается. Но советую не переусердствовать, а то форму нельзя будет закрыть вообще. Правильный код будет выглядеть где-то так:
If ModalResult <> mrOk then Close;
If not (условие проверки правильности) then Action := caNone;
Групповые действия

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

Но Делфи предоставляет нам удобный инструмент. Она ведёт самостоятельно список всех открытых форм. Доступ к этому массиву получить легко. Количество открытых форм – Screen.FormCount. Обратиться к конкретной форме – Screen.Forms[индекс]. Рассмотрим на конкретном примере – закрыть все формы приложения, кроме главной.
procedure TMainForm.btnCloseFormsClick(Sender: TObject);
var
  i: Integer;
begin
  for i := Screen.FormCount - 1 downto 0 do
  if Screen.Forms[i]<>Application.MainForm then
     Screen.Forms[i].Close;
end;
Если присмотреться, то ничего необычного в этом коде нет. Чуточку переделав этот код, можно к примеру поменять заголовки всех форм.

Пойдём чуть дальше. Хочется обращаться к конкретным методам формы. Оказывается, это можно делать, даже не зная типа формы, нужно знать только имя метода и количество параметров. В приложении вы найдёте пример (каталог AllForm), как у всех форм выполнить метод по имени. Сам код вынесен в отдельный юнит и может применяться в любых приложениях. Есть только два ограничения:

- метод должен быть обявлен в published разделе (без этого никак); - типы и количество параметров должно быть известно. В примере вызывается метод без параметров.

Вместо окончания

Иногда, когда кажется, что добавление новой функциональности в работающий проект приводит к переработке половины кода, долгой отладке кода, который никак не относится к добавленному коду, если верить здравой логике, значит приложение плохо спроектировано. Всегда стоит соблюдать простое правило - "Мухи отдельно, котлеты отдельно". То есть, разный по назначению код не должен находиться в одном месте.

Вот и всё, Удачи!

    No results found.
Отменить.