Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Как вы создаете окна в приложении (runtime)?


Автор: ama_kid 31.5.2007, 13:39
Очень тут меня заинтересовал вопрос, который вроде бы для себя решил уже давно... Ан нет... 
Суть вопроса в следующем: как вы в своих приложениях создаете необходимые для себя вспомогательные окна в рантайме? Варианты собсно даны выше...
Сам я скажу, что когда-то давно, еще на заре изучения мной дельфей, я писал приложение, несложное для меня в данный момент, но тогда опупеть какое сложное. Там было около 10 окошек, которые я создавал с помощью Application.CreateForm в обработчиках меню и кнопок главной формы... И когда я уже его практически закончил, вдруг понадобилось перед вызовом главной формы сделать вызов вспомогательной настроечной формы... Написав что-то типа Application.CreateForm(fmOptions,TfmOptions); перед созданием главной формы я получил то, что и должен был получить - после закрытия того окошка, приложение завершалось. Щас-то я знаю о наличии свойства FMainForm объекта TApplication, а тогда это меня немало удивило... Погуглив проблему, я принял решение создавать его по другому:
Код

 ...
 begin
  Application.Initialize;
  try
   fmOptions.ShowModal;  
  except
   fmOptions:=TfmOptions.Create(nil);
   fmOptions.ShowModal;
  end;
  Application.CreateForm(TfmMain, fmMain);
  Application.Run;
 end.
Должен сказать, что в тот раз этот кусок кода я втупую передрал с какого-то форума, не особо понимая его смысла... Впоследствии я разобрался что-почем, но привычка создавать формы таким образом настолько у меня укоренилась, что я стал всегда так писать - для всех форм, не являющимися главными... И собственно, уже не один десяток моих приложений работает именно по такой схеме...
И вот не далее как сегодня один из уважаемых мной форумчан по поводу такого кода высказался в отрицательном аспекте. Мне это показалось удивительным, ибо на мой взгляд такой код не несет в себе принципиальных ошибок, использует только разрешенные конструкции и не приводит к утечкам памяти, более того - защищает от них (при корректном использовании). Но мое мнение может быть ошибочным, и не прислушаться к голосу других я не имею права. Что скажет по этому поводу многоуважаемый ALL?

P.S. Понимаю, что вопрос не особо принципиальный - поэтому просьба отнестись к данной теме как к разговору об индивидуальных предпочтениях...

Автор: Sunvas 31.5.2007, 13:46
Я создаю воспомагательные формы 2 вариантом, но проголосовал за первый, т.к. в последнее время пишу одноформенные приложения.
Цитата(ama_kid @  31.5.2007,  13:39 Найти цитируемый пост)
один из уважаемых мной форумчан по поводу такого кода высказался в отрицательном аспекте.

Ссылку в студию!

Автор: Snowy 31.5.2007, 13:53
Первый способ использую для обычных окон.
Второй использую исключительно для модальных окошек, которые нужно показать и сразу убить.

Добавлено @ 13:56
ama_kid, а кто будет убивать за тебя форму?
И зачем пытаться вызывать форму, которая заведомо ещё не создана?

Автор: ama_kid 31.5.2007, 13:59
Цитата(Snowy @  31.5.2007,  13:53 Найти цитируемый пост)
ama_kid, а кто будет убивать за тебя форму?
Сама форма себя убивает... Action:=caFree;

Автор: Snowy 31.5.2007, 14:12
Цитата(ama_kid @  31.5.2007,  13:59 Найти цитируемый пост)
Сама форма себя убивает... Action:=caFree;
Это не красиво.
Код должен быть строгим и логичным, а не вызывать сомнения.
Код

 begin
  Application.Initialize;
  with TfmOptions.Create(nil) do
  begin
    ShowModal;
    Free;
  end;
  Application.CreateForm(TfmMain, fmMain);
  Application.Run;
end.
Чётко обозначили создание, работу, убийство.

Автор: pseud 31.5.2007, 14:12
1. Application.CreateForm(TMainForm, MainForm);

