إزالة واستبدال مكون مرئي في وقت التشغيل
سؤال
هل من الممكن، على سبيل المثال، استبدال وتحرير 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) لاستنساخ كافة الخصائص المطابقة.لقد كتبت الكود الخاص بهذا منذ فترة، لكن لا يمكنني العثور عليه الآن.سأواصل البحث.