После серии экспериментов удалось избавиться от непонятной ошибки, возникавшей при попытке разрушить фрейм. Ошибка была плавающей, возникала в режиме отладки. В итоге, почитав форумы, я пришел к выводу, что безнаказанно удалить фрейм с главной формы приложения не получится. Но и бросать удобную систему создания элементов интерфейса по мере необходимости я не захотел. Пришлось доработать систему управления видимостью фреймов, разделив навигационный стек и переменные для хранения фреймов.
TdamUIData
Для краткости приведу только свойства и методы, которые относятся к сути вопроса. Списки fFrameList и fFrameStack создаются при создании модуля и разрушаются вместе с ним. Поле fForm заполняется в методе Init().
TdamUIData = class(TDataModule)
procedure DataModuleCreate(Sender: TObject);
procedure DataModuleDestroy(Sender: TObject);
private
fForm: TForm;
fFrameList: TStringList; // созданные фреймы
fFrameStack: TStringList; // стек отображения
public
procedure ShowFrame(AName: string);
procedure GoBack;
procedure Init(AForm: TForm);
end;
procedure TdamUIData.DataModuleCreate(Sender: TObject);
var
tmpPath: string;
tmpFileName: string;
tmpIniFile: TIniFile;
begin
fFrameList := TStringList.Create;
fFrameStack := TStringList.Create;
tmpPath := TPath.Combine(TPath.GetDocumentsPath, BASE_SUBDIR);
if not TDirectory.Exists(tmpPath) then
TDirectory.CreateDirectory(tmpPath);
end;
procedure TdamUIData.DataModuleDestroy(Sender: TObject);
begin
fFrameList.Free;
fFrameStack.Free;
end;
procedure TdamUIData.Init(AForm: TForm);
begin
fForm := AForm;
// задаем папку для хранения файлов БД
fBasePath := TPath.Combine(TPath.GetDocumentsPath, BASE_SUBDIR);
if not TDirectory.Exists(fBasePath) then
TDirectory.CreateDirectory(fBasePath);
end;
Code language: Delphi (delphi)
А вот методы навигации рассмотрим подробней. Сейчас они только управляют видимостью фреймов, в дальнейшем в них будет добавлена анимация.
ShowFrame
Сначала отображаемый фрейм ищется в списке уже созданных фреймов fFrameList, а если его там ещё нет, то с помощью репозитория фреймов создается новый экземпляр. Затем в стеке fFrameStack находится предыдущий фрейм и делается невидимым. После чего новый фрейм окончательно помещается на главную форму (Parent := fForm) и делается видимым (Visible := True). После чего проводится его первичная инициализация методом Inint(). Идентификация производится по имени, а для остальных манипуляций используется методы базового класса TfraBaseFrame.
procedure TdamUIData.ShowFrame(AName: string);
var
i: integer;
j: integer;
s: string;
begin
i := fFrameList.IndexOf(AName);
if i = -1 then
i := fFrameList.AddObject(AName, TfraBaseFrame(FrameRep.GetFrame(AName, fForm)));
fFrameStack.Add(AName);
if fFrameStack.Count > 1 then // скрываем предыдущий фрейм, если он есть
begin
s := fFrameStack.Strings[fFrameStack.Count - 2];
j := fFrameList.IndexOf(s);
TfraBaseFrame(fFrameList.Objects[j]).Visible := False;
end;
TfraBaseFrame(fFrameList.Objects[i]).Parent := fForm;
TfraBaseFrame(fFrameList.Objects[i]).Visible := True;
TfraBaseFrame(fFrameList.Objects[i]).Init;
end;
Code language: Delphi (delphi)
GoBack
Чтобы “вернуться назад” нужно найти в списке фреймов последний элемент из стека фреймов и сделать его невидимым. Затем удалить последний элемент из стека. А теперь сделать последний элемент в стеке видимым.
procedure TdamUIData.GoBack;
var
s: string;
i: integer;
begin
if fFrameStack.Count > 1 then
begin
s := fFrameStack.Strings[fFrameStack.Count - 1];
i := fFrameList.IndexOf(s);
TfraBaseFrame(fFrameList.Objects[i]).Visible := False;
fFrameStack.Delete(fFrameStack.Count - 1);
s := fFrameStack.Strings[fFrameStack.Count - 1];
i := fFrameList.IndexOf(s);
TfraBaseFrame(fFrameList.Objects[i]).Visible := True;
TfraBaseFrame(fFrameList.Objects[i]).Update;
end;
end;
Code language: Delphi (delphi)