Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Для новичков > Создание формы в режиме Autoran


Автор: PsiMagistr 26.5.2010, 09:18
Ребята, наверняка я вам уже надоел со своими вопросами, простите меня, но больше мне не к кому обратиться. Visual Basic с его недо реализацией классов так же похож на ООП  Delphi как торпеда на форель. ))) А пока немножко кода:

Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls; //Подключение заголовочных модулей

type
  TForm1 = class(TForm) //Создание класса TForm1
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end; // Конец описания класса

var
  Form1: TForm1; //Создание переменной указателя

implementation

uses Unit2;

{$R *.dfm}



Вот код, который Дельфи создает в самом начале, при запуске проекта. Мы видим добавление заголовочных модулей, создание класса TForm1 и создание переменной-указателя Form1. А кто нибудь видит инициализацию формы? К примеру вызов конструктора Create? Не видите? И не увидите. А между тем он написан. Но код его не здесь. Но достаточно выбрать в меню Дельфи команду: Project/View/Source и вы отыщите в открывшимся окне знакомую строку:

Код

program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1); //А теперь - конструктор, я сказал - конструктор, выходи! Ключ на старт!
  Application.Run;
end.


Многое сразу прояснилось. Однако не все. Идем дальше. Закройте наш участок кода, где описан конструктор и добавьте в проект новую форму: 
File/New/Form. Перейдите в код первой (главной)  формы и после слова implementation напишите:

uses Unit2; 

Мы сразу подключили  модуль второй формы в код главной, чтобы удобно было работать. Запустите проект на выполнение. Что мы видим? А ничего особенного. Плавает по экрану главная форма и вот собственно и все. Однако еще не все свершились чудеса! Дело в том, что сейчас, при запуске, создались сразу ДВЕ ФОРМЫ! Наша главная и новодобавленная. Просто она то как раз и невидима. По умолчанию. Хотите убедиться? Закройте окно программы и вернитесь в Дельфи. Найдите вторую форму (Form2) и сделайте ее свойство Visible равным True. Запускайте программу. Теперь у нас две формы и мы их отлично видим. Хорошо ли это, что все добавочные формы рождаются автоматически, сразу при старте программы, (напоминаю: все конструкторы форм можно увидеть, если пройтись по меню Project/View/Source)? Ну в принципе, если у вас в проекте 2-3 формы, включая главную, то ничего страшного нет. Рождаются две-три невидимые добавочные формочки, ждут своего часа (метода show или свойства visible:=true). А вот если у нас в проекте 50-100 форм и все так и норовят запрыгнуть  при старте в оперативную память?! Нда... Несладко. Особенно если все эти формы нам не нужны одновременно. Как же сделать так, чтобы побочные формы не инициализировались автоматически? На самом деле это просто. Закройте программу, вернитесь в среду Дельфи. Выберите команду меню: Project/Options и в появившемся служебном окне среды Дельфи отыщите вкладку Forms. Там вы увидите два списка.  В одном из них (левом) находятся имена обеих наших форм. Правый пустой.  Все формы, находящееся в правом списке инициализируются автоматически. Выделите в левом списке нашу Form2, и нажмите кнопочку с угловой скобочкой вправо. Теперь Form2 оказалась в правом списке.  И это значит, что никакого ее автосоздания не будет. Закройте служебное окошко кнопочкой ОК. Запустите программу на выполнение. По экрану плавает окно главной формы. А где вторая? А ее нет. Нет, она не невидима, ее свойство Visible := True, мы сами сделали его таким еще в начале.  но... Ее просто нет. Она еще не появилась на свет. Сейчас мы попробуем вызвать ее конструктор ручками. Закройте окошко программы и вернитесь в Дельфи.  Отыщите главную форму, добавьте на нее кнопочку, измените ее свойство Caption на 'Рождение формы' Запишите в обработчике Onclick

Код

procedure TForm1.Button1Click(Sender: TObject);
begin
Form2:=TForm2.Create(Form1.Owner);
end;


Form2 - это просто указатель  на будущую форму. Сама она объявлена в модуле второй формы, но модуль главной ее отлично видит, в самом начале мы сделали все подключения. В конструктор передается параметр Owner, указывая на владельца порождаемой формы. В данном случае владелец - Form1. Так что можно писать и просто  Owner.

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

Код

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=caFree;
end;



