منتدى فيجوال بيسك لكل العرب | منتدى المبرمجين العرب

نسخة كاملة : تطوير الكونترول Designers الجزء الرابع
أنت حالياً تتصفح نسخة خفيفة من المنتدى . مشاهدة نسخة كاملة مع جميع الأشكال الجمالية .
كاتب الموضوع : silverlight

الموضوع : تطوير الكزنترول Designers
اللغة المستخدمة: الفيجوال بيسك
التطبيق: فيجوال استوديو 2005 و 2008
المستوي: التقييم متروك للقارئ
إعداد: مهندس / عمر أمين إبراهيم

ماذا تعني كلمة Designers ؟

هذا سؤال مهم جدا نسأله لأنفسنا وطبعا ليس المقصود هنا أن نترجم المعني من الإنجليزية الي العربية عموما لكي نجيب علي السؤال فالإجابة ستكون كالأتي إن Designers هي همزة الوصل بين المبرمج و الكونترول بأنواعه المختلفة في مرحلة Design Time.
بشكل عام Designers يمكن استخدامها مع Windows Forms وأيضا مع Web UI

ما هي أنواع Designers ؟

هناك العديد من Designers و هي مرتبه حسب أنواعها كالأتي

ComponentDesigner Class
ControlDesigner Class
ComponentDocumentDesigner Class
ParentControlDesigner Class
ScrollableControlDesigner Class
DocumentDesigner Class

وعموما كل هذه الكلاس Classes مشتقه من بعضها البعض والأصل لجميعها هو ComponentDesigner الذي يتكون من مجموعه من Interfaces التي تمثل أصل Designers Classes

أصل Designer ؟

جميع أنواع Designers المعروفة لنا جاءت من مكان واحد فقط ألا وهو مجموعه من Interfaces وهي كالأتي

IDesigner Interface
IDisposal Interface
IDesgnerFilter Interface
ITreeDesigner Interface
IComponentInitializer Interface
IRootDesigner Interface
IToolboxUser Interface
ITypeDescriptorFilterServices Interface

ولمزيد من التفاصيل يمكن الرجوع الي Namespaces التالية
System.Windows.Forms.Design Namespace
System.ComponentModel.Design Namespace

نبذة عن الكونترول

كما تعلمون إن الكونترول له أنواع مختلفة كثيرة وكلها مشتقه من Control Class ومنها علي سبيل المثال الأنواع التالية

Control Class وهذا النوع هو ما يختص به موضوعنا عن تطوير الكونترول وهو مشتق من Component Class الذي بدوره مشتق من MarshalByRefObject Class
ScrollableControl Class وهو مشتق من Control Class
ContainerControl Class وهو مشتق ScrollableControl
ListControl Class وهو أيضا مشتق من Control Class وهذا النوع من الكونترول هو الذي تم استخدامه في بناء الكومبو بوكس ComboBox Class و أيضا ListBox Class

الفارق بين هذه Control Classes المختلفة انه قد تم عمل Implementation لبعض Interfaces أي انه قد أضيفت بعض Interfaces وبناء عليه يتم تخليق Control Class باسم جديد فقط وطبعا أنت أيضا عزيزي القارئ يمكنك أن تفعل نفس الشيء يعني ببساطه تقدر تخلق أي كلاس بأي اسم تريد وتضيف له مواصفات جديدة عن طريق استخدام Interfaces أو يدون Interfaces
أعتقد الأن أدركت عزيزي القارئ لماذا تطرقنا لمناقشة موضوع Interfaces ولإعطاء مثال عام عن موضوعنا انظر الي الكود الموجود أدناه. وفي هذا المثال قمت باستخدام Attribute وهو عبارة عن System.Windows.Forms.Design. DocumentDesigner وهذا يوضح كيفية اسخدام Designers الموجوده في System بدون اضافة اي Custom Designers واستعمالها في نصميم الكونترول.


كود :
Imports System.ComponentModel
Imports System.ComponentModel.Design

<Designer("System.Windows.Forms.Design.DocumentDesigner, System.Windows.Forms.Design.DLL", _
GetType(IRootDesigner)), *********ategory("Form")> _
Public Class MyControl

Inherits ContainerControl
Implements IArab

' Insert code here.

Public Property myProperty() As System.Drawing.Color Implements IArab.myProperty
Get

End Get
Set(ByVal value As System.Drawing.Color)

End Set
End Property
End Class 'MyForm