использую только для главного окна, иногда для окон, которые присутствуют в приложении в единственном экземпляре и необходимы как и главное коно всегда (например TSettingsForm). И этот код присутствует только в dpr-файле.

2. fmFormTest:=TfmFormTest.Create(Self);
Использую во всех остальных случаях. При чем никогда не использую глобальную переменную fmFormTest: TfmFormTest, чтоб не создавать каши, сугубо через локальные перемнные.
Код

var
  frm: TTestForm;
begin
  frm := TTestForm.Create(Self);
  try
    frm.{Настрока разная}
    if frm.ShowModal = mrOK then
      {чего-нибудь};
  finally
    frm.Free;
  end;
end;


иногда даже так
Код

  with TTestForm.Create(Self) do
  try
    {Настрока разная}
    ShowModal = mrOK then
      {чего-нибудь};
  finally
    Free;
  end;


3. Последнее время делаю окна самостоятельные с отображением на таскбаре винды. Т.е. не MDI приложение, не модальное, а что-то типа MSOutlook'а. Работаю через "фабрику форм". Ну и соответсвенно метод фабрики что-то типа:
Код

var
  frm: TMyForm;
begin
  {ищу не создана ли такая форма уже}
  {если создана то}
  {показываю и Exit}
  frm := TMyForm.Create(Self{, другие параметры});
  if frm <> nil then
  begin
    frm.Name = {Генерю для дальнейшей удобной работы};
    frm.Show
  end else
    TException.Create('Ругаюсь');
end;

Автор: ama_kid 31.5.2007, 14:23
Цитата(Snowy @  31.5.2007,  14:12 Найти цитируемый пост)
Код должен быть строгим и логичным, а не вызывать сомнения.
Это все прекрасно... Но что делать с формами, которые могут быть в нескольких экземплярах? Или которые должны быть немодальными?

Цитата(pseud @  31.5.2007,  14:12 Найти цитируемый пост)
При чем никогда не использую глобальную переменную
А если между формами могут быть взаимосвязи? Понятно, что это - признак плохого проектирования, но я пару раз реально сталкивался с ситуевиной, когда ну никак... вот надо использовать взаимосвязи и все тут...

Автор: Bose 31.5.2007, 14:27
1) Application.CreateForm(TfmMain, fmMain); использую в основном для главной формы
2) Все модальные создаю приемерно так:
Код

function ShowModalForm2: Tmodalresult;
var 
  tmpForm2: Tform2
begin
  tmpForm2:=Tform2.Create(self); //  иногда вместо self пишу nil или Application
  try
    result:=tmpForm2.ShowModal;
  finally
    tmpForm2.Release;
  end;
end;

3) Немодальные формы создаю аналогично
Код

procedure ShowNonModalForm3;
begin
  with Tform2.Create(self) do //  иногда вместо self пишу Application  
     Show;
end;

формы освобождаются через 
Код

  CloseAction := caFree;

4) Через CreateWindow я ещё не создавал ни одного окна и пока что не планирую.

Добавлено через 3 минуты и 28 секунд
Цитата(ama_kid @  31.5.2007,  14:23 Найти цитируемый пост)
А если между формами могут быть взаимосвязи? Понятно, что это - признак плохого проектирования, но я пару раз реально сталкивался с ситуевиной, когда ну никак... вот надо использовать взаимосвязи и все тут...

В таких случаях лучше делать связи через свойства(properties). Глобальные переменные в таких случаях способны только добавить путаницы. 

Автор: Snowy 31.5.2007, 14:31
Цитата(ama_kid @  31.5.2007,  14:23 Найти цитируемый пост)
Но что делать с формами, которые могут быть в нескольких экземплярах? Или которые должны быть немодальными?
А у таких должен быть Owner.

Автор: aktuba 31.5.2007, 14:36
Цитата(Snowy @ 31.5.2007,  15:31)
Цитата(ama_kid @  31.5.2007,  14:23 Найти цитируемый пост)
Но что делать с формами, которые могут быть в нескольких экземплярах? Или которые должны быть немодальными?
А у таких должен быть Owner.

Или Name, Tag и т.д...

