Generic Delegates & ًWindows Forms Control - Part 2

في الموضوعات السابقة الموجودة في اللينك التالية ناقشنا سريعا كيفية إستخدام
Generic Delegates

وفي هذا الموضوع سوف نتطرق لبعضا من الأساليب المختلفة التي من الممكن الاستفادة منها من خلال برامجنا

مثال
المثال التالي مأخوذ من موقع مايكروسوفت في اللينكـــــــــ وهو يوضح كيفية نسخ مصفوفة الي مصفوفة اخري والهدف من استخدام نفس المثال هو سهولة توضحيح الفارق بين ما كتبته مايكروسوفت و بين ما نريد ان نتطرق له هنا في موضوعنا وما يهمني في مثال مايكروسوفت
هو الجزء من الكودالذي يوضح كيفية استخدام
Lambada & Generic Delegates

الكود التالي يوضح شكل الكود الخاص بمايكروسوفت وهو مكتوب للإستخدام مع
Console Application
Public Module TestLambda
   
Public Sub Main()
 
     Dim ordinals() As String = {"First""Second""Third""Fourth""Fifth"}
 
     Dim copiedOrdinals(ordinals.Length 1) As String      
      Dim copyOperation 
As Action(Of String(), String(), Integer) = _
                           Sub
(s1s2posCopyStrings(s1s2pos)
 
     copyOperation(ordinalscopiedOrdinals3)
 
     For Each ordinal As String In copiedOrdinals
         
If String.IsNullOrEmpty(ordinalThen
            Console
.WriteLine("")
 
        Else
 
           Console.WriteLine(ordinal)
 
        End If 
      Next
   End Sub

   
Private Function CopyStrings(source() As Stringtarget() As StringstartPos As Integer) As Integer
      If source
.Length <> target.Length Then
         
Throw New IndexOutOfRangeException("The source and target arrays must have the same number of elements.")
 
     End If

 
     For ctr As Integer startPos To source.Length 1
         target
(ctr) = String.Copy(source(ctr))
 
     Next
      Return source
.Length startPos
   End 
FunctionEnd Module 

أولا : لنعيد كتابة نفس الكود أعلاه حتي نستطيع استخدامه مع الويندوز فورم وذلك قبل التعمق في تطويره وهنا سوف أعيد كتابة المثال ليصلح للاستخدام مع الليست بوكس تحديدا ولتنفيذ الكود نحتاج الي فورم موجود عليه ليست بوكس


Public Class Form1
    Private Sub Form1_Load
(sender As ObjectAs EventArgsHandles MyBase.Load

        Dim ordinals
() As String = {"First""Second""Third""Fourth""Fifth"}
 
       Dim copiedOrdinals(ordinals.Length 1) As String
        Dim copyOperation 
As Action(Of String(), String(), Integer) = Sub(s1s2posCopyStrings(s1s2pos)
 
       copyOperation(ordinalscopiedOrdinals3)

 
       For Each ordinal As String In copiedOrdinals
            If Not String
.IsNullOrEmpty(ordinalThen
                ListBox1
.Items.Add(ordinal)
 
           End If
 
       Next

    End Sub

    Private 
Function CopyStrings(source() As Stringtarget() As StringstartPos As Integer) As Integer
        If source
.Length <> target.Length Then
            Throw 
New IndexOutOfRangeException("The source and target arrays must have the same number of elements.")
 
       End If

 
       For ctr As Integer startPos To source.Length 1
            target
(ctr) = String.Copy(source(ctr))
 
       Next
        Return source
.Length startPos
    End 
FunctionEnd Class

سنلاحط في الكود أن مايكروسوفت تمرر دالة و ليس روتينا الي
 Generic Action 
و هذا يؤكد ما سبق و قلته سابقا ان
 Generic Delegates
تقبل أن نمرر لها إما دالة أو روتين

و الكود التالي هو إعادة لصياغة نفس الكود أعلاه و لكننا هنا نمرر روتين

Public Class Form1
    Private Sub Form1_Load
(sender As ObjectAs EventArgsHandles MyBase.Load

        Dim ordinals
() As String = {"First""Second""Third""Fourth""Fifth"}
 
       Dim copiedOrdinals(ordinals.Length 1) As String
        Dim copyOperation 
As Action(Of String(), String(), Integer) = Sub(s1s2posCopyStrings(s1s2pos)
 
       copyOperation(ordinalscopiedOrdinals3)

 
       For Each ordinal As String In copiedOrdinals
            If Not String
.IsNullOrEmpty(ordinalThen
                ListBox1
.Items.Add(ordinal)
 
           End If
 
       Next

    End Sub

    Private Sub CopyStrings
(source() As Stringtarget() As StringstartPos As Integer)
 
       If source.Length <> target.Length Then
            Throw 
New IndexOutOfRangeException("The source and target arrays must have the same number of elements.")
 
       End If

 
       For ctr As Integer startPos To source.Length 1
            target
(ctr) = String.Copy(source(ctr))
 
       Next

    End Sub

End 
Class 

  السؤال الأن هل انا في حاجة الي تمرير البيانات الي
 Generic Delegates
 في صورة دالة أو في صورة روتين وهل يمكن الاستغناء عن هذا كله و الإجابة ستكون بالطبع ممكن ان نفعل هذا

و الكود التالي يوضح كيفية تمرير البيانات بشكل مباشر و في صورة روتين 

Public Class Form1
    Private Sub Form1_Load
(sender As ObjectAs EventArgsHandles MyBase.Load

        Dim ordinals
() As String = {"First""Second""Third""Fourth""Fifth"}
 
       Dim copiedOrdinals(ordinals.Length 1) As String
        Dim copyOperation 
As Action(Of String(), String(), Integer) = Sub(source() As Stringtarget() As StringstartPos As Integer)
 
                                                                         If source.Length <> target.Length Then
                                                                              Throw 
New IndexOutOfRangeException("The source and target arrays must have the same number of elements.")
 
                                                                         End If

 
                                                                         For ctr As Integer startPos To source.Length 1
                                                                              target
(ctr) = String.Copy(source(ctr))
 
                                                                         Next
                                                                      End Sub
        copyOperation
(ordinalscopiedOrdinals3)

 
       For Each ordinal As String In copiedOrdinals
            If Not String
.IsNullOrEmpty(ordinalThen
                ListBox1
.Items.Add(ordinal)
 
           End If
 
       Next

    End Sub

End 
Class 
 
و الكود التالي يوضح كيفية تمرير البيانات بشكل مباشر في صورة دالة
Public Class Form1
    Private Sub Form1_Load
(sender As ObjectAs EventArgsHandles MyBase.Load

        Dim ordinals
() As String = {"First""Second""Third""Fourth""Fifth"}
 
       Dim copiedOrdinals(ordinals.Length 1) As String
        Dim copyOperation 
As Action(Of String(), String(), Integer) = Function(source() As Stringtarget() As StringstartPos As Integer)
 
                                                                         If source.Length <> target.Length Then
                                                                              Throw 
New IndexOutOfRangeException("The source and target arrays must have the same number of elements.")
 
                                                                         End If

 
                                                                         For ctr As Integer startPos To source.Length 1
                                                                              target
(ctr) = String.Copy(source(ctr))
 
                                                                         Next
                                                                          Return source
.Length startPos
                                                                      End 
Function
 
       copyOperation(ordinalscopiedOrdinals3)

 
       For Each ordinal As String In copiedOrdinals
            If Not String
.IsNullOrEmpty(ordinalThen
                ListBox1
.Items.Add(ordinal)
 
           End If
 
       Next

    End Sub

End 
Class
طبعا هناك كمية من الأسئلة قد يتطرق اليها ذهن أي مبرمج

 ماهي الفائدة من كل هذا ؟

أنا لست محتاجا لكتابة الكود بهذه الطريقة؟

ساستخدم الأساليب القديمة في كتابة الكود؟

العملية مش ناقصة تضييع وقت؟

العملية مش ناقصة صداع أصلا؟

الحقيقة أنا في بداية استخدامي لنفس الأسلوب في كتابة الكود حاولت أن أقنع نفسي ب أنني لا أكتب شيئا جديداولكن بعد ان أضعت يوما كاملا في دراسة الموضوع وأيضا و كلما تقدمت في دراسة الموضوع بدأت الصورة تتضح لي بشكل افضل  و اكتشفت أنني كنت مخطئا في تساؤلاتي

والحقيقة كان أهم سؤال أردت ان أسمع إجابته هو
لماذا مايكروسوفت طورت مثل هذا الاسلوب في البرمجة
هناك مثل مصري قديم يقول الحداية لايمكن أت ترمي كتاكيت او بمعني اخر لماذا مايكروسوفت قد تضيع وقتها و فلوسها في تطوير مثل هذه الاساليب

وهنا سألت نفسي اليس
 Generic Delegates
 عبارة عن
 EventHandler

اذا من الممكن ان نستخدمها من داخل اي كلاس مثلما نفعل مع اي
 EventHandler
 و أيضا اليس علينا ان نقوم بعممل
 Dispse
 لها

ومنا أعدت كتابة وصياغة الكود أعلاه ليأخذ شكل الكلاس التالي

 
Public Class CopyArrayAction
    Implements IDisposable

    Private copyAction 
As Action(Of String(), String(), Integer)
 
   Private disposedValue As Boolean

    Public Sub 
New(source() As Stringtarget() As StringstartPos As IntegercurrentAction As Action(Of String(), String(), Integer))
 
       SourceArray source
        TargetArray 
target
        StartPosition 
startPos
        copyAction 
currentAction
        
' initialize Action
        copyAction(source, target, startPos)
    End Sub

    Public ReadOnly Property SourceArray As String()
    Public ReadOnly Property TargetArray As String()
    Public ReadOnly Property StartPosition As Integer

    Protected Overridable Sub Dispose(disposing As Boolean)
        If disposedValue Then
            Return
        End If

        If disposing Then
            If copyAction IsNot Nothing Then
                copyAction(SourceArray, TargetArray, StartPosition)
                copyAction = Nothing
            End If
        End If
        disposedValue = True
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
    End Sub

End Class ' 
CopyArrayAction incomplete class 

 
الكود التالي يوضح كيفية استخدام الكلاس اعلاه من داخل الفورم 

Public Class Form1
    Private Sub Form1_Load
(sender As ObjectAs EventArgsHandles MyBase.Load

        Dim ordinals
() As String = {"First""Second""Third""Fourth""Fifth"}
 
       Dim copiedOrdinals(ordinals.Length 1) As String

        Using copyAction
= New CopyArrayAction(ordinalscopiedOrdinals3Sub(sourcetargetstartPos)

 
                                                                    If source.Length <> target.Length Then
                                                                         
Throw New IndexOutOfRangeException("The source and target arrays must have the same number of elements.")
 
                                                                    End If

 
                                                                    For ctr As Integer startPos To source.Length 1
                                                                         target
(ctr) = String.Copy(source(ctr))
 
                                                                    Next
                                                                 End Sub
)

 
           For Each s As String In copyAction.TargetArray
                If Not String
.IsNullOrEmpty(sThen
                    ListBox1
.Items.Add(s)
 
               End If
 
           Next
        End Using

    End Sub

End 
Class 

 طبعا من الافضل ان نكتب الدوال او الروتينيات التي سوف تقوم بكل العمل المطلوب في نسخ المصفوفات داخل الكلاس السابق نفسه أو في كلاس منفصل او علي شكل
 Interface
 و لكن لهذا حديث اخر

عموما وعلي ما اعتقد اننا قد نكتب الكود الذي يقوم بكل العمل في شكل
 Interface
 و نمرر فقط مدخلات
 Interface
 ثم في الفورم نطبق فقط الكود و نستخدم
 Generic Action

و ربما و الله أعلم ان تلك هي الفائدة الرئيسية او الهدف الاساسي من بناء
 Generic Action

لكن ومن الواضح هنا هو اننا نستطيع اطلاق حدث به روتينات او دوال تقوم بتنفيذ بعض الكود ثم وفي النهاية نستطيع التخلص من كل هذا عن طريق عمل
 Dispose
 متهيألي ان الاسلوب هذا مناسب جدا لكتابة كلاسات يسهل تنفيذها و التخلص منها بدون التأثير علي ذاكرة الكمبيوتر  

اخر شئ وبما أنني من عشاق الجرافكس و الرسم كان صعب جدا أن أفوت فرصة استخدام الاسلوب الذي نناقشه في الجرافكس و الأكواد التالية توضح بعضا من هذه الأفكار

Public Class CairoPainter
    Implements IDisposable

    Private paintAction As Action(Of Control, Graphics, Color, Rectangle)
    Private disposed As Boolean

    Public Sub New(ctrl As Control, g As Graphics, clr As Color, rect As Rectangle, action As Action(Of Control, Graphics, Color, Rectangle))
        paintAction = action
        paintAction(ctrl, g, clr, rect)
    End Sub

    Protected Overridable Sub Dispose(disposing As Boolean)
        If disposed Then
            Return
        End If

        If disposing Then
            ' dispose paintAction
            If paintAction IsNot Nothing Then
                paintAction = Nothing
            End If
        End If
        disposed = True
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
    End Sub

End Class 


الكود التالي يوضح كيفية استخدام الكلاس اعلاه لرسم مستطيل علي الفورم

  Protected Overrides Sub OnPaint(e As PaintEventArgs)
        MyBase.OnPaint(e)

        Dim rect As Rectangle = New Rectangle(10, 10, 100, 100)
        Using New CairoPainter(Me, e.Graphics, Color.Black, rect, Sub(ctrl, g, clr, r) Draw(ctrl, g, clr, r))
            ' do nothing as the generic action will take care of the paint and will disposed
        End Using

        Using New CairoPainter(Me, e.Graphics, Color.Black, rect, Sub(ctrl, g, clr, r)
                                                                      ' draw what you want here
                                                                  End Sub)
            ' do nothing as the generic action will take care of the paint and will disposed
        End Using

    End Sub

    Private Sub Draw(c As Control, g As Graphics, clr As Color, rect As Rectangle)
        Dim sb As New SolidBrush(clr)
        g.FillRectangle(sb, rect)
    End Sub


 

Comments

Popular posts from this blog

مقدمة الي تشفير الحروف الأبجدية العربية

VB.NET Translucent Control using GDI+

Add Custom Event to a Class in VB.NET