Thursday, 2 January 2014

Event Bubbling from ASP.Net User Control

When working with an ASP.Net application that has child controls nested on a page, you may need to perform some action on the parent page depending on an event which occurs on one of its child controls. For example, if a button is clicked on a child control, you may want a gridview to refresh on the parent page.

As user controls are self-contained (and possibly dynamically loaded at runtime), we cannot directly reference the ID of any element that exists on a different page. One solution would be to subscribe to the child's button click event from the parent page, but this would break some of the object oriented rules of encapsulation.

A neater solution would be to publish an event in the user control. The parent page can then subscribe to this event. With ASP.Net, this functionality already exists, via the OnBubbleEvent and RaiseBubbleEvent methods. 'Event bubbling' using these methods allows a child control to propagate events up its containment hierarchy. I found these methods extremely easy to use, and it prevents the coder from having to explicity raise custom events and subscribe to these in different levels.

Lets see how it works...


 
In my child control, the 'OnClick' code behind of my button contains the following line:

RaiseBubbleEvent(sender, new CustomClickEventArgs());

RaiseBubbleEvent sends the event data up the hierarchy to the control's parent. I've also included a custom EventArgs class - more on this in a bit.


To handle (or to further propagate the bubbled event, upwards) a control must override the OnBubbleEvent method.  A control that has an event bubbled to it does one of the following three things.
  • It does nothing, in which case the event is automatically bubbled up to its parent.
  • It does some processing before continuing to bubble the event. To accomplish this, a control must override OnBubbleEvent and invoke RaiseBubbleEvent from OnBubbleEvent.
  • It stops bubbling the event and handles the event.
In the example code below, I am catchting and handling the bubble event in my parent by overriding OnBubbleEvent:
 


    /// <summary>
    /// Handles the RaiseBubbleEvent event
    /// raised by a control
    /// </summary>
    /// <param name="source"></param>
    /// <param name="e"></param>
    /// <returns>True if event is handled, false if event needs passed to parent</returns>
    protected override bool OnBubbleEvent(object source, EventArgs e)
    {
      bool handled = false;
 
      if (e is CustomClickEventArgs)
      {
        handled = true;
        RefreshParentGridView();
        }
      }
 
      return handled;
    }
 
By returning true, I am preventing the event from bubbling up any further. Alternatively, I could execute some code in the method above and then return false. This would mean the event would bubble up to the next level which overrides OnBubbleEvent, and I would have performed some logic at each level.

In the parent class, we will only have one instance of OnBubbleEvent being overriden. This poses a slight problem if we are calling RaiseBubbleEvent for multiple different events in different child controls - how do we know what logic to perform at the parent level that is specific to the event that has been raised? Easy...
   

In the above OnBubbleEvent method, I have added a clause which checks the type of EventArgs passed from the RaiseBubbleEvent call in the child. Based on the EventArgs, we can determine which logic to execute. I created a custom EventArgs class for the type of event I was raising:

    /// <summary>
    /// Custom Event Arguments class
    /// </summary>
    public class CustomClickEventArgs: EventArgs
    {
        public CustomClickEventArgs()
        {
        }
    }
 
We could even parameterize this custom event args class to expand the logic - for example - we could use the same class for one entire user control but have a parameter, passed to the constructor, which specifies which button on the user control was clicked, and then check this value in the OnBubbleEvent method to perform separate logic.






No comments:

Post a Comment