Domanda

È possibile, ad esempio, sostituire e liberare un TEdit con un componente di sottoclasse istanziato (in modo condizionale) in fase di esecuzione? In tal caso, come e quando dovrebbe essere fatto? Ho provato a impostare il genitore su zero e a chiamare free () nei metodi di costruzione del modulo e AfterConstruction ma in entrambi i casi ho riscontrato un errore di runtime.


Essendo più specifico, ho riscontrato un errore di violazione di accesso (EAccessViolation). Sembra che Fran abbia ragione quando afferma che liberare i componenti durante la costruzione del telaio si rovina con il controllo dei servizi di pulizia del modulo.

È stato utile?

Soluzione

Questa routine più generica funziona con un modulo o un frame (aggiornato per utilizzare una sottoclasse per il nuovo controllo):

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 questa routine per passare le proprietà al nuovo controllo

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;

usalo come:

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;

ATTENZIONE : se lo stai facendo in AfterConstruction of the Frame, fai attenzione che la costruzione del modulo di hosting non è ancora terminata.
Liberare i controlli lì, potrebbe causare molti problemi mentre si incasina la pulizia dei controlli dei moduli.
Guarda cosa ottieni se provi a leggere la nuova Modifica didascalia da visualizzare in ShowMessage ...
In tal caso, ti consigliamo di utilizzare
  ... ReplaceControl (Modifica1, 'Modifica2', Falso )
e poi fai un video   ... FreeAndNil (Edit1)
più tardi.

Altri suggerimenti

Devi rimuovere RemoveControl del genitore di TEdit per rimuovere il controllo. Utilizzare InsertControl per aggiungere il nuovo controllo.

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;

Sostituisci TEdit.Crea nella classe che vuoi usare e copia tutte le proprietà di cui hai bisogno come ho fatto con Sinistra e Inizio.

Puoi effettivamente usare RTTI (guarda nell'unità TypInfo) per clonare tutte le proprietà corrispondenti. Ho scritto il codice per questo qualche tempo fa, ma non riesco a trovarlo ora. Continuerò a cercare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top