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

الكلاس التائه بين +gdi و الويندوز فورم Form

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

الموضوع : ControlPaint الكلاس التائه بين +System.Windows.Forms & GDI

اللغة المستخدمة: الفيجوال بيسك

التطبيق: فيجوال استوديو 2005 و 2008

المستوي: التقييم متروك للقارئ

إعداد: مهندس / عمر أمين إبراهيم

------------------------------------------------------------------------------------------------

مقدمة:

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

داخل الفيجوال استوديو سمات إضافية ومن ثم نستخدمها داخل البرامج التي نقوم بإنشائها لأغراض مختلفة.

إن استخدامي لكلمة الكلاس التائه هنا ليس الهدف منها إن هذا الكلاس كان مفقودا وتم اكتشافه عن طريق احد ما ولكن السبب في اختيار هذا المعني يكمن في شئ واحد فقط أن هذا الكلاس واقعيا ينتمي الي الويندوز فورم Windows Forms ولكن عناصره وخصائصه وسماته المختلفة قطعا تنتمي كليا الي +GD.

بشكل عام ومبسط هذا الكلاس يستخدم في تصميم كل من واجهات التطبيق User Interface و أيضا في تصميم العناصر المكونة لواجهة التطبيق User Interface Elements في الويندوز فورم Windows Forms.

وأمثله علي ذلك

 رسم الباتون Button

 رسم الكومبو بوكس باتون ComboButton

 رسم CheckBox

 رسم RadioButton

 رسم ScrollButton

 رسم DrawCaptionButton

 رسم DrawGrid

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

المثال الأول:

نفتح مشروعا جديدا ونطلق عليه الاسم الذي نرغب وليكن علي سبيل المثال Control_Paint_Example_01

نضيف الي الفورم فقط Picture Box Control ونغير صفه واحده من صفاته وهي Dock ونجعلها Full أو نغير الخاصية عن كريق إضافة الكود عموما الامر متروك للقارئ ثم نضيف الكود التالي الي الفورم فيصبح الشكل النهائي للكود كالأتي.


Public Class Form1

Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint

ControlPaint.DrawCheckBox(e.Graphics, New Rectangle(10, 10, 50, 50), ButtonState.Checked)
ControlPaint.DrawCheckBox(e.Graphics, New Rectangle(70, 10, 30, 30), ButtonState.Normal)
ControlPaint.DrawCheckBox(e.Graphics, New Rectangle(110, 10, 20, 20), ButtonState.Checked)

ControlPaint.DrawButton(e.Graphics, New Rectangle(10, 80, 20, 20), ButtonState.Checked)
ControlPaint.DrawButton(e.Graphics, New Rectangle(50, 80, 20, 20), ButtonState.Flat)
ControlPaint.DrawButton(e.Graphics, New Rectangle(90, 80, 20, 20), ButtonState.Normal)
ControlPaint.DrawFocusRectangle(e.Graphics, New Rectangle(130, 80, 20, 20))

ControlPaint.DrawGrid(e.Graphics, New Rectangle(10, 120, 250, 50), New Size(5, 5), Color.Blue)
ControlPaint.DrawScrollButton(e.Graphics, New Rectangle(10, 180, 20, 20), ScrollButton.Left, ButtonState.Normal)
ControlPaint.DrawScrollButton(e.Graphics, New Rectangle(50, 180, 20, 20), ScrollButton.Max, ButtonState.Pushed)
ControlPaint.DrawScrollButton(e.Graphics, New Rectangle(90, 180, 20, 20), ScrollButton.Down, ButtonState.Pushed)

ControlPaint.DrawMenuGlyph(e.Graphics, New Rectangle(10, 220, 20, 20), MenuGlyph.Arrow)
ControlPaint.DrawMenuGlyph(e.Graphics, New Rectangle(50, 220, 20, 20), MenuGlyph.Checkmark)
ControlPaint.DrawMenuGlyph(e.Graphics, New Rectangle(90, 220, 20, 20), MenuGlyph.Arrow)
ControlPaint.DrawVisualStyleBorder(e.Graphics, New Rectangle(150, 200, 50, 50))

