none
Custom Events and Invoke... RRS feed

  • Question

  • I've noticed with many classes I've viewed with Refractor, or other people's code that is written in C# that they'll do things with events that are different than how they are handled in VB. 

    One such case is the vb's conversion into Custom Events.  Example:

     

    public event AddingNewEventHandler AddingNew
    {
        add
        {
            bool allowNew = this.AllowNew;
            this.onAddingNew = (AddingNewEventHandler) Delegate.Combine(this.onAddingNew, value);
            if (allowNew != this.AllowNew)
            {
                this.FireListChanged(ListChangedType.Reset, -1);
            }
        }
        remove
        {
            bool allowNew = this.AllowNew;
            this.onAddingNew = (AddingNewEventHandler) Delegate.Remove(this.onAddingNew, value);
            if (allowNew != this.AllowNew)
            {
                this.FireListChanged(ListChangedType.Reset, -1);
            }
        }
    }
    
    Public Custom Event AddingNew As AddingNewEventHandler
        AddHandler(ByVal value As AddingNewEventHandler)
            Dim allowNew As Boolean = Me.AllowNew
            Me._onAddingNew = DirectCast(AddingNewEventHandler.Combine(Me._addingNew, value), AddingNewEventHandler)
            If (allowNew <> Me.AllowNew) Then
                Me.FireListChanged(ListChangedType.Reset, -1)
            End If
        End AddHandler
        RemoveHandler(ByVal value As AddingNewEventHandler)
            Dim allowNew As Boolean = Me.AllowNew
            Me._onAddingNew = DirectCast(AddingNewEventHandler.Remove(Me._addingNew, value), AddingNewEventHandler)
            If (allowNew <> Me.AllowNew) Then
                Me.FireListChanged(ListChangedType.Reset, -1)
            End If
        End RemoveHandler
    End Event
    


    The above is from the BindingList implementation, that i've been perusing to get a better handle on how to implement IBindingList.  I cannot use the BIndingLIst class as I already have a much more robust Collection system, and I want to make it even more so by adding binding support.  However, the above there, is not 100% accurate for VB, since vb alro requires a RaiseEvent clause in addition tot he AddHandler and RemoveHandler clauses.  This much is fine, I'm not too worried.  However, in the c# code, to execute the event they simply call onAddingNew.Invoke(me, e).  Which seems fine, on the surface, mostly because how .Net handles events is all a little quirky at times, so I tend not to distress my mind too much.

    Tragically, now I have to.  in C# I have often defined an event and attached to it with += new handler() command line, and then also done the comparison if (_handler != null) { _hander(me, e); }, which is C# and simple to understand.  but, for VB version of this same command, it looks more like: if (not me._handlerevent is nothing) then _handler.Invoke(me, e).  This is due to some of the weirdness between vb and c# where c# actually has more "pointer" referencing than vb does, and alos vb's quirkyness with delegates and events.

    Suddenly, realizing the code above was used in a class design written in c#, and VB does some other things behind the scenes, I began to wonder about multiple event handlers.  How are they handled?

    NOrmally I simply do: public myevent as eventhandler, followed by a "raiseEvent MyEvent()" whenever I want to trigger the event, and vb handles all the funky transitions.  But, if i'm doing the above "Custom Event" declaration, what precisely do I put in the "RaiseEvent" clause?

    Like for example above, if i were to add teh raiseEvent clause would it look something like this:

    Private _addingNew as AddingNewEventHandler
    
    Public Custom Event AddingNew as AddingNewEventHandler
      <insert above add/remove here>
      RaiseEvent(byval sender as Object, byval e as AddingNewEventArgs)
        _addingNew.Invoke(sender, e)
      End RaiseEvent
    End Event

    Or would i need to do something to compensate for multiple Event handlers, such as:

    RaiseEvent(ByVal sender As Object, ByVal e As System.ComponentModel.AddingNewEventArgs)
    	For Each ev As AddingNewEventHandler In _addingNew.GetInvocationList
    		ev.Invoke(sender, e)
    	Next
    End RaiseEvent
    

    Frankly I'm not sure I understand the difference between calling _addingNew.Invoke() and using vb's specific RaiseEvent command, but i figure raiseevent probably just does something similar on the back end.  in c# you have to test the event delegate holder for null or otherwise you get an error, where as VB you don't have to test anythign on the RaiseEvent call, but for mydelegate.Invoke() you do.  Anyway, thanks for any info you can give on the topic to help me undersatnd these things a little better.

    Jaeden "Sifo Dyas" al'Raec Ruiner

     

     


    "Never Trust a computer. Your brain is smarter than any micro-chip."
    PS - Don't mark answers on other people's questions. There are such things as Vacations and Holidays which may reduce timely activity, and until the person asking the question can test your answer, it is not correct just because you think it is. Marking it correct for them often stops other people from even reading the question and possibly providing the real "correct" answer.
    Sunday, April 11, 2010 2:22 PM

