Перейти к содержанию
Fire Monkey от А до Я
  • 0

Как сохранить и восстановить Форму со всеми UI компонентами?


LBox

Вопрос

Опубликовано (изменено)

Всем привет ✌️
Delphi 12 / Windows10
В рамках своего хобби посвящаю свободное время изучению Delphi и на данный момент меня интересует тема сохранения и полного восстановления пользовательского интерфейса.
Честно говоря, я не вижу смысла изучать что-либо дальше, пока не освоюсь в этой теме, ибо любая программа должна иметь опцию сохранения и загрузки проект файлов.
Так вот поискав в интернете, я нашел 3 метода, которые посоветовали использовать специалисты Delphi.
1. Memory Stream
2. ini-файл
3. Fire Monkey Save State

Я выбрал метод Memory Stream, потому что он показался мне простым, полным и понятным.

Сразу скажу, что в итоге мне удалось записать и восстановить Форму в/из *.txt-файла с помощью Stream.WriteComponent(Self).
Но в процессе изучения этой темы я столкнулся с двумя проблемами, решение которых я не могу найти.

Проблема 1. (см. рис.)
Когда я сохраняю всю форму через Stream.WriteComponent(Self) в файл *.txt, все события, назначенные компонентам, корректно записываются в файл и восстанавливаются из файла с помощью Stream.ReadComponent(Self). Но если я передаю только Main_Panel через Stream.WriteComponent(Main_Panel), то по какой-то причине все события, назначенные компонентам, не записываются в файл и в результате восстановление из файла оказывается неполным.
Пожалуйста, объясните мне, почему Stream.WriteComponent() ведет себя так странно? Или я что-то делаю не так?

Проблема 2
В результате из-за проблемы 1 я вынужден сохранять всю форму, чтобы восстановление было полным без потери назначенных событий, но после загрузки форма как будто зависает, то есть ее невозможно переместить, скрыть, открыть или закрыть, пока на форме не сработает какое-либо событие, назначенное дочерним компонентам.
Как вы думаете, почему Форма ведет себя таким образом после загрузки компонентов из файла?

Конечно, Проблема 2 не так критична, так как 98% пользователей после загрузки файла проекта выполняют действия внутри Формы, а не с самой Формой, но меня это все равно раздражает и я очень хочу это исправить.

Моя тестовая программа очень простая. На форме в центре находится «Главная панель», внутри которой находится «Панель 1». Сверху находятся две кнопки, управляющие шириной «Панели 1».
Снизу находятся кнопки «Сохранить поток», «Загрузить поток», «Добавить кнопку на Панель 1», «Удалить кнопку с Панели 1». (см. рис.)

Вот часть кода отвечающая за сохранение и восстановления Формы.

////////////////////////////////////////////////////////////////////////////////
procedure TMain_Form.FormCreate(Sender: TObject);
begin
  AppPath:= ExtractFilePath(GetCurrentDir());
  AppDatFile:= TPath.Combine(AppPath, 'formdata.txt');
end;

////////////////////////////////////////////////////////////////////////////////
procedure TMain_Form.SaveButtonClick(Sender: TObject);
var
  FileStream : TFileStream;
  MemStream : TMemoryStream;
begin
  MemStream := nil;
  FileStream := TFileStream.Create(AppDatFile, fmCreate);
  try
    MemStream := TMemoryStream.Create;
    MemStream.WriteComponent(Self); // Main_Panel
    MemStream.Position := 0;
    ObjectBinaryToText(MemStream, FileStream);
  finally
    MemStream.Free;
    FileStream.Free;
  end;
end;

////////////////////////////////////////////////////////////////////////////////
procedure TMain_Form.LoadButtonClick(Sender: TObject);
var
  i : Integer;
  FileStream : TFileStream;
  MemStream : TMemoryStream;
begin
  MemStream := nil;
  if FileExists(AppDatFile) then
  begin
    for i := pred(Self.ChildrenCount) downto 0 do
    Self.Children[i].Free;

    FileStream := TFileStream.Create(AppDatFile, fmOpenRead);
    try
      MemStream := TMemoryStream.Create;
      ObjectTextToBinary(FileStream, MemStream);
      MemStream.Position := 0;
      MemStream.ReadComponent(Self);
    finally
      MemStream.Free;
      FileStream.Free;
    end;
  end;
  Self.Name:= 'Main_Form';
end;

 

Delphi_Forum_Post_2025_01.jpg

Delphi_Forum_Post_2025_02.jpg

Изменено пользователем LBox

Рекомендуемые сообщения

  • 0
Опубликовано

0. Собственно для каких целей этот изврат? Самообразование, но зачем если это нигде не практикуется?

1. Потому что методы не принадлежат Panel, а только TMain_Form и без TMain_Form сохранение событий не имеет смысла

2. Ты загружаешь уже загруженную форму? не думал куда деваются старые контролы? уничтожаются ли они? тупо перезаписываются? или же

  • 0
Опубликовано
Цитата

0. Собственно для каких целей этот изврат? Самообразование, но зачем если это нигде не практикуется?