Если никакого кода не писать, то параметр Action по умолчанию равен caHide, наша форма просто прячется. Мы же изменили этот параметр на caFree и заставили убраться форму из памяти. 

А теперь вопрос для знатоков:

Если запустить программу и начать жать на кнопку, будут рождаться все новые и новые формы. Так и должно быть. Но вот с чем я так и не разобрался. Указатель на объект-форму у нас только один и это переменная Form2. Туда передается конструктор. Получается, что Form2 всегда указывает на последнюю порожденную форму. Но ведь остальные порожденные формы тоже чувствуют себя неплохо. Их можно активизировать, закрывать. Каким образом это возможно? Ведь указатель всего один.


 








Автор: Dom 26.5.2010, 10:51
При вызове Create для формы с указанием владельца, этому владельцу в список дочерних компонентов добавляется указатель на эту форму (через InsertComponent). И при вызове деструктора этого владельца, разрушаются автоматически все его дочерние компоненты, в т.ч. и форма.

Если владельца не указывать 
Код

Form2 := TForm2.Create(nil)
то утечек памяти тоже не будет, будет отрабатывать 
Код

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;

Автор: PsiMagistr 26.5.2010, 11:01

Спасибо большое, но 

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


Автор: Dom 26.5.2010, 11:02
Дополню. Утечек памяти в последнем случае не будет если закрывать все эти новые формы самостоятельно ручками. Если же закрыть главное окно приложения Form1, когда есть еще открытые экземпляры Form2, то утечки будут.
Вроде бы так...

Автор: PsiMagistr 26.5.2010, 11:12
Спасибо большое. Я понял. Но только я не совсем об этом. Я просто говорю, что у нас всего один указатель 

var Form2:TForm2

И  этим одним указателем управляется множество новорожденных форм. Хотя по идее он указывает только на последнюю рожденную:

Form2:=TForm2.Create(Form1.Owner);

Итак единственный указатель указывает на последнюю форму.

Но остальные старенькие тоже неплохо управляются (сворачиваются, закрываются реагируют на щелчки).

 Кто в этот момент указывает на них?

Автор: Dom 26.5.2010, 11:13
Эмм, ну почему же указатель всего один? Каждый раз при вызове TForm2.Create создается новый указатель.

Автор: PsiMagistr 26.5.2010, 11:19
Да? А как же он появляется? Я всегда думал что объявленный в модуле дочерней формы  указатель  Form2 единственный.  Ведь по идее указатели такие же переменные кто то их должен объявить? Или как.

Автор: kami 26.5.2010, 11:26
Не нужно смешивать указатель на экземпляр формы и область памяти, на которую ссылается этот указатель.
Сразу после создания формы указателю на нее можно присвоить nil (более того, можно вообще без переменной-указателя обойтись), но область памяти, выделенная при создании формы/любого другого объекта от этого никуда не денется, а именно в ней (выделенной под форму памяти) содержатся все поля формы. (реализация методов, afair, имеется в одном экземпляре, сколько бы таких форм мы ни создали). Только при уничтожении формы выделенная под нее память будет возвращена менеджеру памяти программы.

Автор: PsiMagistr 26.5.2010, 11:32
Спасибо, ками. Итак выделенная память у нас есть. Но мы создали 10 форм. Память выделена для первой формы.  И для второй и для десятой тоже. Но что то же должно  указывать на эту конкретную область. К ней же надо как то обращаться. Ну один указатель у нас есть. Но вот откуда берутся остальные девять штук?

Автор: Dom 26.5.2010, 11:34
Ну про детали работы с памятью не расскажу, не знаю.
Должно происходить примерно следующее. При создании объекта создается указатель на него. А выражение Form2 := TForm2.Create(Form1.Owner) всего лишь присваивает этот адрес переменной Form2.

Автор: PsiMagistr 26.5.2010, 11:39
Именно. Вот эта то переменная- указатель указывает на адрес памяти последней созданной формы. Но остальные тоже не висят в памяти балластом. Ими управлять спокойно можно. А вот что дает возможность управления? Или при create создается какой то дополнительный указатель?

Автор: kami 26.5.2010, 11:46
Цитата(PsiMagistr @  26.5.2010,  11:32 Найти цитируемый пост)
Но вот откуда берутся остальные девять штук?