Автор: pseud 31.5.2007, 14:43
Цитата(pseud @  31.5.2007,  14:12 Найти цитируемый пост)
При чем никогда не использую глобальную переменную 


Цитата(ama_kid @  31.5.2007,  14:23 Найти цитируемый пост)
А если между формами могут быть взаимосвязи? Понятно, что это - признак плохого проектирования, но я пару раз реально сталкивался с ситуевиной, когда ну никак... вот надо использовать взаимосвязи и все тут...


Ну и будешь иметь в проекте несколько форм класса TMyForm, но одна из них будет глобальная MyForm, а к остальным будешь обращаться через <поиск окон> . Согласись каша.

Никогда не рекомендую делать TMyForm.Create(nil), т.к. если сами ее не удалите, то останется в памяти до перезагрузки винды (IMHO).
Указывайте Self, тогда хотя бы объект, который Self позаботиться об уничтожении. Или Application.

Как я уже писал - использую "фабрику форм", она и заботиться обо всем.

Кстати, если будете указывать Application, то и проще потом искать форму нужную (если не знаете имени), через Application.Components.
Если зоздаете Create(MainForm), то через MainForm.Components и т.д.

Автор: Snowy 31.5.2007, 14:52
pseud, ндя...

Цитата(pseud @  31.5.2007,  14:43 Найти цитируемый пост)
если сами ее не удалите, то останется в памяти до перезагрузки винды (IMHO).
Нет. Но это не значит, что убирать за собой не нужно.

Цитата(pseud @  31.5.2007,  14:43 Найти цитируемый пост)
стальным будешь обращаться через <поиск окон> . Согласись каша.
Не соглашусь. Help по TScreen табе в руки ;-)

Цитата(pseud @  31.5.2007,  14:43 Найти цитируемый пост)
 проще потом искать форму нужную (если не знаете имени), через Application.Components
А вот так как раз не надо делать.
Это плохой способ.
Правильный - через TScreen!

Автор: Bose 31.5.2007, 15:00
Цитата(pseud @  31.5.2007,  14:43 Найти цитируемый пост)
Кстати, если будете указывать Application, то и проще потом искать форму нужную (если не знаете имени), через Application.Components.Если зоздаете Create(MainForm), то через MainForm.Components и т.д.

Я раньше тоже так мучался. Пока не узнал, что через Screen.Forms можно добраться почти до любой формы. Так что... 

Цитата(Snowy @  31.5.2007,  14:52 Найти цитируемый пост)
 Help по TScreen табе в руки ;-)

 smile 

Автор: pseud 31.5.2007, 15:03
Цитата(Snowy @  31.5.2007,  14:52 Найти цитируемый пост)
А вот так как раз не надо делать.
Это плохой способ.
Правильный - через TScreen! 


Да я тут погорячился. Согласен.
Изучил код своей "фабрики форм" и действительно ее метод GetEntityForm работает со Screen. Давно писал...


Автор: ama_kid 31.5.2007, 15:49
Цитата(Snowy @  31.5.2007,  14:31 Найти цитируемый пост)
А у таких должен быть Owner.
Ну это-то не подвергается сомнению smile Я написал nil просто для примера, понятно, что от главного окна я всегда создаю вспомогательное в параметром Self (или другим, необходимым в данный момент)... Я просто к тому, что не всегда имеется возможность вызвать Free непосредственно после вызова команды Show (более того, я не могу сейчас с ходу сообразить пример, когда это возможно), как в случае с ShowModal, поэтому альтернативы оператору Action:=caFree (который "некрасив") пока не вижу (точнее, вижу - вызов Free в другой области видимости, но это для меня практически неприемлимо из религиозных соображений)...
Цитата(pseud @  31.5.2007,  14:43 Найти цитируемый пост)
 т.к. если сами ее не удалите, то останется в памяти
Тут я пожалуй добавлю, что в своих приложениях я всегда все за собой подчищаю и все приложения у меня снабжены модулем MemCheck, который матерится ругательными словами, если остался хоть байт упущенной памяти... Пример в топике написан от руки, просто для того, чтобы показать принципиальное понимание мной изначального вопроса, который звучит так - "что в этом неправильного/некорректного/опасного и т.д.?"...

