Jump to content
Xtreme .Net Talk

Recommended Posts

Posted
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.
Posted

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?

  • Leaders
Posted

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.

[sIGPIC]e[/sIGPIC]
Posted (edited)
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:

 

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

Edited by rbulph
  • Leaders
Posted

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.

' 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.)

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

[sIGPIC]e[/sIGPIC]
Posted
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.

  • Leaders
Posted

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.

[sIGPIC]e[/sIGPIC]

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...