#forms #delphi #null #modal-dialog
#формы #delphi #null #модальный диалог
Вопрос:
Я пытаюсь создать экземпляр формы и передать параметр, который должен быть специфичным для этого экземпляра.
type
TDataForm = class(TForm)
{ Some components}
{ Some procedures }
constructor Create(AOwner: TComponent; Data: PData; Page: String); reintroduce;
private
{ Private declarations }
public
{ Public declarations }
ViewedData: PData;
end;
var CharacterScreen: TDataForm;
implementation
CharacterScreen — это переменная для одного из экземпляров sucn. Когда я открываю экземпляр than, я делаю это так:
constructor TDataForm.Create(AOwner: TComponent; Data: PData; Page: String);
begin
inherited Create(nil);
ViewedData:=Data;
if Page='Stats' then CharPageControl.TabIndex:=0;
if Page='Equipment' then CharPageControl.TabIndex:=1;
if Page='Effects' then CharPageControl.TabIndex:=2;
if Page='Statistics' then CharPageControl.TabIndex:=3;
ShowModal;
Free;
end;
CharacterScreen:=TDataForm.Create(nil,@Data,'Page 1');
Однако, если позже я вызываю какую-либо процедуру для формы than, я получаю сообщение об ошибке AV. При просмотре переменной CharacterScreen в пошаговой отладке она описывается как nil .
Итак, мой вопрос: как правильно создать экземпляр формы (чтобы позже я мог создать второй экземпляр и так далее)? Кроме того, является ли это правильным способом хранения экземпляра переменной ViewedData, который должен быть уникальным для каждого экземпляра формы?
Комментарии:
1. Не вызывайте Free в конструкторе. Просто.
2. Помимо удаления Free из конструктора, удалите также вызов ShowModal . Это должно быть сделано вне конструктора. Кроме того, удалите глобальную переменную формы. Вам это не нужно. Для модальной формы вы всегда используете локальные переменные для ссылки на форму.
3. Если вы просто хотите показать диалоговое окно и забыть об этом впоследствии, вы можете добавить статический метод в свою форму (например, вызвать его Execute ) и создать /showmodal / освободить форму в этом методе…
Ответ №1:
Проблема в том, что вы вызываете Free из конструктора, поэтому экземпляр станет недоступным при закрытии формы.
Вы можете добавить статический метод в форму и вызывать его из других форм.
рекомендуется удалить глобальную переменную формы, чтобы вызывающий код должен был объявлять локальную переменную (это станет очевидным при создании нескольких экземпляров одной и той же формы). В этом примере я сделал конструктор закрытым, так что только метод execute может быть вызван вне этого модуля.
type
TDataForm = class(TForm)
{ Some components}
{ Some procedures }
private
{ Private declarations }
ViewedData: PData;
constructor Create(AOwner : TComponent);
public
{ Public declarations }
class procedure Execute(Data: PData; APage: String);
end;
implementation
class function TDataForm.Execute(Data: PData; APage: String) :TDataForm;
begin
Result := TDataForm.Create(nil);
Result.ViewedData := PData;
if APage='Характеристики' then Result.CharPageControl.TabIndex:=0 else
if APage='Экипировка' then Result.CharPageControl.TabIndex:=1 else
if APage='Эффекты' then Result.CharPageControl.TabIndex:=2 else
if APage='Статистика' then Result.CharPageControl.TabIndex:=3;
Result.ShowModal;
end;
constructor TDataForm.Create(AOwner: TComponent);
begin
inherited Create(nil);
end;
теперь вы вызываете свою форму следующим образом:
var CharacterScreen : TDataForm;
CharacterScreen := TDataForm.Execute(@Data,'Page 1');
try
// do something with CharacterScreen when it has been closed
finally
CharacterScreen.Free;
end;
** ОБНОВИТЬ **
Теперь, если бы я был вами, я бы сделал что-то вроде этого (псевдокод, я полагаю, вы поймете идею) :
Unit character;
type
TCharacterData = class
public
// some public fields
end;
...
Unit EditCharacter;
type
TDataForm = class(TForm)
procedure OnShow(Sender : TObject);
public
class procedure Execute(Data : TCharacterData) : Boolean;
end;
implementation;
class function TDataForm.Execute(var Character: TCharacter) : Boolean;
var Frm: TDataForm ;
begin
Result := False;
Frm:= TDataForm.Create(nil);
try
Frm.Character := Character;
Frm.ShowModal;
Result := Frm.ModalResult = mrOK;
finally
Frm.Free;
end;
end;
procedure TDataForm.OnShow(Sender : TObject);
begin
if Character = TWizardCharacter then Frm.CharPageControl.TabIndex:=0 else
if Character = TBarbarianCharacter then Frm.CharPageControl.TabIndex:=1
... //etc
end;
теперь вызывающий код может выглядеть следующим образом
var Character : TCharacter;
...
Character := TWizardCharacter.Create;
...
// user wants to edit character
procedure EditCharacter;
begin
if TDataForm.Execute(Character) then
begin
// user has modified character, act accordingly
end;
end;
Комментарии:
1. Большое улучшение, хотя в долгосрочной перспективе я бы, вероятно, не стал делать этот метод статическим и / или не делал его частью класса form.
2. @GolezTrol: не могли бы вы пояснить, почему нет? Я использую это все время (хотя я переработал это в классе формы-предка)
3. В основном потому, что это затрудняет тестирование и разделение проблем. Сейчас я пытаюсь использовать инверсию управления, используя Spring4D, что открывает множество возможностей, среди прочего, для модульного тестирования. Наличие статической реализации вызова модальной формы подразумевает, что модульное тестирование вызывающего кода становится практически невозможным, если только у вас нет другого класса, который обертывает этот, но тогда все равно нет большого преимущества в использовании этого статического метода. Но, возможно, это выходит за рамки этого вопроса. Извините за шум.
![]()
4. @GolezTrol: я понимаю, к чему вы клоните, MVC / MVMM — наш хлеб с маслом, но OP еще не на этом уровне
![]()
5. Перемещение «если страница = … затем …» для выполнения приводит к ошибке «Элемент экземпляра ‘CharPageControl’ недоступен здесь». Предыдущий код работал нормально.