All replies

  • Hi JaedenRuiner,

    I'm not sure whether I catch up with you, however, I just try my best. Hope what I will say below helpful to you.

    I thought you want to write your own class that have binding support. And I thought you don't need to do something like this:

     

    RaiseEvent(ByVal sender As Object, ByVal e As System.ComponentModel.AddingNewEventArgs)
    	For Each ev As AddingNewEventHandler In _addingNew.GetInvocationList
    		ev.Invoke(sender, e)
    	Next
    End RaiseEvent
    

     

    since, I'm not sure whether you notice this line you mentioned above:

     

     this.onAddingNew = (AddingNewEventHandler) Delegate.Combine(this.onAddingNew, value);
    
    You could combine the handler to the event by combine method(you can attach many handlers to one event through this method, and you can check it here ), when the event trigger, all the handler method will happen.

    And about this method you metioned here:

     

    Public Custom Event AddingNew as AddingNewEventHandler
      <insert above add/remove here>
      RaiseEvent(byval sender as Object, byval e as AddingNewEventArgs)
        _addingNew.Invoke(sender, e)
      End RaiseEvent
    End Event
    

     

    I'm not sure why you declare RaiseEvent in AddingNew.

    AddingNew methos should just add the event handler to the List or collection of the Handler(not sure about your implement) instead of raising the event. And RasieEvent should be called when you want the event handler to raise.

    Or, are you going to define your own RaiseEvent? I thought it's not possible, since the defination of RaiseEvent is not virtual.

    What you said here, I'm not very sure about your meaning:

    in C# I have often defined an event and attached to it with += new handler() command line, and then also done the comparison if (_handler != null) { _hander(me, e); }, which is C# and simple to understand.  but, for VB version of this same command, it looks more like: if (not me._handlerevent is nothing) then _handler.Invoke(me, e).  This is due to some of the weirdness between vb and c# where c# actually has more "pointer" referencing than vb does, and alos vb's quirkyness with delegates and events.

    Actually, the events and delegate have many relations, you can see here 1, 2, 3 for more informaion. Event, somehow we can say, is implemented through the help of delegate.

    > the difference between calling _addingNew.Invoke() and using vb's specific RaiseEvent command

    _addingNew.Invoke() Method is to handle the BindingSource. AddingNew event and the RaiseEvent command is to raises an event at run time, or declares what code to execute when raising an event.

    All above are just based on my comprehension. Hope this helpful to you.

    If anything I said is incorrect, please feel free to let me know, and I will do the furthur study on this matter.


    Hope this helpful to you! If you have any further quetions, please feel free to let me know.
    Please mark the right answer at right time.
    Bset Regards,
    Tracy
    • Marked as answer by SamAgain Friday, April 16, 2010 10:07 AM
    • Unmarked as answer by JaedenRuiner Wednesday, April 21, 2010 7:46 AM
    Monday, April 12, 2010 9:03 AM
  • Okay....i will try again.

    I do love the request for "complete detailed questions" and yet the more detail I provide, the more confused people get.  First: Don't think in Specifics.  I used the BindingList<> generic declaration as an example not as what I'm doing.  I haven't done anything yet because I don't understand the underlying theory behind these differences.  In order for me to program I have to understand theory, and that's what I'm asking for.  Vb adds a lot of strange commands that are not inherently necessary for the .Net framework.

    Custom Events:

    If you are using C#, this will not make sense.  You have to try it in VB.  So create a simple stupid quick project, and right there in the Form1 class declaration type:

    Public Custom Event MyEvent as EventHandler

    and hit enter.  The Automatic Code Completion for VB.Net will automatically Create this definition:

     

    Public Custom Event MyEvent As EventHandler
    	AddHandler(ByVal value As EventHandler)
    
    	End AddHandler
    
    	RemoveHandler(ByVal value As EventHandler)
    
    	End RemoveHandler
    
    	RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
    
    	End RaiseEvent
    End Event

     

    If you delete the RaiseEvent clause listed there you will get an ERROR in the error list and will not be able to compile. 

    C# does not use the RaiseEvent command so in the custom event declarations for C#, this clause is NOT INCLUDED .  It doesn't even provide a Code Completion for "Custom" events, since events are handled differently between c# and vb, and thus the reserved word "Custom" doesn't even exist.  You can define an event in c# like this:

     

    public event EventHandler MyEvent;
    OR
    public event EventHandler MyEvent {
     add {
      custom_handler_add_code
     }
     remove {
      custom_handler_remove_code
     }
    }

     

    This is similar to how C# and VB differ on properties.  C# a Readonly property is a property that does not provide a set {} clause.  VB requires you add the "ReadOnly" qualifier to the Property Header.  Thus, a custom event setup like the above in C# requires the "Custom" keyword to reciprocate in VB.  The inverse is that C# uses the += operator to attach a handler method to the event, but you are required to create the Handler Delegate Object Type, where VB you are not:

    obj.MyEvent += new EventHandler(obj_MyEvent)

    AddHandler obj.MyEvent, AddressOf obj_MyEvent

    On to Invoking:

    So, we take this MyEvent event of some object, and there are some differences in invoking.

    protected void OnMyEvent() {
      if (MyEvent != null) {
       MyEvent(this, EventArgs.Empty);
      }
    }
    Protected Sub OnMyEvent()
      RaiseEvent MyEvent(Me, EventArgs.Empty)
    End Sub
    Protected Sub OnMyEvent2()
      If MyEventEvent IsNot Nothing then
       MyEventEvent.Invoke(Me, EventArgs.Empty)
      End If
    End Sub

    As you can see there are TWO ways to invoke the event in VB, because VB creates a backend variable for the event delegate.  Thus in ANY VB class that has Public Event X, there is a XEvent delegate that accessible from within that class.

    The circumstance of a Delegate, it is slightly different when viewed again for both classes:

    public EventHandler myEvent;
    protected void OnMyEvent() {
      if (myEvent != null) {
       myEvent(this, EventArgs.Empty);
     }
    }
    public myEvent as EventHandler
    protected sub onMyEvent()
      if myEvent IsNot Nothing then
       myEvent.Invoke(me, EventArgs.Empty)
     end if
    end sub

    As you can see, C# is identical between the Event and the Delegate, but VB invokes them quite differently.  Because VB Handles Events differently than C#.   Because of that back end delegate variable MyEventEvent, the RaiseEvent command does something that we (as top level programmers) are not privy to, where in C# you treat them the same so it doesn't matter.

    On to My Question:

    If you look at the Reflector disassembled BindingList<T>, in both C# and VB, you can see the nature of my curiosity with regards to VB event handling.  They way it appears to be implemented, is this onAddingNew "DELEGATE" is defined, and the AddingNew "EVENT" is used as a liaison between the public access and private delegate.  Why.  I can see there is something handled in the Add/Remove clauses of the Event declaration that interprets certain facets of the class.  When adding a handler for the AddingNew event this triggers a determination as to whether or not the class should trigger a ListChanged event.  Why?  Because the BindingList<T> is trying to support user object creation.  If the default List Item Type can support Reflection based instance creation, the BindingList<T> automatically returns true from the AllowNew interface property.  However, if it can't use reflection to create a  new List Item, it returns false.  If you add a handler to respond to the AddingNew event when the previous state of AllowNew was false it fires a ListChanged event, indicating the "List Object Has Changed Because It Now will AllowNew".  (BindingSource.AddNew uses this IBindingList.AddNew method to perform this task.  BindingList<T>.AddingNew event is so the user can handle the creation of the Item List Objects when BindingSource.AddNew is invoked) It is "customized" event handling for changes to the class that need to perform other processing when an event handler is assigned to an event. 

    This part makes sense to me.  But since I am Implementing IBindingList on an existing VB class, I need to know how to implement this behavior in VB syntax as opposed to C#.

    C# you can create a: public event EventHandler myEvent, or you can create public EventHandler myEvent, and either way you treat them the same.  so it doesn't matter. 

    In VB, I can Create Public Event myEvent as EventHandler or Public myEvent as EventHandler but I treat them VERY DIFFERENTLY. 

    Would I simply preform:

    Public Custom Event MyEvent As EventHandler
    	AddHandler(ByVal value As EventHandler)
    		Me._myEvent = DirectCast(EventHandler.Combine(Me._myEvent, value), EventHandler)
    		Custom_Add_Code()
    	End AddHandler
    
    	RemoveHandler(ByVal value As EventHandler)
    		Me._myEvent = DirectCast(EventHandler.Remove(Me._myEvent, value), EventHandler)
    		Custom_Remove_Code()
    	End RemoveHandler
    
    	RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
    		If _myEvent IsNot Nothing Then
    			_myEvent.Invoke(sender, e)
    		End If
    	End RaiseEvent
    End Event

    Is that all that is necessary so that when I execute "RaiseEvent MyEvent(Me, EventArgs.Empty)", it actually Invokes the _myEvent delegate, since with the "Custom Event" declaration there is no back end MyEventEvent delegate created.
    Does the Invoke() method of a Delegate type invoke all hanlders associated with that delegate? Is that what the Combine method does, providing the entire invocation list for when you execute the .Invoke()?
    Would it be better to use the delegate's method "GetInvocationList()" and check for length as opposed to checking the _myEvent is Nothing before invoking?

    Basically, the RaiseEvent Command executes something in the vb back end that does a check of some sort on the MyEventEvent delegate that VB automatically generated for me. I need to know what that code is, so I can write my own Custom Event handlers.

    Thanks
    Jaeden "Sifo Dyas" al'Raec Ruiner


    "Never Trust a computer. Your brain is smarter than any micro-chip."
    PS - Don't mark answers on other people's questions. There are such things as Vacations and Holidays which may reduce timely activity, and until the person asking the question can test your answer, it is not correct just because you think it is. Marking it correct for them often stops other people from even reading the question and possibly providing the real "correct" answer.
    Wednesday, April 21, 2010 8:57 AM