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

Overriding WndProc

مغلق
بدأه محمد سامر أبو سلو في 19 يوليو 2007 · 7 رد · 1,116 مشاهدة · في Microsoft Visual Basic.NET
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

يرسل نظام التشغيل – الويندوز – جميع أنواع الرسائل للتطبيقات والتي تخبرها عن التغييرات في بيئة النوافذ. وهذه الرسائل تخبر النموذج بالقيام بعدة أشياء كإعادة الرسم، النقل، إعادة التحجيم، الإخفاء، التصغير، والإغلاق بالإضافة إلى الاستجابة للتغييرات في بيئة النوافذ أو القيام بأي شئ آخر متعلق بالنوافذ. ويكون لجميع تطبيقات النوافذ إجراء يتم إطلاقه للاستجابة لتلك الرسائل ويدعى هذا الإجراء في العادة WindowProc ويعالج فيجول بايزيك دوت نيت هذه الرسائل في إجراء يدعى WndProc الذي يمكنك تجاوزه لعمل أشياء مخصصة عند استقبال برنامجك لرسائل معينة.

فمثلا الكود التالي يظهر كيف يضمن البرنامج إبقاء النافذة لنفس نسب قياسات الواجهة بحيث نقوم بتجاوز الإجراء WndProc الخاص بالنموذج والبحث عن الرسالة WM_SIZING التي تستقبل محددات تتضمن الحافة التي يقوم المستخدم بجرها لتغيير حجم النموذج وتركيب من النوع Rect يعطي للنموذج مكانه وحجمه الجديدين ويبدأ الكود بتحديد التركيب Rect ثم يعلن عن إجراء تجاوز للإجراء WndProc والذي يحدد بعض الثوابت والمتغيرات الساكنة التي تحمل القيم الأساسية للنموذج ثم يحدد الإجراء WndProc نوع الرسالة التي يقوم بمعالجتها فإن كانت WM_SIZING تستخدم الوظيفة PtrToStructure لنسخ m.LParam إلى التركيب Rect الذي يتم استخدامه لحساب العرض والطول الجديدين للنموذج والنسبة بينهما. فإن كانت هذه هي المرة الأولى التي يتم تنفيذ WndProc فيها فتكون بذلك قيمة المتغير الساكن fixed_aspect_ratio مساوية للصفر وعندما يرى أن قيمته مساوية للصفر يقوم بتخزين نسبة الطول والعرض الحاليين للنموذج في ذلك المتغير ثم يقوم WndProc بتحديد فيما إذا كانت النسبة الخاصة بالنموذج مختلفة عن القيمة الأصلية فإن تم تغييرها يقرر أي بعد (طول أو عرض) سيتم حفظه فإن كان المستخدم يقوم بالسحب من إحدى الزوايا يقوم الإجراء بحساب أي البعدين (طول أو عرض) هو الأكبر ثم يقوم بحساب قيمة البعد الآخر الذي يحقق نسبة الطول للعرض الخاصة بالنموذج. وإن كان المستخدم يقوم بسحب أحد الأطراف يقوم البرنامج بالحفاظ على العرض الجديد ويقوم بحساب الارتفاع المناسب حسب النسبة وكذلك إن كان يقوم بالسحب باستخدام إحدى الحافتين العلوية أو السفلية يقوم بتثبيت الارتفاع ويقوم بحساب العرض الملائم حسب النسبة ثم يقوم البرنامج بتقرير فيما إذا كان يجب عليه نقل النموذج بحيث يحرك الطرف الذي يقوم المستخدم بسحبه فمثلا إن كان المستخدم يسحب الزاوية اليسارية السفلى فالبرنامج يغير قيم اليسار والأسفل بحيث تبقى الزاوية العلوية اليمنى ثابتة ثم يقوم WndProc باستدعاء Marshal.StructureToPtr لينسخ التركيب Rect مجددا إلى m.LParam وأخيرا يقوم WndProc باستدعاء MyBase.WndProc ليترك المجال للإجراء WndProc الأب لاستخدام القيم الجديدة لتغيير حجم النموذج. واستدعاء الإجراء WndProc الخاص بالأب هام جدا فإن لم يقم البرنامح باستدعائه لكل رسالة لم يتم معالجتها بشكل كامل فإن تلك الرسالة لن يتم معالجتها وبالنتيجة فالنافذة لن تقوم بمعالجة جميع الرسائل الخاصة بها مما ينتج عن ذلك عدة مشاكل مختلفة كعدم قدرتها على إعادة رسم نفسها أو التحريك أو إظهار القوائم أو غيرها من الأمور الخاصة بالنموذج

