Friday, March 4, 2011

"Re-Raising" Custom Events in .NET (ASP.NET in this case)

Hello all,

I'm working on an ASP.NET page, using VB.NET and I have this hierarchy:

Page A
  - Web User Control 1
    - Web User Control A
    - Web User Control B
    - Web User Control C

I need to raise an event from Web User Control B that Page A will receive (the event flow will be Web User Control B -> Web User Control 1 -> Page A).

My only approach so far has been this: 1) Add a custom event declaration to both Web User Control B and Web User Control 1 and simply RaiseEvent twice until it gets to Page A (this seems ugly and I don't particularly like it).

My other idea was to create a custom Event class that inhertis from some magical base Event class and create an instance of it in both Web User Control B and Web User Control 1, but that is proving fruitless because I can't find any event base classes (maybe b/c they're aren't any, since it appears to be a keyword, not a class name).

Any help would be appreciated! Thanks and happy coding!

From stackoverflow
  • The real question here is is the actual action in Web UserControl B something that should notify both, OR, is WebUserControl1 responsible for some processing BEFORE notifying the page.

    If each step of the chain has a specific action, your method of raising two events is proper. If it is in a manner where the event just needs to notify everyone you will want to look at different subscription methods to communicate.

  • You can create a public method in Page A which gets called from Web User Control B instead of raising an event up the entire control tree.

    This would not be my first choice since this will cause tight coupling between those classes but I hope it solves your problem.

    Sample Page:

    Public Partial Class TestPage
        Inherits Page
    
        Public Sub PerformAction()
            'Whatever needs to be done on Page A
        End Sub
    End Class
    

    Sample User Control:

    Public Partial Class TestControl
        Inherits UserControl
    
        Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
            'This will call the Page, obviously this will only
            'work when the control is on TestPage
            CType(Page, TestPage).PerformAction()
        End Sub
    End Class
    
  • Create a Assembly (or Namespace) that is referenced by everything. Create a interface with the methods you need. Create a class that manages objects that implemented the interface.

    Have Page A implement the interface Have Page A register itself with the manager class done in step #3 Now Web UserControl B can raise the event by retrieving the page from the manager and calling the method on the interface that raises the event you need.

    You avoid tightly coupling the page to the webcontrol because you are using a interface.

    Likely you will find that you will have a multiple interface for different areas of your project. For example in my CAM project I have a interface for the Setup Parameters UI, the Shape Entry UI, and the Cut Entry UI. On our website we have different product categories that uses different interfaces. (Software, Machinery, Services, etc).

  • You can use the BubbleEvent concept to do this. A BubbleEvent goes up the control hierarchy until someone handles it. The GridView and Repeater controls do this with their Row/ItemCommand events.

    You could implement it into WebUserControl1, turning it into a standard event for the page (like the GridView does):

    Class UserControl1 ' Parent
       Protected Override Function OnBubbleEvent(sender as Object, e as EventArgs) as Boolean
           Dim c as CommandEventArgs = TryCast(e, CommandEventArgs)
           If c IsNot Nothing Then
               RaiseEvent ItemEvent(sender, c)
               Return True ' Cancel the bubbling, so it doesn't go up any further in the hierarchy
           End If
           Return False ' Couldn't handle, so let it bubble
       End Function
    
       Public Event ItemEvent as EventHandler(Of CommandEventArgs)
    End Class
    
    Class UserControlB ' Child
        Protected Sub OnClicked(e as EventArgs)
            ' Raise a direct event for any handlers attached directly
            RaiseEvent Clicked(Me, e)
            ' And raise a bubble event for parent control
            RaiseBubbleEvent(Me, New CommandEventArgs("Clicked", Nothing))
        End Sub
    
        Protected Sub OnMoved(e as EventArgs)
            ' Raise a direct event for any handlers attached directly
            RaiseEvent Moved(Me, e)
            ' And raise a bubble event for parent control
            RaiseBubbleEvent(Me, New CommandEventArgs("Moved", Nothing))
        End Sub
    End Class
    
    Class PageA
        Sub UserControl1_ItemEvent(sender as Object, e as CommandEventArgs) Handles UserControl1.ItemEvent
            Response.Write(sender.GetType().Name & " was " & e.CommandName)
        End Sub
    End Class
    

    Or, do it directly in the page. UserControlB (Child) is the same as above, and UserControl1 (Parent) doesn't need to do anything special - OnBubbleEvent defaults to returning False, so the event bubbles up:

    Class PageA
       Protected Override Function OnBubbleEvent(sender as Object, e as EventArgs) as Boolean
           If sender Is UserControlB Then
               Dim c as CommandEventArgs = TryCast(e, CommandEventArgs)
               If c IsNot Nothing Then
                   Response.Write(sender.GetType().Name & " was " & c.CommandName)
               Else
                   Response.Write(sender.GetType().Name & " raised an event, with " & e.GetType().Name & " args)
               End If
               Return True ' Cancel the bubbling, so it doesn't go up any further in the hierarchy
           End If
           Return False ' Not handled
       End Function
    End Class
    

    If your initial event is from a server control (like a Button.Click), then it will have been coded to already raise the bubble event - so UserControlB (Child) doesn't need to do anything to get that to the parent either. You just need to call RaiseBubbleEvent for any of your custom events, or if you want to transform the EventArgs in some way.

    Jason : This is exactly what I was trying to figure out how to do. Perfect answer! Thank you very much (great answers everyone else, too).

0 comments:

Post a Comment