Ниоткуда smile
В каждое событие, каждый метод любого компонента (и формы тоже) неявно передается указатель Self (сам, я).
Поэтому мы имеем возможность писать в событии формы (указатель на процедуру-обработчик события хранится в выделенной под компонент памяти, т.к. является одним из его полей), например,
Код

Caption:='12343455';
 среда поймет, что это идентично коду
Код

Self.Caption:='12343455';

В противном случае действительно нужно было бы хранить валидные указатели на все созданные компоненты самому.

Автор: PsiMagistr 27.5.2010, 10:29
Спасибо, большое, kami.

И только я подумал, что понял все-все-все, как вдруг...

Часть вторая. Утечка памяти или Муки ООП


Сегодня утром столкнулся с совершенно неожиданным для меня результатом.

1) Создайте проект. 
2) Добавьте в проект еще две формы.

Итого у нас Form1, Form2, Form3.

3) Отключите автосоздание Form2 и Form3. Свойство Visible обеих форм сделайте равным True.

 Автосоздаваться у нас будет только одна форма (Form1) Остальные будем создавать динамически.

4) Откройте окно кода Form1  Подключите к модулю Form1 модуль Form2 Для этого после ключевого слова implementation  добавьте:

uses Unit2;

 На форме Form1 нарисуйте кнопку. Смените ее Caption на "Создать окно". Запишите в обработчик щелчка:

Код

procedure TForm1.Button1Click(Sender: TObject);
begin
Form2:= TForm2.Create(Form1.Owner); 
end;


Мы динамически создаем Form2

Теперь перейдите в код Form2. Добавьте после раздела implementation   модуль третей формы:

uses Unit3;


 В обработчике Close запишите:

Код

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=caFree; //Разрушение формы-объекта при закрытии.
end;


На форму Form2 добавьте кнопку. Смените ее Caption на "Создание второй формы".

Запишите в обработчик OnClick;

Код

procedure TForm2.Button1Click(Sender: TObject);
begin
Form3 := TForm3.Create(Form2.Owner); //Внимание. Вы видите как вторая форма рождает третью форму и становится ее владельцем.
end;


Добавьте в обработчик Close третьей формы:

Код

procedure TForm3.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:= caFree;
end;


Запустите программу. Нажмите на кнопку. Породите вторую форму. Нажмите на кнопку второй формы и от второй породите третью...

Итак третья форма рождена от второй Вторая является ее владельцем. НО...


Закройте вторую форму. Владелец разрушен - сработало caFree обработчика-закрытия.

Вот только третья форма дочерняя осталась на экране. Ай как не хорошо. Ай плохо-плохо.... Как же так??? Ведь она должна разрушиться...

Автор: kami 27.5.2010, 12:46
Цитата(PsiMagistr @  27.5.2010,  10:29 Найти цитируемый пост)
Вы видите как вторая форма рождает третью форму и становится ее владельцем.

Неправда Ваша (с) чье-то.
Мы видим, что владельцем(Owner-ом) формы становится владелец текущей формы (Form1.Owner и Form2.Owner - владелец первой и второй форм). Владельцем первой формы (MainForm) является Application, соответственно владельцем второй становится тоже он. С третьей та же история. Чтобы владельцем создаваемой формы стала наша, текущая форма, при нажатии на кнопку нужно писать
Код

Form3:=TForm3.Create(Self);


Цитата(PsiMagistr @  27.5.2010,  10:29 Найти цитируемый пост)
Как же так??? Ведь она должна разрушиться...

Поменяйте параметр в Create и всё встанет на свои места.

Автор: PsiMagistr 27.5.2010, 13:04
kami,  Благодарю вас сердечно. Вы мне глаза открыли.

Стало быть Self в строке

Код

Form3:=TForm3.Create(Self); 


относится не к порождаемой третьей форме. Это Self текущей второй формы.

Правильно ли я Вас понял?

P.S. Огромное спасибо за внимание.


Допустим у нас в проекте только две формы. MainForm и Form2. Есть ли разница при запуске конструктора:

Код

Form2:=TForm2.Create(MainForm.Owner); 


и

Код

Form2:=TForm2.Create(Self); 


Как я понимаю, в первом случае владельцем второй формы становится владелец  MainForm (объект Application), а во втором сама MainForm