Imports System.Runtime.InteropServices

Public Class Form1

	Public Structure Rect
		Public left As Integer
		Public top As Integer
		Public right As Integer
		Public bottom As Integer
	End Structure

	Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
		Const WM_SIZING As Long = &H214
		Const WMSZ_LEFT As Integer = 1
		Const WMSZ_RIGHT As Integer = 2
		Const WMSZ_TOP As Integer = 3
		Const WMSZ_TOPLEFT As Integer = 4
		Const WMSZ_TOPRIGHT As Integer = 5
		Const WMSZ_BOTTOM As Integer = 6
		Const WMSZ_BOTTOMLEFT As Integer = 7
		Const WMSZ_BOTTOMRIGHT As Integer = 8
		Static fixed_aspect_ratio As Double = 0
		Dim new_aspect_ratio As Double
		If m.Msg = WM_SIZING And m.HWnd.Equals(Me.Handle) Then
			' Turn the message’s lParam into a Rect.
			Dim r As Rect
			r = DirectCast( _
			Marshal.PtrToStructure(m.LParam, GetType(Rect)), _
			Rect)
			' Get the current dimensions.
			Dim wid As Double = r.right - r.left
			Dim hgt As Double = r.bottom - r.top
			' Get the new aspect ratio.
			new_aspect_ratio = hgt / wid
			' The first time, save the form’s aspect ratio.
			If fixed_aspect_ratio = 0 Then
				fixed_aspect_ratio = new_aspect_ratio
			End If
			' See if the aspect ratio is changing.
			If fixed_aspect_ratio <> new_aspect_ratio Then
				' To decide which dimension we should preserve,
				' see what border the user is dragging.
				If m.WParam.ToInt32 = WMSZ_TOPLEFT Or _
				 m.WParam.ToInt32 = WMSZ_TOPRIGHT Or _
				 m.WParam.ToInt32 = WMSZ_BOTTOMLEFT Or _
				 m.WParam.ToInt32 = WMSZ_BOTTOMRIGHT Then

					' The user is dragging a corner.
					' Preserve the bigger dimension.
					If new_aspect_ratio > fixed_aspect_ratio Then
						' It’s too tall and thin. Make it wider.
						wid = hgt / fixed_aspect_ratio
					Else
						' It’s too short and wide. Make it taller.
						hgt = wid * fixed_aspect_ratio
					End If
				ElseIf m.WParam.ToInt32 = WMSZ_LEFT Or _
						  m.WParam.ToInt32 = WMSZ_RIGHT Then

					' The user is dragging a side.
					' Preserve the width.
					hgt = wid * fixed_aspect_ratio
				ElseIf m.WParam.ToInt32 = WMSZ_TOP Or _
						  m.WParam.ToInt32 = WMSZ_BOTTOM Then

					' The user is dragging the top or bottom.
					' Preserve the height.
					wid = hgt / fixed_aspect_ratio
				End If
				' Figure out whether to reset the top/bottom
				' and left/right.
				' See if the user is dragging the top edge.
				If m.WParam.ToInt32 = WMSZ_TOP Or _
					 m.WParam.ToInt32 = WMSZ_TOPLEFT Or _
					 m.WParam.ToInt32 = WMSZ_TOPRIGHT Then

					' Reset the top.
					r.top = r.bottom - CInt(hgt)
				Else
					' Reset the bottom.
					r.bottom = r.top + CInt(hgt)
				End If
				' See if the user is dragging the left edge.
				If m.WParam.ToInt32 = WMSZ_LEFT Or _
					 m.WParam.ToInt32 = WMSZ_TOPLEFT Or _
					 m.WParam.ToInt32 = WMSZ_BOTTOMLEFT Then

					' Reset the left.
					r.left = r.right - CInt(wid)
				Else
					' Reset the right.
					r.right = r.left + CInt(wid)
				End If
				' Update the Message object’s LParam field.
				Marshal.StructureToPtr(r, m.LParam, True)
			End If
		End If
		MyBase.WndProc(m)
	End Sub

