Pregunta

¿Es posible, por ejemplo, reemplazar y liberar un TEdit con un componente subclasificado instanciado (condicionalmente) en tiempo de ejecución?Si es así, ¿cómo y cuándo se debe hacer?Intenté establecer el padre en nulo y llamar a free() en el constructor del formulario y en los métodos AfterConstruction, pero en ambos casos obtuve un error de tiempo de ejecución.


Para ser más específico, recibí un error de infracción de acceso (EAccessViolation).Parece que François tiene razón cuando dice que liberar componentes en la construcción del marco afecta el mantenimiento de los controles de Form.

¿Fue útil?

Solución

Esta rutina más genérica funciona con un formulario o un marco (actualizado para usar 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;

usando esta rutina para pasar las propiedades al 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;

úsalo 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 AfterConstruction of the Frame, tenga en cuenta que la construcción del formulario de alojamiento aún no está terminada.
Liberar los controles allí puede causar muchos problemas, ya que estás arruinando la limpieza de los controles del formulario.
Vea lo que obtiene si intenta leer el nuevo Editar título para mostrar en ShowMessage...
En ese caso querrás usar
...ReplaceControl(Editar1, 'Editar2', FALSO)
y luego hacer un
...FreeAndNil(Editar1)
más tarde.

Otros consejos

Tienes que llamar a RemoveControl del padre de TEdit para eliminar el control.Utilice 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;

Reemplace TEdit.Create con la clase que desea usar y copie todas las propiedades que necesita como hice con Left y Top.

De hecho, puede usar RTTI (busque en la unidad TypInfo) para clonar todas las propiedades coincidentes.Escribí código para esto hace un tiempo, pero no puedo encontrarlo ahora.Seguiré buscando.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top