Rimuovere e sostituire un componente visivo in fase di esecuzione
Domanda
È possibile, ad esempio, sostituire e liberare un TEdit con un componente di sottoclasse istanziato (in modo condizionale) in fase di esecuzione? In tal caso, come e quando dovrebbe essere fatto? Ho provato a impostare il genitore su zero e a chiamare free () nei metodi di costruzione del modulo e AfterConstruction ma in entrambi i casi ho riscontrato un errore di runtime.
Essendo più specifico, ho riscontrato un errore di violazione di accesso (EAccessViolation). Sembra che Fran abbia ragione quando afferma che liberare i componenti durante la costruzione del telaio si rovina con il controllo dei servizi di pulizia del modulo.
Soluzione
Questa routine più generica funziona con un modulo o un frame (aggiornato per utilizzare una sottoclasse per il nuovo controllo):
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;
usando questa routine per passare le proprietà al nuovo controllo
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;
usalo come:
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;
ATTENZIONE : se lo stai facendo in AfterConstruction of the Frame, fai attenzione che la costruzione del modulo di hosting non è ancora terminata.
Liberare i controlli lì, potrebbe causare molti problemi mentre si incasina la pulizia dei controlli dei moduli.
Guarda cosa ottieni se provi a leggere la nuova Modifica didascalia da visualizzare in ShowMessage ...
In tal caso, ti consigliamo di utilizzare
... ReplaceControl (Modifica1, 'Modifica2', Falso )
e poi fai un video
... FreeAndNil (Edit1)
più tardi.
Altri suggerimenti
Devi rimuovere RemoveControl del genitore di TEdit per rimuovere il controllo. Utilizzare InsertControl per aggiungere il nuovo controllo.
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;
Sostituisci TEdit.Crea nella classe che vuoi usare e copia tutte le proprietà di cui hai bisogno come ho fatto con Sinistra e Inizio.
Puoi effettivamente usare RTTI (guarda nell'unità TypInfo) per clonare tutte le proprietà corrispondenti. Ho scritto il codice per questo qualche tempo fa, ma non riesco a trovarlo ora. Continuerò a cercare.