End Class

في فيجول بايزيك 6 والنسخ السابقة له يمكن للبرنامج تثبيت إجراء WindowProc خاص لعمل نفس العمليات بصعوبة وتدعى هذه العملية بـ subclassing ويعتبر الاسم غير موفق لأن البرمجة غرضية التوجه تستخدم نفس الاسم subclassing للإشارة إلى إنشاء فئة من أخرى كما نفعل عند استخدامنا للعبارة Inherits وتكون عملية تجاوز WndProc في فيجول بايزيك دوت نيت أسهل بكثير وأكثر أمنا من الطريقة المستخدمة في فيجول بايزيك 6 كما تلاحظ في المثال ولكنك مازلت بحاجة لبعض الخدع لتحويل IntPtr المخزن في m.LParam من وإلى تراكيب مناسبة. كما يكون عليك تقرير أي من الرسائل ستقوم باعتراضها وأي من قيم m.LParam و m.WParam تأخذ وكيف يمكنك التأثير عليهم بأمان. وإحدى الطرق لتعلم هذه الرسائل هو إدراج الإجراء WndProc التالي ثم القيام بالعمل الذي تريد دراسته (تغيير حجم النموذج على سبيل المثال)

	Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
		Debug.Print(m.ToString)
		MyBase.WndProc(m)
	End Sub

والسطر التالي يبين النتيجة من أجل الرسالة WM_SIZING المرسلة إلى النموذج عندما يقوم المستخدم بتغيير حجمه وعلى الأقل يظهر اسم الرسالة WM_SIZING وقيمتها الست عشرية 0x214

msg=0x214 (WM_SIZING) hwnd=0x30b8c wparam=0x2 lparam=0x590e29c result=0x0

والبحث عن اسم الرسالة في موقع مايكروسوفت ومواقع البرمجة الأخرى يعطيك في العادة معلومات أخرى تحتاج لمعرفتها مثل معنى قيم m.WParam و m.LParam كما يجدر ملاحظة أن Form class يرث الإجراء WndProc من Control class والتي ترثه بقية التحكمات على النموذج بدورها و الذي يعني أنك تستطيع تجاوز الإجراء WndProc بأي تحكم منها وبذلك يمكنك التحكم بتصرفاتها. فمثلا ترينا قطعة الكود التالية كيف تعمل الفئة NoCtxMnuTextBox والمشتقة من التحكم TextBox بحيث يقوم الإجراء WndProc الخاص بها بتفحص الرسالة WM_CONTEXTMENU ثم يستدعي الإجراء الخاص بالفئة الأب من أجل جميع الرسائل الأخرى وبالقيام بإفشال معالجة الرسالة WM_CONTEXTMENU نمنع إظهار القائمة المنبثقة الخاصة بالتحكم والتي تضم أوامر مثل النسخ واللصق عندما تنقر بزر الماوس اليميني عليها

	Public Class NoCtxMnuTextBox
		Inherits System.Windows.Forms.TextBox
		Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
			Const WM_CONTEXTMENU As Integer = &H7B
			If m.Msg <> WM_CONTEXTMENU Then
				MyBase.WndProc(m)
			End If
		End Sub
	End Class

