الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

مقال: Custom EventHandler & Classes

بدأه a_pess في 4 فبراير 2010 · 0 رد · 685 مشاهدة · في Microsoft Visual Basic.NET
مشاركة: واتساب X فيسبوك تيليجرام
#1

بسم الله الرحمن الرحيم

السلام عليكم ورحمة الله وبركاته

جميعنا يعلم ماذا تعني كلمات مثل Event و EventArgs و EventHandler حيث أن جميع الكونترول الموجودة بالدوت نت قلما تخلو من مثل هذه المسميات وبالتأكيد الغالبية ممن سيقرأون هذا الموضوع يعرفون جيدا كيف يتعاملون مع مثل هذه الأشياء من حيث بنائها أو إضافتها الي الكونترول أو الكلاسات المختلفة

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

أهم مميزات استخدام Custom EventHandler

تعتبر أهم مميزات استخدام Custom EventHandler هي التوفير في استهلاك ذاكرة الكمبيوتر وأيضا إن استخدامها بشكل دقيق وإعطائها مسميات دقيقة مختلفة عن بعضها البعض لن يؤدي الي أي نوع من انواع Blocking لبعضها البعض أثناء تنفيذها

الكود التالي يوضح شكل Custom EventHandler ولقد أعطيتها إسما شائعا ألا وهو PropertyChanged

سنلاحظ اننا عندما نقوم بكتابة الجملة Public Custom Event PropertyChanged As EventHandler أن الفيجوال استوديو قام بإضافة باقي الكود اتوماتيكيا أي أن الفيجوال استوديو قام بكتابة حوالي خمسون بالمائة من الكود والباق من الكود يجب علينا أن نقوم بكتابته بأنفسنا

 Public Custom Event PropertyChanged As EventHandler
        AddHandler(ByVal value As EventHandler)

        End AddHandler

        RemoveHandler(ByVal value As EventHandler)

        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)

        End RaiseEvent
    End Event

الأن لنضيف الكود المناسب إلي PropertyChanged EventHandler والكود التالي يوضح ذلك

