views:

1711

answers:

4

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!

+1  A: 

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.

Mitchel Sellers
A: 

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
Y Low
+1  A: 

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

RS Conley
+2  A: 

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.

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