Public Interface IArab
Property myProperty() As Color

End Interface
ملحوظه هامه عن الكونترول كلاس

الكونترول يمتلك مجموعه من الصفات Properties وهي صفات أوليه كلها تبدأ باسم Default مثل خاصية DefaultSize و DefaultFont وأيضا بعض الخواص الاخري وطبعا ممكن عمل Overrides لهذه الصفات جميعا لو أردنا وذلك حسب متطلبات الكونترول الذي نقوم بتصميمه.

كيف تصنع Designer ؟

الإجابة علي هذا السؤال تنحصر في الخطوات التالية:

لتصنع Designer فأنت بحاجه الي كونترول أولا أي يجب أن تصنع الكونترول الخاص بك مهما كان نوعه أو شكله أو الهدف منه
أيضا أنت بحاجه الي إضافة Reference هام جدا إلي مشروعك وهو Namespace: System.Windows.Forms.Design
ولكي تربط بين الكونترول و Designer الذي قمت بتصميمه عليك أن تستخدم DesignerAttribute Class

من المهم جدا تحديد نوع Designer الذي سوف تقوم بعمل Inherits له وذلك مرتبط بك وماذا تريد.
مثلا لو أردت أن تصنع Custom Control ولا تريد أن يتم إضافة أي كونترول أخري إليه أي لا تريد إضافة Child Control إليه عليك أن تستخدم ControlDesigner Class أما إذا كنت تريد أن تضيف كونترول أخري الي ما تصمم عليك أن تستخدم ParentControlDesigner Class

ولتوضيح هذه النقطة لنفرض انك تريد أن تصنع Custom Control وأيضا لا تريد من المستخدم أن يقوم بإضافة عناصر أخري Child Control الي هذا الكونترول هنا عليك أن تستخدم ControlDesigner Class وطبعا العكس صحيح إذا أردت أن تضيف عناصر أخري الي الكونترول عليك هنا أن تستخدم ParentControlDesigner Class أو أي كلاس أخر مشتق منه

ببساطه أكثر أن أي كونترول تقوم ببنائه وتستخدم معه ControlDesigner Class لا يمكن استخدامه كوعاء أو حاويه Container لأي أنواع أخري من الكونترول.

الفرق بين ControlDesigner Class و ParentControlDesigner Class

لو نظرنا الي كل من هذين الكلاسين من خلال Help File الخاص بالفيجوال استوديو سنجد أن ParentControlDesigner مشتق من ControlDesigner ولو عقدنا مقارنة بينهم سنجد أيضا أن ParentControlDesigner يمتلك عدة طرق أكثر قليلا من ControlDesigner وعددها سبعة طرق وبالمثل سبعة صفات إضافية أخري وهي موضحه كالأتي

الطرق Methods

AddPaddingSnapLines
CanParent
CreateTool
CreateToolCore
GetControl
GetParentForComponent
GetUpdatedRect

الصفات Properties

AllowControlLasso
AllowGenericDragBox
AllowSetChildIndexOnDrop
DefaultControlLocation
DrawGrid
GridSize
MouseDragTool

لرؤية باق الصفات والخواص والحقول يمكنك عزيزي القاري أن تراجع ملف Help الخاص بالفيجوال استوديو كما يمكنك أيضا ن تقارن بينهم لتري الفرق بنفسك وستجد أيضا أن هناك حقل Filed كان موجودا في ControlDesigner ولكنه لم يعد موجودا في ParentControlDesigner وهذا الحقل يطلق عليه InvalidPoint

مما سبق نكتشف أن الطرق والصفات التي تم إضافتها الي ParentControlDesigner هي التي خلقت الفرق بينهم وهي التي أعطت الإمكانيات لهذا Designer لكي يعطي إمكانية إضافة أي كونترول أخر الي أي Custom Control وبما أن معظم Designers مشتقه من ParentControlDesigner لذلك سوف نتحدث عنه تحديدا ونحاول أن نغطي الأجزاء الهامة به والتي نحتاجها في تصميم كونترول جديد وبمواصفات وشكل جديد.

هيا نصمم Custom Control