Автор: bems 27.5.2010, 16:38
Цитата(PsiMagistr @  27.5.2010,  13:04 Найти цитируемый пост)
относится не к порождаемой третьей форме. Это Self текущей второй формы.
Self означает тот объект, в методе которого он используется.
Поскольку 
Цитата(kami @  27.5.2010,  12:46 Найти цитируемый пост)
Form3:=TForm3.Create(Self);
 находится в методе Form2 то селф означает тут вторую форму.

Про указатели на созданные формы: владелец хранит массив ссылок на все обьъекты, которыми владеет. Этот массив доступен через свойство Components, объявленное в классе TComponent

Автор: PsiMagistr 27.5.2010, 17:20
bems, Спасибо большое-большое. Но я что-то совсем запутался. Ведь здесь:

Код


Form3:=TForm3.Create(Self); 

Мы используем Self в методе конструкторе третьей формы:

 TForm3.Create(Self) 

Стало быть это Self третьей формы.

Или же я перепутал Вы имели ввиду никакой не конструктор, а метод Click, кнопки расположенной на второй форме:

procedure TForm2.Button1Click(Sender: TObject); //Метод Click кнопки второй формы. 
begin
Form3 := TForm3.Create(self); //Внимание. Вы видите как вторая форма рождает третью форму и становится ее владельцем. 
end;



Но тогда возникает вопрос. Используется ли в этом случае Self самой второй формы или Self кнопки, расположенной на второй форме? Ведь это метод Click кнопки, а не формы.

Но если это Self кнопки, то у нас и владелец - кнопка... А может ли такое быть?

Или же TForm2.Button1Click - метод формы, а не кнопки? Больно странно пишется...Как будто метод формы. 
Или не так?


Автор: bems 27.5.2010, 18:44
PsiMagistr, Метод формы. До точки же - форма.

Автор: kami 27.5.2010, 20:28
Цитата(PsiMagistr @  27.5.2010,  13:04 Найти цитируемый пост)
относится не к порождаемой третьей форме. Это Self текущей второй формы.

Да. Если говорить более обобщенно, то в любом обработчике события, например
Код

procedure TMySuperControl.OnClick(...)

Self - это указатель на экземпляр TMySuperControl.
Цитата(PsiMagistr @  27.5.2010,  13:04 Найти цитируемый пост)
Как я понимаю, в первом случае владельцем второй формы становится владелец  MainForm (объект Application), а во втором сама MainForm

Да. Во втором случае (при условии, что Tform1 имеется в единственном экземпляре) запись аналогична
Код

Form2:=TFrom2.Create(Form1)

P.S. MainForm в моих сообщениях подразумевает под собой Application.MainForm.

Добавлено через 2 минуты и 45 секунд
bems, чуть опоздал smile

Автор: PsiMagistr 28.5.2010, 09:34
 bems, благодарю. kami, Точно так. Ведь  Form1 в данном случае -  указатель на тот же объект, что и Self.  СПА-СИ-БО!

С чего все началось:


Я сейчас на коленях с книжкой Фленова. Она довольно доходчива. Но встречаются заблуждения. В частности в главе "Инициализация окон" было написано: 

"В нашем случае (в конструктор формы) передается Owner - свойство, в котором хранится указатель на текущее окно".

Вместо того чтобы написать сермяжную правду:

"В нашем случае передается Owner - свойство, в котором хранится указатель на владельца текущего окна".





Автор: kami 28.5.2010, 12:12
Цитата(PsiMagistr @  28.5.2010,  09:34 Найти цитируемый пост)
Я сейчас на коленях с книжкой Фленова. 

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

Автор: PsiMagistr 28.5.2010, 12:47
kami, наверное Вам виднее. Стиль изложения Фленова мне нравится. Не засорен лишней терминологией. Я вообще начинал с Visual Basic. Но освоить ООП на нем можно только на весьма элементарном уровне, он и указателей то толком не поддерживает. Опять же наследование хромает. Про полиморфизм - молчу. По крайней мере, если не пользоваться хакерскими приемами. Не знаю как, но ухитрился написать на нем ролевую игру. Никакой графики делать не стал. Писал чисто от балды. Справочник - на колени и вперед. Кода там не слишком много вышло, но все равно путаешься. 