Автор: MetalFan 31.5.2007, 15:56
а не проще создать класс-менеджер форм определенного типа, который будет уметь их создавать, убивать, вести их список...

Цитата(ama_kid @  31.5.2007,  13:39 Найти цитируемый пост)
И вот не далее как сегодня один из уважаемых мной форумчан по поводу такого кода высказался в отрицательном аспекте.

видимо это был я.

аналог приведенного в шапке кода
Цитата(ama_kid @  31.5.2007,  13:39 Найти цитируемый пост)
 try
   fmOptions.ShowModal;  
  except
   fmOptions:=TfmOptions.Create(nil);
   fmOptions.ShowModal;
  end;


действительно поверг меня в шок... еще бы BSOD вызывали для проверки ;) шучу
просто по логике в except секции должен находиться код, который может попытаться проанализировать и исправить возникшие ошибки...
он должен выполнять минимум простейших действий, которые сами по себе не могут привести к дальнейшим ошибкам...

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

в шутку предлагаю модифицировать код:
Код

try
   fmOptions.ShowModal;  
  except
  try
    fmOptions:=TfmOptions.Create(nil);
    fmOptions.ShowModal;
   except
      try
        MessageBox(0,'Ну савсем п....ц настал!'....);
       except
           HALT(0);
       end;     
    end;
  end;


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

Автор: ama_kid 31.5.2007, 17:21
Цитата(MetalFan @ 31.5.2007,  16:09)
гораздо страшнее возникающее AV при вызове у "мертвой" формы ShowModal
Вот тут мы и упираемся в корни моих непоняток: чем это страшно, если AV - обработано? Неужели тем, что 
Цитата
неизвестно где и когда там происходит нарушение доступа к памяти... а если по тому адресу уже что-то располагается?
? Но какая разница, что там располагается? Если там располагается не то, что мне надо - я сознательно напарываюсь на исключение и делаю то, что мне надо - создаю форму в данном случае... Вот тут и возникает основной вопрос - может ли там располагаться что-нибудь, благодаря чему я получу "вилку" и из-за чего становится оправданным использование дополнительного класса-менеджера (который надо сопровождать по мере развития проекта) или даже обычных доп.флагов?

Автор: MetalFan 31.5.2007, 22:20
Цитата(ama_kid @  31.5.2007,  17:21 Найти цитируемый пост)
 чем это страшно, если AV - обработано?

потому, что неизвестно, какие данные могут быть повреждены в случае AV.
еще раз повторю - "гашение" Access Violation с помощью try..except блока - не есть 
хорошее решение. 
ВДУМАЙСЯ в смысл ошибки. AV - это не игрушка. AFAIK в случае возникновения нарушение доступа 
в драйвере система уходит в bsod...
AV - нарушение доступа при чтении или записи в неподготовленную(непредназначенную) для этого область памяти...
а что, если часть методов "умершего" экземпляра класса отработает, "успешно" поназапишет чего-нибудь 
в те части памяти, что используются уже другими экземплярами... и в самом конце вдруг все-таки свалится с AV.
ты радостно создаешь новую форму в except блоке... но вдруг у тебя начинают невзначай подглючивать другие
формы/классы... или все будет в шоколаде, а приложение у клиента начнет валиться? а?
или в след.версии делфи что-то поменяется в методе ShowModal и твое приложение попросту откажется нормально работать...

в общем я думаю надо сделать отдельную тему с голосовалкой,
в которой спросить, что "корректно ли использовать AV в качестве уведомления об несозданном/убитом экземпляре класса?"
я думаю, что ответ "нет" все-таки перевесит...




Автор: Igor_thief 18.6.2007, 16:44
Всем очень внимательно надо почитать следующее http://delphi.about.com/od/adptips2005/qt/nilselfapp.htm и ссылки которые есть в статье! Жду комментов статьи? Мне лично не очень понятно почему при создании немодальных форм не стоит передавать в create(self (главную форму)), а лучше передавать Application. Если передать Application, то тогда форму потом не надо освобождать в деструкторе главной формы. Форму уделит Application еще до дестроя главной формы.

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