Skip to content

6. معالجة الأحداث

في الفصل السابق، ناقشنا مفهوم الأحداث المرتبطة بالمكونات. سنرى الآن كيفية إنشاء الأحداث في فئاتنا الخاصة. هذا مفهوم صعب لن يثير اهتمام المطورين المبتدئين، الذين يُنصحون بالانتقال مباشرة إلى الفصل التالي.

6.1. كائنات التفويض

العبارة

    Delegate Function opération(ByVal n1 As Integer, ByVal n2 As Integer) As Integer

يحدد نوعًا يسمى operation، وهو في الواقع نموذج أولي لدالة تقبل عددين صحيحين وتُرجع عددًا صحيحًا. إن الكلمة الرئيسية delegate هي التي تجعل من operation تعريفًا لنموذج أولي لدالة. يمكن تعيين النوع [operation] إلى متغير على النحو التالي:


        Dim op As New opération(AddressOf [fonction])

يتم إنشاء متغير من النوع [delegate] ككائن. معلمة المنشئ هي الدالة المرتبطة بمثيل delegate. في VB، يتم تحديد هذه الدالة على أنها AddressOf function، حيث [function] هو اسم الدالة. بمجرد إنشاء مثيل delegate، يمكن استخدامه كدالة:


        ' the delegate is executed
        Dim n As Integer = op(4, 7)

تم تعريف الوفد [operation] ليأخذ معلمتين من النوع [integer] ويعيد نتيجة من نفس النوع. لذلك، يتم استخدام مثيله [op] أعلاه كدالة من هذا النوع. انظر المثال التالي:


' delegated functions
Imports System