Потом закусил губы и заставил себя сесть на Object Pascal. Решил давать упор на ООП. Вообще ИМХО - знаешь ООП, считай большая часть языка в кармане. Книг у меня очень мало. Друзья далеки от программирования в принципе. Плюс (он же минус )) ) по состоянию здоровья не хожу. Ищу в сети книги, если, грубо говоря, врубаюсь в написаное - беру книжный вариант. Хорошей лит-ры по ООП вообще не видел. В основном простейшее: учат рисованию кнопок на форме, базовой работе с VCL. Я этот VСL вообще методом научного тыка осваиваю. )))   А чуть что посложнее - ошибок много начинается в книжках, только успевай вылавливать.

kami, а Вы сами по каким книжкам учились? Посоветуйте, если не трудно, наименее глючное. )))

Автор: kami 28.5.2010, 13:03
Цитата(PsiMagistr @  28.5.2010,  12:47 Найти цитируемый пост)
 Стиль изложения Фленова мне нравится. Не засорен лишней терминологией. 

Хозяин - барин. Но в случае продолжения обучения по его книгам прошу относиться скептически ко всему изложенному там, так же, как и в этом вопросе.

Цитата(PsiMagistr @  28.5.2010,  12:47 Найти цитируемый пост)
а Вы сами по каким книжкам учились?

Честно - не помню. Давно это было. Но, по содержанию было похоже на Архангельского (что-то типа "Программирование в Delphi 3") - тупой перевод хелпа с некоторыми примерами. Хотя, в той книге затрагивались аспекты разработки собственных компонентов.


Цитата(PsiMagistr @  28.5.2010,  12:47 Найти цитируемый пост)
Посоветуйте, если не трудно, наименее глючное. )))

Вроде, рекомендуют Тейксера и Пачеко, Марко Кэнту и Рея Конопку... Сам не читал ( smile ). Правда, если они изложены сухим языком, то начинать (именно начинать) учиться по ним - скучно. В этом случае, действительно уж лучше кто-нибудь типа Фленова, только при прочтении не стоит принимать всё за аксиому.

Автор: bems 28.5.2010, 17:29
Цитата(PsiMagistr @  28.5.2010,  12:47 Найти цитируемый пост)
Но освоить ООП на нем можно только на весьма элементарном уровне, он и указателей то толком не поддерживает
Зачем тебе указатели, если есть ООП? smile

Автор: PsiMagistr 30.5.2010, 09:58
bems, ООП на Бейсике это чистая жесть. От него одно название.

Автор: БелАмор 10.6.2010, 07:18
Возможно, вас заинтересует ещё это обсуждение:
http://www.delphikingdom.com/asp/answer.asp?IDAnswer=77021
И далее по ссылкам...

Автор: dvamaster 10.6.2010, 08:12
Цитата(PsiMagistr @  26.5.2010,  12:18 Найти цитируемый пост)
Указатель на объект-форму у нас только один и это переменная Form2


Она описана в Unit2.

Определи список форм хотябы в массиве (в Unit2 можешь закоментировать)
Код

var
//  Form2: TForm2;
  Form2List: array [0..9] of TForm2;


В классе Form2 укажи индекс на пизицию в массиве

Код

type
  TForm2 = class(...)
    ...
    FListIndex: Integer;
    ...
  end;


в событии OnCreate найди свободное место в массиве и присвой ему данный экземпляр

Код

  ...
  Form2List[EmptyIndex] := Self;
  FIndexList := EmptyIndex
  ...


в событии OnDestroy по индексу формы очисти занятое место в массиве (вызывать Destroy или Free не нужно, форма и так уничтожается)

Код

   Form2List[FIndexList] := nil;


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

Код

  for i := 0 to 9 do
    if Form2List[i] <> nil then
      Form2List[i].Free;


Добавлено через 1 минуту и 7 секунд
если максимальное число форм не известно, то лучше список реализовать на динамических массивах, и код соответсвенно переделать

Автор: PsiMagistr 10.6.2010, 10:13
Спасибо большое, БелАмор, dvamaster.


Макс. число форм известно. 

1) Форма- Главное меню игры.

2) Форма регистрация нового игрока.

3) Форма открытия персонажей. (думаю убрать, а код перенести в главное меню игры)

4) Форма карта локаций.

5) Форма-магазин.

6) Форма боевого полигона. (Все бои там с монстриками - там)

P.S. Только не запутаться бы. )))


Автор: PsiMagistr 10.6.2010, 12:00
Ради эксперимента создал Главную форму (MainForm) и Дочернюю форму (DochForm). Автосоздание   DochForm отключил. Visible DochForm := true;

