Удалить и заменить визуальный компонент во время выполнения
Можно ли, например, заменить и освободить TEdit с подклассовым компонентом, созданным (условно) во время выполнения? Если да, то как и когда это должно быть сделано? Я попытался установить для родителя значение nil и вызвать free() в конструкторе формы и методах AfterConstruction, но в обоих случаях я получил ошибку во время выполнения.
Чтобы быть более конкретным, я получил ошибку нарушения доступа (EAccessViolation). Кажется, Франсуа прав, когда говорит, что освобождение компонентов при сборке кадров мешает ведению форм.
3 ответа
Эта более общая процедура работает либо с Form, либо с Frame (обновлена для использования подкласса для нового элемента управления):
function ReplaceControlEx(AControl: TControl; const AControlClass: TControlClass; const ANewName: string; const IsFreed : Boolean = True): TControl;
begin
if AControl = nil then
begin
Result := nil;
Exit;
end;
Result := AControlClass.Create(AControl.Owner);
CloneProperties(AControl, Result);// copy all properties to new control
// Result.Left := AControl.Left; // or copy some properties manually...
// Result.Top := AControl.Top;
Result.Name := ANewName;
Result.Parent := AControl.Parent; // needed for the InsertControl & RemoveControl magic
if IsFreed then
FreeAndNil(AControl);
end;
function ReplaceControl(AControl: TControl; const ANewName: string; const IsFreed : Boolean = True): TControl;
begin
if AControl = nil then
Result := nil
else
Result := ReplaceControlEx(AControl, TControlClass(AControl.ClassType), ANewName, IsFreed);
end;
используя эту процедуру для передачи свойств в новый элемент управления
procedure CloneProperties(const Source: TControl; const Dest: TControl);
var
ms: TMemoryStream;
OldName: string;
begin
OldName := Source.Name;
Source.Name := ''; // needed to avoid Name collision
try
ms := TMemoryStream.Create;
try
ms.WriteComponent(Source);
ms.Position := 0;
ms.ReadComponent(Dest);
finally
ms.Free;
end;
finally
Source.Name := OldName;
end;
end;
используйте это как:
procedure TFrame1.AfterConstruction;
var
I: Integer;
NewEdit: TMyEdit;
begin
inherited;
NewEdit := ReplaceControlEx(Edit1, TMyEdit, 'Edit2') as TMyEdit;
if Assigned(NewEdit) then
begin
NewEdit.Text := 'My Brand New Edit';
NewEdit.Author := 'Myself';
end;
for I:=0 to ControlCount-1 do
begin
ShowMessage(Controls[I].Name);
end;
end;
ПРЕДУПРЕЖДЕНИЕ. Если вы делаете это внутри AfterConstruction фрейма, помните, что создание формы размещения еще не завершено.
Освобождение элементов управления может вызвать много проблем, так как вы не можете справиться с управлением элементами формы.
Посмотрите, что вы получите, если попытаетесь прочитать новую Редактировать подпись для отображения в ShowMessage...
В этом случае вы хотели бы использовать
... ReplaceControl (Edit1, 'Edit2', False)
а затем сделать
...FreeAndNil(Edit1)
потом.
Вы должны вызвать RemoveControl родителя TEdit, чтобы удалить элемент управления. Используйте InsertControl, чтобы добавить новый элемент управления.
var Edit2: TEdit;
begin
Edit2 := TEdit.Create(self);
Edit2.Left := Edit1.Left;
Edit2.Top := Edit2.Top;
Edit1.Parent.Insertcontrol(Edit2);
TWinControl(Edit1.parent).RemoveControl(Edit1);
Edit1.Free;
end;
Замените TEdit. Создайте класс, который вы хотите использовать, и скопируйте все нужные вам свойства, как я сделал с Left и Top.
На самом деле вы можете использовать RTTI (смотрите в модуле TypInfo), чтобы клонировать все соответствующие свойства. Я написал код для этого некоторое время назад, но я не могу найти его сейчас. Я буду продолжать искать.