End Sub

End Class

عناصر الكلاس Class Members:

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

طرق الكلاس

CreateHBitmap16Bit

CreateHBitmapColorMask

Dark

CreateHBitmapTransparencyMask

DrawBorder

DarkDark

DrawBorder3D

DrawButton

DrawCaptionButton

DrawCheckBox

DrawComboButton

DrawContainerGrabHandle

DrawFocusRectangle

DrawGrabHandle

DrawGrid

DrawImageDisabled

DrawLockedFrame

DrawMenuGlyph

DrawMixedCheckBox

DrawRadioButton

DrawReversibleFrame

DrawReversibleLine

DrawScrollButton

DrawSelectionFrame

DrawSizeGrip

DrawStringDisabled

DrawVisualStyleBorder

Equals

FillReversibleRectangle

Finalize

GetHashCode

GetType

Light

LightLight

MemberwiseClone

ToString

صفات الكلاس

ContrastControlDark

و بالرغم أن الأوامر واضحة جدا لكن بعضها قد يوحي بالغموض أو عدم الوضوح أحيانا وعلي سبيل المثال CaptionButton لا يرسم باتون تحديدا ولكنه يرسم أشياء مثل Minimize, Close, Restore, Help Boxes أي باتون مثل التي نجدها دائما في أي فورم في الجزء الأيمن العلوي تحديدا وهي مفاتيح لإغلاق الفورم أو تكبيره الي أخره

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

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

 
   ControlPaint.DrawRadioButton(e.Graphics, New Rectangle(20, 20, 15, 15),	  ButtonState.Normal)

لكن يجب الوضع في الاعتبار شئ مهم جدا أن هذا الكلاس يرسم فقط الكونترول المطلوب لكن علي المبرمج طبعا إضافة أوامر أخري لكي يظهر الكونترول بالشكل المطلوب مثال علي ذلك الكلاس يرسم الباتون لكن المبرمج يجب أن يتعامل مع الكود لكي يظهر الباتون في حالاته المختلفة مثل Normal State, Pushed Stat وذلك عندما يقوم المستخدم بتمرير الماوس مثلا علي الكونترول

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

 
   ControlPaint.DrawSelectionFrame(Graphics, active, outsideRect, insideRect, backColor)

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

 
   ControlPaint.DrawSelectionFrame(Graphics, active, insideRect, outsideRect, backColor)

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

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

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

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

	Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
		   MyBase.OnPaint(e)

		   Dim theRectangle As New Rectangle(New Point(0, 0), New Size(200, 200))
		   ControlPaint.DrawReversibleFrame(theRectangle, Me.BackColor, _
			   FrameStyle.Dashed)
		   Dim p1 As New Point(0, 0)
		   Dim p2 As New Point(150, 10)
		   ControlPaint.DrawReversibleLine(p1, p2, Me.BackColor)

   End Sub

أيضا هناك ملحوظه أخيره أود فقط أن أوضحها للقارئ وما بهمنا هنا لنتحدث عنه هو كلمتان محددتان وهما Overloads وأيضا Overrides

الكلمة Overloads تستخدم مع Property أو Procedure معين وهي تسمح لنا بتخليق وتعريف Property معينه أو Procedure معين باستخدام نفس الاسم الموجود داخل أي كلاس ولكن بثوابت مختلفة.

الكلمة Overrides وهي ببساطه تستخدم عندما نريد أن تتخطي أو نهيمن علي شئ معين داخل الكلاس مثل Properties أو Procedures

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

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

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

 OnMouseEnter

 OnMouseLeave

 OnMouseUp

 OnMouseDown

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

 
   Protected Overloads Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
		   ' هنا نضييف او نكتب الكود الذي نريده
		   MyBase.OnMouseDown(e)
	   End Sub

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

تصميم الباتون Button

بخصوص إنشاء Button الامر يتم كالأتي

أولا: نفتح مشروع جديد وليكن اسمه Control_Paint_Example_01 ثم نضيف كلاس Class بأي اسم مناسب وليكن مثلا Private Class ButtonExample وذلك يتم من قائمة Project ثم استخدام Add Class…

