Позови меня тихо по имени



Shot through the heart
And you're to blame
You give love a bad name
I play my part and you play your game
You give love a bad name
John Bon Jovi

Я уже достаточно давно программирую на Delphi и за всю практику передо мной ни разу не возникала необходимость выполнять инициализацию формы, зная лишь ее имя. Сразу хочу уточнить, что под инициализацией формы «по имени» я подразумеваю создание формы именно, зная только ее символьное название. Т.е. не банальный:

var
 fMyForm: TMyForm;
begin
 fMyForm := TMyForm.Create(nil);
 fMyForm.ShowModal();.
End;

Грубо говоря, в качестве входных данных мне известно лишь имя типа моей формы – TMyForm (или какой-либо другой), а на выходе мне требуется получить созданную форму.

Я уже говорил, но повторюсь. За всю практику, я ни разу не пробовал делать нечто подобное. Да, я писал приложения, в которых компоненты создаются в рантайме и т.д., но вот с такой «хитрой» инициализацией форм я столкнулся лицом к лицом впервые. Попробовав быстренько накидать пример, я, заработал себе шишку на голове. Все оказалось несколько сложнее, чем, то, что представил мой мозг. Набив себе шишку, я не стал отчаиваться и все-таки нашел решение данной задачки и вот сейчас, я хочу поделиться им с тобой. Вдруг, ты тоже столкнешься с чем-то подобным.

Простота – залог хорошего начала

Я понимаю, что ты не маленький и уже наверняка знаешь, что условно, все формы твоего проекта можно разделить на два вида: автосоздаваемые и создаваемые вручную. По умолчанию, когда ты добавляешь к своему проекту новую форму, она сразу же становиться автосоздаваемой – т.е. создается во время запуска твоего приложения. Тебе не нужно заботиться о выделении/освобождении для этой формы памяти, т.к все уже готово. При таком раскладе, твои действия сводятся к вызову одного из методов для ее отображения (например, ShowModal). Если же форма не является автосоздаваемой, то ее создание полностью лежит на твоей совести.

Чтобы освежить весь процесс в памяти, давай попробуем создать новый проект и потренироваться. Итак, запускай дельфина, отдавай ему команду File -> New -> Application и сразу же засейв все это дело.

На форму брось один компонент TEdit и кнопку. Поле ввода мы будем использовать для написания имя формы, которую будем инициализировать. Ну а кнопу, будем юзать непосредственно для выполнения кода инициализации. Мою форму ты можешь увидеть на рисунке 1.

Перед тем как заняться разбором полетов с кодом, давай создадим несколько форм для пробы. Добавь к своему прожекту две новые формы. Сразу же присвой им имена (свойство name) и сохрани их модули. В своем варианте проекта, я дал им такие имена: FirstForm и SecondForm.

Ты помнишь, что по умолчанию все вновь создаваемые формы становятся автосоздаваемыми. Для нашей сегодняшней задачи это неприемлемо, поэтому давай исправлять положение дел. Зайти в окно настроек проекта (Project -> Options). Перейди на закладку Forms и перенеси недавно созданные, нами в формы в список неиницилизируемых (рисунок 2).

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

А что нам надо?

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

var
  fMyForm: TFirstForm;
begin
  fMyForm := TFirstForm.Create(nil);
  fMyForm.Caption := 'Эту форму я инициализировал в ручную!';
  fMyForm.Label1.Caption := 'Пример ручной инициализации формы';
  fMyForm.ShowModal;
 
  fMyForm.Free;

Как видишь, ничего сложного. Ok, вспомнили, едем дальше. Теперь рассказываю самое интересное. Для создания формы мы объявили форму типа TFirstForm. По сути, этот тип соответствуют классу создаваемой, формы. Получается, что для создания формы по ее имени, нам необходимо знать название ее класса. «Так мы же его знаем!» - скажешь ты. Да, знаем, но перед тем как заюзать его в боевых условиях мы должны его зарегистрировать. После регистрации, мы сможем найти его (зарегистрированный класс) при помощи функции FindClass(). Принцип работы этой функции достаточно прост. Она пробегает по списку всех зарегистрированных классов и ищет класс, по символьному названию, переданному в качестве первого параметре.

Если поиск завершиться успешно, то функция вернет ссылку на объект типа TPersistentClass. Эту ссылку, мы впоследствии сможем привести к объекту типа TFormClass() и уже с его помощью произвести инициализацию формы. На первый взгляд все кажется все слишком сложным, но не будем впадать в панику раньше времени, а попробуем во всем разобраться на практике. Итак, возвращайся к своему созданному проекту, перейди в редактор кода (сейчас нас будет интересовать модуль главной формы) и в разделе Private опиши новый метод:

procedure RegisterAllFormClass();

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

RegisterClass(TFirstForm);
RegisterClass(TSecondForm);

С помощью метода RegisterClass мы производим регистрацию необходимого нам класса. В нашем случае, требуется выполнять инициализацию классов форм, т.е. TFirstForm и TSecondForm. Обрати внимание, что мы передаем именно тип наших форм, а не их имена. Название типа формируются из имени формы, но перед ним добавляется бука «T».

Ok, здесь должно быть все понятно. После выполнения этого куска кода, в списке зарегистрированных классов появятся классы наших форм. Чтобы этот код выполнился, нам нужно где-то вызвать этот метод. Рекомендую, повесить вызов метода RegisterAllFormClass() на обработчик события OnCreate() нашей главной формы. В своем примере, я поступил именно так.

А вот теперь настал момент истины – описание метода, который будет отвечать за непосредственное создание формы. Этот метод я вежливо обозвал CreateFormFromName. Полное описание метода выглядит так:

procedure CreateFormFromName(FormName:String);

В теле этого метода тебе напиши этот код:

var
 FormClass:TFormClass;
 Form:TForm;
begin
 FormClass := TFormClass(FindClass(FormName));
 Form := FormClass.Create(Application);
 Form.ShowModal;
 
 Form.Free;

Рассмотрим этот код немного подробней. В самой первой строчке кода, я привожу найденный класс с помощью метода FindClass() к классу TFormClass(). Как только класс будет получен, мы можем переходить к инициализации формы. Ее выполняем стандартным образом – путем вызова метода Create(), объета FormClass.

После выполнения метода Create(), можно оперировать формой привычными способами. Для примера, я вызвал метод ShowModal() и сразу же метод Free.
Попробуем заюзать пример в действии. Создай обработчик события OnClick для нашей единственной кнопки и в нем напиши единственную строчку кода:

CreateFormFromName(Edit1.Text);

Вот, собственно говоря, все и готово. Запускай приложений и попробуй ввести в поле ввода имя формы, например TFirstForm. Заметь, имя формы нужно вводить с буквой T. Как введешь название – клацай на кнопку и наслаждайся результатом. После закрытия формы, выделенная под нее память сразу же освободиться, т.к. в самом конце метода CreateFormFromName() я сразу же вызываю метод Free.

Для наглядности, я бросил на каждую из создаваемых форм по одному компоненту TLabel и в свойстве caption указал название формы. Результат работы программы ты можешь увидеть на рисунке 3.

Outro

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

Written by Антонов Игорь aka Spider_NET
E-mail: