динамически создавать формы с помощью STRING

У меня есть 5 форм, созданных во время разработки. Мне нужно динамически создать экземпляр каждой формы и поместить вкладку.

Мой вопрос: если имена форм находятся в массиве строк, и я вызываю свою процедуру следующим образом:

ShowForm(FormName[3]);// To show the 3rd form on a tab page.

Как определить и создать новый экземпляр для каждой формы?

Вот что у меня есть на данный момент:

procedure TForm1.ShowFormOnTab(pProcName:String);
var
  NewForm: TfrmSetupItemCategories;//***HERE IS MY PROBLEM***

  NewTab: TTabSheet;
  FormName: String;

begin
  NewTab := TTabSheet.Create(PageControl1);
  NewTab.PageControl:= PageControl1;
  NewTab.Caption:='hi';
  PageControl1.ActivePage :=  NewTab;

  if pProcName='ProcfrmSetupItemCategories' Then
     begin
       NewForm:=TfrmSetupItemCategories.Create(NewTab);
       NewTab.Caption := NewForm.Caption;
     end;
  if pProcName='ProcfrmZones' Then
     begin
       NewForm:=TfrmZones.Create(NewTab);
       NewTab.Caption := NewForm.Caption;
     end;
.
.
.
end;

мне нужна помощь в строке "Вот моя проблема". Я не могу повторно использовать NewForm как переменную со второй формой таким образом...

Примечание. Моя проблема НЕ во вкладке. Скорее это создание нового экземпляра формы с использованием того же имени переменной.


person itsols    schedule 11.12.2012    source источник
comment
Почему вы используете жестко закодированные текстовые строки? Вы не кодируете на PHP здесь! Используйте перечисляемый тип, или тип класса, или фабрику.   -  person David Heffernan    schedule 11.12.2012
comment
@DavidHeffernan Во-первых, как старый таймер с долгой историей Pascal, C, dbase, FoxPro, а затем и VB6, я думаю, что мое мышление немного отличается (и может не обязательно иметь отношение к платформе). Но что еще более важно, я думаю, что мне просто нужно превосходное сообщество с такими людьми, как вы, чтобы направлять меня и раскрывать мне эти методы, которые вы представили... И в более технической части процедуры и формы, которые нужно вызывать, загружаются из базы данных. и назначить меню во время выполнения. Поскольку мой код Lazarus предназначен для работы в Linux/Windows, я избегаю API и MDI. Так что работы много   -  person itsols    schedule 11.12.2012


Ответы (3)


Объявите переменную NewForm как TForm:

var
  NewForm: TForm;
begin
  NewForm := TMyForm.Create(Tab1); //compiles OK
  NewForm := TMyOtherForm.Create(Tab2); //also compiles OK
end;

Я предполагаю, что TMyForm и TMyOtherForm являются производными от TForm.

СУХОЙ

Вы также можете сократить повторяющийся код, используя переменную ссылки на класс, например:

procedure TForm1.ShowFormOnTab(pProcName:String);
var
  NewForm: TForm;
  ClassToUse: TFormClass;
  NewTab: TTabSheet;
  FormName: String;

begin
  NewTab := TTabSheet.Create(PageControl1);
  NewTab.PageControl:= PageControl1;
  NewTab.Caption:='hi';
  PageControl1.ActivePage :=  NewTab;

  if pProcName='ProcfrmSetupItemCategories' then
    ClassToUse := TfrmSetupItemCategories
  else if pProcName='ProcfrmZones' then
    ClassToUse := TfrmZones
  else
    ClassToUse := nil;
  if Assigned(ClassToUse) then
  begin
    NewForm := ClassTouse.Create(NewTab);
    NewTab.Caption := NewForm.Caption;
    //if you access custom properties or methods, this is the way:
    if NewForm is TfrmZones then
      TfrmZones(NewForm).ZoneInfo := 'MyInfo';
  end;
end;

Зарегистрируйте свои классы, а затем создайте формы из строки

Как указывает сэр Руфо в своем комментарии, вы даже можете продолжить регистрацию своих классов (я не уверен, что это можно сделать в Lazarus, это упражнение на ваше усмотрение).

Во-первых, зарегистрируйте классы форм, которые вы хотите создать, из имени класса перед любым вызовом вашего метода ShowFormOnTab, например:

procedure TMainForm.FormCreate(Sender: TObject);
begin
  RegisterClass(TfrmSetupItemCategories);
  RegisterClass(TfrmZones);
  //and other classes
end;

Затем вы можете изменить код, чтобы получить ссылку на класс из строки имени класса:

procedure TForm1.ShowFormOnTab(pProcName:String);
var
  NewForm: TForm;
  ClassToUse: TFormClass;
  ClassNameToUse: string;
  NewTab: TTabSheet;
  FormName: String;

begin
  NewTab := TTabSheet.Create(PageControl1);
  NewTab.PageControl:= PageControl1;
  NewTab.Caption:='hi';
  PageControl1.ActivePage :=  NewTab;
  //get rid of 'Proc' and add the T
  //or even better, pass directly the class name
  ClassNameToUse := 'T' + Copy(pProcName, 5, MaxInt);
  ClassToUse := TFormClass(FindClass(ClassNameToUse));

  if Assigned(ClassToUse) then
  begin
    NewForm := ClassTouse.Create(NewTab);
    NewTab.Caption := NewForm.Caption;
    //if you access custom properties or methods, this is the way:
    if NewForm is TfrmZones then
      TfrmZones(NewForm).ZoneInfo := 'MyInfo';
  end;
end;

Таким образом, код остается одинаковым для любого количества классов.

Дополнительные сведения об этом см. в разделе Создание формы Delphi из строки в delphi.about. ком.

person jachguate    schedule 11.12.2012
comment
это немного уродливо, как вы определяете FormClass, хотя вы могли бы зарегистрировать эти классы, чтобы получить класс по строке; o) - person Sir Rufo; 11.12.2012
comment
@Sir Вы правы для Delphi, я не уверен насчет части Lazarus, поэтому я придерживаюсь того, что, по-видимому, работает для OP. - person jachguate; 11.12.2012
comment
@ Сэр, я добавил это к ответу. - person jachguate; 11.12.2012
comment
Подсказка: ShowFormOnTab может получить параметр типа TFormClass. - person David Heffernan; 11.12.2012
comment
@ Дэвид, да, но параметр все равно может быть в текстовом представлении, например, из базы данных. Вы, кажется, думаете, что это похоже на константу в месте вызова, а я не смотрю на это, просто на этот кусок кода. Правда в том, что мы не знаем, откуда оно взялось. - person jachguate; 11.12.2012
comment
Это вполне может быть правдой. В этом случае должен быть специальный метод, который преобразует текст в ссылку на класс. Плохой тон — смешивать все это в один большой клякс. Но я не считаю тебя ответственным за это! - person David Heffernan; 11.12.2012
comment
@David Лучшее имя будет CreateFormOnTab, чтобы рассказать, что там происходит. Show... мне кажется, что я имею дело с существующим экземпляром. Но мы за это ответственности не несем :о) - person Sir Rufo; 11.12.2012
comment
+1 за дополнительные усилия с определением класса из строки. - person Marjan Venema; 11.12.2012
comment
Вау! Это было выше моего понимания... Я никогда не знал о TFormClass... Честно говоря, я ценю все ваши советы и навыки, а не сам язык ;) Миллион благодарностей! - person itsols; 11.12.2012
comment
@jachguate Большое спасибо за ваш очень подробный ответ. У меня новый вопрос по тому же поводу. Форма на вкладке имеет кнопку. При нажатии я освобождаю форму. Но я также хочу закрыть эту вкладку. Какой лучший способ вы бы порекомендовали мне использовать здесь? Спасибо! - person itsols; 11.12.2012
comment
@itsols, это еще один вопрос. Краткий ответ: у вас есть ссылка на вкладку (родительскую) в форме, поэтому при уничтожении формы просто уведомите основную форму (через метод или сообщение), чтобы удалить эту вкладку. - person jachguate; 11.12.2012

Объявите свою переменную как тип предка:

var
  NewForm: TForm;

or

var
  NewForm: TCustomForm;

Недостаток: вам нужно будет привести переменную к определенному классу, если вы хотите вызвать любые методы вашей формы, которые вы объявили сами.

Используйте «мягкое» приведение, если вы хотите, чтобы компилятор проверял, что NewForm на самом деле является TMyForm во время выполнения:

(NewForm as TMyForm).MyMethod;

Когда вы абсолютно уверены, что NewForm является TMyForm (например, когда вы только что создали его), вы также можете использовать «жесткое» приведение:

TMyForm(NewForm).MyMethod;
person Marjan Venema    schedule 11.12.2012
comment
@marjan_venema Спасибо за этот вклад и +2 за ваши мысли о кастинге. Честно говоря, я не очень понимаю. О чем этот кастинг? - person itsols; 11.12.2012
comment
@itsols: о-о-о, дорогой... Пожалуйста, ознакомьтесь с приведением с использованием традиционного и кастинг - person Marjan Venema; 11.12.2012

С зарегистрированными классами при инициализации используемых форм вы можете сократить его до

Function CreateAndDock(pc:TPageControl;const FormName:String):Boolean;
begin
  Result := false;
  if Assigned(GetClass(FormName)) and GetClass(FormName).InheritsFrom(TCustomForm)   then
  With TFormClass( GetClass(FormName)).Create(pc.Owner) do
    begin
     ManualDock(pc);
     Show;
     Result := true;
    end;
end;

procedure TForm4.Button1Click(Sender: TObject);
begin
   ShowMessage(IntToStr(Integer(CreateAndDock(pagecontrol1,'TDockForm'))));
   ShowMessage(IntToStr(Integer(CreateAndDock(pagecontrol1,'TNotExists'))));
end;
person bummi    schedule 11.12.2012
comment
Вы пробовали это на Lazarus или Delphi? Кажется, я сталкиваюсь с ошибками, пытаясь использовать TFormClass(GetClass(FormName)).Create... в Lazarus - person itsols; 15.12.2012
comment
Работает с Delphi. У меня нет опыта работы с Lazarus, извините. - person bummi; 15.12.2012