На пузе MainForm - кнопка:

Код

procedure TMainForm.Button1Click(Sender: TObject);
begin
DochForm:=TDochForm.Create(MainForm); //Строим дочернюю форму.
MainForm.Hide; // Скрываем главную. 
end;


При закрытии дочерней формы пишем:

Код

procedure TDochForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree; //Уничтожаем
MainForm.Show; // Показываем главную форму.
end;


Ставим кнопку на дочернюю форму. Пишем:

Код

procedure TDochForm.Button1Click(Sender: TObject);
begin
Close; //Закрываем дочернюю форму. 
MainForm.Show; //Главную показать. Вроде эта строка не должна выполняться, мы же форму только что закрыли. Любопытно. 
end;


А при попытке в этой же кнопке написать:

Код

procedure TDochForm.Button1Click(Sender: TObject);
begin
DochForm.Free; // Весьма любопытно. Закрываем дочернюю форму. БАЦ - ОШИБКА. Хотя тот же Close, в котором Action := caFree записан работает на ура. 

MainForm.Show; //Главную показать. Вроде эта строка не должна выполняться, мы же форму только что закрыли, то есть из этой процедуры вышли или нет?. Любопытно.

end;





Автор: БелАмор 11.6.2010, 02:46
Цитата

Автосоздание   DochForm отключил. Visible DochForm := true;

Зачем устанавливать в дизайнере Visible=True ?
Привыкайте к тому, что обычно формы (кроме MDIChild) невидимы после создания и отображаются вручную через вызов Show или ShowModal.

Цитата

Ставим кнопку на дочернюю форму. Пишем:
procedure TDochForm.Button1Click(Sender: TObject);
begin
Close; //Закрываем дочернюю форму. 
MainForm.Show; //Главную показать. Вроде эта строка не должна выполняться, мы же форму только что закрыли. Любопытно. 
end;
 
А если форма закрывается по Alt-F4 ? Кто тогда будет показывать главную форму?
Есть MainForm.Show в обработчике FormClose?
А тогда зачем оно в обработчике нажатия кнопки?

И кстати, зачем вообще скрывать главную форму? Не лучше ли показывать вторую в модальном режиме через вызов ShowModal? (вот тут, кстати, ваш Visible=True и встанет поперёк дороги...)

Цитата

А при попытке в этой же кнопке написать:
 procedure TDochForm.Button1Click(Sender: TObject);
begin
DochForm.Free; // Весьма любопытно. Закрываем дочернюю форму. БАЦ - ОШИБКА. Хотя тот же Close, в котором Action := caFree записан работает на ура. 

MainForm.Show; //Главную показать. Вроде эта строка не должна выполняться, мы же форму только что закрыли, то есть из этой процедуры вышли или нет?. Любопытно.

end;
 
 Метод Button1Click - метод той формы, которую вы удаляете.
Купите красивую рамочку, распечатайте крупным шрифтом, поместите напечатанное в рамочку и повесьте на стену. Надпись - следующая:

Запрещается уничтожать объекты в их собственных обработчиках.

В данном конкретном случае:
Обработка клика по кнопке не начинается и не заканчивается вызовом назначенного вами обработчика. И до его вызова, и после - выполняется куча кода. Когда где-то в середине этого процесса, а конкретно, в вашем обработчике, уничтожается форма, то форма также уничтожает и кнопку, и вся дальнейшая работа идёт уже с уничтоженным объектом (кнопкой) и там возможно всё, что угодно.
Обратите внимание, что Button1Click - обработчик нажатия кнопки, но является методом формы.
По этому поводу, возможно, будет интересно почитать здесь: http://www.delphikingdom.com/asp/answer.asp?IDAnswer=68289
По поводу caFree смотрите ссылку: http://www.delphikingdom.com/asp/answer.asp?IDAnswer=75600
Применять описанный там способ я вам не рекомендую, но сопутствующие объяснения могут пригодиться.


Автор: PsiMagistr 11.6.2010, 07:10
БелАмор, ох, БИГ-БИГ, СЕНКС. Ну я могу в принципе исходники сюда поставить. Если вам не жалко будет посмотреть на это жалкое зрелище. 



