اثناء عملى ببرامج قواعد البيانات و التى كانت فى الأغلب Disconnected mode كان امامى اختيار من اثنين إما ان اتبع منهجية SE فى تصنيع البرمجيات (و هذا ما يرفضه رؤسائى) و اما ان اقوم بعمل بعض الأدوات التى تتيح لى انشاء جمل Insert و Update و Delete بشكل ديناميكى.
فى الواقع الأمر لم يكن بالهين و لكنى اثناء بحثى لاحظت اداه مثل ToolTip حيث تقوم بإضافة خصائص لكل الأدوات الأخرى و جائتنى الفكره لماذا لا اقوم بعمل اداه تضع مجموعه من الخصائص لكل الأدوات الأخرى و على حسب نوع الأداه بمعنى الأداه نفسها ستحتوى على الخاصيه TableName و هو اسم الجدول الذى سأقوم بالحفظ داخله اما بالنسبه للادوات الأخرى فهى اما ان تكون TextBox و فى هذه الحاله سأضيف الخاصيه FieldName و التى تعبر عن اسم الحقل الذى سأضيف قيمة الـ TextBox داخله و طبعا القيمه ستأتى من الخاصيه Text بالنسبه لأدوات اخرى مثل ListBox سأقوم بإضافة الخاصيه FieldName و هو المكان الذى سيتم الحفظ به داخل الجدول و لكن المشكله انى سأحتاج لـ 3 خصائص اخر الـ Lان الـ ListBox تحتاج لإسم الجدول لتملء منه البيانات التى تظهر فيها و ايضا اسم الحقل الذى ستكون قيمته هى التى ظاهره امام العميل و اسم الحقل الذى سيتم حفظ قيمته داخل الجدول الخاص بالأداه.
ملحوظه :
1 - فى حال عدم فهمك ايا مما سبق لا تقلق فسأقوم بالتفصيل بعد قليل.
2 - سأقوم بإستخدام اللفظين عنصر و خاصيه ليدلوا على خاصيه.
3 - لن اقوم بالتعامل مع قواعد البيانات و لكنى احببت وضع الفكره حتى نكون على اتصال فكرى داخل المثال.
الأداه ToolTip تقوم بتضمين الواجهه IExtenderProvider و التى تتيح لك اضافة خصائص لادوات اخرى.
فى هذا الدرس سنقوم بمعرفة الأتى
- ماهى IExtenderProvider و كيف تعمل.
- كيف تصنع اداه تدعم IExtenderProvider.
فى هذا الدرس سيتم اضافة خصائص لاداة الـ TextBox فقط.
ماهى IExtenderProvider و كيف تعمل
الـ IExtenderProvider تأخذ الشكل التالى
Public Interface IExtenderProvider Function CanExtend(ByVal extendee As Object) As Boolean End Interface
هذه الواجهه هى واجهه خاصه بالـ Desgin-Time لـ FormsDesigner حيث يقوم الـ FormsDesigner بإستدعاء الداله CanExtend لكل الأداوت الموجوده على أى ContainerControl فإذا عادت الداله بالقيمه True يتم إضافة عنصر جديد (لن اقول خاصيه و ستعرف لماذا) داخل الـ PropertyGrid لهذه الأداه.
كيف تصنع اداه تدعم IExtenderProvider
اولا ماهى الخاصيه : الخاصيه Property هى عباره عن دالتين احداهما تقوم بوضع قيمه فى مكان ما و الأخرى تقوم بإحضار هذه القيمه مكان ما (فى حالتنا هذه من نفس المكان)، بإفتراض ان الـ VB لا يدعم الخصائص لذا يمكن تعريف البديل لها كالتالى
private m_age as Integer Public Sub setAge(ByVal age as Integer) m_age = age End Sub Public Function getAge() as Integer Return m_age End Function
السابق هو فعليا تعريف لما نسميه خاصيه حيث انك اذا قمت بفتح برنامج Reflector و قمت برؤية الكود الخاص بالخاصيه Text الخاصه بـ TextBox ستجدها شبيها بالسابق.
فى هذا الجزء سنقوم بعمل اداه من نوع Component و هذا النوع من الأدوات لا يظهر على الـ UI مثله مثل Timer او الـ ToolTip ايضا لا يمكنك الوصول له عن طريق الخاصيه Controls الموجوده بالـ ContainerControl و انما تصل له عن طريق الحقل components و الذى يقوم الـ FormsDesigner بإنشائه عوضا عنك و يقوم بوضعه داخل ملف اسمه يأخذ الشكل التالى XX.Designer.vb
يمكنك انشاء Component عن طريق الكود التالى
Imports System.ComponentModel Class MyTool Inherits Component . . . End Class
و لكن يفضل (او الأفضل) أدراجها من قائمة Project.AddComponent حيث يتم اضافة بعض الأكواد الهامه و الخاصه بالـ Desgin للاداه و قم بتسميتها MyTool.
الأن و بعد إضافتك لها قمت بفتح الكود الخاص بها و اضف هذا السطر قبل تعريف الـ Class
Imports System.ComponentModel
و هذا هو المكان الموجود به الواجهه IExtenderProvider بعد ذلك قم بعمل تضمين للواجهه IExtenderProvider داخل الـ Class ليصبح شكلها كالتالى:
Imports System.ComponentModel Public Class MyTool Implements IExtenderProvider Public Function CanExtend(ByVal extendee As Object) As Boolean Implements IExtenderProvider.CanExtend End Function End Class
و حيث ان هذه الاداه موجهه للتعامل مع TextBox فقط ستصبح الداله CanExtend كالتالى:
Public Function CanExtend(ByVal extendee As Object) As Boolean Implements IExtenderProvider.CanExtend If TypeOf extendee Is TextBox Then Return True End If End Function
قلنا فى السابق ان الأداه ستحتوى على خاصيه و هى TableName و الذى سيتم الحفظ داخله و ايضا الأداه ستقوم بإضافة الخاصيه FieldName داخل الاداوت التى ستدعمها MyTool و فى حالتنا هذه TextBox فقط.
بالنسبه للخاصيه TableName ستكون كالتالى
Private mTableName As String Public Property TableName() As String Get Return mTableName End Get Set(ByVal value As String) mTableName = value End Set End Property
اما بالنسبه للخاصيه FieldName فلابد من اعلام الـ FormsDesginer ان هذه الخاصيه هى المراد وضعها لكل الـ TextBoxes، و لتقوم بفعل هذا يوفر لنا FormsDesigner الـ ProvidePropertyAttribute و التى تطلب منك معاملين اولهما اسم الخاصيه و ثانيهما نوع الاداه التى سيتم اضافه هذه الخاصيه لها و يتم احضار نوع الاداه بإستخدام GetType.
ملحوظه : سنقوم بتغيير اسم الخاصيه FieldName إلى ZFieldName و ساقوم بإعلامك السبب بعد قليل
لذا يصبح شكل MyTool كالتالى
<ProvideProperty("ZFieldName", GetType(TextBox))> _
Public Class MyTool
Implements IExtenderProvider
.
.
.
End Classالأن و قد عرف الـ Designer اسم الخاصيه كيف سنقوم بتضمينها داخل اداتنا، فى الواقع سيتم تضمينها بإسلوب الدالتين و ليس بإسلوب الخصائص المعتاده و السبب فى ذلك لا اعلمه حقيقة و لكن وثائق الـ MSDN تقول انه بالنسبه لهذه الخاصيه سيتم تضمينها على شكل دالتين احداهما تبدء بـ Set و هى تستخدم فى وضع القيمه داخل الخاصيه و الأخرى تبدء بـ Get لاحظ ان الكلمتين لابد ان يكونوا بهذا الشكل و الا لن تعمل الخاصيه (لن تظهر :blink: ).
Public Sub SetZFieldName(ByVal ctl As TextBox, ByVal value As String) End Sub Public Function GetZFieldName(ByVal ctl As TextBox) As String End Function
فى كل الأحوال دالة الـ Set دائما تأخذ معاملين اولهما الأداه التى سيتم وضع الخاصيه لها ثانيهما القيمه التى تم وضعها للأداه و فى حالتنا هذه المعامل الأول هو TextBox اما المعامل الثانى فهو اسم الحقل الذى سيتم الحفظ به و طبعا هو نص
بالنسبه للداله Get فتأخذ معامل واحد فقط هو الاداه التى سيتم إحضار قيمة الخاصيه منها و تعود بالقيمه نفسها و فى حالتنا هذه الكائن المدخل هو الـ TextBox اما القيمه المعاده هى اسم الحقل.
الأن و قد عرفنا شكل الدالتين المفترض كى تقوم الدالتين بعملهم لابد من تخزين القيم فى مكان ما حتى نستطيع استرجاعها فى وقت لاحق. انا استخدم Generic Dictionary لحفظ القيم حيث يكون اسم الاداه هو الـ Key و القيمه هى الـ value و حيث ان الإثنان نصوص لذا يكون الـ Dictionary من نوع نص.
Private mData As New Dictionary(Of String, String) Public Function GetZFieldName(ByVal ctl As TextBox) As String If Not mData.ContainsKey(ctl.Name) Then Return String.Empty End If Return mData.Item(ctl.Name) End Function Public Sub SetZFieldName(ByVal ctl As TextBox, ByVal value As String) If mData.ContainsKey(ctl.Name) Then If value.Trim = String.Empty Then mData.Remove(ctl.Name) Else mData.Item(ctl.Name) = value End If Else If value.Trim = String.Empty Then Return If ctl.Name = String.Empty Then Return mData.Add(ctl.Name, value) End If End Sub
فى البدايه قمنا بتعريف الكائن mData و الذى سيحتوى على القيم و بعدها قمنا بتعريف الداله GetZFieldName فى الواقع المفترض ان يتم شرحها بعد الداله SetZFieldName و لكنى احبذ شرحها فى البدايه.
فى بدايه الداله GetZFieldName نتحقق من وجود اسم الاداه داخل mData فإن كان غير موجود نرعود بنص فارغ و غير ذلك نعود بقيمته من mData.
فى بدايه الداله SetZFieldName نقوم بالتحقق من وجود اسم الأداه فإن كانت موجوده نتحقق من ان القيمه المدخله ليست فارغه فإن كان اسم الاداه موجود و القيمه المدخله فارغه سنقوم بحذف اسم الاداه من mData و هذا بسبب انك عندما تجعل قيمة خاصيه ما فارغه فهذا يعنى حذفك للقيمه.
اما إن كان اسم الاداه غير موجود داخل mData فنقوم بالتحقق من ان القيمه المدخله ليست فارغه و ان اسم الأداه نفسها ليس فارغ بعدها نقوم بإضافة اسم الاداه و القيمه لـ mData.
الأداه الأن جاهزه للعمل فقط قم بعمل Build للمشروع و ستظهر داخل Toolbox.
يتم لتعامل مع هذه الخصائص وقت تشغيل البرنامج من داخل الأداه نفسها عن طريق الدالتين SetZFieldName و GetZFieldName.
بالنسبه لتغييرى لإسم الخاصيه من FieldName إلى ZFieldName السبب هو ان الخاصيه الخاصه بإسم الاداه Name ترتيبها الأبجدى يأتى بعد FieldName و بالتالى عندما يقوم FormsDesginer بعمل الكود الخاص بوضع القيمه داخل الاداه سيقوم بوضع تعريف SetFieldName قبل اسم الأداه داخل الخاصيه Name و حيث اننا نمرر اسم الأداه للداله SetFieldName فبالتالى سيكون دائما فارغ لهذا قمت بجعل اسم الخاصيه ZFieldName حتى ياتى تريبها داخل كود الـ Designer بعد الخاصيه Name.
و الله ولى التوفيق