لنفتح مشروع جديد Form Application ثم نضيف له كلاس نطلق عليه MyControl و نضيف له كونترول عن طريق Inherits و أيضا في نهاية هذا الكلاس نضيف كلاس ونطلق عليه MyDesigner ونضيف أيضا له ParentControlDesigner عن طريق Inherits الأن لنربط بين الكونترول و Designer لكي نربط بين الكلاسين سوف نستخدم DesignerAttribute Class ومن ثم علينا أن نضيف Attribute الي MyControl Class والهدف هنا هو أننا نقول للكونترول MyControl أن Designer المسئول عنك هو MyDesigner ويتم إضافة DesignerAttribute (<Designer(GetType(MyDesigner))> _) مباشرة قبل MyControl Class وبذلك يكون الكود كالأتي


كود :
Imports System.ComponentModel
Imports System.Windows.Forms.Design

<Designer(GetType(MyDesigner))> _
Public Class MyControl
Inherits Control

End Class 'MyControl

Public Class MyDesigner
Inherits ParentControlDesigner

End Class 'MyDesigner
كيف نقوم بإضافة تعديلات الي Custom Designer

لكي نقوم بذلك نحتاج أن نتحدث قليلا عن ParentControlDesigner
إن الحديث عن ParentControlDesigner Class سوف يغطي بالتبعية الحديث عن ControlDesigner Class ولقد أوضحنا الفرق بينهم وأنهم لا يختلفان علي الإطلاق إلا في أشياء قليله وعموما ParentControlDesigner Class له صفات Properties و طرق Methods أكثر من ControlDesigner Class لذلك سيكون هو هدفنا هنا في هذا الجزء من تطوير الكونترول.
ملحوظه هامه هناك بعض الصفات التي تخص ParentControlDesigner لا نحتاج الي التغيير في قيمها ألأوليه لان الأمر حينئذ سيكون عبارة عن مضيعة للوقت لكن بما أن الهدف هنا هو توضيح وشرح ParentControlDesigner فلسوف أحاول أن إعطاء فكره عن معظم الصفات التي تخص ParentControlDesigner والتي تهمنا في تصميم Custom Control

AllowControlLasso Property

هذه الصفة لها قيمه عبارة عن Boolean أي True أو False وهي خاصية يتم استدعاؤها بعد أن نضيف عنصر ما من Toolbox وتقوم برسم ReversibleRectangle ومن الأفضل عدم التعديل فيها واستخدام قيمتها الافتراضية لكن لو أراد القارئ أن يقوم بتعديلها فكل ما عليه أن يري الكود المرفق. وأيضا لمزيد من المعلومات عن ReversibleRectangle يمكنك أن تراجع كلاس مهم جدا وهو ControlPaint Class وهناك مقال قمت بنشره بالموقع تحت عنوان الكلاس التائه عن هذا الموضوع يمكن أن ترجع إليه إن أردت

AllowGenericDragBox Property

هذه الصفة لها قيمه عبارة عن Boolean أي True أو False وهي تحدد هل يتم رسم Drag Box عندما نحاول أن نقوم بعمل Dragging لآي عنصر من Toolbox والقيمة الافتراضية لها True ومن الأفضل عدم التعديل بها لكن لو أراد القارئ أن يقوم بتعديلها فكل ما عليه أن يري الكود المرفق.

ِAllowSetChildIndexOnDrop Property

هذه الصفة الغرض منها هو الحفاظ علي z-Oorder لكل الكونترول التي يتم سحبها وإضافتها Toolbox و z-Order عبارة عن visual layering للكونترول Control التي تضاف الي Custom Control وقيمة هذه الصفة عبارة عن Boolean أي True أو False وعموما القيم الافتراضية لها عبارة عن True ومن الأفضل عدم التعديل فيها واستخدام قيمتها الافتراضية لكن لو أراد القارئ أن يقوم بتعديلها أن يري الكود المرفق.

DrawGrid Property

واضح جدا أن هذه الصفة تسمح برسم Grid داخل Custom Control وهي عبارة عن Boolean أي True أو False والقيمة الافتراضية لها False ولو أردت أن تظهر Grid انظر الي الكود المرفق.

DefaultControlLocation Property

هذه الصفة الهدف منها شئ هام جدا فهي المسئولة عن تحديد النقطة Point الافتراضية أو الأولية أو مكان ChildControl داخل الكونترول أي عندما نقوم بعمل Double-Click لكي نضيف أي كونترول من Toolbox الي داخل الكونترول الخاص بنا.
مثال علي ذلك عندما تقوم باستخدام الماوس والضغط علي أي كونترول عن طريق Double-Click وإضافة Button مثلا من Toolbox الي أي كونترول أو حتى الي الفورم سنجد أن المكان سيقع داخل الفورم في منطقه معينه وهذه الصفة التي نتحدث عنها هي المسئولة عن ذلك.
إذن لكي نحدد المكان الذي سيقع فيه أي كونترول داخل الكونترول الذي نصممه علينا أن نقوم بعمل Overrides لهذه الخاصية و مثال غلي ذلك انظر الي الكود المرفق.

