2008-09-23 18 views
7

¿Es posible, por ejemplo, reemplazar y liberar un TEdit con un componente subclase instanciado (condicionalmente) en tiempo de ejecución? Si es así, ¿cómo y cuándo debería hacerse? Intenté configurar el padre en cero y llamar a free() en el constructor de formularios y en los métodos AfterConstruction, pero en ambos casos recibí un error de tiempo de ejecución.Eliminar y reemplazar un componente visual en tiempo de ejecución


Siendo más específico, recibí un error de violación de acceso (EAccessViolation). Parece que François tiene razón cuando dice que liberar componentes en el armazón del armazón es un problema con el control de la forma.

Respuesta

8

esto más genéricos trabajos de rutina, ya sea con un formulario o marco (actualizado para utilizar una subclase para el nuevo control):

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; 

utilizando esta rutina para pasar las propiedades del nuevo control

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; 

usarlo como:

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; 

PRECAUCIÓN: si está haciendo esto dentro de la construcción posterior del marco, tenga en cuenta que la construcción del formulario de alojamiento no ha finalizado aún.
Freeing Controles allí, pueden causar muchos problemas ya que estás jugando con controles de forma.
Vea lo que se obtiene si se intenta leer la nueva edición de subtítulos para mostrar en el ShowMessage ...
En ese caso, habría que desee utilizar
... ReplaceControl (Edit1, 'Edit2', Falso)
y luego hacer un
... FreeAndNil (Edit1)
posterior.

+0

Esto no ayudará, ya que quería reemplazar el TEdit con otro tipo de componente. (algunos descendientes) Por lo demás, esta es exactamente la misma primera respuesta, excepto que la envuelve en una función. Quizás la sugerencia de AfterConstruction sea útil, sin embargo. – Loesje

+0

En realidad, la misma idea funciona con una subclase, consulte la versión actualizada. Obtuve que no leía atentamente, ya que había omitido la "subclase" ... –

+0

2 comentarios adicionales: 1. Lo mejor es no profundizar en IsertControl/RemoveControl a menos que sea realmente necesario. SetParent hace todo lo que se necesita. 2. Y pido diferir acerca de ser el mismo: es mucho más genérico y flexible utilizando el enfoque de fábrica de clase con TControlClass. –

8

Tienes que llamar a RemoveControl del padre de TEdit para eliminar el control. Use InsertControl para agregar el nuevo control.

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; 

Reemplazar TEdit.Create a la clase que desea utilizar y copiar todas las propiedades que necesita, como lo hice con Izquierda y Superior.

+0

¿Por qué no establecer la propiedad principal en lugar de InsertControl y RemoveControl? –

+0

Simplemente porque en la pregunta se indicó que la propiedad principal no funcionó. Y recordé que tenía un código escrito una vez usando InsertControl y RemoveControl. Así que simplemente no probé la propiedad Parent. – Loesje

+0

El código Loesje funcionaba dentro del método TMyForm.AfterConstruction, pero no en TMyFrame.AfterConstruction. ¿Dónde debería colocarlo cuando uso esto con un objeto TFrame? – user16120

1

En realidad, puede utilizar RTTI (busque en la unidad TypInfo) para clonar todas las propiedades coincidentes. Hace un tiempo escribí código para esto, pero no puedo encontrarlo ahora. Seguiré buscando.

+0

¿Puede compartir la rutina de clonación utilizando TypInfo/Rtti? – menjaraz

+0

@menjaraz Creo que [respuesta de François] (http://stackoverflow.com/a/122915/255) es una mejor respuesta.Tendría que volver a crear el código que tenía. Básicamente usaría RTTI para examinar el componente antiguo y el nuevo componente y luego copiar los valores de propiedad en común. –

+0

Gracias por responder: la publicación de François es valiosa. – menjaraz

Cuestiones relacionadas