Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > Программа и интерфейс
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 21.07.2024, 20:47
Аватар для Guaho
Guaho Guaho вне форума
Начинающий
 
Регистрация: 27.08.2017
Сообщения: 178
Версия Delphi: Delphi7
Репутация: 10
По умолчанию Динамическое создание "тяжёлой" формы

Доброго времени суток, форумчане!
Долгое время я работал над проектом базы данных для своих нужд, и по итогу работы пришёл к выводу, что интерфейс программы надо серьёзно переделать. Старый интерфейс был многооконным: нужна какая-нибудь справочная таблица - вызываем окно с ней из главного окна. В работе это было удобно, но пришлось очень много всего дублировать в интерфейсе. И я пришёл к выводу, что новую версию программы нужно делать однооконной, со вкладками. Но возник вопрос - а как запускать такое однооконное приложение, содержащее уйму интерфейсных элементов? По старой базе я провёл анализ времени выполнения задач при запуске, и вышло, что суммарно процентов до 40 всего времени запуска приходилось на создание форм (то есть примерно 10...12 секунд). Поскольку форм там было много, и они создавались последовательно, можно было отображать прогресс-бар загрузки. Все свойства и события форм прописывались в дизайн-тайме, а при запуске для каждой из них вызывалось Application.CreateForm(...). Но как быть в случае однооконного приложения, у которого форма будет создаваться 10 секунд? Это же явно неприемлемо. Тем более что этот этап идёт первым, а потом уже идёт загрузка настроек и активация датасетов?
Возможен ли такой вариант: главное и единственное окно программы в дизайн-тайме содержит только PageControl-ы с пустыми вкладками - это для того, чтобы создание такой формы заняло минимальное время. И я делаю отдельные формы, которые будут относиться к проекту, но в ран-тайме создаваться не будут. И на этих формах я в дизайн-тайме размещаю интерфейсные элементы, которые впоследствии будут на вкладках единственного окна программы. Можно ли так сделать, чтобы динамически, при старте программы, в пустые вкладки главного окна вставлялись интерфейсные элементы из упомянутых заранее подготовленных в дизайн-тайме окон, причём желательно сразу группами - например, всё, что лежит на такой-то панели, затем - на другой, и т.д.?
Понимаю, что теоретически можно и всё "до атомов" руками прописать в процедуре запуска, но это ж застрелиться можно, да и неудобно при разработке. Хотелось бы всё-таки максимально упростить себе жизнь, чтобы больше сосредоточиться на функционале программы, и не тратить слишком много труда на код процедуры запуска.
Ответить с цитированием
  #2  
Старый 22.07.2024, 03:26
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,052
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Ну, делал нечто подобное. Только вместо страничек делал MDI приложение. Естественно, с контролем создания форм, что бы не создавать 2 одинаковые формы. Собственно, там был написан очень простой фреймворк для создания формы, добавления соотв. пункта меню и т.д. Ну и, конечно, все формы "растут" от базовой для обеспечения общего интерфейса управления. Естественно, к диалоговым окнам это не относится.
Ответить с цитированием
Этот пользователь сказал Спасибо lmikle за это полезное сообщение:
Guaho (23.07.2024)
  #3  
Старый 23.07.2024, 22:48
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,052
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Да, если не оставил идею делать главное окно программы на вкладках, то тут тебе могут помочь фреймы. Во время разработки фрейм выглядит как окно, но показать просто фрейм нельзя. Его надо "класть" на какой-то компонет наследник 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  
Старый 25.07.2024, 20:16
Аватар для Guaho
Guaho Guaho вне форума
Начинающий
 
Регистрация: 27.08.2017
Сообщения: 178
Версия Delphi: Delphi7
Репутация: 10
По умолчанию

О, спасибо! Опять я забыл про фреймы. Правда, когда-то оооочень давно обжёгся с ними - что-то там слетало периодически, подробностей не помню. Но то была среда Delphi5 ещё. И другие люди на фреймы жаловались, читал где-то. Но попробовать надо.
Отдизайнить все фреймы, а потом в OnCreate их все создать и положить на соотв. вкладки - отличная идея! Автоматизировать, возможно, не буду, а просто руками пропишу в загрузчике. Ибо описанная методика сложновата для меня, как для начинающего (хотя уже лет двадцать прошло с момента, как начал, уровень "погружения" в предмет почти никакой). Хотя, кто знает, там посмотрим.
А MDI мне не подходит, ибо всегда нужен режим "сейчас активно только вот это, остальное - запрещено".
В любом случае, спасибо! И у меня ещё один вопрос, собственно, относящийся всё к той же процедуре загрузки, а именно - как отображать прогресс-бар. Я вижу это так: создаётся главное окно с пустыми PageControl-ами, но полностью прозрачное (пока). Из него показывается форма с логотипоом и прогресс-баром, и происходит процесс создания фреймов на главной форме, которая продолжает оставаться невидимой из-за полной прозрачности. По окончании процесса окно с логотипом закрывается, и главная форма становится видимой. С виду вроде безошибочная логика, по идее должно работать.
Ответить с цитированием
  #5  
Старый 26.07.2024, 06:58
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,052
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Ну, вообще-то, 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  
Старый 29.07.2024, 23:14
Аватар для Guaho
Guaho Guaho вне форума
Начинающий
 
Регистрация: 27.08.2017
Сообщения: 178
Версия Delphi: Delphi7
Репутация: 10
По умолчанию

Большое спасибо за такой подробный ответ! Хотя и выглядит немного сложновато для моего уровня, но в целом - почти понятно. Сохранил себе этот код вместе с комментариями. Пока не уверен, пойду ли я по такому универсальному пути, т.к. пока только прикидываю, как по-новому построить приложение. Да и вкладок и фреймов у меня будет не сильно много, в принципе можно и руками прописать, чтобы не усложнять. Для пробы создал два фрейма, накидал на них группу интерфейсных элементов из текущего проекта, и в 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;
Где p1 и p2 - пустые вкладки главного PageControl-а. Всё отлично - содержимое фреймов появилось на нужных вкладках. Обработчики событий, прописанные для фреймов, нормально работают. В общем, как будто всё ок! Спасибо ещё раз за помощь!
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 10:52.


 

Сайт

Форум

FAQ

RSS лента

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2023

ВКонтакте   Facebook   Twitter