GridSize Property

من الواضح الهدف من الصفة فهي تحدد مقاس Grid وهي مرتبطة بالصفة DrawGrid واقل قيمه افتراضية 2 واكبر قيمة افتراضية 200 لكن لو أراد القارئ أن يقوم بتعديلها فكل ما عليه أن يري الكود المرفق.

MouseDragTool Property

هذه الصفة تستخدم لإضافة ToolBoxItem أثناء عملية السحب والإفلات الي Designer وهي مرتبطة بكلاس أخر وهوToolBoxItem Class وهذا الكلاس مرتبط نشئ أخر مهم و هو System.Drawing.Design Namespace. الكود المرفق.يوضح كيفية استخدام هذه الصفة
الأن نقوم بعمل Build للكونترول وسوف نراه موجودا داخل Toolbox لنقوم بإضافته الي الفورم باستخدام Double Click نقوم بإضافة Button إلي الكونترول سنلاحظ أن مقاس الكونترول Size صغير إذن نحن نحتاج الي تحديد Size جديد وأيضا لو وقفنا بالماوس علي الباتون الذي أضفناه الي الكونترول ونظرنا الي صفاته المختلفة داخل Properties Window سنجد أن Button.Location أصبح (20, 20).

هناك ملحوظه هامه جدا ذكرتها مايكروسوفت بخصوص التعديل في Size Property وهي للحصول علي أداء أفضل للكونترول يجب أن نقوم بتعديل خاصية Size عن طريق عمل Overrides لخاصية DefaultSize Property وليس عن طريق Constructor أي (Public Sub New)
لنتوقف هنا قليلا لنستعرض الكود الي يغطي كيفية استخدام هذه الصفات السابقة


كود :
Imports System.ComponentModel
Imports System.Windows.Forms.Design
Imports System.Security.Permissions
Imports System.ComponentModel.Design
Imports System.Drawing.Design

<System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
<Designer(GetType(MyDesigner), GetType(IDesigner))> _
Public Class MyControl
Inherits Control

' Set the Default size of the Control
Protected Overrides ReadOnly Property DefaultSize() As System.Drawing.Size
Get
Return New Size(200, 200)
End Get
End Property

End Class 'MyControl ------------> Custom Control

<System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
Public Class MyDesigner
Inherits ParentControlDesigner


Protected Overrides ReadOnly Property DefaultControlLocation() As System.Drawing.Point
Get
' any Controlwill be aded at point to be at x = 20 and y = 20
Return New Point(20, 20)
End Get
End Property

Protected Overrides ReadOnly Property AllowControlLasso() As Boolean
Get
Return False
End Get
End Property

Protected Overrides ReadOnly Property AllowGenericDragBox() As Boolean
Get
Return True

End Get
End Property

Private TrueOrFalse As Boolean = True
Protected Overrides Property DrawGrid() As Boolean
Get
Return TrueOrFalse
End Get
Set(ByVal value As Boolean)
TrueOrFalse = value
End Set
End Property

Private ZOrdersTrueOrFalse As Boolean = True
Protected Overrides ReadOnly Property AllowSetChildIndexOnDrop() As Boolean
Get
Return ZOrdersTrueOrFalse
End Get
End Property

Private gsize As Size = New Size(20, 20)
Protected Overloads Property GridSize() As Size
Get
Return gsize
End Get
Set(ByVal value As Size)
gsize = value
End Set
End Property

Private m_Item As ToolBoxItem = Nothing
Protected Overloads ReadOnly Property MouseDragTool() As ToolboxItem
Get
Return m_Item
End Get
End Property


End Class 'MyDesigner ------------> Custom Designer
الأنواع الأخرى من الصفات Properties التي تخص ParentControlDesigner كلها موجودة في ControlDesigner Class أي أنهم يشتركان معا في هذه الصفات ولكي نكتب عنها جميعا سنحتاج الي الكثير من الوقت والجهد ولأن بعض هذه الصفات لا يحتاج الي أن نقوم بالتعديل بها لذلك سنقوم بالتركيز علي الصفات والطرق التي تهمنا لكي نقوم بتصميم Custom Control

