Supprimer et remplacer un composant visuel au moment de l'exécution
Question
Est-il possible, par exemple, de remplacer et de libérer un TEdit par un composant sous-classé instancié (sous condition) au moment de l'exécution? Si oui, comment et quand cela devrait-il être fait? J'ai essayé de définir le parent sur nil et d'appeler free () dans le constructeur de formulaire et les méthodes AfterConstruction, mais dans les deux cas, une erreur d'exécution s'est produite.
Étant plus spécifique, une erreur de violation d'accès (EAccessViolation) s'est produite. Il semblerait que François ait raison quand il dit que la libération de composants lors de la reconstruction de trames entraîne des problèmes avec la gestion des commandes de formulaire.
La solution
Cette routine plus générique fonctionne avec un formulaire ou un cadre (mis à jour pour utiliser une sous-classe pour le nouveau contrôle):
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;
utilisant cette routine pour transmettre les propriétés au nouveau contrôle
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;
utilisez-le comme:
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;
ATTENTION : si vous effectuez cette opération dans AfterConstruction du cadre, prenez garde que la construction du formulaire d'hébergement n'est pas encore terminée.
Libérer les contrôles là-bas pourrait causer beaucoup de problèmes pendant que vous bousiller avec l'entretien des contrôles de formulaires.
Découvrez ce que vous obtenez si vous essayez de lire la nouvelle légende de modification à afficher dans ShowMessage ...
Dans ce cas, vous voudriez utiliser
... ReplaceControl (Edit1, 'Edit2', False )
et ensuite faire un
... FreeAndNil (Edit1)
plus tard.
Autres conseils
Vous devez appeler RemoveControl du parent du TEdit pour supprimer le contrôle. Utilisez InsertControl pour ajouter le nouveau contrôle.
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;
Remplacez TEdit.Create dans la classe que vous souhaitez utiliser et copiez toutes les propriétés dont vous avez besoin, comme je l'avais fait avec Left et Top.
Vous pouvez réellement utiliser RTTI (regardez dans l'unité TypInfo) pour cloner toutes les propriétés correspondantes. J'ai écrit du code pour cela il y a quelque temps, mais je ne le trouve pas maintenant. Je vais continuer à chercher.