بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله وبركاته
جميعنا يعلم ماذا تعني كلمات مثل 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 ClassImports 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 ClassImports 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أتمني ان تكون جميع الأمثلة واضحة للقارئ وكما سبق وأوضحت أن هذا الموضوع ليس للمبتدئين ولكن بالتأكيد تستطيعون الاستفسار عن ما تريدون
بالتوفيق للجميع............... وتقبلوا تحياتي
أخوكم عمر