Вариант 3. Динамические имена новых компонентов.
Для создания отличающихся имен новых компонентов на Панели была сделана несложная функция, которая используется в Create. Работает аналогично среде, которая размещает новый компонент на форме – «Имя + Число»
function TrtePeriodPanel.
NewCompName(AName: String): String;
var
I: Integer;
S: String;
begin
I := 1;
while I < 100 do
begin
S := AName + IntToStr(I);
if Owner.FindComponent(S) = nil then
begin
Result := S;
exit;
end;
Inc(I);
end;
Raise Exception.CreateFmt('Уже создано 100 элементов ' + AName + '. Продолжение невозможно. ', []);
end;
Разумеется, в
Create была добавлена и строка:
btPeriodEdit.Name :=
NewCompName('PeriodEdit');
Для удаления компонентов изначально было решено попробовать такой алгоритм:
- Create – создание нового с именем из NewCompName
- Переименование его в Notification
- Удаление лишнего в Loaded
Все было хорошо до того момента, пока ты на форму не кидаешь 2-3 созданных компонента и затем удаляешь первый из них. После этого ошибка при Alt+F12 и обратно – неизбежна, т.к. новый
Create снова создает компонент по номером
1.
Все что в
Notification, вызывается после создания Кнопки btPeriodEdit = 'PeriodEdit##'
(По смыслу ее надо переименовать, т.к. при нажатии Alt+F12 происходит Create компонента + загрузка данных из DFM)
Как происходит загрузка из DFM:
Сначала
TReader читает имя класса и создает путем
Create Компонент, в момент
Create вызывается
Notification всем объектам
csReading - только в момент считывания свойств.
Как только считывание окончено
csReading – выключается.
После создания всех (!) компонентов на форме - производится рассылка Loaded.
И перед Loaded
csLoading выключается.
procedure TrtePeriodPanel.
Notification(AComponent: TComponent; Operation: TOperation);
var
AComp: TComponent;
s: string;
begin
//
inherited;
// для корректного удаления вручную Кнопки с Панели в IDE
if csDestroying in AComponent.ComponentState then
if (csDesigning in ComponentState) and (AComponent.ClassType = TcxButton) then
if Operation =
opRemove then
begin
if Assigned(btPeriodEdit) then // Параноидальная проверка
if btPeriodEdit = AComponent then
begin
btPeriodEdit := nil; //
только так !!! без всяких Free или FreeAndNil !!
exit;
end;
end;
if not (csReading in ComponentState) then exit;
// if not (
csLoading in ComponentState) then exit; этого не достаточно !!
// if
csInline in ComponentState then mes('csInline'); это не работает !!
if
Operation =
opInsert then
if (csDesigning in ComponentState) and (AComponent.ClassType = TcxButton) then
begin
// if TcxButton(AComponent).Parent =
self then // на этом этапе у него
нет Parent
if AComponent.Name = '' then // ТОЛЬКО при вставке
нового компонента
begin
// ищем такой же созданный ранее
AComp := Owner.
FindComponent(ButtonName{'PeriodEdit'});
if AComp <> nil then AComp.Name := 'A' + ButtonName; //PeriodEdit';
end;
end;
end;
procedure TrtePeriodPanel.
Loaded;
var
AComp, LoadComp: TComponent;
begin
inherited Loaded; //
???
if btPeriodEdit = nil then exit;
// после загрузки компонента нужно удалить переименованный до этого экземпляр
if (csDesigning in ComponentState) then
begin
try
AComp := Owner.
FindComponent('A' + ButtonName{PeriodEdit'}); // ранее переименованный экземпляр из Create
LoadComp := Owner.FindComponent(ButtonName{'PeriodEdit'});
if Assigned(AComp) and Assigned(LoadComp) then
begin
// удаляем дубликат из Create
RemoveControl(TcxButton(AComp));
AComp.
Free;
end else
// переименовываем загруженный из DFM
if Assigned(AComp) then AComp.Name := {Name + '_' +} ButtonName;
except
mes('ERROR TrtePeriodPanel.Loaded');
end;
end else
begin
Caption := '';
btPeriodEdit.
OnClick := Self.btPeriodEditClick;
end;
end;