ملخص ما سبق:
فى الدرس الأول قمنا بالتعرف على الـ IExtenderProvider و عرفنا ماهيتها و كيفية صنع Component و تضمين هذه الواجهه داخلها و ايضا جعلنا الاداه تقوم بوضع خاصية ZFieldName داخل أداة الـ TextBox فقط.
فى هذا الدرس:
1 - مقدمه.
A - كيف سيتم انشاء جمل DML.
B - ما الأداوت التى سنتعامل معها.
2 - الأداه BasicBind.
3 - أضافة PropertyTab لـ PropertyGrid لتظهر خصائصنا نحن فقط.
4 - افكار اضافيه.
1 - مقدمه
يعتمد هذا الدرس بشكل اساسى على فهمك للدرس السابق.
الاداه التى سنصنعها هذا الدرس شبيها بعض الشئ بأختها MyTool من الدرس السابق مع بعض الإضافات الجديده و لكن قبل البدء بالتكلم لابد ان نعرف ماذا نحتاج منها هى نصنعها بشكل يلائم احتياجنا لها.
الهدف من هذه الأداه هو ان تقوم بعمل جمل Insert و update و delete للبيانات الموجوده داخل الأدوات الموجوده على الفورم الحاليه لذا السؤال الذى يتبادر إلى الذهن هو كيف ستقوم بذلك و ما الاداوات التى ستدعمها الأداه.
ملحوظه : مره اخرى لن يتم التعامل مع قواعد البيانات و الهدف وضع فكره يمكن تطبيقها او الإستفاده منها
1 - A - كيف سيتم انشاء جمل DML
لإنشاء جملة Insert نحتاج الأتى إسم الجدول و اسماء الحقول التى سيتم الإضافه فيها و ايضا القيم التى سيتم إضافتها.
لإنشاء جملة Update لابد اولا ان نكون فى وضع تعديل اى ان السجل الذى سيتم تعديله له وجود و رقم لا يتكرر ايضا الحقول التى سيتم عمل تحديث بالقيم الجديده لها بدلالة ذلك الرقم و بالطبع إسم الجدول
لإنشاء جملة Delete نحتاج لإسم الجدول و رقم السجل الذى سيتم حذفه
فكما نلاحظ ان كل ما نحتاجه فى كل الأحوال هو اسم الجدول و اسم الـ PrimaryKey كذلك اسماء الحقول التى سنتعامل معها.
بالنسبه لإسم الجدول و الـ PK سنقوم بعمل خاصيتان داخل الأداه لهما و بالتالى فى كل مره نحتاج لهما او للقيم الخاصه بهم نستطيع الحصول عليهم.
بالنسبه لأسماء الحقول فلابد ان نتكلم فى هذه النقطه قليلا، سنقوم بصنع خاصيه يتم وضعها داخل كل الاداوت التى سيتم التعامل معها من قبل الأداه و سنسميها FieldName.
لكن ما هى القيمه التى سنحفظها و كيف سنحصل عليها؟
كل اداه من ادوات الدوت النت تحفظ القيم التى يتعامل معها المستخدم بشكل مختلف فالـ TextBox يستخدم الخاصيه Text و الـ CheckBox يستخدم Checked و الـ DropDown لها اكثر من حاله فإما الـ SelectedIndex او Text طبعا يوجد ايضا CheckedListBox واداوت اخرى، لذا كل اداه من هؤلاء ستكون لها مجموعه من الخصائص تبعاُ لنوع الاداه و هى كالتالى:
الخاصيه FieldName عامه على كل الاداوت حيث انها اسم الحقل الذى سيتم الحفظ به داخل الـ TableName.
الاداه TextBoxBase : اغلب ادوات ادخال النصوص تكون مشتقه من هذه الفئه لذا سنقوم بالتعامل معها لنجعل اداتنا ذات مدى بعيد فى التعامل مع ادوات النصوص و حيث ان القيمه تخزن فى الخاصيه Text لذا سنأخذ القيمه دائما من هذه الخاصيه، لا يوجد
خصائص جديده لهذه الاداه.
الاداه ListControl : اغلب الأدوات التى تعتمد على List مشتقه من هذه الفئه مثل ListBox و ComboBox و لكن التعامل مع هذا النوع من الأداوت يتطلب مجهود أضافى و هو وضع القيم بها حتى يستطيع المستخدم ان يختار ما يريد و يالتالى
سنقوم بإضافة 5 خصائص جديده لمشتقات هذه الفئه و هم:
1 - TableToLoad : و هو اسم الجدول الذى سنقوم بإحضار قيم الـ List منه.
2 - FieldToShow : و هو اسم الحقل الذى سنستخدمه للقيمه Text.
3 - FieldValue : و هو اسم الحقل الذى قيمته سيتم حفظها داخل الحقل الموجود اسمه بـ FieldName.
الأداه CheckedListBox : قد تتسأل أليست هذه الأداه مشتقه من ListBox و بالتالى تكون مشتقه من الـ ListControl و اجابتى لك هى نعم لذا قد تتسأل و ما الداعى من وضع خصائص لها اذا حيث انه سيكون لها خصائص الـ ListControl؟ لديك الحق للتتعجب و لكن إذا عرف السبب بطل العجب السبب فى وضعى لهذه الاداه هو الـ CheckBox الموجود داخل الـ List و الذى يحتاج لكود اضافى حتى يتم التعامل معه فانت لا تحتاج فقط التعامل مع عناصر اللست و لكن ايضا تحتاج لمعرفة حالة الـ Checkbox لكل عنصر. عند التعامل مع الـ CheckedListBox بدون اى داوات فأنت تحتاج على الأقل 3 جداول اولهما يحتوى على البيانات التى سيتم إظهارها داخل الـ List ثانيهما الجدول الخاص بالشاشه التى يتم الحفظ بها بشكل
عام و التى تحتوى على PK الجدول الثالث و هو الجدول الذى يتم حفظ عناصر تم اختيار الـ CheckBox لها و ابسط شكل لهذا الجدول يكون مكون من حقلين على الأقل اولهما PK لهذا الجدول و ثانيهما PK ايضا و لكن يحفظ قيمة الـ PK للجدول الأول، لهذا و بالإضافة
للخصائص الموجوده سنحتاج لـ 3 خصائص اضافيه و هم اسم الجدول الثالث و اسم حقل الـ PK لهذا الجدول و اسم حقل الـ PK الخاص بالجدول الأول و الموجود بالجدول الثالث و بالنسبه لقيمة FieldValue سيتم تجاهلها.
اسماء الخاصيتين هم:
1 - DestTable : و هو اسم الجدول الثالث.
2 - DestPK : و هو اسم حقل الـ PK للجدول الثالث.
3 - OwnerPK : و هو اسم حقل الـ PK للجدول الأول.
الأداه ButtonBase : كل الأزار القائمه بذاتها مثل Button و CheckBox و RadioButton مشتقه من هذه الفئه. داخل اداتنا سيتم تجاهل الـ Button و التعامل مع الباقين حيث انهم يحتووا على الخاصيه Value و التى سنأخذ القيمه منها ايضا لن نحتاج لإضافة خصائص جديده لهذه الفئه.
1 - B - ما الأداوت التى سنتعامل معها
مما سبق نلخص الأدوات التى سنتعامل معها و الخصائص الموجوده بكل منهم:
1 - الأداه Control : سنضع لها الخاصيه FieldName و تذكر انه قمنا بإختيار هذه الفئه خصيصا لإنها تعتبر الفئه الأب لكل الأداوت التى تكلمنا عنها فى السابق لذا و حتى نستطيع وضع خاصيه لمجموعه من الأدوات المختلفه لابد من ايجاد عامل مشترك بينهم و هو فى حالتنا هذه تلك الفئه. شئ اخر قد تعتقد انه سيتم وضع هذه الخاصيه داخل كل الادوات المشتقه من هذه الفئه تقنيا كلامك صحيح و لكنك نسيت الداله CanExtend و التى تقوم بعمل Filter للادوات التى ستوضع لها خصائص بمعنى اخر يمكن
شرح هذا الموقف من وجهة نظر الـ FormsDesigner كالتالى:
A - قام المبرمج بوضع اداه تدعم الواجهه IExtender على الفورم و تحتوى الفورم على 10 اداوت ما بين TextBox و Calendar و List و غيرهم.
B - اداه المبرمج و التى اسمها X تحتوى على ProvidePropertyAttribute و التى تقوم بالسماح بإضافة خاصيه بإسم Test (لا يهم نوع الأداه الأن) لذا سأقوم بتمرير كل الأدوات العشره الموجوده على الفورم للداله CanExtend و سأرى ماذا سترجع لى: الاداه الاولى Button تم الرجوع بـ False لذا سأتخطها، الثانيه Calendar تم الرجوع بـ False .. إللى بعده، الأداه TextBox تم الرجوع بـ True لذا سأذهب مره اخرى لـ ProvidePropertyAttribute لأرى هل يوجد خصائص يمكن اضافتها لاداه من نوع TextBox او من مشتقاتها... همم نعم نعم يوجد و اسمها Test سأقوم بإضافتها و لكن قبل أن اضيفها لابد ان ارى هل قام هذا المبرمج بإضافة الداله Set و Get لهذه الخاصيه و لا بيضحك عليا ... يبدو انه راجل طيب لقد اضافهم بالشكل الصحيح لذا سأقوم بوضع هذه الخاصيه لهذا الـ TextBox .. اللى بعده، الأداه الحاليه هى IPAddressTextBox (اداه خاصه بى مشتقه من TextBox) تم الرجوع بـ True. و هكذا يقوم الـ FormsDesigner مع باقى الاداوت. ارجو ان تكون الفكره وضحت.
2 - الاداه TextBoxBase و ButtonBase : لا خصائص جديده لهم لذا يندرجوا تحت بند الأداه Control.
3 - الاداه ListControl : سنضع لها 3 خصائص و هم TableToLoad و FieldToShow و FieldValue.
4 - الأداه CheckedListBox : بالإضافه للخصائص الموجوده برقم 3 سيتم اضافة DestTable و DestPK و OwnerPK.
2 - الأداه BasicBind
افتح مشروع جديد من نوع DLL و قم بإضافة مجلد و سمه Component و داخله قم بإضافة New Component و سمها BasicBind.
من خصائص المشروع اختر الـ References Tab و قم بعمل Check على العنصر System.ComponentModel
قم ايضا بإضافة Reference للمكتبه System.Windows.Forms.
قبل ان نكمل توجد ملاحظه وهى اننى سأقوم بتجميع الـ 3 خصائص الذين سيتم اضافتهم لـ ListControl داخل Class و سأستخدم TypeConvertor لها نفس الكلام ينطبق على الـ 3 خصائص الذين سيتم اضافتهم لـ CheckedListBox فإذا كنت لا تعرف ما هو الـ TypeConvertor او كيفية عمله ارجو منك ان تذهب إلى هذا الموضوع و الذى يشرحه بالتفصيل. (شكرا للأخ Ghost على و ضعه هذا الموضوع).
قم بإضافة مجلد جديد و سمه Type Convertor و قم بداخله بإضافة 4 فئات و هم بالشكل التالى:
الأولى : ListControlInfo و تحتوى على الخصائص التى سيتم إضافتها لـ ListControl.
''' <summary>
''' Contain defination for ListControl that will add to ListControlBindCollection
''' </summary>
<TypeConverter(GetType(ListControlInfoConverter))> _
Public Class ListControlInfo
Private mTableToLoad As String
Private mFieldToShow As String
Private mFieldValue As String
''' <summary>
''' Table to load data from
''' </summary>
<DefaultValue(vbNullString), _
Description("Master Table which data will load from"), _
NotifyParentProperty(True)> _
Public Property TableToLoad() As String
Get
Return mTableToLoad
End Get
Set(ByVal value As String)
mTableToLoad = value.Trim()
End Set
End Property
''' <summary>
''' Field which value will be displayed in list control
''' </summary>
<DefaultValue(vbNullString), _
Description("Field which value will be displayed in list control"), _
NotifyParentProperty(True)> _
Public Property FieldToShow() As String
Get
Return mFieldToShow
End Get
Set(ByVal value As String)
mFieldToShow = value.Trim()
End Set
End Property
''' <summary>
''' Field which value will be stored in current form table
''' </summary>
<DefaultValue(vbNullString), _
Description("Field which value will be stored in current form table"), _
NotifyParentProperty(True)> _
Public Property FieldValue() As String
Get
Return mFieldValue
End Get
Set(ByVal value As String)
mFieldValue = value.Trim()
End Set
End Property
''' <summary>
''' Is one of this class elements are empty?
''' </summary>
''' <returns>true if any element empty,false otherwise</returns>
Public Function IsEmpty() As Boolean
Return (mTableToLoad = String.Empty) AndAlso _
(mFieldToShow = String.Empty) AndAlso _
(mFieldValue = String.Empty)
End Function
Public Overrides Function ToString() As String
If IsEmpty() Then Return String.Empty
Return String.Format("{0}, {1}, {2}", If(mTableToLoad <> String.Empty, mTableToLoad, "{EMPTY}"), _
If(mFieldToShow <> String.Empty, mFieldToShow, "{EMPTY}"), _
If(mFieldValue <> String.Empty, mFieldValue, "{EMPTY}"))
End Function
End Classالثانيه: ListControlInfoConverter و هى ExpandableObjectConverter لـ ListControlInfo.
''' <summary>
''' A Convertor to display ListControlInfo in PropertyGrid
''' </summary>
''' <remarks></remarks>
Public Class ListControlInfoConverter
Inherits ExpandableObjectConverter
Public Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext,
ByVal destinationType As System.Type) As Boolean
If destinationType Is GetType(ListControlInfo) Then
Return True
Else
Return MyBase.CanConvertTo(context, destinationType)
End If
End Function
Public Overrides Function ConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal
culture As System.Globalization.CultureInfo, ByVal value As Object, ByVal destinationType As System.Type) As
Object
If destinationType Is GetType(ListControlInfo) Then
Return _ToString(value)
Else
Return MyBase.ConvertTo(context, culture, value, destinationType)
End If
End Function
Public Overrides Function CanConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext,
ByVal sourceType As System.Type) As Boolean
If sourceType Is GetType(ListControlInfo) Then
Return True
Else
Return MyBase.CanConvertFrom(context, sourceType)
End If
End Function
Public Overrides Function ConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext,
ByVal culture As System.Globalization.CultureInfo, ByVal value As Object) As Object
If TypeOf value Is ListControlInfo Then
Return _FromString(value)
Else
Return MyBase.ConvertFrom(context, culture, value)
End If
End Function
Private Function _ToString(ByVal value As Object) As String
Return CType(value, ListControlInfo).ToString
End Function
Private Function _FromString(ByVal value As Object) As ListControlInfo
Dim arr() As String = CStr(value).Split(","c)
If arr Is Nothing Then Return New ListControlInfo
If arr.Length <> 3 Then
Throw New Exception("Error: can not convert the value")
End If
Try
Dim li As New ListControlInfo
li.TableToLoad = arr(0)
li.FieldToShow = arr(1)
li.FieldValue = arr(2)
Return li
Catch ex As Exception
Throw New Exception("Error: can not convert the value")
End Try
End Function
End Classالثالثه: CheckListInfo و تحتوى على الخصائص التى سيتم اضافتها لـ CheckedListBox.
<TypeConverter(GetType(CheckListInfoConverter))> _
Public Class CheckListInfo
Private mDestTable As String
Private mDestPK As String
Private mOwnerPK As String
''' <summary>
''' Table that check values stored in
''' </summary>
<DefaultValue(vbNullString), _
Description("Table that check values stored in"), _
NotifyParentProperty(True)> _
Public Property DestTable() As String
Get
Return mDestTable
End Get
Set(ByVal value As String)
mDestTable = value.Trim()
End Set
End Property
''' <summary>
''' check field which check value are stored
''' </summary>
<DefaultValue(vbNullString), _
Description("Check field which check value are stored"), _
NotifyParentProperty(True)> _
Public Property DestPK() As String
Get
Return mDestPK
End Get
Set(ByVal value As String)
mDestPK = value.Trim()
End Set
End Property
''' <summary>
''' field which primary table store its Code in
''' </summary>
<DefaultValue(vbNullString), _
Description("field which primary table store its Code in"), _
NotifyParentProperty(True)> _
Public Property OwnerPK() As String
Get
Return mOwnerPK
End Get
Set(ByVal value As String)
mOwnerPK = value.Trim()
End Set
End Property
''' <summary>
''' Is one of this class elements are empty?
''' </summary>
''' <returns>true if any element empty,false otherwise</returns>
Public Function IsEmpty() As Boolean
Return (mDestTable = String.Empty) AndAlso _
(mDestPK = String.Empty) AndAlso _
(mOwnerPK = String.Empty)
End Function
Public Overrides Function ToString() As String
If IsEmpty() Then Return String.Empty
Return String.Format("{0}, {1}, {2}", If(mDestTable <> String.Empty, mDestTable, "{EMPTY}"), _
If(mDestPK <> String.Empty, mDestPK, "{EMPTY}"), _
If(mOwnerPK <> String.Empty, mOwnerPK, "{EMPTY}"))
End Function
End Classالأخيره : CheckListInfoConverter و هى ExpandableObjectConverter لـ CheckListInfo.
''' <summary>
''' A Converter to display CheckListInfo in PropertyGrid
''' </summary>
''' <remarks></remarks>
Public Class CheckListInfoConverter
Inherits ExpandableObjectConverter
Public Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext,
ByVal destinationType As System.Type) As Boolean
If destinationType Is GetType(CheckListInfo) Then
Return True
Else
Return MyBase.CanConvertTo(context, destinationType)
End If
End Function
Public Overrides Function ConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal
culture As System.Globalization.CultureInfo, ByVal value As Object, ByVal destinationType As System.Type) As
Object
If destinationType Is GetType(CheckListInfo) Then
Return _ToString(value)
Else
Return MyBase.ConvertTo(context, culture, value, destinationType)
End If
End Function
Public Overrides Function CanConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext,
ByVal sourceType As System.Type) As Boolean
If sourceType Is GetType(CheckListInfo) Then
Return True
Else
Return MyBase.CanConvertFrom(context, sourceType)
End If
End Function
Public Overrides Function ConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext,
ByVal culture As System.Globalization.CultureInfo, ByVal value As Object) As Object
If TypeOf value Is CheckListInfo Then
Return _FromString(value)
Else
Return MyBase.ConvertFrom(context, culture, value)
End If
End Function
Private Function _ToString(ByVal value As Object) As String
Return CType(value, CheckListInfo).ToString
End Function
Private Function _FromString(ByVal value As Object) As CheckListInfo
Dim arr() As String = CStr(value).Split(","c)
If arr Is Nothing Then Return New CheckListInfo
If arr.Length <> 3 Then
Throw New Exception("Error: can not convert the value")
End If
Try
Dim li As New CheckListInfo
li.DestTable = arr(0)
li.DestPK = arr(1)
li.OwnerPK = arr(2)
Return li
Catch ex As Exception
Throw New Exception("Error: can not convert the value")
End Try
End Function
End Classملاحظات على الأكواد السابقه:
1 - الـ DescriptionAttribute : تتيح لك كتابة وصف قصير لما تفعله هذه الخاصيه هذا الوصف يظهر فى اخر PropertyGrid عند احداهما.
2 - الـ DefaultValueAttribute: تتيح لك وضع قيمه افتراضيه لخاصيه معينه داخل الـ PropertyGrid و هنا انا استخدم vbNullString و هى تعادل Nothing او القيمه الإفتراضيه لهذا النوع.
3 - الـ NotifyParentPropertyAttribute : عندما تكون الـ Class عباره عن ExpandableObject داخل الـ PropertyGrid يصبح حينها اسم الـ Class هو Parent و كل
الخصائص الموجوده داخله هى Child و بالتالى عند استخدام هذه المواصفه و تمرير true لها تقوم الـ PropertyGrid بعمل تحديث للقيمة المكتوبه بجانب اسم الفئه تماما مثل ما يحدث مع الخاصيه Font فعند تغييرك لاحد الخصائص الموجدوه داخلها يتم تحديث القيم الموجوده بجانب الخاصيه الأب Font.
4 - توجد Attribute سنقوم بوضعها على الخصائص بكلتا الفئتان (طبعا بإستثناء الـ TypeConvertors) و اسمها DatabaseBindAttribute و هى مواصفه سنقوم بعملها لاحقا و الهدف من ذكرى لها هنا انى سأطلب منك الرجوع لفئات الخصائص لتضيفها عليهم.
قمنا الأن بعمل الفئات التى ستدعم عملنا داخل الاداه BasicBind لذا لنبدء العمل بها.
قم بفتح شاشة الكود الخاصه بالاداه و قم بتعديل الكود الموجود بها ليأخذ الشكل التالى:
Imports System.Windows.Forms
Imports System.Drawing
Imports System.ComponentModel.Design.Serialization
<ProvideProperty("ZFieldName", GetType(Control)), _
ProvideProperty("ZListBind", GetType(ListControl)), _
ProvideProperty("ZCheckBind", GetType(CheckedListBox))> _
Public Class BasicBind
Implements IExtenderProvider
' for all controls that will use FieldName
Dim mBasic As Dictionary(Of String, String)
' for all listcontrol that will use this class
Dim mList As Dictionary(Of String, ListControlInfo)
' for all checkedlistbox that will use CheckField
Dim mCheckList As Dictionary(Of String, CheckListInfo)
End Classقمنا بتعريف 3 خصائص و هم ZFieldName و ZListBind و ZCheckBind وقمنا بتحديد نوع الأداوت لكل منهم بعدها قمنا بعمل 3 متغيرات من نوع Generic Dictionary ليتم اضافة القيم الخاصه بالخصائص داخلهم حيث mBasic خاصه بـ FieldName و mList خاصه بـ ListControl و mCheckList خاصه بـ CheckedListBox.
نأتى الأن لتعريف الداله CanExtend
#Region "IExtenderProvider Methods" Private Function IsControlSupported(ByVal ctl As Object) As Boolean If TypeOf ctl Is ButtonBase AndAlso Not (TypeOf ctl Is Button) Then Return True End If If TypeOf ctl Is TextBoxBase Then Return True End If If TypeOf ctl Is ListControl Then Return True End If Return False End Function Public Function CanExtend(ByVal extendee As Object) As Boolean Implements IExtenderProvider.CanExtend Return IsControlSupported(extendee) End Function #End Region
اولا قمت بعمل Region بإسم IExtenderProvider Methods حتى اقوم بتجميع الكود كل مجموعه متجانسه فى مكان واحد بعدها قمت بتعريف الداله IsControlSupported و التى تتحقق من نوع الأدوات التى ستتعامل معها الأداه و من داخل الداله CanExtend قمت فقط بإستدعاء IsControlSupported و العوده بالقيمه التى تعود بها.
نأتى الأن لتعريف الخاصيه FieldName
#Region "FieldName Property"
<Description("FieldName which we will bind"), _
Category("Control Bind"), _
DisplayName("FieldName")> _
Public Function GetZFieldName(ByVal ctl As Control) As String
If Not mBasic.ContainsKey(ctl.Name) Then
Return String.Empty
End If
Return mBasic.Item(ctl.Name)
End Function
<Description("FieldName which we will bind"), _
Category("Control Bind")> _
Public Sub SetZFieldName(ByVal ctl As Control, ByVal value As String)
If mBasic.ContainsKey(ctl.Name) Then
If value.Trim = String.Empty Then
mBasic.Remove(ctl.Name)
Else
mBasic.Item(ctl.Name) = value
End If
Else
If value.Trim = String.Empty Then Return
If ctl.Name = String.Empty Then Return
mBasic.Add(ctl.Name, value)
End If
End Sub
#End Regionالكود الخاص بهذه الخاصيه تم شرحه من قبل (فى الدرس السابق) و لكن الجديد هنا هو المواصفه Category و هى تتيح لك وضع الخاصيه الخاصه بك تحت مجموعه معينه اما المواصفه DisplayName فتقوم بوضع بوضع الخاصيه داخل PropertyGrid و لكن بإسم مختلف هو الذى تحدده، و الهدف من استخدامى لها هو ان اسم ZFieldName ليس جيد و لكنى مجبر عليه (توجد بعض الأساليب للتحايل على هذه مشكله منها استخدام CodeDom) لذا فقمت بتغيير الإسم الذى يظهر داخل الـ PropertyGrid لـ FieldName.
التالى هو كود الخاصيتين ZListBind و ZCheckBind الكود متشابه إلى حد كبير مع ZFieldName.
#Region "ListBind Property"
<Description("Info we'll use to bind"), _
Category("ListBox Bind"), _
DisplayName("Mater Table")> _
Public Function GetZListBind(ByVal ctl As ListControl) As ListControlInfo
Dim tmp As ListControlInfo = Nothing
If Not mList.ContainsKey(ctl.Name) Then
tmp = New ListControlInfo
mList.Add(ctl.Name, tmp)
Else
tmp = mList.Item(ctl.Name)
End If
Return tmp
End Function
<Description("Info we'll use to bind"), _
Category("ListBox Bind"), _
DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
Public Sub SetZListBind(ByVal ctl As ListControl, ByVal value As ListControlInfo)
If mList.ContainsKey(ctl.Name) Then
If value.IsEmpty = True Then
mList.Remove(ctl.Name)
Else
mList.Item(ctl.Name) = value
End If
Else
If value.IsEmpty = True Then Return
mList.Add(ctl.Name, value)
End If
End Sub
#End Region
#Region "CheckField Property"
<Description("Info that will allow bind the checkbox"), _
Category("CheckedListBox Bind"), _
DisplayName("Detail Table")> _
Public Function GetZCheckBind(ByVal ctl As CheckedListBox) As CheckListInfo
Dim tmp As CheckListInfo = Nothing
If Not mCheckList.ContainsKey(ctl.Name) Then
tmp = New CheckListInfo
mCheckList.Add(ctl.Name, tmp)
Else
tmp = mCheckList.Item(ctl.Name)
End If
Return tmp
End Function
<Description("CheckField which we will bind to checkbox into list"), _
Category("CheckedListBox Bind"), _
DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
Public Sub SetZCheckBind(ByVal ctl As CheckedListBox, ByVal value As CheckListInfo)
If mCheckList.ContainsKey(ctl.Name) Then
If value.IsEmpty = True Then
mCheckList.Remove(ctl.Name)
Else
mCheckList.Item(ctl.Name) = value
End If
Else
If value.IsEmpty = True Then Return
mCheckList.Add(ctl.Name, value)
End If
End Sub
#End Regionالجديد داخل الخاصيتين هو استخدامى للمواصفه DesignerSerializationVisibilityAttribute الهدف من استخدامها هنا هو جعل الـ Code Generator الخاص بالـ Form Designer انتاج كود بشكل دائم للفئتين CheckListInfo و ListControlInfo.
المواصفه DesignerSerializationVisibilityAttribute تأخذ معامل واحد من نوع DesignerSerializationVisibility وهو Enum و يحتوى على مجموعه من العناصر كل واحده منهما لها معنى خاص للـ Code Generator حيث :
1 - Hidden : تحث الـ Code Generator على عدم توليد كود لهذه الخاصيه لذا فمهما حدث لن يتم حفظ القيم التى ادخلتها بها داخل الـ XX.Designer.vb و بالتالى بمجرد ابتعادك عن الأداه و الرجوع لها مره اخرى ستجد ان القيم قد ذهبت ادراج الرياح.
2 - Visible: و تعنى ان العناصر التى تم اسناد قيم لها هى فقط ما يتم توليد كود لأجله فقط.
3 - Content: و تعنى انه فى كل الأحوال قم بتوليد قيم لكل عناصر هذه الخاصيه (و من اجل هذه جعلنا Default value للخصائص داخل الفئات حيث اذا تم توليد كود لها يتم جعل القيمه المبدئي فارغه).
حان الوقت الأن لعمل اضافه تجميليه للاداه و هى ايقونه لها، لعمل ايقونه تظهر داخل الـ Toolbox لابد من توافر بعض الشروط بها و هم:
1 - حجمها يساوى 16 * 16.
2 - الـ Color Mask لها هو Fushia و يعادل R = 255, G = 0, B = 255.
3 - يتم اضافتها كـ Resource داخل المشروع و بعد اضافتها قم بتحديدها داخل الـ Solution Explorer و بعدها قم بالذهاب إلى Properties Window و غير قيمة الخاصيه BuildAction لتصبح Emmbeded Resource.
4 - استخدم المواصفه ToolBoxBitmapAttribute لنسب هذه الصوره للأداه، هذه المواصفه لها اكثر من مشيد الوحيد الذى نحتاجه هنا هو مشيد بمعاملين اولهما يطلب Type و الأخر FileName بالنسبه للأول ستقوم بتمرير اسم اداتك له بإستخدام GetType و الاخر هو اسم الصوره الموجود داخل مجلد Resources و لا تنسى الإمتداد.
فى حالتنا هذه قمت بعمل صوره بنفس الشورط السابقه ستجدها فى المرفقات و لكن يبقى عليك اضافة الكود التالى لمجموعة المواصفات الخاصه بالأداه
ToolboxBitmap(GetType(BasicBind), "BasicBind.bmp")
3 - أضافة PropertyTab لـ PropertyGrid لتظهر خصائصنا نحن فقط
هذه الأداه واحده من مجموعه من اداوت قمت بتصنيعها لتسهيل عملية ربط الأداوت بقاعدة البيانات و لكن المشكله - هى ليست مشكله حقيقة - انه عند اضاضفة بعض اداوات الـ Bind على الفورم تصبح الخصائص المضلفه لبعض الاداوت حوالى 10 خصائص و هى كميه كبيره لذا فكل مره احدد الاداه اقوم بالبحث عن خاصيه معينه داخل الـ Property Grid لذا فكرت لماذا لا اجعل اداتى عندما تنزل على الفورم ان تقوم بإضافة Tab جديد داخل PropertyGrid الخاصه بالـ VS و تقوم فقط بإظهار الخصائص التى اقوم بإضافتها للأداوت.
فى الواقع هذا هو ما سنقوم بفعله بعد قليل و لكن قبل البدء لابد من وجود معرفة بسيطه عن كيفية عمل الـ PropertyGrid.
تعتمد الـ PropertyGrid بشكل اساسى على الفئه TypeDescriptor بالإضافه إلى الفئه PropertyTab التى لا تستطيع انشاء نسخه منها و لكن عوضا عن ذلك تقوم بوراثتها و تقوم بإستخدام المواصفه PropertyTab لتقوم بإسناد النسخه الخاصه بك من الـ PropertyTab لأداه معينه. يعتمد الـ PropertyTab فى اظهاره للخصائص على المواصفات بحيث تستخدم لعمل Filter للخصائص فمثلا المواصفه Browseable تستخدم لجعل خاصيه معينه قابله او غير قابله للظهور داخل PropertyGrid.
بالنسبه للفئه TypeDescriptor فهى تحضر لك بيانات معينه عن محتويات اداه معينه او نسخه منها بالطبع عن طريق الـ MetaData. طبعا لشرح هذه الفئه سنحتاج لدرس طويل و ضخم و لكنها ليست ضمن هذا الدرس لذا سيقتصر شرحى لها على الدوال التى سأستخدمها منها.
فى البدايه قم بإضافة مجلد و سمه PropertyTab و قم بإضافة فئه جديده و سمها DatabaseBindAttribute ثم اجعلها شبيها بالتاليه
''' <summary> ''' allow object to be seen in DatabaseLib Property Tab ''' </summary> <AttributeUsage(AttributeTargets.All)> _ Public Class DatabaseBindAttribute Inherits Attribute End Class
كما تلاحظ استخدمنا المواصفه AttributeUsage و مررنا لها All حتى نستطيع استخدام مواصفتنا الجديده مع اى عنصر برمجى سواء كان حدث او خاصيه او داله او structure او ايا كان.
الأن قم بالذهاب إلى الفئتين الذين يحتووا على الخصائص و قم بإضافة هذه المواصفه لكل الخصائص الموجوده بهم كذلك اذهب للاداه و قم بوضع هذه المواصفه على دالة الـ Set او الـ Get او كلاهما.
بعدها قم بإضافة فئه جديده داخل نفس المجلد و سمها BindTab و اجعل الكود الخاص بها مشابه للتالى:
Imports System.Windows.Forms.Design
Public Class BindTab
Inherits PropertyTab
Private Const mTabName As String = "DataBind Properties"
Public Overloads Overrides Function GetProperties(ByVal component As Object, ByVal attributes() As
Attribute) As PropertyDescriptorCollection
Dim arr() As Attribute = {New DatabaseBindAttribute}
Return TypeDescriptor.GetProperties(component, arr)
End Function
Public Overrides ReadOnly Property TabName() As String
Get
Return mTabName
End Get
End Property
Public Overrides ReadOnly Property Bitmap() As System.Drawing.Bitmap
Get
Return My.Resources.BindTab.ToBitmap()
End Get
End Property
End Classبمجرد ان تقوم بعمل Inherit للفئه PropertyTab سيتم اضافة خاصيه اسمها TabName كذلك سيتم اضافة الداله GetProperties بالنسبه للخاصيه فهى تعود بإسم الـ Tab و الذى سيظهر عندما ستقف على ايقونة التاب بالماوس اما بالنسبه للداله فتجاهلها قليلا للأن.
ايضا قمت بعمل override للخاصيه Bitmap و هى صوره رمزيه للـ Tab و حيث انى قمت بإضافة صوره من نوع Icon و اسمها BindTab.ico لذا فلابد منى تحويلها لصوره و ذلك يتم عن طريق استدعاء الداله ToBitmap.
نأتى الأن للداله GetProperties و التى لابد من ان تعود بـ Collection تحتوى على الخصائص المراد اظهارها داخل هذا الـ Tab و حيث ان الخصائص التى يتم اضافتها للأداوت تم وضع المواصفه DatabaseBindAttribute عليهم لذا كل ما سنفعله هو عمل Filter للمعامل component - و الذى يمثل الأداه التى قمت بتحديدها الأن - بإستخدام المواصفه DatabaseBindAttribute و بالتالى القيم المعاده ستكون هى الخصائص التى تحتوى على هذه المواصفه، و لكن كيف سنقوم بعمل هذا الـ Filter؟؟ بالطبع سنستخدم الـ TypeDescriptor و هو يحتوى على الداله GetProperties و التى لها اكثر من نسخه احداهما - و التى نحتاجاها الان - تطلب معاملين اولهما الكائن الذى سنقوم بالبحث داخله و هو متوفر لدينا component ثانيهما و هو مصفوفه من المواصفات التى ستسخدم كـ Filter لهذه الأداه لذا قمت بصنع المتغير arr و هو عباره عن مصفوفه من نوع Attribute و قمت بعمل اضافة عنصر واحد لهذه المصفوفه و هو New DatabaseBindAttribute و بعدها قمت بتمرير هذه المصفوفه كمعامل ثانى.
و بذلك نكون انتهينا من صنع الـ Tab و لكن انتظر فلم نقوم حتى الأن بإعلام الأداه BasicBind بإضافة ذلك الـ Tab عندما تضاف او ان تستخدمه اذا كان موجود. و سيتم ذلك عن طريق وضع الكود التالى فى مجموعة المواصفات على الاداه
PropertyTab(GetType(BindTab), PropertyTabScope.Document)
المواصفه PropertyTab تأخذ معاملين اولهما نوع الفئه الوارثه للـ PropertyTab و ثانيهما المدى الخاص لإستخدام هذا الـ Tab، ذلك المدى يحدد بـ 4 حالات:
1 - Static : حيث يتم اضافة هذا الـ Tab مره واحده و لا يحذف ابدا.
2 - Global : يتم اضافة هذا الـ Tab على مستوى جميع الفورم او الاداوات التى تستخدم Designer بمعنى انك اذا حددت شاشة كود سيختفى اما اذا قمت بتحديد Designer سيظهر.
3 - Document: و هى التى نستخدمها و تعنى اضافة الـ Tab للمكان الموجود به الاداه التى قامت بإضافته و على مستوى كل الأداوت الموجوده و فى حال تحديدك لفورم اخرى لا تحتوى على هذه الاداه سيتم حذف هذا الـ Tab.
4 - Component: و تعنى اضافة الـ Tab على مستوى الاداه بمعنى عندما ينتقل التحديد من هذه الاداه إلى اى مكان اخر سيتم حذف هذا الـ Tab.
و بذلك نكون قد انتهينا من تصنيع الاداه كلها. التالى هى مجرد افكار فقط تتيح لك التعامل مع الأداه فى الـ Run-Time.
4 - افكار اضافيه
لا اصدق انى انتهيت من كتابة كل هذا الكم من الكلام و اتمنى من الله ان ينفعك به و تجد به ما تحتاج او ما يعطيك افكارا جديد. بعد ان قمت بالإنتهاء من صنع ادوات مماثله و بدءت فى استعمالها لاحظت حدوث امر متكرر و هو انىة احتاج لتغيير قيم فى هذه الخصائص على سبيل
التجربه فكنت اوقف عمل البرنامج اذهب لتغيير القيمه داخل الـ Desgin-Time و اجرب و اعود مره اخرى و هكذا لدرجة اننى احيانا كنت انسى ارجاع القيمه الأصليه حيث كانت و ايضا كنت انسى ما هى القيمه الأصليه اصلا.
طبعا الامر مزعج لذا فقد قمت بعمل بعض الإضافات على الاداوت لأقوم بتعديل قيم الخصائص وقت التشغيل بدلا من وقت التصميم و بالتالى حفظت وقتى من عمل Compile كل دقيقه لمجموعه من الشاشت و ايضا سرعت عملية تنقيح الأخطاء.
الفكره تتبلور فى صنع قائمه تضاف للـ SystemMenu الخاصه بالفورم و التى من خلالها تجعلنى استطيع معرفة مجموعه من البيانات او تعديلها و هذه هى مجموعة صور لها
طبعا لا داعى من جعلك تفكر الفئه موجوده فى مرفق منفصل و لكن السؤال هو كيف ستجعلها تعمل سأضع لك الكيفيه بعد قليل
و الله ولى التوفيق