Автор: PsiMagistr 11.6.2010, 07:20
БелАмор, В Close формы тоже стоит MainForm.Show. Если нетрудно просмотрите, пожалуйста, исходники. Буду очень благодарен. Кода там совсем немного, так как я только начал. Как по мне так все работает нормально. Но если у тебя все работает, это еще не значит, что ты святее Папы Римского.  smile 


Автор: БелАмор 11.6.2010, 13:05
Проект посмотрел, "рецензию" напишу позже.

Автор: PsiMagistr 11.6.2010, 14:19
БелАмор, 

спс, буду ждать.

Там правда почти не реализовано ничего. Разве что выбор пола smile и динамическое создание форм. 

Планов громадье. ) Работаю над созданием математической модели боя. Первичная, использованная мной в игрушке на VB была слишком примитивна. Впору писать диздок.

Автор: БелАмор 13.6.2010, 16:04
Цитата(PsiMagistr @  11.6.2010,  07:10 Найти цитируемый пост)
Если вам не жалко будет посмотреть на это жалкое зрелище.


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

Цитата(PsiMagistr @  11.6.2010,  14:19 Найти цитируемый пост)
Там правда почти не реализовано ничего.


"Спокойствие, только спокойствие!" Я найду, до чего докопаться...

По поводу стиля оформления кода

Настоятельно рекомендую поработать над стилем оформления кода.
В качестве основы я бы рекомендовал статью http://www.delphikingdom.com/asp/viewitem.asp?catalogid=802
Посмотрите также её http://www.delphikingdom.com/asp/articles_forum.asp?ArticleID=802
Рекомендую, в частности, обратить внимание на сообщение в обсуждении от 15-02-2007 00:37

По поводу пользовательского интерфейса

1. На мой взгляд, достаточно неудачной идеей является использование радиогруппы в форме запуска игры, поскольку любое действие становится двухэтапным, даже просто закрытие формы. Я бы в данном случае использовал обычные кнопки для всех пунктов. Кроме того, надписи на пунктах вводят в заблуждение, поскольку в них речь идёт только о персонаже. На самом деле, речь должна идти о персонаже и запуске игры с ним (поскольку именно так у вас сейчас реализовано). Так что, если что-то менять в рамках заданной вами модели, то в этой форме я бы сделал кнопки (названия условны):

Запуск игры с новым персонажем
Запуск игры с существующим персонажем
Выход


2. Окно с персонажами должно иметь кнопку "Отмена".

3. Запрос на подтверждение выхода в ситуции, когда выход не приводит к потере чего-либо (настроек, игры и т.д.), не имеет смысла и только раздражает.

4. Ситауция, когда после нажатия на что-то, появляется череда окон, каждое из которых является единственным отображаемым, лично мной воспринимается как некомфортная. На мой взгляд, гораздо лучше, когда есть некая "опорная" (она-же главная) форма, которая маячит на заднем плане, а всё, что происходит на переднем плане, относится именно к ней, например, выбор персонажа для игры где? Првильно, на главной форме... Поэтому на мой взгляд, логичнее было бы главной сделать именно форму с игровым полем, а уже там - меню или кнопки "Выбрать персонаж", "Начать новую игру" и т.д. В принципе, вы писали, что у вас есть мысль сделать главной формой именно игровую форму. Мысль вполне здравая. Причём я бы не стал предлагать выбор персонажа при открытии игровой формы. Пусть человек спокойно зайдёт, посмотрит на пустую игровую форму, осмотрится, и уже когда сам захочет, нажмёт кнопку "Начать новую игру" или "Выход".

По поводу алгоритмов и реализации

1. Сейчас у вас реализовано так: из главного меню открывается форма создания/выбора персонажа, а в обработчике закрытия этой формы открывается игровая форма. Это крайне неудачное решение, поскольку при просмотре обработчика главного меню эта логика полностью скрыта. Гораздо логичнее было бы в обработчике главного меню сначала показать форму выбора персонажа, после её закрытия проанализировать код возврата, и если было подтверждение, а не отмена, открыть игровую форму. Но для этого форма выбора персонажа должна отображаться в модальном режиме. Немодальные формы должны применяться только в случаях, когда небходим параллельный доступ к нескольким окнам. В случае окна выбора персонажа такой необходимости нет. Недостатком модального режима является нестандартное поведение при сворачивании, поэтому желательно, чтобы основная рабочая форма не была модальной. Большинство же вспомогательных форм логично делать модальными. Не забудьте у ваших форм в дизайнере вернуть Visible в False.

