|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#1
|
||||
|
||||
Динамическое создание "тяжёлой" формы
Доброго времени суток, форумчане!
Долгое время я работал над проектом базы данных для своих нужд, и по итогу работы пришёл к выводу, что интерфейс программы надо серьёзно переделать. Старый интерфейс был многооконным: нужна какая-нибудь справочная таблица - вызываем окно с ней из главного окна. В работе это было удобно, но пришлось очень много всего дублировать в интерфейсе. И я пришёл к выводу, что новую версию программы нужно делать однооконной, со вкладками. Но возник вопрос - а как запускать такое однооконное приложение, содержащее уйму интерфейсных элементов? По старой базе я провёл анализ времени выполнения задач при запуске, и вышло, что суммарно процентов до 40 всего времени запуска приходилось на создание форм (то есть примерно 10...12 секунд). Поскольку форм там было много, и они создавались последовательно, можно было отображать прогресс-бар загрузки. Все свойства и события форм прописывались в дизайн-тайме, а при запуске для каждой из них вызывалось Application.CreateForm(...). Но как быть в случае однооконного приложения, у которого форма будет создаваться 10 секунд? Это же явно неприемлемо. Тем более что этот этап идёт первым, а потом уже идёт загрузка настроек и активация датасетов? Возможен ли такой вариант: главное и единственное окно программы в дизайн-тайме содержит только PageControl-ы с пустыми вкладками - это для того, чтобы создание такой формы заняло минимальное время. И я делаю отдельные формы, которые будут относиться к проекту, но в ран-тайме создаваться не будут. И на этих формах я в дизайн-тайме размещаю интерфейсные элементы, которые впоследствии будут на вкладках единственного окна программы. Можно ли так сделать, чтобы динамически, при старте программы, в пустые вкладки главного окна вставлялись интерфейсные элементы из упомянутых заранее подготовленных в дизайн-тайме окон, причём желательно сразу группами - например, всё, что лежит на такой-то панели, затем - на другой, и т.д.? Понимаю, что теоретически можно и всё "до атомов" руками прописать в процедуре запуска, но это ж застрелиться можно, да и неудобно при разработке. Хотелось бы всё-таки максимально упростить себе жизнь, чтобы больше сосредоточиться на функционале программы, и не тратить слишком много труда на код процедуры запуска. |
#2
|
|||
|
|||
Ну, делал нечто подобное. Только вместо страничек делал MDI приложение. Естественно, с контролем создания форм, что бы не создавать 2 одинаковые формы. Собственно, там был написан очень простой фреймворк для создания формы, добавления соотв. пункта меню и т.д. Ну и, конечно, все формы "растут" от базовой для обеспечения общего интерфейса управления. Естественно, к диалоговым окнам это не относится.
|
Этот пользователь сказал Спасибо lmikle за это полезное сообщение: | ||
Guaho (23.07.2024)
|
#3
|
|||
|
|||
Да, если не оставил идею делать главное окно программы на вкладках, то тут тебе могут помочь фреймы. Во время разработки фрейм выглядит как окно, но показать просто фрейм нельзя. Его надо "класть" на какой-то компонет наследник TWinControl (окно, страничку TabSheet, панель и т.д.).
Т.е. ты можешь отдизайнить все фреймы, а потом в OnCreate просто их все создать и положить на соотв. вкладки. Для автоматизации этого процесса можно сделать регистрацию таких фреймов: 1. Создаем синглтон на основе, например, TList. В нем определяем тип в котором храним имя класса и имя вкладки (ну и еще что, если нужно) 2. В каждом модуле фрейма в секции initialization регистрируем этот фрейм в нашем синглтоне. 3. В главной форме пробегаемся по синглтону и создаем все вкладки с фреймами на них. Как создавать класс по имени можно посмотреть, например, тут: https://stackoverflow.com/questions/...-from-a-string (тут, возможно, придется перекрыть конструктор Create, т.к. RTTI, вроде, умеет вызывать только дефолтный конструктор). Ну или придумать свой механизм... Последний раз редактировалось lmikle, 24.07.2024 в 02:17. |
Этот пользователь сказал Спасибо lmikle за это полезное сообщение: | ||
Guaho (25.07.2024)
|
#4
|
||||
|
||||
О, спасибо! Опять я забыл про фреймы. Правда, когда-то оооочень давно обжёгся с ними - что-то там слетало периодически, подробностей не помню. Но то была среда Delphi5 ещё. И другие люди на фреймы жаловались, читал где-то. Но попробовать надо.
Отдизайнить все фреймы, а потом в OnCreate их все создать и положить на соотв. вкладки - отличная идея! Автоматизировать, возможно, не буду, а просто руками пропишу в загрузчике. Ибо описанная методика сложновата для меня, как для начинающего (хотя уже лет двадцать прошло с момента, как начал, уровень "погружения" в предмет почти никакой). Хотя, кто знает, там посмотрим. А MDI мне не подходит, ибо всегда нужен режим "сейчас активно только вот это, остальное - запрещено". В любом случае, спасибо! И у меня ещё один вопрос, собственно, относящийся всё к той же процедуре загрузки, а именно - как отображать прогресс-бар. Я вижу это так: создаётся главное окно с пустыми PageControl-ами, но полностью прозрачное (пока). Из него показывается форма с логотипоом и прогресс-баром, и происходит процесс создания фреймов на главной форме, которая продолжает оставаться невидимой из-за полной прозрачности. По окончании процесса окно с логотипом закрывается, и главная форма становится видимой. С виду вроде безошибочная логика, по идее должно работать. |
#5
|
|||
|
|||
Ну, вообще-то, splash screen показывается из файла проекта, а не из главной формы. Более того, если ты будешь создавать странички/фреймы в OnCreate, то и делать форму полупрозрачной нет необходимости, т.к. она и так в этот момент не видна. Как я понимаю, создание фрейма занимает какое-то время. А вот созщдать вкладку и "кинуть" на нее фрейм практически мгновенно. Тогда делаем примерно так:
Файл проекта (.dpr) Код:
procedure AddFrameToColleaction(AFrame : TBaseFrame; ASplash : TSplashForm); begin GetListOfFrames().Add(AFrame); ASplash.ProgressBar.Progress := Splash.ProgressBar.Progress + 1; ASplash.Invalidate; // Тут не помню какой именно метод вызывать для перерисовки окна end; var Splash : TSplashForm; begin Splash := TSplashForm.Create(Nil); Try Splash.Show; Splash.ProgressBar.Max := 15; Splash.ProgressBar.Progress := 0; AddFrameToColleaction(TCustomFrame1.Create(Nil),Splash); AddFrameToColleaction(TCustomFrame2.Create(Nil),Splash); ... AddFrameToColleaction(TCustomFrame15.Create(Nil),Splash); Splash.Close; Finally Splash.Free; End; // ... далее создание дополнительных форм и запуск программы (Application.Run) end. Модуль с масивом фреймов Код:
Unit ListOfFrames; interface function GetListOfFrames : TList<TBaseFrame>; implementation var AList : TList<TBaseFrame>; function GetListOfFrames : TList<TBaseFrame>; begin if Not Assigned(AList) Then AList := TList<TBaseFrame>.Create; Result := AList; end; initialization AList := Nil; finalization IF Assigned(AList) Then FreeAndNil(AList> end. Базовый фрейм Код:
unit TBaseFrame; interface type TBaseFrame = class(TFrame) public function GetTabName : String; virtual; abstract; // перекрыть в каждом наследнике, вощвращаем имя вкладки end ... Главная форма Код:
procedure TMainForm.FormCreate(Sender : TObject); var I : Integer; Sheet : TTabSheet; Frame : TBaseFrame; begin For I := 0 To GetListOfFrames.Count-1 Do Begin Frame := GetListOfFrames().Items[i]; Sheet := TTabSheet.Create(Self); Sheet.Caption := Frame.GetTabName; Frame.Parent := Sheet; Frame.Aligh := alClient; Sheet.PageControl := PageControl1; End; Ну вот как-то так. Код не проверял, просто набросал саму идею. Последний раз редактировалось lmikle, 26.07.2024 в 18:08. |
Этот пользователь сказал Спасибо lmikle за это полезное сообщение: | ||
Guaho (29.07.2024)
|
#6
|
||||
|
||||
Большое спасибо за такой подробный ответ! Хотя и выглядит немного сложновато для моего уровня, но в целом - почти понятно. Сохранил себе этот код вместе с комментариями. Пока не уверен, пойду ли я по такому универсальному пути, т.к. пока только прикидываю, как по-новому построить приложение. Да и вкладок и фреймов у меня будет не сильно много, в принципе можно и руками прописать, чтобы не усложнять. Для пробы создал два фрейма, накидал на них группу интерфейсных элементов из текущего проекта, и в OnCreate главной формы написал:
Код:
procedure Tfm_main.FormCreate(Sender: TObject); var Frame : TFrame; begin Frame := Tfr1.Create(Self); Frame.Parent := fm_main.p1; Frame.Align := alClient; Frame := Tfr2.Create(Self); Frame.Parent := fm_main.p2; Frame.Align := alClient; end; |