رأيت بعض البرامج التى تحتوى على استخدامات مختلفه للماوس و من هذه البرامج IE و Firefox و Notepad حيث دائما عندما يكون الـ scrollbars الخاص بأى منهم ظاهر و قمت بالضغط على الزر الأوسط للماوس يظهر لك ايقونه فى الوسط و تتغير ايقونه الماوس تبعا للإتجاه الذى يذهب إليه الماوس و بالطبع يتم تحريك الـ scrollbar فى اتجاه الماوس.
الصور التاليه توضح ما اريد قوله
منذ فتره طويله كنت دائم الدخول على موقع vbacelerator لما يحتوى على اداوت ممتازه بالـ VB6 و كذلك الـ C# و من ضمن هذه الأداوت كانت توجد فئه MouseGestureFilter و التى كانت تتيح لك تحريك الماوس بأى شكل اثناء ضغطك على الزر الأيمن و بعد تركه تقوم الفئه بإخبارك فى اتجاه قام الموس بالتحرك.
ستجد فى المرفقات مثال vbAccelerator.
بالطبع انا لم افتح الموضوع لأعرض هذه الأداه و لكنها كانت مصدر إلهامى لهذا الموضوع.
تحتوى هذه الفئه على بعض القصور (تعتمد على استخدامك لها) فهى اولا لا تعطيك الإحداثى الحالى لمؤشر الماوس بدئا من نقطة الصفر (التى قمت بالضغط منها على الماوس)، ايضا لا تتيح لك معرفة اتجاهك الحالى من نقطة البدايه و لا تتيح لك تغيير ايقونة الماوس حسب موقعه من نقطة البداية او وضع ايقونه مكان نقطة البدايه لمعرفة مكانها و معرفة الإتجاه المتاح تحريك الماوس به.
طبعا هذه الأمور تعقيكلة تطباولة تطبيقالموجوه الموجوده بالمذكوج المذكوره بالأعلى و من هنا جائت الفئه موضع هذا الدرس.
الفئه التى اتحدث عنها هنا اسمها MouseGesture و قبل التكلم عنها لابد ان اعطيك فكرة عامه لكيفية عملها.
تعتمد هذه الفئه بشكل كامل على الرسالة WM_MOUSEMOVE او كما هو معروف لمبرمجى الدوت نت الحدث MouseMove.
كل ما افعله هنا هو معرفة موقع الماوس بإستمرار و اقارنه بنقطة البدايه الرئيسيه و منها ابدء بحساب اتجاه التحرك هل هو لأعلى ام لأسفل ام لليمين ام لليسار ام لأعلى اليمين ام لأعلى اليسار ام لأسفل اليمين ام لأسفل اليسار.
فكرت بدلا من استخدام الأسماء اعلى و افل و يمين و يسار استخدام الكلمات الشرق و الغرب و الشمال و الجنوب و لكنى لم اشرع بإرتياح تجاههم لذا فقد استخدمت اعلى و اسفل ....
الأن عرفنا اتجاهات تحرك الماوس و لكن قبل معرفة الإتجاه الذى يتحرك به الماوس هل دائما ما ستريد انت كمبرمج أستخدام اتجاهات الماوس كلها، بمعنى هل ستريد استخدام الاعلى و الأسفل و اليمين و اليسار و اتجاهاتهم الفرعيه؟
بالطبع لا، فإحيانا ما تريد معرفة ما إذا كان الماوس يتحرك لأعلى ام لأسفل فقط او تريد معرفة هل يتحرك لليمين ام لليسار فقط او تريد معرفة مكان تحركه فى كل الإتجاهات، و لهذا و قبل ان تبدأ الفئه فى عملها اطلب منك تحديد نوع الإتجاه الذى و بناءاً عليه سيتم تحديد موقع الماوس بالنسبه لمركز التحرك.
و لهذا فقط صنعت هذا الـ Enum و الذى يمثل الإتجاه الذى سيتم معرفة معرفة موقع الماوس بالنسبه له.
Public Enum MouseGestureDirection All LeftRight UpDown End Enum
بناءا على اتجاه الماوس الذى ستختاره سيتم العوده بموقع الماوس بمعنى الـ Enum السابق يحدد نوع القيمه العائده من هذا الـ Enum التالى
Public Enum MouseGestureTypes None = &H1 Left = &H2 Right = &H4 Up = &H8 Down = &H10 UpLeft = &H20 UpRight = &H40 DownLeft = &H80 DownRight = &H100 End Enum
فإذا اخترت الإتجاه المتاح للماوس All سيتم العوده بأى قيمه من قيم الـ Enum السابق، أما إذا اخترت UpDown فكما هو واضح من المسمى، القيمة المعاده ستكون إما Up او Down بالطبع نفس الكلام ينطبق على LeftRight.
بعد ان تبدأ الفئه فى عملها بالطبع ستحتاج لمعرفة بعض المعلومات و ذلك يتوفر من استخدام الأحداث لذلك قمت بعمل الفئه MouseGestureEventArgs و التى تحتوى على بعض الخصائص (للقراءه فقط) و هذا هو شكلها (بدون كود).
Public Class MouseGestureEventArgs Inherits EventArgs Public ReadOnly Property StartPoint() As Point Public ReadOnly Property EndPoint() As Point Public ReadOnly Property RelativeEndPoint() As Point Public ReadOnly Property GestureType() As MouseGestureTypes Public ReadOnly Property GestureDirection() As MouseGestureDirection Public Sub New(ByVal sp As Point, ByVal ep As Point, ByVal gt As MouseGestureTypes, ByVal gd As MouseGestureDirection) End Class
الخاصيه StartPoint تحتوى على موقع نقطة البدء داخل إحداثيات الشاشه و الخاصيه EndPoint تحتوى على الموقع الحالى للماوس داخل إحداثيات الشاشه RelativeEndPoint و تحتوى على الفرق بين نقطة البدء و الموقع الحالى للماوس و الخاصيه GestureDirection تحتوى على نوع التحرك المتاح للماوس و الخاصيه GestureType تحتوى على اتجاه الماوس بالنسبه لنقطة البدء و لقيمة GestureDirection.
نأتى الأن لكود الفئه، عندما بدأت فى برمجتها و اجهتنى مشكله ألا و هى انى سأحتاج للتعامل مع رسائل النظام المرسله للأداه التى ستعمل عليها هذه الفئه و هذا النوع الأكواد له اماكن محدده جدا ليكتب بها منها الداله WndProc.
و حيث ان الداله WndProc هى خاصه بالاداه لذا وجب عليك استدعاء دالة قمت بعملها بهذه الفئه من داخل WndProc .... انا بضحك معاك هذا لن يحدث.
البديل هو تضمين الواجهه IMessageFilter و التى ستقوم بإضافة الداله PreFilterMessage و التى يقوم نظام التشغيل بتمرير الرسائل لها قبل النافذه التى تحتوى على الفئه التى تتضمن هذه الواجهه.
بعد اتن تقوم بكتابة الكود الخاص بك داخل الداله PreFilterMessage لابد من الداله ان تعود بـ True او False، اجعل الداله تعود بـ True إذا اردت منع النافذه من استقبال الرسائل التى قمت بمعالجتها او False لتتيح تمرير الرساله للنافذه.
بالطبع لا تعتقد انك بمجرد انشائك لنسخه من الفئه انك بهذا فعلت الـ IMessageFilter الموجوده بداخل الفئه و لتقوم بتفعيلها لابد من استدعاء الداله AddMessageFilter و تمرر لها Me (و هى الفئه المتضمنه للواجهه) هذه الداله موجوده بالفئه Application، و لا تنسى بعد الإنتهاء ان تستدعى الداله RemoveMessageFilter.
تذكر دائما مع كل مره تضيف فيها IMessageFilter للـ MessagePump الخاصه بالبرنامج فهذا يقلل من ادائه فلا تنسى ابدا من حذف هذه الواجهه بعد انتهاء عملها.
انتينا من شرح الأفكار و يأتى دور الكود الرئيسى.
Public Delegate Sub MouseGestureEventHandler(ByVal sender As Object, ByVal e As MouseGestureEventArgs) Public Class MouseGesture Implements IMessageFilter #Region "Event Declaration" Public Event MouseGesture As MouseGestureEventHandler #End Region #Region "Private Fields" Private mIsRunning As Boolean Private mDirection As MouseGestureDirection Private mMainPoint As Point Private mCurrentPosition As Point Private Const DEFAULT_MOVE As Integer = 20 #End Region #Region "Native Win32API" Private Const WM_MOUSEMOVE As Integer = &H200 #End Region #Region "Public Properties" Public Property GestureDirection() As MouseGestureDirection Public ReadOnly Property IsRunning() As Boolean Public ReadOnly Property SourcePoint() As Point #End Region Private Function PreFilterMessage(ByRef m As Message) As Boolean Implements IMessageFilter.PreFilterMessage Public Sub Start() Public Sub [Stop]() Private Function GetGestureType() As MouseGestureTypes End Class
سأقوم بكتابة الكود لكل داله اقوم بشرحها فى وقتهاو ذلك جتى لا اجعل المشاركه طويله جدا.
فى البدايه قمت بتعريف MouseGestureEventHandler ليمثل بصمة الحدث الذى سيتم استدعائه عندما يتحرك الماوس، بعد ذلك قمت بتعريف الحدث MouseGesture و جعلت نوعه MouseGestureEventHandler.
بعد ذلك تم تعريف المتغيرات mIsRunning و الذى أعرف من خلاله هل يتم الأن تعقب حركة الماوس ام لا، و mDirection هو متغير خاص بالخاصيه GestureDirection و التى تحدد%ل27لإتجاه الخاص بالماوس، و mMainPoint و هو موقع الماوس وقت بدء التعقب، و mCurrentPosition هو الموقع الحالى للماوس.
كما ترى من القائمه MouseGestureTypes يوجد العنصر None و العناصر Up و Down و Left و Right، السؤال الأن كيف يمكن تحديد ان اتجاه الماوس الأن هو احداهما ؟؟
قد تقول بعد طرح mMainPoint من mCurrentPosition عندما يكون المحور X يساوى صفر و المحور Y يساوى صفر فذلك يعنى اننا فى مركز التحرك أى None و عند زيادة X و Y بصفر فهاذا يعنى اننا نتجه لليمين أى Right.
تقنيا كلامك صحيح و لكن فعليا ليحدث هذا يحتاج من يستخدم هذه الفئه إلى مسطره ليحرك بها الماوس و هذا ليكون X او Y او كلاهما بصفر و ذلك حتى يحصل على القيمه None او اى من الإتجاهات الأصليه. بالطبع هذا الوضع غير مقبول لذا سنحتاج لعمل تعديل و هو ازاحة نقطة البدء لكل من الإتجاهات الأربعه بعيدا عن نقطة الأصل (مركز التحرك للماوس) و ذلك بمقدار ثابت.
كما يقال رب صورة خير من الف كلمه
بالنظر إلى الصوره السابقه و الأخذ فى الإعتبار انها تمثل الـ Form نجد مايلى:
- سيتم اعتبار حالة الماوس UpLeft ، UpRight ، DownLeft ، DownRight فقط إذا كان موقع الماوس الحالى داخل المنطقه البرتقاليه.
- سيتم اعتبار حالة الماوس Up ، Down ، Left ، Right فقط إذا كان موقع الماوس الحالى داخل المنطقه البيضاء.
- سيتم اعتبار حالة الماوس None فقط إذا وقع الماوس داخل المنطقه الزرقاء.
و لكن السؤال هو كيفية حدوث ذلك؟؟
الإجابه بسيطه سنحدد إزاحة معينه يتم بناءا عليها حساب موقعه و هذه هى وظيفة الثابت DEFAULT_MOVE (سيتم التفصيل فيما يلى).
إذا افترضنا ان نقطة التحرك للماوس كانت X=12 ، Y=20 فإن:
يعتبر حالة الماوس None إذا كان الموقع الحالى X >= 12 و X <= 12 + DEFAULT_MOVE و Y >= 20 و Y <= 20 + DEFAULT_MOVE.
يعتبر حالة الماوس Right إذا كان الموقع الحالى X >12 + DEFAULT_MOVE و Y >= 20 و Y <= 20 + DEFAULT_MOVE.
يعتبر حالة الماوس DownRight إذا كان الموقع الحالى X >12 + DEFAULT_MOVE و Y > 20 + DEFAULT_MOVE.
أعتقد ان الفكره وضحت الأن و لكن توجد نقطه هامه لابد من تذكيرك بها و هى اذا نظرت فى الصوره السابقه ستجد ان المحور المقابل للكلمه Up هو Negative Y اى ان كلما ارتفعنا إلى اعلى قلت قيمة Y و هذا صحيح لاننا نعمل داخل احداثيات الشاشه و بها لا وجود للقيم السالبه ايضا قيمة X تزيد لليمين و قيمة Y تزيد
داخ.
داخل الفئه تم تعريف الثابت WM_MOUSEMOVE و هو احد رسائل النظام و تعبر عن ان الماوس يتحرك.
بعد ذلك قمت بتعريف الخصائص و هم GestureDirection و بها تحدد اتجاه حركة الماوس و IsRunninاصيه ل خاصيه للقراءه فقط و تعود لك بـ True إذا كانت الفئه تعمل و SourcePoint و تعود لك بمركز الحركه للماوس.
(سنتخطى الداله PreFilterMessage و سنعود لها لاحقا).
بعد ذلك تم تعريف الداله Start و التى تقوم ببدء عملية التحقق من موقع الموس او Mouse Gesturing و شكلها كالتالى
Public Sub Start() mIsRunning = True mMainPoint = Cursor.Position Application.AddMessageFilter(Me) End Sub
كما ترى فى بداية الداله نقوم بتفعيل المتغير mIsRunning بعدها نحدد نقطة البدء لحركة الماوس و بعدها نقوم بإضافة هذه الفئه للـ Message pump الخاص بالبرنامج.
ايضا الداله Stop تفعل عكس ما تفعله الداله Start فهى تقوم بإيقاف عمل الفئه و شكلها كالتالى
Public Sub [Stop]() mIsRunning = False Application.RemoveMessageFilter(Me) End Sub
لا يحتاج الكود للشرح حيث انه واضح كفايه، نقطه واحده اريد لفت انتباهك لها الكلمه Stop هى كلمه محجوزه للـ VB.Net لذا لا يمكن استخدامها كأسم لداله او متغير او اى شئ و حتى اقوم بإستخدامها يتيح لنا الـ VB استخدام الـ Brackets [] حول الكلمه ليتم اعتبارها متغير او داله او اى شئ اخر خلاف الكلمه المحجوزه، حقيقه هذا الإسلوب سئ و لكن كما ترى لا يوجد بديل للكلمه Stop كأسم لهذه الداله.
نأتى الأن للداله قبل الأخيره و هى GetGestureType و هى المسئوله عن معرفة الإتجاه الحالى للماوس و تقوم بالرجوع به
Private Function GetGestureType() As MouseGestureTypes Dim east, west, north, south As Boolean Dim x, y As Integer x = mCurrentPosition.X - mMainPoint.X y = mCurrentPosition.Y - mMainPoint.Y Dim x_abs, y_abs As Integer Dim x_in_range, y_in_range As Boolean x_abs = Math.Abs(x) y_abs = Math.Abs(y) If x_abs >= 0 AndAlso x_abs <= DEFAULT_MOVE Then x_in_range = True If y_abs >= 0 AndAlso y_abs <= DEFAULT_MOVE Then y_in_range = True If x_in_range AndAlso y_in_range Then Return MouseGestureTypes.None ElseIf x_in_range = True Then If mDirection = MouseGestureDirection.LeftRight Then If x > 0 Then Return MouseGestureTypes.Right Else Return MouseGestureTypes.Left End If ElseIf mDirection = MouseGestureDirection.All Then If y > 0 Then Return MouseGestureTypes.Down Else Return MouseGestureTypes.Up End If End If ElseIf y_in_range = True Then If mDirection = MouseGestureDirection.UpDown Then If y > 0 Then Return MouseGestureTypes.Down Else Return MouseGestureTypes.Up End If ElseIf mDirection = MouseGestureDirection.All Then If x > 0 Then Return MouseGestureTypes.Right Else Return MouseGestureTypes.Left End If End If End If east = x > 0 west = Not east north = y < 0 south = Not north If mDirection = MouseGestureDirection.All Then If north And east Then Return MouseGestureTypes.UpRight If north And west Then Return MouseGestureTypes.UpLeft If south And east Then Return MouseGestureTypes.DownRight If south And west Then Return MouseGestureTypes.DownLeft End If If mDirection = MouseGestureDirection.UpDown OrElse mDirection = MouseGestureDirection.All Then If north Then Return MouseGestureTypes.Up If south Then Return MouseGestureTypes.Down End If If mDirection = MouseGestureDirection.LeftRight OrElse mDirection = MouseGestureDirection.All Then If east Then Return MouseGestureTypes.Right If west Then Return MouseGestureTypes.Left End If Return MouseGestureTypes.None End Function
كود طويل و لكن كل ما يفعله هو التحقق من قيمة X و Y و من خلالهما يتم تحديد اتجاه الماوس، كما انه تم شرح فكرة هذا الكود من قبل.
الأن نأتى لأخر داله فى هذه الفئه و هى PreFilterMessage
Private Function PreFilterMessage(ByRef m As Message) As Boolean Implements IMessageFilter.PreFilterMessage If mIsRunning Then Select Case m.Msg Case WM_MOUSEMOVE mCurrentPosition = Cursor.Position RaiseEvent MouseGesture(Me, New MouseGestureEventArgs(mMainPoint, mCurrentPosition, GetGestureType, mDirection)) End Select End If Return False End Function
كما ترى يتم تنفيذ الكود فى هذه الداله فقط فى حالة ان mIsRunning تساوى true (فى الواقع يمكنك حذف هذا الكود لإن هذه الداله لن يتم استدعائها ابدا إلا فى حالة ان الفئه تعمل اى تم استدعاء الداله Start)، بعدها يتم التحقق من نوع الرساله فإن كانت هى WM_MOUSEMOVE يتم تسجيل الموقع الحالى للماوس داخل المتغير mCurrentPosition و بعدها يتم تفعيل الحدث MouseGesture و يتم تمرير له المعاملات التى يحتاجها.
داخل المرفقات و فى المشروع الخاص بهذا الدرس (بالطبع ليس الخاص بـ vbAccelerator) ستجد ان المشروع به شاشتين احداهما اسمها frmMouseGesture و هى التى تحتوى على مثال تطبيقى لهذه الفئه فقط بعد تشغيل البرنامج قم بجعلها هى الـ Startup From و هذا هو شكلها:
هذا هو مثال vbAccelerator
و هذا هو مثال هذا الدرس (باقى شرح المثال سأضعه فى هذا الدرس) (استخدم الزر الأيسر لتفعيل الفئه و ايضا هو نفسه للإيقاف)
و الله ولى التوفيق
