Удалить и заменить визуальный компонент во время выполнения - PullRequest
7 голосов
/ 23 сентября 2008

Можно ли, например, заменить и освободить TEdit с подклассовым компонентом, созданным (условно) во время выполнения? Если да, то как и когда это должно быть сделано? Я попытался установить для родителя значение nil и вызвать free () в конструкторе формы и методах AfterConstruction, но в обоих случаях я получил ошибку времени выполнения.


Если быть более конкретным, я получил ошибку нарушения доступа (EAccessViolation). Кажется, Франсуа прав, когда говорит, что освобождение компонентов при сборке кадров мешает ведению форм.

Ответы [ 3 ]

8 голосов
/ 23 сентября 2008

Эта более общая подпрограмма работает либо с формой, либо с фреймом (обновлен для использования подкласса для нового элемента управления):

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;

с помощью этой подпрограммы для передачи свойств новому элементу управления

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;

используйте это как:

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;

ВНИМАНИЕ : Если вы делаете это в AfterConstruction фрейма, имейте в виду, что создание формы размещения еще не завершено. Освобождение элементов управления может вызвать много проблем, так как вы не можете справиться с управлением элементами формы.
Посмотрите, что вы получите, если попытаетесь прочитать новый заголовок редактирования для отображения в ShowMessage ...
В этом случае вы хотели бы использовать
... ReplaceControl (Edit1, 'Edit2', False )
а затем сделать
... FreeAndNil (Edit1)
позже.

8 голосов
/ 23 сентября 2008

Вы должны вызвать RemoveControl родителя TEdit, чтобы удалить элемент управления. Используйте InsertControl, чтобы добавить новый элемент управления.

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;

Замените TEdit. Создайте класс, который вы хотите использовать, и скопируйте все нужные вам свойства, как я сделал с Left и Top.

1 голос
/ 23 сентября 2008

Вы можете фактически использовать RTTI (смотрите в блоке TypInfo), чтобы клонировать все соответствующие свойства. Я написал код для этого некоторое время назад, но я не могу найти его сейчас. Я буду продолжать искать.

...