Public Custom Event PropertyChanged As EventHandler
        AddHandler(ByVal value As EventHandler)
            Me.Events.AddHandler("PropertyChangedEvent", value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            Me.Events.RemoveHandler("PropertyChangedEvent", value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            CType(Me.Events("PropertyChangedEvent"), EventHandler).Invoke(sender, e)
        End RaiseEvent
    End Event

كيف نضيف Custom EventHandler إلي Component أو إلي Control

في واقع الأمر إن إضافة أي Custom EventHandler إلي Component أو Control يمكن إعتباره أمر تقليدي وتكراري وهو لن يختلف كثيرا عن الكود الذي كتبناه أعلاه

أيضا من الممكن إعتباره هو الجزء الأسهل في موضوعنا هذا والسبب في ذلك هو أن أي Component أو كونترول يمتلك Events Property وبالتالي هي تعمل بمثابة المخزن الذي يتم فيه تخزين أي EventHandler او Delegates يتم إضافتها الي الكونترول أو إل Component

الكود التالي يوضح كيفية إضافة Custom EventHandler إلي Control ما

سنلاحظ من الكود كيف نقوم بإضافة Custom EventHandler ومن ثم ربطها مع بعض Property وأيضا كيفية عمل Dispose لها وأيضا كيفية الإستفادة من Custom EventHandler في إخفاء بعض Handler الموجودة في الكونترول مثل BackColorChanged

Public Class ControlTest
    Inherits Control

    Public Sub New()
    End Sub

    Private m_color As Color = Color.Transparent
    Public Property MyColor() As Color
        Get
            Return m_color
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Empty
            End If
            Me.m_color = value
            Me.OnPropertyChanged(Me, EventArgs.Empty)
        End Set
    End Property

    Private m_BackColor As Color = Color.Transparent
    <System.ComponentModel.Browsable(False)> _
    <System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)> _
    Public Overrides Property BackColor() As System.Drawing.Color
        Get
            Return Me.m_BackColor
        End Get
        Set(ByVal value As System.Drawing.Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            ' do nothing
        End Set
    End Property

    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            Dim handler As EventHandler = CType(Me.Events("PropertyChangedEvent"), EventHandler)
            If (handler IsNot Nothing) Then
                handler = Nothing
            End If
        End If
        MyBase.Dispose(disposing)

    End Sub

    Protected Overridable Sub OnPropertyChanged(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim handler As EventHandler = CType(Me.Events("PropertyChangedEvent"), EventHandler)
        If (handler IsNot Nothing) Then
            handler.Invoke(sender, e)
            ' Or we can use 
            'RaiseEvent PropertyChanged(sender, e)
        End If
    End Sub

    Public Custom Event PropertyChanged As EventHandler
        AddHandler(ByVal value As EventHandler)
            Me.Events.AddHandler("PropertyChangedEvent", value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            Me.Events.RemoveHandler("PropertyChangedEvent", value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            CType(Me.Events("PropertyChangedEvent"), EventHandler).Invoke(sender, e)
        End RaiseEvent
    End Event

    <System.ComponentModel.Browsable(False)> _
    <System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)> _
    Public Shadows Custom Event BackColorChanged As EventHandler
        AddHandler(ByVal value As EventHandler)
            Me.Events.AddHandler("BackColorChangedEvent", value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            Me.Events.RemoveHandler("BackColorChangedEvent", value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            CType(Me.Events("BackColorChangedEvent"), EventHandler).Invoke(sender, e)
        End RaiseEvent
    End Event

End Class

الكود التالي يوضح كيفية إضافة Custom EventHandler إلي Component ما

Public Class ComponentTest
    Inherits System.ComponentModel.Component

    Public Sub New()
    End Sub

    Private m_color As Color = Color.Transparent
    Public Property MyColor() As Color
        Get
            Return m_color
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Empty
            End If
            Me.m_color = value
            Me.OnPropertyChanged(Me, EventArgs.Empty)
        End Set
    End Property

    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            Dim handler As EventHandler = CType(Me.Events("PropertyChangedEvent"), EventHandler)
            If (handler IsNot Nothing) Then
                handler = Nothing
            End If
        End If
        MyBase.Dispose(disposing)

    End Sub

    Protected Overridable Sub OnPropertyChanged(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim handler As EventHandler = CType(Me.Events("PropertyChangedEvent"), EventHandler)
        If (handler IsNot Nothing) Then
            handler.Invoke(sender, e)
            ' Or we can use 
            'RaiseEvent PropertyChanged(sender, e)
        End If
    End Sub

    Public Custom Event PropertyChanged As EventHandler
        AddHandler(ByVal value As EventHandler)
            Me.Events.AddHandler("PropertyChangedEvent", value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            Me.Events.RemoveHandler("PropertyChangedEvent", value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            CType(Me.Events("PropertyChangedEvent"), EventHandler).Invoke(sender, e)
        End RaiseEvent
    End Event

End Class

كيف نضيف Custom EventHandler إلي أي كلاس

لكي نقوم بذلك نحتاج إلي شيئان أساسيان وهما كالأتي:

أن يكون لدينا Property تشير إلي EventHandlerList وهي التي سوف تعمل بمثابة المخزن الذي سوف نقوم بتخرين جميع EventHandler به

أن نقوم بعمل Dispose لهذه EventHandler وبالتالي يجب علينا أن نقوم باستخدام IDisposable Interface

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

ولكي نبني شيئا مناسبا يمكن استخدامه بشكل جيد فنحن غالبا سنحتاج الي الأشياء التالية

الكلاس الموجود به الألوان

نحتاج إلي كلاس تم توريثه من TypeConverter

نحتاج الي كلاس تم توريثه من TypeEditor

ثم اي كلاسات أخري نريد إضافتها

ثم بالنهاية كلاس تم توريثه من الكونترول مثلا حيث سنضيف له الكلاس الموجودة به الألوان ومن ثم نستخدمها في تلوين الكونترول

الأن سوف نجيب علي السؤال الذي طرحناه سابقا ألا وهو كيف نضيف Custom EventHandler إلي أي كلاس

بالتأكيد أن من سيقرأ هذا الموضوع يعلم جيدا ما هو الكلاس Class وأيضا يعلم جيدا كيف يقوم ببناء كلاس ما........

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

الكود التالي يوضح الشكل المبدئي للكلاس

Imports System.Drawing.Design

Public Class ThemeColors

#Region " Fields "

    Private m_northBegin As Color
    Private m_northEnd As Color
    Private m_southBegin As Color
    Private m_southEnd As Color
    Private m_borderOut As Color
    Private m_borderIn As Color

#End Region

#Region " Constructor "

    Public Sub New()
        Me.m_northBegin = Color.Transparent
        Me.m_northEnd = Color.Transparent
        Me.m_southBegin = Color.Transparent
        Me.m_southEnd = Color.Transparent
        Me.m_borderOut = Color.Transparent
        Me.m_borderIn = Color.Transparent
    End Sub

    Public Sub New(ByVal northColorBegin As Color, _
                   ByVal northColorEnd As Color, _
                   ByVal southColorBegin As Color, _
                   ByVal southcolorEnd As Color, _
                   ByVal borderOut As Color, _
                   ByVal borderIn As Color)

        Me.m_northBegin = northColorBegin
        Me.m_northEnd = northColorEnd
        Me.m_southBegin = southColorBegin
        Me.m_southEnd = southcolorEnd
        Me.m_borderOut = borderOut
        Me.m_borderIn = borderIn

    End Sub

#End Region

#Region " Properties "

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeNorthBegin() As Color
        Get
            Return m_northBegin
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_northBegin = value

        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeNorthEnd() As Color
        Get
            Return Me.m_northEnd
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_northEnd = value

        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeSouthBegin() As Color
        Get
            Return m_southBegin
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_southBegin = value

        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeSouthEnd() As Color
        Get
            Return m_southEnd
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_southEnd = value

        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeBorderOut() As Color
        Get
            Return Me.m_borderOut
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_borderOut = value

        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeBorderIn() As Color
        Get
            Return Me.m_borderIn
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_borderIn = value

        End Set
    End Property

#End Region

End Class

الأن لنضيف لهذا الكلاس متغير يشير إلي EventHandlerList وهي ستعمل هنا بمثابة المخزن الذي سوف يتم تخزين جميع Custom EventHandler به ثم نضيف Property تعبر عن هذا المتغير والكود التالي يوضح ذلك

Private m_events As EventHandlerList = Nothing

    Protected ReadOnly Property Events() As EventHandlerList
        Get
            If m_events Is Nothing Then
                m_events = New EventHandlerList()
            End If
            Return m_events
        End Get
    End Property

بعد أن قمنا بإضافة Property أعلاه الأن يمكننا أن نضيف أي EventHandler وعلي سبيل المثال لسوف أضيف Custom EventHandler بإسم ThemeChanged كما هو موضح بالكود التالي

#Region " Events "
    Public Custom Event ThemeChanged As EventHandler
        AddHandler(ByVal value As EventHandler)
            Me.Events.AddHandler("ThemeChangedEvent", value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            Me.Events.RemoveHandler("ThemeChangedEvent", value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            CType(Me.Events("ThemeChangedEvent"), EventHandler).Invoke(sender, e)
        End RaiseEvent
    End Event
#End Region

ثم بعد ذلك نضيف Sub يشير الي هذه Event وهو الذي سوف نستخدمه لإستدعاء أو Fire لهذه Event من داخل Properties الخاصة بالألوان والتي تم تعريفها في الكلاس والكود التالي يوضح شكل هذا Sub

 Protected Overridable Sub OnThemeChanged(ByVal sender As Object, ByVal e As EventArgs)
        Dim handler As EventHandler = CType(Me.Events("ThemeChangedEvent"), EventHandler)
        If (handler IsNot Nothing) Then
            RaiseEvent ThemeChanged(sender, e)
        End If
    End Sub

الأن لنقوم بعمل Implementation او نقوم باستخدام IDisposable Interface

عندما نكتب جملة Implements IDisposable سيقوم الفيجوال استوديو بإضافة الكود التالي الي الكلاس حيث هنا علينا أن نقوم بعمل Dispse لأي EventHandler أو أي شئ أخر نريده

Implements IDisposable

    Private disposedValue As Boolean = False        ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                ' TODO: free other state (managed objects).
            End If

            ' TODO: free your own state (unmanaged objects).
            ' TODO: set large fields to null.
        End If
        Me.disposedValue = True
    End Sub

#Region " IDisposable Support "
    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region

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

ولقد قمت بكتابة بعض الأحداث الإضافية وبعض الكلاسات الاضافية حتي استطيع كتابة كلاسا بشكل جيد يمكنكم أن تستفيدوا منه جيدا

والكود التالي يوضح الشكل النهائي للكلاس ThemeColors

Imports System.ComponentModel
Imports System.Drawing.Design

<TypeConverter(GetType(ThemeConverter)), Editor(GetType(ThemeEditor), GetType(UITypeEditor))> _
Public Class ThemeColors
    Implements IDisposable

#Region " Fields "

    Private m_northBegin As Color
    Private m_northEnd As Color
    Private m_southBegin As Color
    Private m_southEnd As Color
    Private m_borderOut As Color
    Private m_borderIn As Color

    Private m_events As EventHandlerList = Nothing
    Private m_disposed As Boolean = False

#End Region

#Region " Constructor "

    Public Sub New()
        Me.m_northBegin = Color.Transparent
        Me.m_northEnd = Color.Transparent
        Me.m_southBegin = Color.Transparent
        Me.m_southEnd = Color.Transparent
        Me.m_borderOut = Color.Transparent
        Me.m_borderIn = Color.Transparent
    End Sub

    Public Sub New(ByVal northColorBegin As Color, _
                   ByVal northColorEnd As Color, _
                   ByVal southColorBegin As Color, _
                   ByVal southcolorEnd As Color, _
                   ByVal borderOut As Color, _
                   ByVal borderIn As Color)

        Me.m_northBegin = northColorBegin
        Me.m_northEnd = northColorEnd
        Me.m_southBegin = southColorBegin
        Me.m_southEnd = southcolorEnd
        Me.m_borderOut = borderOut
        Me.m_borderIn = borderIn

    End Sub

#End Region

#Region " Properties "

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Protected ReadOnly Property Events() As EventHandlerList
        Get
            If m_events Is Nothing Then
                m_events = New EventHandlerList()
            End If
            Return m_events
        End Get
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeNorthBegin() As Color
        Get
            Return m_northBegin
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_northBegin = value
            Me.OnThemeChanged(Me, EventArgs.Empty)
        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeNorthEnd() As Color
        Get
            Return Me.m_northEnd
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_northEnd = value
            Me.OnThemeChanged(Me, EventArgs.Empty)
        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeSouthBegin() As Color
        Get
            Return m_southBegin
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_southBegin = value
            Me.OnThemeChanged(Me, EventArgs.Empty)
        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeSouthEnd() As Color
        Get
            Return m_southEnd
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_southEnd = value
            Me.OnThemeChanged(Me, EventArgs.Empty)
        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeBorderOut() As Color
        Get
            Return Me.m_borderOut
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_borderOut = value
            Me.OnThemeChanged(Me, EventArgs.Empty)
        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeBorderIn() As Color
        Get
            Return Me.m_borderIn
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_borderIn = value
            Me.OnThemeChanged(Me, EventArgs.Empty)
        End Set
    End Property

#End Region

#Region " Methods "

    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.m_disposed Then
            If disposing Then
                Dim handler As EventHandler = CType(Me.Events("ThemeChangedEvent"), EventHandler)
                If (handler IsNot Nothing) Then
                    handler = Nothing
                End If
            End If
        End If
        Me.m_disposed = True
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    Friend Sub DrawTheme(ByVal gr As Graphics, ByVal rect As Rectangle)
        ThemesUtility.DrawGradient(gr, rect, Me.ThemeNorthBegin, Me.ThemeNorthEnd, Me.ThemeSouthBegin, Me.ThemeNorthEnd, Me.ThemeBorderOut, Me.ThemeBorderIn)
    End Sub

    Friend Function ApplyTheme() As Boolean
        Return ((Me.ThemeNorthBegin <> Color.Empty) AndAlso (Me.ThemeNorthEnd <> Color.Empty) AndAlso (Me.ThemeSouthBegin <> Color.Empty) AndAlso (Me.ThemeSouthEnd <> Color.Empty))
    End Function

    Protected Overridable Sub OnThemeChanged(ByVal sender As Object, ByVal e As EventArgs)
        Dim handler As EventHandler = CType(Me.Events("ThemeChangedEvent"), EventHandler)
        If (handler IsNot Nothing) Then
            RaiseEvent ThemeChanged(sender, e)
        End If
    End Sub

    Public Overrides Function ToString() As String
        Return String.Empty
    End Function

#End Region

#Region " Events "
    Public Custom Event ThemeChanged As EventHandler
        AddHandler(ByVal value As EventHandler)
            Me.Events.AddHandler("ThemeChangedEvent", value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            Me.Events.RemoveHandler("ThemeChangedEvent", value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            CType(Me.Events("ThemeChangedEvent"), EventHandler).Invoke(sender, e)
        End RaiseEvent
    End Event
#End Region

End Class

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

الكود التالي يوضح بعض الكلاسات الإضافية التي قمت بكتابتها وهي كالأتي

ThemeConverter Class

ThemeEditor Class

ThemesUtilty Clss

Imports System.ComponentModel.Design.Serialization
Imports System.ComponentModel
Imports System.Globalization
Imports System.Reflection
Imports System.Drawing

Public Class ThemeConverter
    Inherits ExpandableObjectConverter

#Region " Constructor "
    Public Sub New()
    End Sub
#End Region

#Region " Methods "

    Public Overrides Function CanConvertFrom(ByVal context As ITypeDescriptorContext, _
                                             ByVal sourceType As Type) As Boolean
        Return ((sourceType Is GetType(InstanceDescriptor)) OrElse MyBase.CanConvertFrom(context, sourceType))
    End Function

    Public Overrides Function CanConvertTo(ByVal context As ITypeDescriptorContext, _
                                           ByVal destinationType As Type) As Boolean

        Return ((destinationType Is GetType(String)) OrElse ((destinationType Is GetType(InstanceDescriptor)) OrElse MyBase.CanConvertTo(context, destinationType)))
    End Function

    Public Overrides Function GetProperties(ByVal context As ITypeDescriptorContext, _
                                            ByVal value As Object, _
                                            ByVal attributes As Attribute()) As PropertyDescriptorCollection

        Return TypeDescriptor.GetProperties(value, attributes).Sort(New String() {"ThemeNorthBegin", _
                                                                                  "ThemeNorthEnd", _
                                                                                  "ThemeSouthBegin", _
                                                                                  "ThemeSouthEnd", _
                                                                                  "ThemeBorderOut", _
                                                                                  "ThemeBorderIn"})
    End Function

    Public Overrides Function ConvertTo(ByVal context As ITypeDescriptorContext, _
                                        ByVal culture As CultureInfo, _
                                        ByVal value As Object, _
                                        ByVal destinationType As Type) As Object

        If (destinationType Is GetType(InstanceDescriptor)) Then

            Dim t As Type = value.GetType
            Dim ThemeColor As PropertyDescriptorCollection = TypeDescriptor.GetProperties(value)

            Dim m_ThemeNorthBegin As Color = DirectCast(ThemeColor.Item("ThemeNorthBegin").GetValue(value), Color)
            Dim m_ThemeNorthEnd As Color = DirectCast(ThemeColor.Item("ThemeNorthEnd").GetValue(value), Color)
            Dim m_ThemeSouthBegin As Color = DirectCast(ThemeColor.Item("ThemeSouthBegin").GetValue(value), Color)
            Dim m_ThemeSouthEnd As Color = DirectCast(ThemeColor.Item("ThemeSouthEnd").GetValue(value), Color)
            Dim m_ThemeBorderOut As Color = DirectCast(ThemeColor.Item("ThemeBorderOut").GetValue(value), Color)
            Dim m_ThemeBorderIn As Color = DirectCast(ThemeColor.Item("ThemeBorderIn").GetValue(value), Color)


            Dim constructor As ConstructorInfo = t.GetConstructor(New Type() {GetType(Color), _
                                                                              GetType(Color), _
                                                                              GetType(Color), _
                                                                              GetType(Color), _
                                                                              GetType(Color), _
                                                                              GetType(Color)})

            If (constructor IsNot Nothing) Then
                Return New InstanceDescriptor(constructor, New Object() {m_ThemeNorthBegin, _
                                                                         m_ThemeNorthEnd, _
                                                                         m_ThemeSouthBegin, _
                                                                         m_ThemeSouthEnd, _
                                                                         m_ThemeBorderOut, _
                                                                         m_ThemeBorderIn}, True)
            End If

        End If

        Return MyBase.ConvertTo(context, culture, value, destinationType)
    End Function

#End Region

End Class ' ThemeConverter Class

Imports System.Drawing.Design
Public Class ThemeEditor
    Inherits UITypeEditor

#Region " Constructor "
    Public Sub New()
    End Sub
#End Region

#Region " Methods "
    Public Overrides Function GetEditStyle(ByVal context As System.ComponentModel.ITypeDescriptorContext) As System.Drawing.Design.UITypeEditorEditStyle
        Return UITypeEditorEditStyle.None
    End Function

    Public Overrides Function GetPaintValueSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
        Return True
    End Function
#End Region

End Class ' ThemeEditor Class

Imports System.Drawing.Drawing2D

Friend NotInheritable Class ThemesUtility

#Region " Methods "

    Public Shared Sub DrawGradientRectangle(ByVal g As Graphics, ByVal rect As Rectangle, _
                                             ByVal colorUpBegin As Color, _
                                             ByVal colorUpEnd As Color, _
                                             ByVal colorDownBegin As Color, _
                                             ByVal colorDownEnd As Color, _
                                             ByVal colorBorderOut As Color, _
                                             ByVal colorBorderIn As Color)

        Dim mode As SmoothingMode = g.SmoothingMode
        g.SmoothingMode = SmoothingMode.AntiAlias

        Dim r As New Rectangle(rect.X, rect.Y, rect.Width, (rect.Height / 4))


        Dim topBlend As New Blend
        Dim topBlendFactors As Single() = {0.0F, 0.2F, 0.5F, 0.7F, 1.0F, 0.7F, 0.5F, 0.2F, 0.0F}
        Dim topBlendPositions As Single() = {0.0F, 0.1F, 0.3F, 0.4F, 0.5F, 0.6F, 0.7F, 0.8F, 1.0F}
        topBlend.Factors = topBlendFactors
        topBlend.Positions = topBlendPositions

        Dim bottomBlend As New Blend
        Dim bottomBlendFactors As Single() = {0.0F, 0.2F, 0.5F, 0.7F, 1.0F, 0.7F, 0.5F, 0.2F, 0.0F}
        Dim bottomBlendPositions As Single() = {0.0F, 0.1F, 0.3F, 0.4F, 0.5F, 0.6F, 0.7F, 0.8F, 1.0F}
        bottomBlend.Factors = bottomBlendFactors
        bottomBlend.Positions = bottomBlendPositions

        Using lgbBottom As LinearGradientBrush = New LinearGradientBrush(rect, colorDownBegin, colorDownEnd, 90.0F, True)
            lgbBottom.Blend = bottomBlend
            g.FillRectangle(lgbBottom, rect)
        End Using

        Using lgbTop As LinearGradientBrush = New LinearGradientBrush(r, colorUpBegin, colorUpEnd, 200, True)
            lgbTop.Blend = topBlend
            g.FillRectangle(lgbTop, r)
        End Using

        Dim InnerBorderRectanggle As New Rectangle((rect.X + 1), (rect.Y + 1), (rect.Width - 2), (rect.Height - 2))

        Using innerBorderPen As New Pen(colorBorderIn)
            g.DrawRectangle(innerBorderPen, InnerBorderRectanggle)
        End Using

        Using OuterBorderPen As New Pen(colorBorderOut)
            g.DrawRectangle(OuterBorderPen, InnerBorderRectanggle)
        End Using

        g.SmoothingMode = mode

    End Sub

    Public Shared Function DrawGradient(ByVal g As Graphics, ByVal rect As Rectangle, _
                                          ByVal colorUpBegin As Color, _
                                          ByVal colorUpEnd As Color, _
                                          ByVal colorDownBegin As Color, _
                                          ByVal colorDownEnd As Color, _
                                          ByVal colorBorderOut As Color, _
                                          ByVal colorBorderIn As Color) As Boolean

        DrawGradientRectangle(g, rect, colorUpBegin, colorUpEnd, colorDownBegin, colorDownEnd, colorBorderOut, colorBorderIn)
        Return True
    End Function

#End Region

End Class

الأن لنوضح كيف نستفيد من الكلاس ThemeColors عن طريق إضافته إلي اي كونترول

كل ما نحتاجه هو تعريف متغير يشير الي هذا الكلاس ومن ثم نضيف Property تشير اليه ثم نضيف أيضا Handler يشير الي ما نريده ان يحدث عندما تتغير الألوان أو أي لون موجود بالكلاس ThemeColors ثم نستخدم بعض Sub الموجود به لنرسم شيئا ما علي الكونترول والكود يوضح أيضا كيف نقوم بعمل Dispsoe للكلاس ThemeColors

والكود التالي يوضح شكل الكونترول

Public Class ThemedControl
    Inherits Control

    Private m_ThemeColors As ThemeColors

    Public Sub New()

        SetStyle(ControlStyles.SupportsTransparentBackColor, True)
        SetStyle(ControlStyles.Opaque, False)
        SetStyle(ControlStyles.DoubleBuffer, True)
        SetStyle(ControlStyles.AllPaintingInWmPaint, True)
        SetStyle(ControlStyles.UserPaint, True)
        UpdateStyles()

        m_ThemeColors = New ThemeColors
        AddHandler Me.m_ThemeColors.ThemeChanged, New EventHandler(AddressOf Me.ThemeColor_ThemeChanged)

    End Sub

    Public Property Theme() As ThemeColors
        Get
            Return Me.m_ThemeColors
        End Get
        Set(ByVal value As ThemeColors)

            If Me.m_ThemeColors IsNot value Then
                Me.m_ThemeColors = value
                AddHandler Me.m_ThemeColors.ThemeChanged, New EventHandler(AddressOf Me.ThemeColor_ThemeChanged)
            End If
        End Set
    End Property

    Protected Overrides ReadOnly Property DefaultSize() As System.Drawing.Size
        Get
            Return New Size(200, 100)
        End Get
    End Property

    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then

            If (Me.m_ThemeColors IsNot Nothing) Then
                RemoveHandler Me.m_ThemeColors.ThemeChanged, New EventHandler(AddressOf Me.ThemeColor_ThemeChanged)
                Me.m_ThemeColors.Dispose()
            End If
            m_ThemeColors = Nothing
        End If
        MyBase.Dispose(disposing)
    End Sub

    Private Sub ThemeColor_ThemeChanged(ByVal sender As Object, ByVal e As EventArgs)
        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)
        If m_ThemeColors.ApplyTheme Then
            m_ThemeColors.DrawTheme(e.Graphics, New Rectangle(0, 0, Me.Width, 24))
            Dim r As New Rectangle(0, 0, Me.ClientRectangle.Width - 1, Me.ClientRectangle.Height - 1)
            Using controlPen As New Pen(Me.m_ThemeColors.ThemeBorderOut)
                e.Graphics.DrawRectangle(controlPen, r)
            End Using
        End If
    End Sub

    Protected Overrides Sub OnResize(ByVal e As System.EventArgs)
        MyBase.OnResize(e)
        Invalidate()
    End Sub

End Class

الأن ولربما يخطر علي ذهن القارئ السؤال الأتي ؟

كيف نضيف Custom EventHandler إلي أي كلاس لكن بدون استخدام EventHandlerList .........................الخطوات التاليه توضح ذلك

أولا نفوم بتعريف متغير يشير الي EventHandler

ثانيا نقوم بإضافة Custom EventHandler كما فعلنا في الجزء الأول من المقال

ثالثا علينا أن نكتب الكود المناسب الي هذه Custom EventHandler

الكودالتالي يوضح ذلك

Private m_themeChanged As EventHandler = Nothing

#Region " Events "
    Public Custom Event ThemeChanged As EventHandler
        AddHandler(ByVal value As EventHandler)

        End AddHandler

        RemoveHandler(ByVal value As EventHandler)

        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)

        End RaiseEvent
    End Event
#End Region

الأن لنضيف الكود المناسب إلي ThemeChanged EventHandler والكود التالي يوضح ذلك

#Region " Events "
    Public Custom Event ThemeChanged As EventHandler
        AddHandler(ByVal value As EventHandler)
            Me.m_themeChanged = CType([Delegate].Combine(Me.m_themeChanged, value), EventHandler)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            Me.m_themeChanged = CType([Delegate].Remove(Me.m_themeChanged, value), EventHandler)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            Dim handler As EventHandler = Me.m_themeChanged
            If (handler IsNot Nothing) Then
                handler.Invoke(sender, e)
            End If
        End RaiseEvent
    End Event
#End Region

من الكود أعلاه سنلاحظ كيفية استخدام Delegate.Combine وأيضا Delegate.Remove وذلك لكي نقوم بعمل ارتباط أو إلغاء لهذا الارتباط بين المتغير m_themeChanged من داخل Custom EventHandler

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

ThemeConverter Class

ThemeEditor Class

ThemesUtilty Clss

<TypeConverter(GetType(ThemeConverter)), Editor(GetType(ThemeEditor), GetType(UITypeEditor))> _
Public Class Themes

#Region " Fields "

    Private m_startColor As Color
    Private m_startColorMiddle As Color

    Private m_endColor As Color
    Private m_endColorMiddle As Color

    Private m_northBegin As Color
    Private m_northEnd As Color
    Private m_southBegin As Color
    Private m_southEnd As Color
    Private m_borderOut As Color
    Private m_borderIn As Color

    Private m_themeChanged As EventHandler = Nothing

#End Region

#Region " Constructor "

    Public Sub New()
        Me.m_northBegin = Color.Transparent
        Me.m_northEnd = Color.Transparent
        Me.m_southBegin = Color.Transparent
        Me.m_southEnd = Color.Transparent
        Me.m_borderOut = Color.Transparent
        Me.m_borderIn = Color.Transparent
    End Sub

    Public Sub New(ByVal northColorBegin As Color, _
                   ByVal northColorEnd As Color, _
                   ByVal southColorBegin As Color, _
                   ByVal southcolorEnd As Color, _
                   ByVal borderOut As Color, _
                   ByVal borderIn As Color)

        Me.m_northBegin = northColorBegin
        Me.m_northEnd = northColorEnd
        Me.m_southBegin = southColorBegin
        Me.m_southEnd = southcolorEnd
        Me.m_borderOut = borderOut
        Me.m_borderIn = borderIn

    End Sub

#End Region

#Region " Properties "

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeNorthBegin() As Color
        Get
            Return m_northBegin
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_northBegin = value
            Me.OnThemeChanged(Me, EventArgs.Empty)
        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeNorthEnd() As Color
        Get
            Return Me.m_northEnd
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_northEnd = value
            Me.OnThemeChanged(Me, EventArgs.Empty)
        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeSouthBegin() As Color
        Get
            Return m_southBegin
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_southBegin = value
            Me.OnThemeChanged(Me, EventArgs.Empty)
        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeSouthEnd() As Color
        Get
            Return m_southEnd
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_southEnd = value
            Me.OnThemeChanged(Me, EventArgs.Empty)
        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeBorderOut() As Color
        Get
            Return Me.m_borderOut
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_borderOut = value
            Me.OnThemeChanged(Me, EventArgs.Empty)
        End Set
    End Property

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property ThemeBorderIn() As Color
        Get
            Return Me.m_borderIn
        End Get
        Set(ByVal value As Color)
            If value.IsEmpty Then
                value = Color.Transparent
            End If
            Me.m_borderIn = value
            Me.OnThemeChanged(Me, EventArgs.Empty)
        End Set
    End Property

#End Region

#Region " Methods "

    Friend Sub DrawTheme(ByVal gr As Graphics, ByVal rect As Rectangle)
        ThemesUtility.DrawGradient(gr, rect, Me.ThemeNorthBegin, Me.ThemeNorthEnd, Me.ThemeSouthBegin, Me.ThemeNorthEnd, Me.ThemeBorderOut, Me.ThemeBorderIn)
    End Sub

    Friend Function ApplyTheme() As Boolean
        Return ((Me.ThemeNorthBegin <> Color.Empty) AndAlso (Me.ThemeNorthEnd <> Color.Empty) AndAlso (Me.ThemeSouthBegin <> Color.Empty) AndAlso (Me.ThemeSouthEnd <> Color.Empty))
    End Function

    Protected Overridable Sub OnThemeChanged(ByVal sender As Object, ByVal e As EventArgs)
        RaiseEvent ThemeChanged(sender, e)
    End Sub

    Public Overrides Function ToString() As String
        Return String.Empty
    End Function

#End Region

#Region " Events "
    Public Custom Event ThemeChanged As EventHandler
        AddHandler(ByVal value As EventHandler)
            Me.m_themeChanged = CType([Delegate].Combine(Me.m_themeChanged, value), EventHandler)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            Me.m_themeChanged = CType([Delegate].Remove(Me.m_themeChanged, value), EventHandler)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            Dim handler As EventHandler = Me.m_themeChanged
            If (handler IsNot Nothing) Then
                handler.Invoke(sender, e)
            End If
        End RaiseEvent
    End Event
#End Region

End Class

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

Public Class ThemeControl

    Inherits Control

    Private m_theme As Themes

    Public Sub New()

        SetStyle(ControlStyles.SupportsTransparentBackColor, True)
        SetStyle(ControlStyles.Opaque, False)
        SetStyle(ControlStyles.DoubleBuffer, True)
        SetStyle(ControlStyles.AllPaintingInWmPaint, True)
        SetStyle(ControlStyles.UserPaint, True)
        UpdateStyles()

        m_theme = New Themes
        AddHandler Me.m_theme.ThemeChanged, New EventHandler(AddressOf Me.ThemeColor_ThemeChanged)

    End Sub

    Public Property Theme() As Themes
        Get
            Return m_theme
        End Get
        Set(ByVal value As Themes)

            If Me.m_theme IsNot value Then
                Me.m_theme = value
                AddHandler Me.m_theme.ThemeChanged, New EventHandler(AddressOf Me.ThemeColor_ThemeChanged)
            End If
        End Set
    End Property

    Protected Overrides ReadOnly Property DefaultSize() As System.Drawing.Size
        Get
            Return New Size(200, 100)
        End Get
    End Property

    Private Sub ThemeColor_ThemeChanged(ByVal sender As Object, ByVal e As EventArgs)
        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnResize(ByVal e As System.EventArgs)
        MyBase.OnResize(e)
        Invalidate()
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)
        If m_theme.ApplyTheme Then
            m_theme.DrawTheme(e.Graphics, New Rectangle(0, 0, Me.Width, 24))
            Dim r As New Rectangle(0, 0, Me.ClientRectangle.Width - 1, Me.ClientRectangle.Height - 1)
            Using controlPen As New Pen(Me.m_theme.ThemeBorderOut)
                e.Graphics.DrawRectangle(controlPen, r)
            End Using
        End If
    End Sub

End Class

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

بالتوفيق للجميع............... وتقبلوا تحياتي

أخوكم عمر

مواضيع مشابهة