Цели сохранить и восстановить Форму со всеми ее дочерними компонентами и програмной логикой. Другими словами реализовать опцию сохранения и загрузки проджект файлов, ибо это является стандартом индустрии, да и желательно сохранять в собственное расшерение файла, а не в *.txt, *.dat, *.ini, но это уже следующая тема для изучения.  На счет того что это нигде не практикуется спорить не буду, ибо я еще чайник в программировании )) просто поиск дал мне имнно эти 3 метода из которых я выбрал Memory Stream.      
Вот ссылки 
https://stackoverflow.com/questions/3163586/how-to-save-and-restore-a-form
https://stackoverflow.com/questions/33333859/delphi-firemonkey-store-information-from-components?fbclid=IwZXh0bgNhZW0CMTAAAR2JSy0hM3FzQXH3z0IoJHNHqKaEtfX5Njywv3FQllMlfCaY6Pgws-tag9o_aem_iHznVwquRNhCRxzzn8ID3A
https://docwiki.embarcadero.com/RADStudio/Athens/en/FireMonkey_Save_State?fbclid=IwZXh0bgNhZW0CMTAAAR04VDcvl4cYtaPcQ4IEX22aTcDFwrsRz8x_M1zTgynxrupIQecIMcRx9os_aem_k5tgqWl26YAcngDzb-Pe5g

 

Цитата

1. Потому что методы не принадлежат Panel, а только TMain_Form и без TMain_Form сохранение событий не имеет смысла

Спасибо за подсказку теперь буду знать 👍
 

Цитата

2. Ты загружаешь уже загруженную форму? не думал куда деваются старые контролы? уничтожаются ли они? тупо перезаписываются? или же

Да в процедуре "TMain_Form.LoadButtonClick" перед загрузкой все дочерние компоненты формы удаляются.

 for i := pred(Self.ChildrenCount) downto 0 do
    Self.Children[i].Free;

 

  • 0
Опубликовано

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

  • 0
Опубликовано
50 минут назад, Slym сказал:

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

Спасибо за совет буду штудировать в этом направлении. Кстати в целом было бы интресно узнать у экспертов какой из методов считается лучшей практикой для решения данной задачи? 🤔  

  • 0
Опубликовано

для начала не совсем понятно что ты пытаешься сделать:
1. Редактор кастомных форм - но как ты будешь кастомные обработчики событий делать?..
2. или же просто сохранение размеров положения контролов на форме - тогда это избыточный подход
3. а вообще дельфисты любят велосипеды :)

  • 0
Опубликовано (изменено)
3 часа назад, Slym сказал:

для начала не совсем понятно что ты пытаешься сделать:
1. Редактор кастомных форм - но как ты будешь кастомные обработчики событий делать?..
2. или же просто сохранение размеров положения контролов на форме - тогда это избыточный подход
3. а вообще дельфисты любят велосипеды :)

В долгосрочной перспективе если мне хватит терпения изучить Дельфи на хорошом уровне, и к тому времени у меня останется мотивация, то планирую создать программу для 2D-3D анимации и композа 🙂
Собственно желание создать кросплатформенную программу для 2D-3D графики меня и привело к Дельфи 😀  
Ну и как любая программа вне зависимотси от сферы ее применения она должна иметь возможность сохранять и загружать проджект файлы. 
Как я понимаю процесс сохранения и загрузки проджект файла технически реализуется именно через сохранение состояния всех дочерних компонентов на форме без потери внтурипрограмной логики плюс массив всех важных данных(переменных) без которых врядли получится корректно воссоздать проджект файл.
Короче это мое ИМХО как чайника, но как оно делается на самом деле пока изучаю )))          

Изменено пользователем LBox
  • 0
Опубликовано
В 09.01.2025 в 22:30, LBox сказал:

я понимаю процесс сохранения и загрузки проджект файла технически реализуется именно через сохранение состояния всех дочерних компонентов на форме

Думаю тут именно нужно сохранять свойства объектов отдельно и использовать json.

Допустим у вас в layout находятся нужные вам объекты, вы проходитесь по ним циклом и для объектов tcontrol записываете расположение, дальше уже по типу объекта нужные вам данные, а при загрузке этого файла также через цикл загружаете по данным, и свойствам, допустим можете использовать tagString = кругприм и тогда присваивать ему в onClick нужный метод.

  • 0
Опубликовано
2 часа назад, OnePeople сказал:

Думаю тут именно нужно сохранять свойства объектов отдельно и использовать json.

Допустим у вас в layout находятся нужные вам объекты, вы проходитесь по ним циклом и для объектов tcontrol записываете расположение, дальше уже по типу объекта нужные вам данные, а при загрузке этого файла также через цикл загружаете по данным, и свойствам, допустим можете использовать tagString = кругприм и тогда присваивать ему в onClick нужный метод.

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

Возможно, что я ошибаюсь в своих выводых на счет json и xml, но на данный момент для меня это пока не важно, ибо Memory Stream прекрасно справляется с поставленной задачей и при чем без надобности проходить по циклам с разными спецефическими условиями потому что он записывает все древо в лоб сохраняя зависимости предок-потомок и все важные параметры каждого компонента в дереве для последующего корректного восстановления всего дерева. 
В любом случаи спасибо за ответ 🙏 Будем учиться 🙂👍         

  • 0
Опубликовано
В 09.01.2025 в 12:44, Slym сказал:

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

Спасибо за совет 🙏  TFrame сработал идеально 😀👍 
При сохранении и загрузке события назанченные на компоненты записываются и корректно восстанавливаются, а так же решилась проблема странного зависания Формы после восстановления-загрузки компонентов из файла 🤘😎 
Теперь мне надо нагрузить Форму всеми возможными компонентами из списка Fire Monkey и проверить их совместимость с Memory Stream в контексте их корректного сохранения и корректного восстановления. Короче мое приключение в Delphi - Fire Monkey продолжается 👍

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...