Keeping track of event handlers

rbulph

Junior Contributor
Joined
Feb 17, 2003
Messages
397
If you have an event which is handled by a number of different handlers, is there an easy way to know when the event has been handled by all of those handlers? Obviously you could keep a record of all of the handlers, but I'd rather something neater.
 
Event handling works by "fire and forget." Basically, the object that fires the event has no knowledge of what might be listening or or how methods or classes might be handling. You can't even predict who will handle what first since handling events is asynchronous -- once fired, all handlers are alerted at the same time so it's a crap shoot as to which handler will go first from run to run.

As far as I know, the application that owns the handlers is the only one that can keep track of when each handler handles an event. You might be able to do something elegant with a callback back to the class that owns the event, but then you could be potentially coupling classes that really should not be coupled in that way. The class that fires the event doesn't need to know when or even if the events it fires are handled.

Perhaps event-driven is no the right architecture and you need to tweak your design?
 
A better solution to your problem could probably be given if we knew more about what exactly you are trying to achieve. Who needs to know that the event has been dispatched to all handlers? Are the classes in question something you've written, a .Net component, or a third party component?

Events are for the very simple purpose of broadcasting information. What you want is slightly beyond the scope of a single simple event. If you are writing the class that is raising events you could consider doing something like a second event which is fired after the first (similar to some .Net events where you have OnFormClosing/OnFormClosed and BeforeLabelEdit/AfterLabelEdit). When the second event is fired you can safely assume that the first event is finished.
 
mskeel said:
Perhaps event-driven is no the right architecture and you need to tweak your design?

LOL- almost every post I make gets a comment like the above!! Let me explain what I'm trying to do. I have a number of objects, all drawn in a picturebox. Each has a withevents reference to that picturebox and checks for whether the cursor is over it on the mousemove event. If it is then it sets the cursor to a crosshair. If the cursor is not over any object it should be the default cursor. But where do you set that given that the picturebox's mousemove event occurs before any of the withevents mousemove events? If you set it in the picturebox's mousemove event and the cursor has in fact remained over an object then the user will see the default cursor for a moment before it is switched back to the crosshair by the object.

I could redesign things and carry out a check for each object in the picturebox's mousemove event. But I like it this way, it's simple and well encapsulated.

One solution I've come up with is to have the owning class set up a new event handler from within its own event handler in an instance of a private class. That handler always seems to occur last. Seems to be good enough for my purposes. Something like the following:

Visual Basic:
Private Sub MouseMoveSub(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.PictureBox1.MouseMove
         'This procedure occurs first.
         MouseOverObj = Nothing
         lh = New LateHandler(Me.PictureBox1)
End Sub
Private lh As LateHandler

Private Class LateHandler

        Private ff As PictureBox
        Private Sub mp2(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
            'Set the cursor to default if not over any object - this occurs last, after any WithEvents handlers.
            If MouseOverObj Is Nothing then ff.Cursor = Cursors.Default
            RemoveHandler ff.MouseMove, AddressOf mp2

        End Sub

        Public Sub New(ByVal f As PictureBox)
            ff = f
            AddHandler f.MouseMove, AddressOf mp2
        End Sub
End Class
 
Last edited:
Did you know that it is free code week?

I don't quite understand your code, but I'm sure it works. Either way, I whipped up this example to show how this could be done in a more "standard .Net" way. You don't have to use it, of course, but it is food for thought.

This code is pretty well commented, but here is the jist. I took the PictureBox class and added a new event that is similar to the MouseMove event. If the cursor is over an object then the handler should call the SetCrossHair method on the event args. Otherwise, don't. Simple.
Visual Basic:
' This class exposes an event that allows event handlers to set crosshairs.
Public Class CursorPictureBox
    Inherits PictureBox
 
    ' This variable keeps track of whether the current cursor is a crosshair.
    Dim Crosshair As Boolean
 
    ' The event where the magic will happen.
    Public Event CheckCrosshair As EventHandler(Of CrossHairEventArgs)
 
    ' This is the important stuff:
    Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
        ' We are supposed to call the base method to make sure MouseMove events are raised.
        ' This is not significant.
        MyBase.OnMouseMove(e)
 
        ' We create our new event args class. We initialize the CrossHair
        ' property to False. If any event handlers feel compelled to show a crosshair,
        ' they should call SetCrossHair, which will set the property to true.
        Dim EventArgs As New CrossHairEventArgs(e, False)
 
        ' Raise the event so that handlers can do their thing.
        RaiseEvent CheckCrosshair(Me, EventArgs)
 
        ' Now, if our new Crosshair value is different than the old one
        ' then change the cursor to the appropriate cursor.
        If EventArgs.CrossHair <> Crosshair Then
            If EventArgs.CrossHair Then
                Cursor = Cursors.Cross
            Else
                Cursor = Cursors.Arrow
            End If
        End If
 
        ' Store the new Crosshair value.
        Crosshair = EventArgs.CrossHair
    End Sub
End Class
 
' This class extends the MouseEventArgs and allows a handler to specify that
' a crosshair should be used.
Public Class CrossHairEventArgs
    Inherits MouseEventArgs
 
    Dim _CrossHair As Boolean
    Public ReadOnly Property CrossHair() As Boolean
        Get
            Return _CrossHair
        End Get
    End Property
 
    'This is the method that handlers should call to indicate that a crosshair should be shown.
    Public Sub SetCrossHair()
        _CrossHair = True
    End Sub
 
    Public Sub New(ByVal e As MouseEventArgs, ByVal CrossHair As Boolean)
        MyBase.New(e.Button, e.Clicks, e.X, e.Y, e.Delta)
 
        _CrossHair = CrossHair
    End Sub
End Class
Here is the usage. I know that my code listing was much longer than yours, but the nifty part is how simple and intuitive the event handlers are.

(I used a picture with a circle centered at 20, 20 with a radius of 20 and then used the following event handler to show a crosshair when the mouse is over the circle.)
Visual Basic:
Private Sub MyBox_CheckCrosshair(ByVal sender As Object, ByVal e As CrossHairEventArgs) Handles MyBox.CheckCrosshair
    If ((e.X - 20) ^ 2 + (e.Y - 20) ^ 2 < 400) Then _
        e.SetCrossHair()
End Sub
 
marble_eater said:
Did you know that it is free code week?

No I didn't know this. Shame you only told me on Friday evening - I would have got you to write code for password protecting files with encryption, had I known!

Anyway, thanks for this free code. It's better than my idea since mine was a bit cryptic and there was no way of being certain that the LateHandler would always handle the event last.
 
Actually, for the sake of experience and entertainment, I wrote a program that used my own algorithms to encrypt files with a key based on a password. After the fact I looked over some encryption algorithms on Wikipedia and was surprised to see how similar some of them were to mine (the biggest difference being that they used a larger encryption key).

Anyways, events really boil down to multicast delegates under the hood, which, behaviorally, resemble an instance of List(Of Delegate). In other words, they are relatively predictable. Generally, the events will be fired in the order that they are attached. I say generally because this probably is not part of the .Net standard, meaning that other implementations (ex. Mono) and future versions of .Net do not guarantee the same behavior.
 
Back
Top