تقييم الموضوع :
  • 0 أصوات - بمعدل 0
  • 1
  • 2
  • 3
  • 4
  • 5
مقال- Custom EventHandler & Classes - الجزء الأول
#1
كاتب الموضوع : silverlight

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

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


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


يتبع في المشاركة التالية ...............................................
}}}
تم الشكر بواسطة:
#2
كاتب المشاركة : Islam Ibrahim

جزاك الله خيراً مهندس عمر,

لكن لي تعليقان:

أولاً: لا أرى ضرورة لعمل Disposing للـ EventHandler الخاص بالحدث الذي أضفناه وأعتقد أن هذه العملية لا تقع على عاتق ال Control أو Component الذي نعمل على بنائه , ولكن تقع على عاتق ال Form أو ال Container الذي يستضيف ال Control الخاص بنا فهو الذي سيقوم بعمل Subscribing لل EventHandler وهو من يفترض أن يقوم بعمل العملية العكسية وهي Unsubscribing .

والسبب أن ال EventHandler لا يخص الكلاس الخاص بنا وليس أحد ال Members الخاص به بل يخص كما قلت ال Container الخاص بالكلاس سواء كان Form أو غيره. وهذا ال Container هو الذي سيتولى هذه المهمة نيابة عنا.

ثانياً: فإنه ينصح بعمل Lock عند إضافة أو إزالة أو إطلاق أي Eventhandler وذلك حتى نتفادى أي تضارب عندما يتم بإضافة نفس الحدث أو إزالته في نفس الوقت من أكثر من مسار واحد Thread وجعل مسار واحد فقط هو الذي يطلق الحدث في حين تنتظر البقية حتى ينتهي المسار الحالي, كما يوضّح المثال التالي:


كود :
Public Class ComponentTest
Inherits System.ComponentModel.Component

Public Sub New()
End Sub

Dim PropertyChangedObject As New Object()

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)
RaiseEvent PropertyChanged(sender, e)
End Sub

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

RemoveHandler(ByVal value As EventHandler)
SyncLock PropertyChangedObject
Me.Events.RemoveHandler(PropertyChangedObject, value)
End SyncLock
End RemoveHandler

RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
Dim handler As EventHandler = Nothing
SyncLock PropertyChangedObject
handler = CType(Me.Events(PropertyChangedObject), EventHandler))
End SyncLock
If handler IsNot Nothing Then
handler.Invoke(sender, e)
End If
End RaiseEvent
End Event

End Class
وفي هذه الحالة لابد من استخدام RaiseEvent بدلاً من الطريقة اليدوية.
}}}
تم الشكر بواسطة:
#3
كاتب المشاركة : silverlight

أخي الفاضل إسلام

الأسلوب الذي تفضلت باستخدامه في مثالك أسلوب كانت مايكروسوفت تعتمده قديما في الكلاسات الخاصة بها وبما انهم كانوا يقومون بتعريف Delegate علي أنه Object لذلك ومن المؤكد انهم كانوا يحتاجون الي استخدام SyncLock التي تعمل هنا كغلاف لأي Handler حتي يتم تنفيذه وحتي لا يحدث Blocking بين Handlers المختلفة

والسبب في ذلك هو التأكد من أن أي Handler يتم تنفيذه عليه أن ينتظر ما قبله من Delegates حتي يبدا هو في تنفيذ مهامه وهنا تأتي أهمية جملة SyncLock لأنها ستضمن تنفيذ Handler كاملا وبالتالي لا يحدث تداخل أو Blocking بين Delegates المختلفة

أما بالنسبة للفارق بين Custom EventHandler وبين الأسلوب القديم وهو تعريف Delegates علي أنها Objects اسمح لي أن اوضح الأمر بفكرة بسيطة جدا

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

وعندما نقوم بتعريق Delegate جميعها علي أنها Objects يصبح الأمر مثل العائلة التي يحمل أولادهم جميعا نفس الإسم ولذلك نحتاج الي استخدام SyncLock

أما عندما نقوم باستخدام Custom EventHandler أو Custom Delegates فهنا نحن نحرص علي أن يكون كل منهم له اسم مختلف عن الأخر وهذا سيؤدي الي سهولة عمل كل منهم علي حدة وبالتالي لا ينتظرون بعضهم البعض لتنفيذ مهامهم المختلفة

