Say what you will about User Controls (ASCX controls) versus WebControls (classes inheriting from System.Web.UI.WebControl) but the simple fact is that often a User Control is the easiest and best way to design a piece of re-usable functionality in ASP.NET. An essential component to re-use is encapsulation. Without encapsulation of the inner-workings of a class, you're liable to have serious problems when it comes to maintenance and ease of re-use. For anything but the simplest of components, you're component is going to need to notify it's container by raising events. So, if you're going to create re-usable ASP.NET UserControls, you're going to need to know how to create and consume events.
Unfortunately, many ASP.NET developers don't implement events from their user controls. I think there's one big reason for this, and the problem lies in the Visual Studio IDE. Visual studio does not automatically add a protected member variable for UserControls that are dropped onto and ASCX or ASPX control. I have no idea why this was left out of the IDE, but I have a suspicion that it's led to developers not properly encapsulating their User Controls. Since there's no automatic member variable, often controls are not thought of as components, like any other control on a web form.
Fortunately, there's a real easy way to solve this. Say you drop a UserControl called MyControl.ascx onto another user control. If this is the first control of this type dropped onto this control the control will be named MyControl1 in the HTML source. The key to accessing events and other properties of this control is to add a member variable. To add a member variable to the container class, you can simply add the line (C#):
protected MyControl MyControl1;
... to the top of your container control's code behind. Okay, so what does this do for us? Well, now, we've got a control that we can communicate with, without doing Page.FindControl. More importantly, we can easily wire up events that are raised in the child control and handle them in our parent! Now, we're able to encapsulate the inner-workings of the child control and notify the parent when something interesting happens.
So, very quickly, there are two essential methods to have a handle on for raising events. First, if you just need to notify the parent control that something happened, use standard System.EventHandler. This is cake. Simply define your event as a public member, like so:
public event System.EventHandler TabClicked;
Raise it whenever you want like this:
// Raise the tabclicked event.
if(this.TabClicked != null)
this.TabClicked(this, new EventArgs());
Then in your containing class you wire up the event like so:
this.MyControl1.TabClicked +=new EventHandler(MyControl_TabClicked);
Where MyControl_TabClicked is a method with a signature that looks like this:
private void MyControl_TabClicked(object sender, EventArgs e){}
Simple! Even cooler, if you type “this.MyControl.TabClicked +=” in the IDE, an press TAB twice, VS will wire up the event handler and create your method for you!Now, say you wanted to pass some specific data to the container control. One way is to use a delegate. This comes in really handy, and is in fact quite easy too. For example, what if you wanted to pass some private value from the encapsulated child class? First create a custom class deriving from System.EventArgs with a member variable that contains your interesting private value:
public class TabClickEventArgs : EventArgs
{
private String myString;
public String MyString
{
get { return myString; }
set { myString = value; }
}
}
Then create a delegate for your event like so:
public delegate void TabClickEventHandler(object sender, TabClickEventArgs e);
The rest is just like before, except that you define your custom event hander, instead of the basic System.EventHandler.
public event TabClickEventHandler TabClicked;
And raise it like so.
// Raise the tabclicked event.
if(this.TabClicked != null)
{
TabClickEventArgs e = new TabClickEventArgs();
e.SomeString = “test“;
this.TabClicked(this, e);
}
Finally, when you wire the event up, your method signature for the handler, just needs to match the signature of your delegate:
private void MyControl_TabClicked(object sender, TabClickEventArgs e) {}
This stuff is so easy and essential, that I can say with certainly that if you're doing ASP.NET development, and not raising events from your UserControls, you're probably not getting the maximum reuse out of your controls.
-Brendan