ActionLists Property

هذه الصفة تهمنا جدا لأنه من خلالها نستطيع استرجاع مجموعة من Action Lists التي تخص أي Property قمنا بإضافتها الي DesignerActionList Class وهذا الكلاس هو الذي يتعامل تحديدا مع Smart Tags أو من خلاله نستطيع أن نضيف Smart Tags الي الكونترول الذي نصممه ومن ثم
نستطيع أن نسترجع كل الصفات التي وضعناها في هذا الكلاس ونربط بينها وبين الكونترول الذي نصممه من خلال استخدام هذه الخاصية ActionLists Property
إذن لكي نستخدم هذه الخاصية علينا أن نضيف كلاس جديد تماما الي مشروعنا وليكن اسم هذا الكلاس MyActionLists Class ثم نضيف له DesignerActionList Class عن طريق Inherits ونضيف إليه الصفات التي نريد أن نستخدمها مع الكونترول والتي تظهر في صورة Smart Tags وبعد ذلك نقوم بعمل Overrides لهذه الصفة ActionLists Property من داخل MyDesigner Class لكي نسترجع الصفات الموجودة داخل MyActionLists Class
انظر المثال المرفق الي هذا المقال أو الي المثال الموجود تحت المقال المكتوب عن تطوير الكونترول Smart Tags

SelectionRules Property

عندما نضيف أي كونترول الي أي فورم مثلا فإننا نستطيع التحكم به من خلال الماوس أي نستطيع نقله من مكان الي مكان داخل الفورم نستطيع أيضا تغيير أبعاده مثل ارتفاعه أو عرضه باستخدام الماوس وهذه الخاصية مسئولة عن ذلك أو هي التي نحدد من خلالها كيفية التحكم في حركة الكونترول وأيضا المناطق التي من خلالها يستطيع المستخدم التحكم في أبعاد للكونترول في مرحلة التصميم باستخدام الماوس وذلك بالإضافة الي متغيرات أخري وهذه المتغيرات مصنفة كالأتي
None, Movable, Visible, Locked, TopSizeable, BottomSizeable, LeftSizeable, RightSizeable, AllSizeable وكما نلاحظ أن هذه المتغيرات جميعها عبارة عن Enum
نفرض مثلا انك تريد منع المستخدم من تحريك الكونترول إذن تستطيع أن تقوم بعمل Overrides لهذه الخاصية واستخدام Locked




كود :
Public Overrides ReadOnly Property SelectionRules() As System.Windows.Forms.Design.SelectionRules
Get
Return SelectionRules.Moveable Or Windows.Forms.Design.SelectionRules.Visible
End Get
End Property
Verbs Property

ماذا تعني هذه الصفة؟ لأوضح أهمية الصفة سوف أعطي مثالا سريعا عندما نضيف الي الفورم أي كونترول أخر مثل DataGridView مثلا وكلنا يعلم أن هذا الكونترول يمتلك بعض Smart Tags بعضها عبارة عن Property وبعضها عبارة عن اختيارات أخري مثل LinkLabel أو Hyperlinks ولو نظرنا الي Properties Windows سنجد بعض هذه اللينك موجودة هناك في الأسفل ولو وقفتا بالماوس علي DataGridView ثم ضغطنا يمين الماوس سيظهر لنا أيضا ContextMenu وهنا سنجد أن بعض Smart Tags موجودة بالفعل داخل ContextMenu وعند الضغط علي أي لينك سوف يحدث حدث ما في الواقع هذه الصفة هي المسئولة عن ذلك والرائع في هذه الصفة أنها تضيف LinkLabel Smart Tags أوتوماتيكيا الي Smart Tag Panel وأيضا الي ContextMenu
كيف يمكننا أن نقوم بإضافة Verbs الي الكونترول الي نقوم بتصميمه.
أن ذلك يتم عن طريق عمل Overrides لهذه الخاصية ومن ثم نضيف متغير عبارة عن DesignerVerbCollection Class ونستخدمه لإضافة ما نريد من اللينك مع إضافة EventHandler تربط اللينك بحدث ما


كود :
Public Overrides ReadOnly Property Verbs() As System.ComponentModel.Design.DesignerVerbCollection
Get
Dim m_verbs As New DesignerVerbCollection()
m_verbs.Add(New DesignerVerb("Dock Fill", New EventHandler(AddressOf OnArabDemoDockFillEvent)))
m_verbs.Add(New DesignerVerb("Message..", New EventHandler(AddressOf OnArabDemoMessageEvent)))