Public Class delegate1
    ' function prototype definition
    ' accepts 2 integers as parameters and returns an integer
    Delegate Function opération(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
 
    ' two instance methods corresponding to the prototype
    ' add
    Public Function ajouter(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
        Console.Out.WriteLine(("ajouter(" & n1 & "," & n2 & ")"))
        Return n1 + n2
    End Function
 
    ' subtract
    Public Function soustraire(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
        Console.Out.WriteLine(("soustraire(" & n1 & "," & n2 & ")"))
        Return n1 - n2
    End Function
 
    ' a static method corresponding to the prototype
    Public Shared Function augmenter(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
        Console.Out.WriteLine(("augmenter(" & n1 & "," & n2 & ")"))
        Return n1 + 2 * n2
    End Function
 
    ' test program
    Public Shared Sub Main()
        ' define an operation object to store functions
        ' we register the static function increase
        Dim op As New opération(AddressOf delegate1.augmenter)
        ' the delegate is executed
        Dim n As Integer = op(4, 7)
        Console.Out.WriteLine(("n=" & n))
        ' creation of a c1 object of type class1
        Dim c1 As New delegate1
        ' we register c1's add method in the delegate
        op = New delegate1.opération(AddressOf c1.ajouter)
        ' execution of delegated object
        n = op(2, 3)
        Console.Out.WriteLine(("n=" & n))
        ' we register the subtract method of c1 in the delegate
        op = New delegate1.opération(AddressOf c1.soustraire)
        n = op(2, 3)
        Console.Out.WriteLine(("n=" & n))
    End Sub
End Class

نتائج التنفيذ هي كما يلي:

augmenter(4,7)
n=18
ajouter(2,3)
n=5
soustraire(2,3)
n=-1

6.2. معالجة الأحداث

ما هي الاستخدامات الممكنة لكائن الوكيل؟

وكما سنرى في المثال التالي، يُستخدم هذا بشكل أساسي لمعالجة الأحداث. يمكن لفئة C1 إنشاء أحداث evti. وعندما يحدث حدث evti، يقوم كائن من النوع C1 بتشغيل كائن evtiDéclenché من النوع delegate. سيتم بعد ذلك تنفيذ جميع الوظائف المسجلة في كائن الوكيل evtiDéclenché. إذا أراد كائن C2 يستخدم كائن C1 أن يتم إخطاره بحدوث حدث evti على الكائن C1، فسيقوم بتسجيل إحدى طرقه، C2.f، مع كائن الوكيل C1.evtiDéclenché الخاص بالكائن C1 بحيث يتم تنفيذ الطريقة C2.f في كل مرة يحدث فيها حدث evti على الكائن C1. نظرًا لأن كائن الوكيل C1.evtiDéclenché يمكنه تسجيل وظائف متعددة، يمكن لكائنات Ci المختلفة التسجيل مع الوكيل C1.evtiDéclenché لتلقي إشعار بحدوث الحدث evti على C1.

6.2.1. إعلان حدث

يتم إعلان الحدث على النحو التالي (1):

Delegate Sub proc(liste d'arguments)
Public Event événement as proc

أو (2)

Public Event événement(liste d'arguments)

تُعرّف الكلمة الرئيسية [Event] جزءًا من البيانات على أنه حدث. ويرتبط الحدث بالتوقيع الذي يجب أن تمتلكه معالجاته. ويمكن تعريف هذا التوقيع باستخدام مندوب (الطريقة 1) أو بشكل مباشر (الطريقة 2).

6.2.2. تحديد معالجات الأحداث

عندما يتم تشغيل حدث بواسطة عبارة [RaiseEvent]، يجب معالجته. تسمى الإجراءات المسؤولة عن معالجة الأحداث بمعالجات الأحداث. عند إنشاء كائن [Event]، يتم إعلان توقيع معالجات الأحداث فقط. هذا التوقيع هو توقيع كائن [delegate] المرتبط بالحدث. بمجرد الانتهاء من ذلك، يمكن ربط المعالجات بالحدث. هذه إجراءات يجب أن يكون لها نفس التوقيع مثل [delegate] المرتبط بالحدث. لربط معالج حدث بحدث، استخدم عبارة [AddHandler] بالصيغة التالية:

AddHandler event, AddressOf eventhandler
  • event: متغير الحدث الذي يتم ربط معالج الحدث الجديد به
  • معالج الحدث: اسم معالج الحدث — يجب أن يكون له نفس التوقيع مثل [المندوب] المرتبط بالحدث

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

6.2.3. تشغيل حدث

لتشغيل حدث برمجياً، استخدم عبارة [RaiseEvent]:

RaiseEvent eventname[( argumentlist )]
  • eventname: متغير الحدث (المعلن باستخدام الكلمة الرئيسية Event)
  • قائمة_الحجج: حجج المندوب المرتبط بالحدث.

سيتم استدعاء جميع المعالجات التي تم ربطها بالحدث باستخدام عبارة AddHandler. وستتلقى كمعلمات المعلمات المحددة في توقيع الحدث.

6.2.4. مثال

لنأخذ المثال التالي:


'event management
Imports System
 
Public Class myEventArgs
    Inherits EventArgs
    ' the class of an evt
    ' attribute
    Private _saisie As String
 
    ' manufacturer
    Public Sub New(ByVal saisie As String)
        _saisie = saisie
    End Sub
 
    ' read-only property
    Public Overrides Function ToString() As String
        Return _saisie
    End Function
End Class
 
Public Class émetteur
    ' the class issuing an event
    ' attribute
    Private _nom As String    ' name of issuer
 
    ' manufacturer
    Public Sub New(ByVal nom As String)
        _nom = nom
    End Sub
 
    ' ToString
    Public Overrides Function ToString() As String
        Return _nom
    End Function
 
    ' the prototype of the functions responsible for processing the event
    Delegate Sub _evtHandler(ByVal sender As Object, ByVal evt As myEventArgs)
 
    ' pool of event managers
    Public Event evtHandler As _evtHandler
 
    ' method of requesting the issue of an evt
    Public Sub envoyerEvt(ByVal evt As myEventArgs)
        ' all subscribers are notified
        ' we act as if the event originated here
        RaiseEvent evtHandler(Me, evt)
    End Sub
End Class
 
' an evt treatment class
Public Class souscripteur
    ' attribute
    Private nom As String    ' name of subscriber
    Private sender As émetteur    ' evts sender
 
    ' manufacturer
    Public Sub New(ByVal nom As String, ByVal e As émetteur)
        ' we note the name of the subscriber
        Me.nom = nom
        ' and the sender of the evts
        Me.sender = e
        ' register to receive news from the transmitter e
        AddHandler e.evtHandler, AddressOf traitementEvt
    End Sub
 
    ' event manager
    Public Sub traitementEvt(ByVal sender As Object, ByVal evt As myEventArgs)
        ' evt display
        Console.Out.WriteLine(("L'objet [" + sender.ToString + "] a signalé la saisie erronée [" + evt.ToString + "] au souscripteur [" + nom + "]"))
    End Sub
End Class
 
' a test program
Public Class test
    Public Shared Sub Main()
        ' creation of an evts transmitter
        Dim émetteur1 As New émetteur("émetteur1")
        ' create a table of subscribers
        ' for events issued by issuer1
        Dim souscripteurs() As souscripteur = {New souscripteur("s1", émetteur1), New souscripteur("s2", émetteur1)}
        ' read a sequence of integers from the keyboard
        ' as soon as one is wrong, we issue an evt
        Console.Out.Write("Nombre entier (rien pour arrêter) : ")
        Dim saisie As String = Console.In.ReadLine().Trim()
        ' as long as the line entered is not empty
        While saisie <> ""
            ' is the input a whole number?
            Try
                Dim n As Integer = Integer.Parse(saisie)
            Catch
                ' not an integer
                ' we warn everyone
                émetteur1.envoyerEvt(New myEventArgs(saisie))
            End Try
            ' we warn everyone
            ' new entry
            Console.Out.Write("Nombre entier (rien pour arrêter) : ")
            saisie = Console.In.ReadLine().Trim()
        End While
        ' end
        Environment.Exit(0)
    End Sub
End Class

الرمز أعلاه معقد إلى حد ما. دعونا نحلله. في معالجة الأحداث، يوجد مرسل للأحداث يقوم بإرسال تفاصيل الحدث (EventArgs) إلى المشتركين الذين أبدوا اهتمامًا بالأحداث المعنية. فئة إرسال الأحداث هنا هي كما يلي:


Public Class émetteur
    ' the class issuing an event
    ' attribute
    Private _nom As String    ' name of issuer
 
    ' manufacturer
    Public Sub New(ByVal nom As String)
        _nom = nom
    End Sub
 
    ' ToString
    Public Overrides Function ToString() As String
        Return _nom
    End Function
 
    ' the prototype of the functions responsible for processing the event
    Delegate Sub _evtHandler(ByVal sender As Object, ByVal evt As myEventArgs)
 
    ' pool of event managers
    Public Event evtHandler As _evtHandler
 
    ' method of requesting the issue of an evt
    Public Sub envoyerEvt(ByVal evt As myEventArgs)
        ' all subscribers are notified
        ' we act as if the event originated here
        RaiseEvent evtHandler(Me, evt)
    End Sub
End Class

يحتوي كل كائن باعث على اسم محدد افتراضيًا. تمت إعادة تعريف طريقة ToString بحيث يتم إرجاع اسم كائن الباعث عند تحويله إلى سلسلة. تحدد الفئة حدثًا:


        ' the prototype of the functions responsible for processing the event
        Delegate Sub _evtHandler(ByVal sender As Object, ByVal evt As myEventArgs)
 
        ' the event
        Public Event evtHandler As _evtHandler

ستكون الحجة الأولى لدالة معالج الحدث هي الكائن الذي يصدر الحدث. وستكون الحجة الثانية من النوع myEventArgs، وهو كائن يوفر تفاصيل حول الحدث، وسنناقشه لاحقًا. لتشغيل حدث من خارج كائن المصدر، نضيف طريقة envoyerEvt إلى الفئة:


    ' method of requesting the issue of an evt
    Public Sub envoyerEvt(ByVal evt As myEventArgs)
        ' all subscribers are notified
        ' we act as if the event originated here
        RaiseEvent evtHandler(Me, evt)
    End Sub

نحتاج إلى تحديد نوع الأحداث التي تطلقها فئة emitter. للقيام بذلك، قمنا بتعريف فئة myEventArgs:


Public Class myEventArgs
    ' the class of an evt
    ' attribute
    Private _saisie As String
 
    ' manufacturer
    Public Sub New(ByVal saisie As String)
        _saisie = saisie
    End Sub
 
    ' read-only property
    Public Overrides Function ToString() As String
        Return _saisie
    End Function
End Class

الأحداث التي تهمنا هي المدخلات الخاطئة من لوحة المفاتيح. سنطلب من المستخدم كتابة أعداد صحيحة على لوحة المفاتيح، وبمجرد أن يكتب سلسلة لا تمثل عددًا صحيحًا، سنقوم بتشغيل حدث. وكتفاصيل للحدث، سنقدم ببساطة المدخلة الخاطئة. وهذا هو الغرض من السمة _input الخاصة بالفئة. وبالتالي، يتم إنشاء كائن myEventArgs مع المدخلة الخاطئة كمعلمة له. كما نقوم بتجاوز طريقة ToString بحيث عندما نقوم بتحويل كائن myEventArgs إلى سلسلة، نحصل على الخاصية _input.

لقد حددنا مصدر الحدث ونوع الأحداث التي يصدرها. بعد ذلك، نحدد نوع المشتركين المهتمين بهذه الأحداث.


    ' an evt treatment class
    Public Class souscripteur
        ' attribute
        Private nom As String     ' name of subscriber
        Private sender As émetteur     ' evts sender
 
        ' manufacturer
        Public Sub New(ByVal nom As String, ByVal e As émetteur)
            ' we note the name of the subscriber
            Me.nom = nom
            ' and the sender of the evts
            Me.sender = e
            ' register to receive news from the transmitter e
            AddHandler e.evtHandler, AddressOf traitementEvt
        End Sub
 
        ' event manager
        Public Sub traitementEvt(ByVal sender As Object, ByVal evt As myEventArgs)
            ' evt display
            Console.Out.WriteLine(("L'objet [" + sender.ToString + "] a signalé la saisie erronée [" + evt.ToString + "] au souscripteur [" + nom + "]"))
        End Sub
    End Class

يتم تعريف المشترك بواسطة معلمتين: اسمه (سمة name) وكائن الباعث الذي يريد معالجة أحداثه (سمة sender). يتم تمرير هاتين المعلمتين إلى منشئ الكائن. وأثناء عملية الإنشاء هذه، يشترك المشترك في أحداث الباعث:


            ' on s'inscrit pour recevoir les evts de l'émetteur e
            AddHandler e.evtHandler, AddressOf traitementEvt

الوظيفة المسجلة لدى الناشر هي traitementEvt. تعرض هذه الطريقة لفئة المشترك الحجتين اللتين تلقتهما (sender, evt) بالإضافة إلى اسم المستقبل (nom). تم تعريف أنواع الأحداث المنتجة، ونوع ناشر هذه الأحداث، وأنواع المشتركين. كل ما تبقى هو تنفيذها:


    ' a test program
    Public Class test
        Public Shared Sub Main()
            ' creation of an evts transmitter
            Dim émetteur1 As New émetteur("émetteur1")
            ' create a table of subscribers
            ' for events issued by issuer1
            Dim souscripteurs() As souscripteur = {New souscripteur("s1", émetteur1), New souscripteur("s2", émetteur1)}
            ' read a sequence of integers from the keyboard
            ' as soon as one is wrong, we issue an evt
            Console.Out.Write("Nombre entier (rien pour arrêter) : ")
            Dim saisie As String = Console.In.ReadLine().Trim()
            ' as long as the line entered is not empty
            While saisie <> ""
                ' is the input a whole number?
                Try
                    Dim n As Integer = Integer.Parse(saisie)
                Catch
                    ' not an integer
                    ' we warn everyone
                    émetteur1.envoyerEvt(New myEventArgs(saisie))
                End Try
                ' we warn everyone
                ' new entry
                Console.Out.Write("Nombre entier (rien pour arrêter) : ")
                saisie = Console.In.ReadLine().Trim()
            End While
            ' end
            Environment.Exit(0)
        End Sub

نقوم بإنشاء كائن مُصدر أحداث:


        ' creation of an evts transmitter
        Dim émetteur1 As New émetteur("émetteur1")

نقوم بإنشاء مصفوفة من مشتركين اثنين للأحداث التي يصدرها الكائن emetteur1:


        ' create a table of subscribers
        ' for events issued by issuer1
        Dim souscripteurs() As souscripteur = {New souscripteur("s1", émetteur1), New souscripteur("s2", émetteur1)}

نطلب من المستخدم إدخال أعداد صحيحة عبر لوحة المفاتيح. بمجرد إدخال قيمة غير صحيحة، نطلب من issuer1 إرسال حدث إلى مشتركيه:


                ' on prévient tout le monde
                émetteur1.envoyerEvt(New myEventArgs(saisie))

الحدث المرسل هو من النوع myEventArgs ويحتوي على الإدخال غير الصحيح. يجب أن يتلقى كلا المشتركين هذا الحدث ويبلغا عنه. يظهر ذلك في التنفيذ التالي.

dos>evt1
Nombre entier (rien pour arrêter) : 4
Nombre entier (rien pour arrêter) : a
L'objet [émetteur1] a signalé la saisie erronée [a] au souscripteur [s1]
L'objet [émetteur1] a signalé la saisie erronée [a] au souscripteur [s2]
Nombre entier (rien pour arrêter) : 1.6
L'objet [émetteur1] a signalé la saisie erronée [1.6] au souscripteur [s1]
L'objet [émetteur1] a signalé la saisie erronée [1.6] au souscripteur [s2]
Nombre entier (rien pour arrêter) :