إزالة واستبدال مكون مرئي في وقت التشغيل

StackOverflow https://stackoverflow.com/questions/120858

  •  02-07-2019
  •  | 
  •  

سؤال

هل من الممكن، على سبيل المثال، استبدال وتحرير TEdit بمكون فئة فرعية تم إنشاء مثيل له (بشكل مشروط) في وقت التشغيل؟إذا كان الأمر كذلك، كيف ومتى ينبغي القيام بذلك؟لقد حاولت تعيين الأصل على لا شيء والاتصال بـ free() في منشئ النموذج وأساليب AfterConstruction ولكن في كلتا الحالتين حصلت على خطأ في وقت التشغيل.


لكي أكون أكثر تحديدًا، لقد حصلت على خطأ انتهاك الوصول (EAccessViolation).يبدو أن فرانسوا على حق عندما قال إن تحرير المكونات في بناء الإطار يعبث بالتدبير المنزلي لضوابط النموذج.

هل كانت مفيدة؟

المحلول

يعمل هذا الروتين الأكثر عمومية إما مع نموذج أو إطار (تم تحديثه لاستخدام فئة فرعية لعنصر التحكم الجديد):

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;

حذر:إذا كنت تفعل ذلك داخل ما بعد بناء الإطار، فاحذر من أن بناء نموذج الاستضافة لم ينته بعد.
قد يتسبب تحرير عناصر التحكم هناك في حدوث الكثير من المشكلات لأنك تعبث بالتدبير المنزلي لعناصر التحكم في النماذج.
تعرف على ما ستحصل عليه إذا حاولت قراءة "تحرير التسمية التوضيحية" الجديدة لعرضها في ShowMessage...
في هذه الحالة قد ترغب في استخدام
...ReplaceControl(Edit1, 'Edit2', خطأ شنيع)
ثم قم بعمل
...حر ولا نيل (تحرير 1)
لاحقاً.

نصائح أخرى

يجب عليك الاتصال بـ 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.Create بالفئة التي تريد استخدامها، وانسخ جميع الخصائص التي تحتاجها كما فعلت مع Left وTop.

يمكنك بالفعل استخدام RTTI (انظر وحدة TypInfo) لاستنساخ كافة الخصائص المطابقة.لقد كتبت الكود الخاص بهذا منذ فترة، لكن لا يمكنني العثور عليه الآن.سأواصل البحث.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top