Return m_verbs
End Get
End Property

' new EventHandler
Private Sub OnArabDemoDockFillEvent(ByVal sender As Object, ByVal e As EventArgs)
Me.Control.Dock = DockStyle.Fill
End Sub

' EventHandler
Private Sub OnArabDemoMessageEvent(ByVal sender As Object, ByVal e As EventArgs)
MessageBox.Show("Hello All Arabs", "Message", MessageBoxButtons.OK)
End Sub

PostFilterProperties

هل فكرت يوما أن تقوم بإلغاء أو تعديل بعض الصفات الموجودة مع الكونترول أعتقد إن بعض القراء فكروا في الأمر عموما هذا ممكن عن طريق عمل Overrides الي PostFilterProperties Sub لكن من الأفضل أن لا نلغي أي صفة Property من صفات الكونترول لأن بعض الصفات مرتبطة مع بعضها البعض عن طريق Attribute ولكن أنصحك أن تقوم بعمل Override من داخل الكونترول الذي تقوم بتصميمه مع ضبط خاصية BrowsableAttribute الي False وبذلك تضرب عصفورين بحجر أي نستفيد بأن الصفة مازالت موجودة وبالتالي ممكن للمبرمج استخدامها في أي وقت يشاء من داخل الكود فقط مع أيضا إمكانية عدم ظهورها للمستخدم في مرحلة Design وبشكل عام لو أردت عزيزي القارئ أن تقوم بإلغاء بعض الصفات نهائيا فإن ذلك يتم طبقا للكود التالي


كود :
Protected Overrides Sub PostFilterProperties(ByVal Properties As IDictionary)
Properties.Remove("BackgroundImage")
Properties.Remove("BackgroundImageLayout")
End Sub
PreFilterProperties

هذا Sub يستخدم في إضافة صفات الي الكونترول لكي يتم استخدامها في مرحلة التصميم فقط وهي مرتبطة بخاصية أخري يمكننا من خلالها من الرسم في الكونترول في مرحلة التصميم فقط وهي OnPaintAdornments والكود التالي يوضح الأمر.


كود :
Private MyDesignColor As Color = Color.Blue

Public Property DesignColor() As Color
Get
Return MyDesignColor
End Get
Set(ByVal Value As Color)
MyDesignColor = Value
End Set
End Property

Protected Overrides Sub PreFilterProperties(ByVal properties As System.Collections.IDictionary)

Dim descriptor As PropertyDescriptor = _
TypeDescriptor.CreateProperty(GetType(myDesigner), "DesignColor", _
GetType(System.Drawing.Color), _
New Attribute() {New DesignOnlyAttribute(True)})

properties.Add("DesignColor", descriptor)

End Sub
OnPaintAdornments

هذا Sub يستخدم في رسم أي شئ داخل الكونترول وهو في مرحلة Design وعند التشغيل تختفي هذه الرسومات التي هي أحيانا مجرد تحديد لأبعاد الكونترول الخارجية مثلا ممكن نلاقي مرسوم Rectangle حول الكونترول وعند اختيار الكونترول فإن اللون المرسوم به المستطيل يكون في حالة Active وعندما نترك الكونترول ونختار أي كونترول أخر موجود داخل الفورم مثلا فان هذا المربع يتحول لونه الي حالة Inactive ومع ذلك مازلنا نراه في الفورم وأيضا عند التشغيل للمشروع يختفي ه1ا المربع ولا نراه إذن الأمر واضح هذا Sub يقوم برسم أي شئ داخل الكونترول ولمن يعرف كيفية التعامل مع GDI+ فإن هذا Sub يشبه تماما Form_Paint مثلا أو Panel_Paint إذن فهو يؤدي نفس الوظيفة مع الكونترول ولكن في مرحلة Design فقط وبالتالي عند استخدام هذا Sub يجب علينا أن نتعامل مع حركة الماوس أيضا مثل OnMouseEnter و OnMouseLeave وهكذا يعني ببساطه هذا Sub يتيح لنا أن نزين ونجمل ونضيف ديكور للكونترول حتى وهو في مرحلة التصميم مع الوضع في الاعتبار أن كل ما سوف يرسم سوف يختفي تماما عند التشغيل ويظهر فقط في مرحلة التصميم المثال التالي يوضح كيفية التعامل مع الامر طبعا تستطيع عزيزي القارئ أن ترسم ما تريد طبقا لمتطلباتك وخيالك. ومن عنده فكره جيده عن GDI+ يستطيع أن يبدع في هذا الأمر لكني أنصحك عزيزي القارئ أن تجعل الرسم في مرحلة التصميم بسيط جدا بقدر الإمكان حثي لا تشتت المبرمج الذي سوف يستخدم الكونترول الخاص بك.