تم تعديل هذه المشاركة بواسطة محمد سامر أبو سلو في 21 يوليو 2007 في 01:57

عن عائشة رضي الله عنها أن النبي صلى الله عليه وسلم قال: إن الله يحب إذا عمل أحدكم عملا أن يتقنه

نعيب زماننا والعيب فينا ... وما لزماننا عيب سوانا

ونهجو ذا الزمان بغير ذنب ... ولو نطق الزمان لنا هجانا

محمد سامر أبو سلو

#2

بارك الله فيك و في كل المشرفين . الحقيقة درس صعب جداً , لا أعرف كيف يمكن استثمار هذه المعلومات ....

#3

جرب المثال عمليا وطبقه على الشرح وعندما تفهم عمل المثال هنا تماما يمكنك البحث عن الرسائل التي تهمك في مكتبة MSDN واستخدامها كما تشاء

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

فعلى سبيل المثال هناك الرسالة التي تم استخدامها لشرح الدرس WM_SIZING التي تصل للنافذة عند محاولة المستخدم تغيير حجم النافذة والرسالة WM_ACTIVATE التي ترسل للنافذة عندما يتم تفعيلها أو إلغاء تفعيلها والرسالة WM_CONTEXTMENU التي تخبر النافذة بأن المستخدم ضغط زر الفأرة الأيمن وغيرها من الرسائل المفيدة

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

على كل حال بعدها إن كان لديك أي سؤال فسأحاول الإجابة عنه قدر الإمكان وإن لم أكن موجودا أعتقد أن العديد من الإخوة في المنتدى لن يقصروا

تم تعديل هذه المشاركة بواسطة محمد سامر أبو سلو في 21 يوليو 2007 في 01:41

عن عائشة رضي الله عنها أن النبي صلى الله عليه وسلم قال: إن الله يحب إذا عمل أحدكم عملا أن يتقنه

نعيب زماننا والعيب فينا ... وما لزماننا عيب سوانا

ونهجو ذا الزمان بغير ذنب ... ولو نطق الزمان لنا هجانا

محمد سامر أبو سلو

#4

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

#5

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

موضوع مهم جدا خاصة وأن الكثير لم يفهم بعد المعنى الحقيقي للوراثة

فقط أردت أن أسأل حول جزء من الموضوع

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

اضافة فقط : قائمة الرسائل المعروفة

http://www.tek-tips.com/faqs.cfm?fid=6019

#6
DreamNet كتب:
السلام عليكم ورحمة الله وبركاته

موضوع مهم جدا خاصة وأن الكثير لم يفهم بعد المعنى الحقيقي للوراثة

فقط أردت أن أسأل حول جزء من الموضوع

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

اضافة فقط : قائمة الرسائل المعروفة

http://www.tek-tips.com/faqs.cfm?fid=6019

شكرا لك على الإضافة

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

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

تم تعديل هذه المشاركة بواسطة محمد سامر أبو سلو في 23 يوليو 2007 في 12:13

عن عائشة رضي الله عنها أن النبي صلى الله عليه وسلم قال: إن الله يحب إذا عمل أحدكم عملا أن يتقنه

نعيب زماننا والعيب فينا ... وما لزماننا عيب سوانا

ونهجو ذا الزمان بغير ذنب ... ولو نطق الزمان لنا هجانا

محمد سامر أبو سلو

#8

تطبيق حلو للفكرة

مثال كتير حلو

عن عائشة رضي الله عنها أن النبي صلى الله عليه وسلم قال: إن الله يحب إذا عمل أحدكم عملا أن يتقنه

نعيب زماننا والعيب فينا ... وما لزماننا عيب سوانا

ونهجو ذا الزمان بغير ذنب ... ولو نطق الزمان لنا هجانا

محمد سامر أبو سلو

هذا الموضوع مغلق.

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