وفي الحالتين والمقصد هنا أنه في حالة استخدام الاسلوب القديم وهو تعريف Delegates علي أنها Objects أو استخدام الأسلوب الأحدث قليلا الأ وهو استخدام Custom EventHandler هنا تأتي أهمية عمل Dispose لهذه EventHandler فالهدف هنا هو المساعدة علي تحرير ذاكرة الكمبيوتر وبالتالي أنت تساعد الكلاس GC علي تنفيذ مهامه بشكل أفضل

عموما الأمثلة التي سوف أضعها لاحقا ربما ستفسر الأمر بشكل أفضل

تقبل تحياتي
أخوك عمر
}}}
تم الشكر بواسطة:
#4
كاتب المشاركة : silverlight


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

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


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

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

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

والأن لنكتب كلاسا بسيطا يحتوي علي مجموعة من 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

الأن لنضيف لهذا الكلاس متغير يشير إلي 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

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

يتبع في المشاركة التالية ...............................................
}}}
تم الشكر بواسطة:
#5
الكود التالي يوضح بعض الكلاسات الإضافية التي قمت بكتابتها وهي كالأتي

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 وكيف نضيفه الي كونترول ومن ثم نستخدمه في رسم شئ ما علي الكونترول

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

تقبلوا تحياتي
اخوكم عمر
}}}
تم الشكر بواسطة:
#7
كاتب المشاركة : Islam Ibrahim

جزاك الله خيراً

أريد أن أوضح فقط بخصوص المثال الذي وضعته في مشاركتي أنني قمت بتعريف Custom EventHandlers بشكل عادي جداً بالإضافة إلى Lock Objects والتي سيتم عمل Locking لها وليس للـ EventHandlers نفسها, وكما تعلم فإن EventHandlerList يحفظ ال Delegates أو EventHandlers بداخله على شكل KeyValue Pairs وأنا قمت بعمل Locking لل Key وليس لل EventHandler,

كما أن هذا الأسلوب لا زالت مايكروسوفت تنصح باستخدامه في Design Guidelines لتحقيق ما يسمى Thread Safety.

تقبل تحياتي , وأعتذر على مقاطعة الدرس
}}}
تم الشكر بواسطة:


المواضيع المحتمل أن تكون متشابهة .
الموضوع : الكاتب الردود : المشاهدات : آخر رد
  الجزء الثالث من:كيف تجعل الـ Text Box ذكي!يترجم العمليات الحسابية ويخرج الناتج (الأقواس المتعددة) !! أنس محمود 10 7,827 19-07-22, 12:15 AM
آخر رد: StartLight4000
  مقال: الكومبو بوكس ComboBox كيف تضيف أيقونات Blue Sky 1 3,159 30-06-19, 10:41 AM
آخر رد: invocker
  [درس فيديو] مثال بسيط لبرنامج إجازات فقط لأغراض الشرح (الدرس الأول) عبدالله الدوسري 7 11,538 28-04-18, 06:55 PM
آخر رد: moniam
  الفرق بين الأصناف Classes و الكائنات Objects RaggiTech 1 8,069 28-03-18, 10:30 PM
آخر رد: alsouf
  حساب قيمة معادلة(اقصد صيغة دون مجاهيل) مكتوبة بالتكست : الجزء الخامس والاخير محمد شريقي 4 4,517 23-02-18, 10:44 PM
آخر رد: العواد الصغير
  مقدمة إلي إخفاء المعلومات - الجزء الأول silverlight 5 4,151 07-01-17, 10:44 PM
آخر رد: Basil Abdallah
  مقدمة إلي إخفاء المعلومات - الجزء الثاني silverlight 1 3,026 06-01-17, 11:52 AM
آخر رد: silverlight
  تحويل الفيديو في برامجك-الجزء الثاني( إصلاح للمشاكل + تعديل للروابط + توضيح للأمر ) RaggiTech 1 3,276 10-12-14, 06:37 PM
آخر رد: abulayth
  الجزء الثاني من:كيف تجعل الـ Text Box ذكي!يترجم العمليات الحسابية ويخرج الناتج ( العمليات المتعددة)! أنس محمود 0 2,819 22-02-13, 12:39 AM
آخر رد: أنس محمود
  مقال- كيفية الاستغناء عن الداتا بيز التقليدية في برامجنا – ألجزء الأول RaggiTech 1 3,426 06-10-12, 12:23 AM
آخر رد: RaggiTech

التنقل السريع :


يقوم بقرائة الموضوع: بالاضافة الى ( 1 ) ضيف كريم