Est-il possible, par exemple, de remplacer et de libérer un TEdit avec un composant sous-classé instancié (conditionnellement) lors de l'exécution? Si oui, comment et quand cela devrait-il être fait? J'ai essayé de mettre le parent à zéro et d'appeler free() dans le constructeur de formulaire et les méthodes AfterConstruction mais dans les deux cas j'ai eu une erreur d'exécution. Etant plus précis, j'ai reçu une erreur de violation d'accès (EAccessViolation). Il semble que François a raison quand il dit que la libération des composants au niveau de la structure de la structure empiète sur les contrôles de formulaire.Supprimer et remplacer un composant visuel à l'exécution
Répondre
Cette routine fonctionne plus génériques, soit 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;
avec cette routine pour passer les propriétés du 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;
utiliser 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 faites cela à l'intérieur de l'AfterConstruction du cadre, méfiez-vous que la construction du formulaire d'hébergement n'est pas encore terminée.
Libérer des contrôles là-bas, pourrait causer beaucoup de problèmes que vous êtes en train de jouer avec les contrôles de formulaire d'entretien ménager.
Voir ce que vous obtenez si vous essayez de lire la nouvelle Modifier la légende à afficher dans la ShowMessage ...
Dans ce cas, vous souhaitez utiliser
... ReplaceControl (Edit1, « Edit2 », Faux)
puis faites un
... FreeAndNil (Edit1)
plus tard.
Vous devez appeler RemoveControl du parent de 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;
Remplacer TEdit.Create à la classe que vous souhaitez utiliser et copier toutes les propriétés dont vous avez besoin comme je l'ai fait avec gauche et en haut.
Pourquoi ne pas définir la propriété Parent à la place de InsertControl et RemoveControl? –
Tout simplement parce que dans la question, il a été déclaré que la propriété parent ne fonctionnait pas. Et je me suis souvenu que j'avais du code écrit une fois en utilisant InsertControl et RemoveControl. Donc, je n'ai simplement pas essayé la propriété Parent. – Loesje
Le code Loesje a fonctionné à l'intérieur de la méthode TMyForm.AfterConstruction, mais pas dans TMyFrame.AfterConstruction. Où dois-je le placer lors de l'utilisation avec un objet TFrame? – user16120
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 ça il y a quelques temps, mais je ne peux pas le trouver maintenant. Je vais continuer à regarder.
Pouvez-vous s'il vous plaît partager la routine de clonage en utilisant TypInfo/Rtti? – menjaraz
@ menjaraz Je pense que [François réponse] (http://stackoverflow.com/a/122915/255) est une meilleure réponse.Je devrais recréer le code que j'avais. Fondamentalement, il utilisera RTTI pour examiner l'ancien composant et le nouveau composant, puis copier sur les valeurs de propriété en commun. –
Merci d'avoir répondu: le message de François est précieux. – menjaraz
Cela ne va pas aider, car il voulait remplacer le TEdit avec un autre type de composant. (un descendant) Pour le reste, c'est exactement la même chose que la première réponse, sauf que vous l'avez enveloppée dans une fonction. Peut-être que le conseil d'AfterConstruction est utile, cependant. – Loesje
En fait, la même idée fonctionne avec une sous-classe, voir la version mise à jour. Je n'ai pas lu attentivement cependant, comme j'avais raté le "sous-classé" ... –
2 autres commentaires: 1. Il est préférable de ne pas forcer vers IsertControl/RemoveControl sauf si vraiment nécessaire. SetParent fait tout ce qui est nécessaire. 2. Et je ne suis pas du même avis: c'est beaucoup plus générique et flexible en utilisant TControlClass. –