2. Рекомендации по коду на примере TFrmCreate.RGenderClick
  а) Если имеется несколько блоков кода, различающихся только небольшой частью, логично оставить только один блок, а разичающуюся часть обработать отдельно, возможно, заранее (но не обязательно), а в основном блоке использовать уже вычисленный результат.
  б) Если в коде много раз встречается одинаковая длинная конструкция (GBX.Controls[i] as TPanel), то логично завести отдельную переменную нужного типа, поместить в неё результат и далее использовать только её.
  в) Если один раз проверили тип объекта через оператор is (if GBX.Controls[i] is TPanel), то в дальнейшем нет необходимости использовать приведение типа с проверкой (GBX.Controls[i] as TPanel). Вместо этого логичнее применять приведение типа без проверки (TPanel(GBX.Controls[i])).
  г) При формировании сложных строк удобно использовать функцию Format. Это значительно облегчает воприятие кода.
  д) При формировании длинных строк, различающихся только в одном месте, бывает очень удобна функция IfThen из модуля StrUtil (есть варианты для числовых типов, см. справку). Поскольку при этом вычисляются оба значения, следует использовать эту функцию только для случаев, когда имеются оба готовых значения, в данном случае - литералы.

Код

// Исходный вариант (отформатирован)

procedure TFrmCreate.RGenderClick(Sender: TObject);
var
  i : Integer;
begin
  if RGender.ItemIndex = 1 then
  begin
    for i := 0 to GBx.ControlCount - 1 do
      if GBX.Controls[i] is TPanel then
      begin
        ((GBX.Controls[i] as TPanel).Controls[0] as TImage).Picture.LoadFromFile(
          ExtractFilePath(Application.ExeName)+ 'Avatars/Female/'+
          IntToStr((GBX.Controls[i] as TPanel).Tag) +'.jpg' );
        (GBX.Controls[i] as TPanel).Color := clSilver;
      end;
  end
  else
  begin
    for i := 0 to GBx.ControlCount - 1 do
      if GBX.Controls[i] is TPanel then
      begin
        ((GBX.Controls[i] as TPanel).Controls[0] as TImage).Picture.LoadFromFile(
          ExtractFilePath(Application.ExeName)+ 'Avatars/Male/'+
          IntToStr((GBX.Controls[i] as TPanel).Tag) +'.jpg' );
        (GBX.Controls[i] as TPanel).Color := clSilver;
      end;
  end;
end;

Код

// Доработанный вариант

procedure TFrmCreate.RGenderClick(Sender: TObject);
var
  i : Integer;
  FileName: String;
  Panel: TPanel;
begin
  FileName := ExtractFilePath(Application.ExeName) +
    IfThen(RGender.ItemIndex = 1, 'Avatars\Female\', 'Avatars\Male\') + '%d.jpg';

  for i := 0 to GBx.ControlCount - 1 do
    if GBX.Controls[i] is TPanel then
    begin
      Panel := TPanel(GBX.Controls[i]);
      (Panel.Controls[0] as TImage).Picture.LoadFromFile(
        Format(FileName, [Panel.Tag]));
      Panel.Color := clSilver;
    end;
end;

Автор: PsiMagistr 13.6.2010, 16:54
БелАмор, к сожалению пока не имею возможности повысить Вам репутацию, но сердечно благодарю. Вникаю в Ваш код.

(Плюсик за мной)

Автор: bems 13.6.2010, 16:59
PsiMagistr, не вопрос

Автор: PsiMagistr 14.6.2010, 13:37
Ребят, немного позорного оффтопа, но не открывать же новую тему из за него.

Скачал Дельфи 2009. (чтоб утечек не было, хочу поставить), но... Можно свою 6-ку не трогать, не удалять с компа? Ибо привык уже. Или конфликты будут?

Поставил на 6ку кучу всевозможных патчей. Думал мож поправит чего. Увы, как текло так и течет. Похоже еще хуже стало. (Проверял Fast MM44 на девственных проектах).

2010 чего то не охота.  Обалденные сис. требования. Я ахнул. Слона они запихнули, что ли?)


Автор: bems 14.6.2010, 16:47
Конфликтов быть не должно.
В 2010 исправлены многие баги 2009

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)