السلام عليكم و رحمة الله وبركاته
من المميزات الجميلة التي كانت لدينا كمبرمجين VB6 هي مصفوفات الأدوات او الـ Controls Arrays و ذلك انها تفيد في إنشاء مجموعة من الادوات المرتبطة ببعضها البعض والتحكم فيها بشيء من السهولة و اليسر بحيث يتشارك الجميع في الأحداث المختلفة ولا يخفى على مجرب فائدتها العظيمة في كثير من البرامج التطبيقية ...
و تنقسم مصفوفات الأدوات الى قسمين من حيث زمن الأنشاء فمنها ما يتم إنشائه في وقت التصميم Design Time و منها ما ننتجه في وقت التنفيذ RunTime ولكل منها فوائده و إستخداماته ... و تكمن إستخدامات وقت التصميم عند الحاجة الى تصميم واجهات معقدة بها مجموعات من الادوات المرتبطة بالاحداث فمثلاً اذا كان لدينا مجموعة من الخيارات والمتمثلة في عنواين Labels و نريد ان يظهر العنصر الذي فوقه المؤشر بشكل مختلف قليلاً ...
اما استخدامات وقت التنفيذ فانها تفيد في حالة كانت هذة العناصر كثيرة ويصعب بنائها في وقت التصميم فأننا نلجأ الى تحميلها عند تشغيل البرنامج , كما ان هناك حالات اخرى كثيرة خاصة اذا كنا لا نعلم عدد الادوات التي نحن بحاجة اليها .. خذ مثلاً انك تقوم بتصميم برنامج لعرض الصور في مجلدات النظام بحيث قمت بتوفير مكنيكية للتنقل بين مجلدات النظام و تريد بمجرد انتقال المستخدم الى مجلد معين ان تقوم بعرض الصور التي فيه على شكل مصغرات في برنامجك بواسطة اداة مثل PictureBox فانك في هذة الحالة ستواجه مشكلة انك لاتعرف عدد الصور التي في كل مجلد وبذلك فانك سوف تلجأ الى تحميل عدد مناسب من الاداة PictureBox لكل مجلد بمجرد الانتقال اليه ..
وفي الاصدار القديم VB6 فانك سوف تستخدم طرق مختلفة لتوليد مصفوفات الادوات في وقت التنفيذ ومنها استخدام الامر Load لتحميل العناصر الى الـ Form بشكل تلقائي ... واما في وقت التصميم فان الطريقة ابسط بكثير فيكفيك مجرد نسخ العنصر عدة مرات وسوف يسألك الفيجوال بيسك < هل تريد انشاء مصفوفة ادوات ؟ > فإذا كانت اجابتك بالإيجاب فانه سوف يقوم بانشاء هذة المصفوفة لك بمجرد نسخك للعناصر مع تنظيم تلقائي لرقم كل عنصر منها Index بحيث تتشارك جميع العناصر بنفس الأسم وتختلف في الـ Index فقط ...
و لكن الفيجوال بيسك دوت نت لا توجد بها امكانية تصميم مصفوفة عناصر في وقت التصميم فاذا قمت بنسخ عنصر عدة مرات فانه سوف يقوم بتحديد إسم فريد لكل عنصر تنسخه بحيث يلغي امكانية تصميم المصفوفة وقت التصميم ! ...
و هنا بيت القصيد فهل يعني ذلك اننا قد فقدنا هذة الميزة العظيمة في الإصدار الجديد للفيجوال VB.NET ؟
الاجابة على هذا السؤال و بحمد من الله و فضل تكون بالنفي ... اذ انه يوجد عدة طرق يمكن للمرء ان يفكر فيها لتلافي هذة المسألة .. وهو ما اسعى لتوضيحه هنا بإذن الله تعالى ...
و الطريقة التي اقترحها هنا هي تكوين مصفوفة ادواتنا بإنفسنا ولتسهيل الأمر وإحتمال احتياجنا المتكرر اليها في برامجنا المستقبلية .. فقد قمت ببناء فئة بسيطة أسميتها clsControlArrays ستفيدني كثيراً في توضيح الأمر ...
دعنا الان نتصفح شفرة هذة الفئة البسيطة معاً .. ونناقش سوياً سبل تطويرها فهي وليدة الساعة و لا اشك في احتوائها على شوائب تخفى علي في هذة اللحظة اذ اني لم اعرضها للاختبار واضعها للفائدة و التطوير ....
اما اذا كنت تريد ان تخرج فقط بالكود ومميزاته فاحيلك عندها الى خاتمة الموضوع والتي تحتوي على المميزات المحتواه في هذة الفئة وكذلك كود برنامجين لتوضيح استخدام الفئة في مشاريعك ..
اما اذا كنت تريد تصفح الكود ومناقشته فبسم الله تعالى نبدأ وعليه نتوكل و اليه ننيب ...
الان احيطك علماً بأن الشفرة الاولى للفئة تتلخص بالتالي :
Public Class ControlArrays Inherits System.Collections.CollectionBase #Region " Overloading AddNew Method..." Overloads Sub AddNew(ByVal Parent As Control, ByVal YourControl As Object, ByVal Visible As Boolean) YourControl.Visible = Visible list.Add(YourControl) Parent.Controls.Add(YourControl) End Sub Overloads Sub AddNew(ByVal Parent As Control, ByVal YourControl As Object, ByVal Location As Point, ByVal Visible As Boolean) YourControl.Location = Location AddNew(Parent, YourControl, Visible) End Sub Overloads Sub AddNew(ByVal Parent As Control, ByVal YourControl As Object, ByVal Location As Point, ByVal Size As Size, ByVal Visible As Boolean) YourControl.Size = Size AddNew(Parent, YourControl, Location, Visible) End Sub #End Region Overloads Sub Remove(ByVal Index As Integer) list.RemoveAt(Index) End Sub Overloads Sub Remove(ByVal YourObject As Object) list.Remove(YourObject) End Sub Default ReadOnly Property Item(ByVal Index As Integer) As Object Get Return list(Index) End Get End Property End Class
يظهر بوضوح ان الفئة تسفيد كثيراً من الفئة الرئيسية CollectionBase وترث خصائصها كما هو ظاهر في السطر
Inherits System.Collections.CollectionBase
بادى ذي بدء قمنا بتضمين الفئة طريقتان Methods هما AddNew وتقوم بإضافة عنصر جديد الى المصفوفة ... و الطريقة الاخرى هي Remove وتقوم بإزالة العنصر من داخل المصفوفة ... واخيراً خاصية افتراضية هي Item تقوم باعادة العنصر المحدد من المصفوفة ...
الطريقتان AddNew و Remove هما طرق قمنا بإعادة تعريفها اكثر من مرة Overloading حتى نسهل استخدامها بطرق مختلفة ...
نبدأ اولاً بالطريقة AddNew نسطيع اضافة عناصر جديدة للمصفوفة ولها المدخلات التالي :
Parent
يحمل هذا المدخل اسم الكائن الحاضن للعنصر الجديد فاذا اردنا ان يكون العنصر الجديد ضمن ادوات الفورم الرئيسي Form1 مثلاً فأننا نجعل قيمته هي Form1 وهكذا الامر اذا اردنا مثلاً ان ننشأ العنصر الجديد ضمن مجموعة ادوات لوحة مثل Panel1 وهكذا ....
YourControl
هذا المدخل يشير الى الكائن الجديد الذي تريد انشائه فهو يمكن ان يكون Label او Button او PictureBox ... إلخ ...
Location
وهو موقع العنصر داخل العنصر الحاضن Parent .
Size
حجم العنصر الجديد .
Visible
يحدد فيما اذا كان العنصر الجديد سيظهر مباشرة (Visible=True) او ستحتاج الى إظهارة يدوياً فيما بعد.
والان لنفرض اننا نريد ان ننشأ زر امر جديد نضيفة الى الفورم فاننا سوف نحتاج الى شفرة كالتالي :
Dim CA As New ControlArrays() CA.AddNew(Me, New Button(), True)
وكذلك الحال اذا اردنا ان نضيف عنصر PictureBox الى فورم في الموضوع (10,10) وبحجم 100x100 فأن الشفرة المطلوبة ستكون كالتالي :
Dim CA As New ControlArrays() CA.AddNew(Me, New PictureBox(), New Point(10, 10), New Size(100, 100), True)
وكما نرى ان الامر يصبح بسيطاً بإذن الله تعالى ... و اذا اردنا ان نغير من خصائص العنصر الاول ( Index=0 ) في المصفوفة بحيث يصبح لون خلفيته BackColor هو اللون الاحمر Color.Red فاننا سنكتب التالي :
CA.Item(0).BackColor = Color.Red
وبما ان الخاصية Item هي الخاصية الافتراضية فان يمكن كتابة السطر السابق كالتالي :
CA(0).BackColor = Color.Red
وهكذا الحال عند الرغبة في ازالة عنصر من المصفوفة فمثلاً لإزالة العنصر السابق من المصفوفة نكتب التالي :
CA.Remove(0)
و بالتالي اذا اردنا ان ننشأ مصفوفة مكونة من اربعة Labels في وقت التنفيذ فاننا سوف نكتب كود مشابه للتالي في الحدث Load للفورم الرئيسي :
Dim CA As New ControlArrays() Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim I As Integer For I = 0 To 3 CA.AddNew(Me, New Label(), New Point(15, 25 * I + 40), True) CA(I).Text = "Label" & (I + 1) Next End Sub
اذا نفذت البرنامج السابق فسوف تجد ان هناك اربعة عناصر Label مرتبة فوق بعضها البعض اعتمادا على المعادلة المعبرة عن المدخل Location في الشفرة وهي New Point(15, 25 * I + 40) ...
و الان ما هو الحال اذا اردنا ان نجعل المصفوفة السابقة تتشارك في الحدث Click مثلاً بحيث يظهر العنصر الذي تم النقر عليه بلون مختلف ولنقل مثلاً Color.Red و بخط مائل Italic ...
سنحتاج للقيام بهذا العمل الى بناء وضيفة Method وليكن اسمها myHandler و ضيفهتا معالجة الحدث Click لمصفوفتنا وشفرتها كالتالي :
Sub myHandler(ByVal sender As Object, ByVal e As System.EventArgs) Dim I As Integer For I = 0 To CA.Count - 1 If (sender Is CA(I)) Then CA(I).ForeColor = Color.Red CA(I).Font = New Font(Me.Font, FontStyle.Italic) Else CA(I).ForeColor = Color.Black CA(I).Font = New Font(Me.Font, FontStyle.Regular) End If Next End Sub
وكما نرى انها تقوم بالدوران على جميع العناصر بحيث تقوم بإسناد لون احمر وخط مائل للكائن الذي فجر الحدث Click وهو الكائن Sender ...
والان ينبغي علينا ان نغيير شفرة الحدث Load للفورم بحيث نخبر مصفوفتنا باننا نريد ان نجعل الوظيفة myHandler تعالج الحدث Click لعناصر مصفوفتنا بالشكل التالي :
Dim CA As New ControlArrays() Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim I As Integer For I = 0 To 3 CA.AddNew(Me, New Label(), New Point(15, 25 * I + 40), True) CA(I).Text = "Label" & (I + 1) AddHandler CType(CA(I), Label).Click, AddressOf myHandler Next End Sub
لاحظ معي ان التغيير يكمن فقط في السطر :
AddHandler CType(CA(I), Label).Click, AddressOf myHandler
الذي اخبرنا فيه البرنامج باننا نريد ان نوكل معالجة الحدث Click للوظيفة myHandler ...
و لعلك ترى معي اننا وبحمد الله وفضله قد حصلنا على طريقة سهلة لإنشاء مصفوفة أدوات تضاهي ما كنا نحصل عليه في الأصدار الاقدم VB6 وذلك في وقت التنفيذ ...
و الان و إن لم تكن قد اصبت بالملل مما سبق فانك قد تلحظ اننا لم نتطرق الى كيفية تصميم مصفوفة ادوات وقت التصميم ! ..
وهنا اود ان اللفت انتباهك الى انه يمكن و بنفس الطريقة مع قليل من التحايل إضافة وظيفة بسيطة الى فئتنا السابقة clsControlArrays بالشكل التالي :
Sub Add(ByRef YourControl As Object) list.Add(YourControl) End Sub
وبالتالي تصبح شفرة الفئة clsControlArrays كالتالي :
Public Class ControlArrays Inherits System.Collections.CollectionBase #Region " Overloading AddNew Method..." Overloads Sub AddNew(ByVal Parent As Control, ByVal YourControl As Object, ByVal Visible As Boolean) YourControl.Visible = Visible list.Add(YourControl) Parent.Controls.Add(YourControl) End Sub Overloads Sub AddNew(ByVal Parent As Control, ByVal YourControl As Object, ByVal Location As Point, ByVal Visible As Boolean) YourControl.Location = Location AddNew(Parent, YourControl, Visible) End Sub Overloads Sub AddNew(ByVal Parent As Control, ByVal YourControl As Object, ByVal Location As Point, ByVal Size As Size, ByVal Visible As Boolean) YourControl.Size = Size AddNew(Parent, YourControl, Location, Visible) End Sub #End Region Sub Add(ByRef YourControl As Object) list.Add(YourControl) End Sub Overloads Sub Remove(ByVal Index As Integer) list.RemoveAt(Index) End Sub Overloads Sub Remove(ByVal YourObject As Object) list.Remove(YourObject) End Sub Default ReadOnly Property Item(ByVal Index As Integer) As Object Get Return list(Index) End Get End Property End Class
وهذة الوظيفة الجديدة تقوم بإضافة عنصر الى المصفوفة ولكن دون إضافته الى الكائن Parent لان العناصر ستكون موجودة اصلاً في وقت التصميم ..
فمثلاً لإضافة العنصر Button1 الى المصفوفة فانك تحتاج الى السطر التالي :
CA.Add(Button1)
وليس كما في السابق :
CA.AddNew(Me, New Button, True)
لان الوظيفة Add ليست مثل AddNew حيث تقوم الاولى فقط بنسب العنصر Button1 الموجود اصلاً الى عنصر جديد في المصفوفة اما الـ AddNew فانها تقوم بانشاء عنصر جديد وتضيفه الى الفورم اولاً (Parent) ثم تقوم بعد ذلك باضافته الى عناصر مجموعتنا ..
والان لكي تستفيد من الدالة السابقة Add بنفس الطريقة التي قمنا فيها بمثالنا السابق ولكن في وقت لتصميم هذة المرة وليس في وقت التنفيذ ..
قم اولاً بتضمين فئتنا clsControlArrays في مشروع جديد ثم ضع اربعة عناصر Label من Label1 الى Label4 في الفورم في وقت التصميم مع تضبيط خصائص كلاً منها كما يحلو لك وفي المواقع التي تريدها و الان لكي تضيف هذة العناصر الى مصفوفتك بحيث تتشارك في الحدث Click كما هو الحال في الماضي فانه يمكنك كتابة التالي :
Dim CA As New ControlArrays() Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim Ctrl As Control For Each Ctrl In Me.Controls If TypeOf Ctrl Is Label Then CA.Add(Ctrl) AddHandler Ctrl.Click, AddressOf myHandler End If Next End Sub Sub myHandler(ByVal sender As Object, ByVal e As System.EventArgs) Dim I As Integer For I = 0 To CA.Count - 1 If (sender Is CA(I)) Then CA(I).ForeColor = Color.Red CA(I).Font = New Font(Me.Font, FontStyle.Italic) Else CA(I).ForeColor = Color.Black CA(I).Font = New Font(Me.Font, FontStyle.Regular) End If Next End Sub
لاحظ معي اننا قمنا اولاً بالبحث عن جميع العناصر Label التي في الفورم ثم اسندناها لمصفوفتنا كي نتعامل معها بعد ذلك بسهولة ...
و يعيب الطريقة السابقة انه ربما كان هناك عناصر Labels لا نريدها ان تكون ضمن مصفوفتنا لهذا يمكن تعديل الشفرة السابقة بحيث تبحث عن عناصر محددة من الـ Labels الموجودة في الفورم فمثلاً نقوم بتسمية جميع العناصر التي نريدها ان تكون ضمن مصفوفتنا باسماء تبدأ بحروف معينة مثلاً arr وبالتالي تكون اسماء الـ Labels مثلاً arrLabel1 arrLabel2 arrLabel3 ... وهكذا ...
ثم بعد ذلك عندما نقوم بالبحث فاننا نبحث عن العناصر التي احتوت هذة الحروف بالشكل التالي :
Dim CA As New ControlArrays()
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim Ctrl As Control
For Each Ctrl In Me.Controls
If Ctrl.Name.StartsWith("arr") Then
CA.Add(Ctrl)
AddHandler Ctrl.MouseEnter, AddressOf myHandler
End If
Next
End Sub
Sub myHandler(ByVal sender As Object, ByVal e As System.EventArgs)
Dim I As Integer
For I = 0 To CA.Count - 1
If (sender Is CA(I)) Then
CA(I).ForeColor = Color.Red
CA(I).Font = New Font(Me.Font, FontStyle.Italic)
Else
CA(I).ForeColor = Color.Black
CA(I).Font = New Font(Me.Font, FontStyle.Regular)
End If
Next
End Subوبالتالي اذا كان هناك عناصر Labels تبدأ باحرف مختلفة فانها لن تكون ضمن مصفوفتنا ... فقط التي تبدأ بالحروف "arr" ...
وبهذا نكون قد غطينا جميع الجوانب غير انك اذا كنت تقاسمني نفس الكسل وعدم الرغبة في كتابة اي اسطر برمجية اضافية في برنامجي فانه يسرني ان اخبرك انني تخطر لي فكرة الان وهي جعل فئتنا العزيزة clsControlArrays تقوم هي بالنيابة عنا في البحث عن العناصر التي تبدأ باحرف معينة وتضيفها الى المصفوفة وذلك باضافة وظيفة اخرى الى فئتنا وليكن اسمها AddAll ولها مدخلين الاول هو Parent والثاني هو StartWithLetters وبذا يصبح الكود النهائي للفئة clsControlArrays هو :
Public Class ControlArrays Inherits System.Collections.CollectionBase #Region " Overloading AddNew Method..." Overloads Sub AddNew(ByVal Parent As Control, ByVal YourControl As Object, ByVal Visible As Boolean) YourControl.Visible = Visible list.Add(YourControl) Parent.Controls.Add(YourControl) End Sub Overloads Sub AddNew(ByVal Parent As Control, ByVal YourControl As Object, ByVal Location As Point, ByVal Visible As Boolean) YourControl.Location = Location AddNew(Parent, YourControl, Visible) End Sub Overloads Sub AddNew(ByVal Parent As Control, ByVal YourControl As Object, ByVal Location As Point, ByVal Size As Size, ByVal Visible As Boolean) YourControl.Size = Size AddNew(Parent, YourControl, Location, Visible) End Sub #End Region Sub Add(ByRef YourControl As Object) list.Add(YourControl) End Sub Sub AddAll(ByVal Parent As Control, ByVal StartWithLetters As String) Dim Ctrl As Control For Each Ctrl In Parent.Controls If Ctrl.Name.StartsWith(StartWithLetters) Then list.Add(Ctrl) End If Next End Sub Overloads Sub Remove(ByVal Index As Integer) list.RemoveAt(Index) End Sub Overloads Sub Remove(ByVal YourObject As Object) list.Remove(YourObject) End Sub Default ReadOnly Property Item(ByVal Index As Integer) As Object Get Return list(Index) End Get End Property End Class
وكود برنامجنا باستخدام الوظيفة AddAll يصبح كالتالي :
Dim CA As New ControlArrays() Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim I As Integer CA.AddAll(Me, "arr") For I = 0 To CA.Count - 1 AddHandler CType(CA(I), Label).Click, AddressOf myHandler Next End Sub Sub myHandler(ByVal sender As Object, ByVal e As System.EventArgs) Dim I As Integer For I = 0 To CA.Count - 1 If (sender Is CA(I)) Then CA(I).ForeColor = Color.Red CA(I).Font = New Font(Me.Font, FontStyle.Italic) Else CA(I).ForeColor = Color.Black CA(I).Font = New Font(Me.Font, FontStyle.Regular) End If Next End Sub
--------------------------------------------------------------------
الخاتمة :
--------------------------------------------------------------------
بعد المقال المطول السابق نخرج بفائدة انه على الرغم من اننا حرمنا من مصفوفات الادوات في الفيجوال بيسك دوت نت بشكل مباشر ... إلا ان السبل لم تنقطع و الطرق البرمجية كفيلة بالتحايل على الموضوع .. وما يحظرني الان هو الفئة البسيطة السابقة ...
والتي تضم المميزات التالية :
@ إنشاء مصفوفات الأدوات من الصفر في وقت التنفيذ .
@ امكانية تصميم ادوات في وقت التصميم ثم ربطها بسطور برمجية بسيطة في وقت التنفيذ .
@ أمكانية مشاركة الاحداث .
@ و اخيراً امكانية تضمين عناصر من ادوات مختلفة في نفس الوقت ومشاركتها في احداث معينة على الرغم من اختلاف انواع الادوات (Labels , TextBoxes , CheckBoxes , .... ) .
اخيراً اقول يسرني كثيراً اي اضافات للأداه او اي اقتراحات من قبلكم ...
تجد في الرابط التالي مجلد يحتوي على برنامجين احدهما لتطبيق الفكرة في وقت التصميم و الآخر في وقت التنفيذ على هذا الرابط ControlArrays
وأخيراً اقول .. السلام عليكم ورحمة الله وبركاته ...