كود :
Private mousemotion As Boolean = False

Protected Overrides Sub OnPaintAdornments(ByVal pe As System.Windows.Forms.PaintEventArgs)

Dim OuterRect As Rectangle = New Rectangle()
OuterRect = Me.Control.ClientRectangle
Dim InnerRect As Rectangle = Rectangle.FromLTRB(OuterRect.X + 2, OuterRect.Y + 2, OuterRect.Width - 2, OuterRect.Height - 2)

If mousemotion Then
' ^_^ First Method
ControlPaint.DrawFocusRectangle(pe.Graphics, OuterRect)

Else
' ^_^ Second rect
pe.Graphics.DrawRectangle(Pens.Blue, InnerRect)

End If

End Sub

Protected Overrides Sub OnMouseLeave()
Me.mousemotion = False
Me.Control.Refresh()

End Sub

Protected Overrides Sub OnMouseEnter()

Me.mousemotion = True
Me.Control.Refresh()

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


تبقي شئ أخير وهذا الأمر تركته للنهاية لأهميته القصوى وأنا أتصور أن الكثير منكم لا يعلمون عنه شيئا لأن لا أحد يتحدث عنه مطلقا ولكي تجد بيانات عنه يجب أن تعرفه أولا وطبعا إذا كنت تجهله عزيزي القارئ فكيف ستبحث عنه.
كل المبرمجين يعلمون ما هو ClientRectangle Area وأيضا NonClientRectangle Area السؤال الأن كيف نحدد ذلك في الكونترول الذي نقوم بتصميمه؟ هذا السؤال ربما دار بعقل الكثيرين والشيء الغريب أن لا حدا يتحدث عنه علي الإطلاق وحتى لو قمت بالبحث سوف تجد إجابات غير واضحة تماما ولربما تجعلك هذه الإجابات تدخل في متاهة كبري وتؤدي بك الي أبواب مسدودة.
أكرر السؤال كيف نحدد المنطقة التي تسمي بمنطقة NonClientRectangle ؟

الإجابة علي هذا السؤال بسيطة جدا ولربما تجعلك عزيزي القارئ تفعل كما فعل أرشميدس يوما وخرج عاريا وهو يقول وجدتها Eureka .
الإجابة تكمن في كلمة واحدة وهي :الأتي

DisplayRectangle

وهذه خاصية Read-Only Property من خواص Control Class وبما أن كل شئ مشتق من الكونترول حتى الفورم مشتق من الكونترول و لكي نقوم بالتغيير في هذه Property من الأفضل أن نربطها بصفة أخري وهي ClientRectangle Property والمثال التالي يوضح كيف نقوم بعمل Overrides لهذه الصفة.


كود :
Public Overrides ReadOnly Property DisplayRectangle() As Rectangle
Get
display_rectangle.X = ClientRectangle.X + 5
display_rectangle.Y = ClientRectangle.Y + 24
display_rectangle.Width = ClientRectangle.Width - 10
display_rectangle.Height = ClientRectangle.Height - 42

Return display_rectangle

End Get
End Property
الخلاصة Conclusion

لكي تستطيع التطوير ولكي تكون Developer ناجحا عليك بالأتي:-
إبحث في أصل كل كلاس تقابله وستكتشف أن من السهل أن تكتب أي كلاس وسوف نوضح ذلك في الجزء القادم من سلسلة مقالات تطوير الكونترول الذي سوف يغطي VisualStyleRenderer Class.
استخدم الكونترول الموجودة ولا تحاول أن تبني الكونترول من الصفر وإلا ستحتاج لكتابة الكثير والكثير من الكود.
تعلم GDI+ وتعلم أن تتقنها جيدا فهي المسئولة عن كيفية الرسم داخل أي كونترول.


اتمني ان يكون الموضوع واضح وبخصوص المثال الذي وعدت به قسوق اضعه اليوم او غدا بالمنتدي

بالتوفيق

اخوكم عمر