Pergunta

É possível, por exemplo, substituir e libertar um TEdit com um componente subclassed instanciado (condicionalmente) em tempo de execução? Se sim, como e quando deve ser feito? Tentei definir o pai a zero e para chamar métodos livres () no construtor forma e AfterConstruction mas em ambos os casos, eu tenho um erro de execução.


Sendo mais específico, eu tenho um erro de violação de acesso (EAccessViolation). Parece François está certo quando diz que liberando componentes no quadro messes Costruction com controles de formulário limpeza.

Foi útil?

Solução

Esta rotina mais genérica funciona tanto com uma forma ou Frame (atualizado para usar uma subclasse para o novo controle):

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 essa rotina para passar as propriedades para o novo controle

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;

usá-lo 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;

ATENÇÃO :. Se você está fazendo isso dentro do AfterConstruction da Carroçaria, tomar cuidado para que a construção Form hospedagem ainda não está terminado
Libertar Controls lá, pode causar uma série de problemas como você está atrapalhando com Forma controles de limpeza.
Veja o que você ganha se você tentar ler o novo Editar legenda para exibição no ShowMessage ...
Nesse caso, você iria querer usar
... ReplaceControl (Edit1, 'Edit2', false )
e depois fazer um
... FreeAndNil (Edit1)
mais tarde.

Outras dicas

Você tem que chamar removecontrol do pai do TEdit para remover o controle. Use InsertControl para adicionar o novo controle.

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;

Substituir TEdit.Create para a classe que deseja usar e copiar todas as propriedades que você precisa, como eu fiz com o Left e Top.

Você pode realmente usar RTTI (olhar na unidade TypInfo) para clonar todas as propriedades correspondentes. Eu escrevi código para isso um tempo atrás, mas eu não posso encontrá-lo agora. Vou continuar procurando.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top