إذن الكود سيكون كالأتي

 Private Class ButtonExample

   End class

ثانيا: نضيف للكلاس باتون Button عن طريق استخدام الوراثة Inherits ثم نضيف أيضا تعريف لحالة الباتون ButtonState

فيصيح الكود كالتالي

 Public Class ButtonExample

	   Inherits Button
   Private state As ButtonState

   End Class

بعد ذلك نقوم بعمل Overloads & Overrides لحركة الماوس وهي MouseUP & MouseDown وأيضا نقوم بعمل Overloads & Overrides ونقوم برسم الباتون في OnPaint فيصبح الكود بالشكل التالي

 Public Class ButtonExample

	   Inherits Button
	   Private state As ButtonState

	   Protected Overloads Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
		   state = ButtonState.Pushed
		   MyBase.OnMouseDown(e)
	   End Sub

	   Protected Overloads Overrides Sub OnMouseUp(ByVal e As MouseEventArgs)
		   state = ButtonState.Normal
		   MyBase.OnMouseUp(e)
	   End Sub

	   Protected Overloads Overrides Sub OnPaint(ByVal e As PaintEventArgs)
		   MyBase.OnPaint(e)
		   System.Windows.Forms.ControlPaint.DrawComboButton(e.Graphics, 0, 0, Width, Height, state)
	   End Sub


   End Class

نلاحظ أننا قد قمنا برسم الباتون في حالة OnPaint فقط وأننا قد قمنا برسم ComboButton أما في حالة MouseUp & MouseDown قمنا بتعيير حالة الباتون ButtonState فقط لا غير ألان لا يتبقي إلا أن نضيف لهذا الكلاس شئ واحد ألا وهو Sub New وذلك لكي نقوم بعمل ضبط لخاصية ٍ للكونترول SetStyle فيصبح الكود النهائي للكلاس بالشكل التالي وهنا سنلاحظ أن الكود غباره عن مجموعه من الأسطر القليلة أي أننا قد قمنا بتصميم الباتون بأسهل الطرق وبسرعة جدا

 Public Class ButtonExample

	   Inherits Button
	   Private state As ButtonState

	   Public Sub New()
		   SetStyle(ControlStyles.UserPaint, True)
		   SetStyle(ControlStyles.DoubleBuffer, True)

		   SetStyle(ControlStyles.AllPaintingInWmPaint, True)
	   End Sub

	   Protected Overloads Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
		   state = ButtonState.Pushed
		   MyBase.OnMouseDown(e)
	   End Sub

	   Protected Overloads Overrides Sub OnMouseUp(ByVal e As MouseEventArgs)
		   state = ButtonState.Normal
		   MyBase.OnMouseUp(e)
	   End Sub

	   Protected Overloads Overrides Sub OnPaint(ByVal e As PaintEventArgs)
		   MyBase.OnPaint(e)
		   System.Windows.Forms.ControlPaint.DrawComboButton(e.Graphics, 0, 0, Width, Height, state)
	   End Sub


   End Class

ألان لا يتبقي إلا إن نضيف هذا الباتون الذي قمنا بتصميمه الي الفورم لكي يعمل بشكل جيد وذلك سيتم بسهوله جدا كل ما علينا عمله أن نقوم بعمل Build للكنترول وذلك يتم من قائمة Build ثم أخر شئ لو أننا فتحنا الفورم في وضعية التصميم سنري أن الكلاس الذي قمنا بتصميمه موجود في ToolBox وكل ما علينا هو سحبه وإضافته الي الفورم ثم إضافة الكود الذي نراه مناسبا لهذا الباتون وليكن مثلا إغلاق البرنامج وذلك يتم بعمل DoubleClick علي الباتون الذي أضفناه للفورم رقم 1 وبذلك يكون الكود كالتالي

 Public Class Form1


	   Private Sub ButtonExample1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonExample1.Click

		   Application.Exit()

	   End Sub

   End Class

بالتوفيق

أخوكم عمر

تم تعديل هذه المشاركة بواسطة a_pess في 8 يونيو 2008 في 19:25

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