Approaches for User Control Event Handling
Introduction
In this article we will be discussing different ways of handling events in the user controls. For more details on user control you can visit http://msdn.microsoft.com/en-us/library/y6wb1a0e.aspx
You can think of a user control as a self contained composite control, like a control used to show the Logged in user details and which does not interact with the hosting pages. But we can have user controls which contain buttons, links and are supposed to interact with the hosting page (Container). To handle this scenario normally one of the following two approaches are used.
Approach 1: Direct Subscription.
Using this approach the user control directly subscribes the container page event in itself. This approach is considered as bad practice.
Let’s see this by example.
Conceder a control having 4 buttons “Add”, “Delete”, “Update”
and
“Reset”
button and we want this control to be used in many screens in
a ASP.NET
project. To avoid the duplicacy and common appearance in
all the pages lets create a common control for this as following.

ASCX Code:
<div style="width: 100%;">
<asp:Button ID="btnAdd" runat="server" Text="Add" OnClick="btnAdd_Click"></asp:button>
<asp:button id="btnEdit" runat="server" text="Edit" onclick="btnEdit_Click"> </asp:button>
<asp:button id="btnDelete" runat="server" text="Delete" onclick="btnDelete_Click"> </asp:Button>
<asp:button id="btnReset" runat="server" text="Reset" onclick="btnReset_Click"></asp:button>
</div>
Code Behind for the control is like as following.
public partial class Direct : System.Web.UI.UserControl
{
protected void btnAdd_Click(object sender, EventArgs e)
{
}
protected void btnEdit_Click(object sender, EventArgs e)
{
}
protected void btnDelete_Click(object sender, EventArgs e)
{
}
protected void btnReset_Click(object sender, EventArgs e)
{
}
}
Now add a page to the project which will be using this control.
ASPX Code (Designer)
Add the user control which we have created earlier to the page. Your designer code will look like this.
<%@ Register src="Controls/Direct.ascx" tagname="Direct" tagprefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<uc1:Direct ID="Direct1" runat="server" />
</div>
</form>
</body>
</html>
Now add four methods “Add”, “Delete”, “Update” and “Reset” in your code behind page. Your code behind page will look like this.
public partial class Direct : System.Web.UI.Page
{
public void Add()
{
Response.Write("Added.");
}
public void Delete()
{
Response.Write("Deleted.");
}
public void Update()
{
Response.Write("Updated.");
}
public void Reset()
{
Response.Write("Reset done.");
}
}
To call above methods on click of the Action control button you need to write the logic directely in the control button event directely. Once you’ll do this your user control code behind will look something like followng code snippet.
protected void btnAdd_Click(object sender, EventArgs e)
{
//In case of multiple pages you need to check each and every page.
Action.Direct direct = this.Page as Action.Direct;
if (direct != null)
{
direct.Add();
}
}
protected void btnEdit_Click(object sender, EventArgs e)
{
//In case of multiple pages you need to check each and every page.
Action.Direct direct = this.Page as Action.Direct;
if (direct != null)
{
direct.Update();
}
}
protected void btnDelete_Click(object sender, EventArgs e)
{
//In case of multiple pages you need to check each and every page.
Action.Direct direct = this.Page as Action.Direct;
if (direct != null)
{
direct.Delete();
}
}
protected void btnReset_Click(object sender, EventArgs e)
{
//In case of multiple pages you need to check each and every page.
Action.Direct direct = this.Page as Action.Direct;
if (direct != null)
{
direct.Reset();
}
}
Disadvantages
In this approach Control is not generic; you need to check for each and every page to execute anything. If some new pages get added in the project which wants to use the common control in that case the control might need modification for handling the new page.
Approach 2: Event Delegation
In this case all the pages which want to use the control and like to perform any operation on the basis of the event raised by the user control, for this the user control needs to publish events and all the consuming pages need to handle the event.
Normally we go for this approach if we want the complete encapsulation and don’t want to make our methods public. This is the most recommended way to achieve the problem we are currently addressing.
Lets conceder the same example as mentioned for the approach 1 and we will try to implement this using Event Delegation.
ASCX Code:
<div style="width: 100%;">
<asp:Button ID="btnAdd" runat="server" Text="Add" OnClick="btnAdd_Click"></asp:button>
<asp:button id="btnEdit" runat="server" text="Edit" onclick="btnEdit_Click"> </asp:button>
<asp:button id="btnDelete" runat="server" text="Delete" onclick="btnDelete_Click"> </asp:Button>
<asp:button id="btnReset" runat="server" text="Reset" onclick="btnReset_Click"></asp:button>
</div>
Code Behind for the control is like as following.
public delegate void ActionClick();
public partial class EventDelegation : System.Web.UI.UserControl
{
public event ActionClick OnAddClick;
public event ActionClick OnDeleteClick;
public event ActionClick OnEditClick;
public event ActionClick OnResetClick;
protected void btnAdd_Click(object sender, EventArgs e)
{
if(OnAddClick!= null)
{
OnAddClick();
}
}
protected void btnEdit_Click(object sender, EventArgs e)
{
if (OnEditClick != null)
{
OnEditClick();
}
}
protected void btnDelete_Click(object sender, EventArgs e)
{
if(OnDeleteClick!= null)
{
OnDeleteClick();
}
}
protected void btnReset_Click(object sender, EventArgs e)
{
if(OnResetClick!= null)
{
OnResetClick();
}
}
}
Above code will be looking very different from the Approach 1 code.
Let me try to explain the main point. The user control specifies some public events like “OnAddClick”, “OnEditClick” etc which declares a delegate. Anyone want to use these events needs to add the EventHandler to execute when the corrosponding button click event occurs.
Let’s take following code.
protected void btnAdd_Click(object sender, EventArgs e)
{
if(OnAddClick!= null)
{
OnAddClick();
}
}
In the above code snippet we are first checking if anyone is attached to this event or not if found then raise the event.
Now add a page to the project which will be using this control
ASPX Code (Designer)
Add the user control which we have created earlier to the page. Your designer code will look like this.
<%@ Register src="Controls/EventDelegation.ascx" tagname="EventDelegation" tagprefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<uc1:Direct ID="Direct1" runat="server" />
</div>
</form>
</body>
</html>
Code behind of the page will look like this.
public partial class EventDelegation : System.Web.UI.Page
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
ActionControl.OnAddClick += ActionControl_OnAddClick;
ActionControl.OnDeleteClick += ActionControl_OnDeleteClick;
ActionControl.OnEditClick += ActionControl_OnEditClick;
ActionControl.OnResetClick += ActionControl_OnResetClick;
}
private void ActionControl_OnResetClick()
{
Response.Write("Reset done.");
}
private void ActionControl_OnEditClick()
{
Response.Write("Updated.");
}
private void ActionControl_OnDeleteClick()
{
Response.Write("Deleted.");
}
private void ActionControl_OnAddClick()
{
Response.Write("Added.");
}
}
}
In the above code you can find that the container page needs to add event handlers during the OnInit event.
I don’t find any disadvantages in this approach but there are few things which make this little problematic.
1- You need to add the event handler for each and every event. If you are not adding the event handler in the OnInit event of the page you might face some problems that on page post back you will lose the event assignment( as ASP.NET is stateless not the case with windows control).
2- Some time when you are working on designer there might be a case when the eventhander gets lost without your notice.
3- Even if you have not added the event handler you’ll not get any error or warning.
4- If you have multiple pages for performing the same action in that case there is no guarantee that all the method names will be same, the developer can chose their own method name which reduces the maintainability of the code.
Approach 3: Indirect Subscription.
Here we are discussing an approach which is extension of the direct subscription and trying that all the problems to be resolved in our new Implementation. The main problem in the Direct Subscription approach was the maintenance; every time we add a page to the project which wants to use the common control in that case we need to modify the common control to handle the page event this is because we are directly referring the container in the control, whenever the container gets changed that needs to be handled in a different way.
Let’ discuss the Indirect Subscription approach using the code. Here we are considering the same example which we have discussed in Approach 1 and Approach 2.
IAction Interface
public interface IAction
{
void Add();
void Update();
void Delete();
void Reset()
}
In the above code we are creating an Interface IAction where we are defining the rules which our user control is going to support, in other words we are defining the contract here rather than the actual implementation. This will help us in de-coupling of the user control and the container(Page).
ASCX Code:
<div style="width: 100%;">
<asp:Button ID="btnAdd" runat="server" Text="Add" OnClick="btnAdd_Click"></asp:button>
<asp:button id="btnEdit" runat="server" text="Edit" onclick="btnEdit_Click"> </asp:button>
<asp:button id="btnDelete" runat="server" text="Delete" onclick="btnDelete_Click"> </asp:Button>
<asp:button id="btnReset" runat="server" text="Reset" onclick="btnReset_Click"></asp:button>
</div>
Code Behind for the control is like as following.
public partial class ActionBar : System.Web.UI.UserControl
{
protected void btnAdd_Click(object sender, EventArgs e)
{
Action.Add();
}
protected void btnEdit_Click(object sender, EventArgs e)
{
Action.Update();
}
protected void btnDelete_Click(object sender, EventArgs e)
{
Action.Delete();
}
protected void btnReset_Click(object sender, EventArgs e)
{
Action.Reset();
}
private IAction Action
{
get
{
IAction action = Page as IAction;
// Check if Page implements IAction interface or not.
if (action == null)
{
throw new Exception(
"No A valid Container type. Page should implement the IAction interface."
);
}
return action;
}
}
}
In the above code the property Action returns the IAction type of object in case the container is not implementing the IAction interface an exception will be thrown.
All the methods are getting raised without checking if someone is subscribing the event handler or not.
For example
Action.Add();
Now add a page which will be using the user control and add the user control to it. The page designer code will look like following code snippet.
<%@ Register src="Controls/ActionBar.ascx" tagname="ActionBar" tagprefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<uc1:ActionBar ID="ActionBar1" runat="server" />
</div>
</form>
</body>
</html>
The serverside code will look like following.
public partial class _Default : System.Web.UI.Page, IAction
{
#region IAction Members
public void Add()
{
Response.Write("Added.");
}
public void Update()
{
Response.Write("Updated.");
}
public void Delete()
{
Response.Write("Deleted.");
}
public void Reset()
{
Response.Write("Reset done.");
}
#endregion
}
In the above code you can see that we are implementing the IAction interface, which force us to implement the Add, Update, Delete and Reset method into it. You can implement all the methods and write the page specific code in those blocks. Notice that here we are not bothered to register the event handler. This is the responsibility of the user control itself to invoke the methods on appropriate event (Button Click).
Advantages of this approach.
1- You don’t need to register the event handlers in the page.
2- All pages which are going to use this control should have the same method names; this will help in code uniformity and will improve the maintainability.
3- For any new subscription you don’t need to modify the user control.
But this approach3 is having a major flaw in terms of the object oriented programming, that we can’t have encapsulation. As we are implementing the IAction interface in that case all the methods are by default public. In case if you are designing a control library so that some third party can use it then go for the second approach.