CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Brendan Tompkins [MVP]

Blog First. Ask Questions Later.

Easily Raise Events From ASP.NET ASCX User Controls

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



Comments

Brendan Tompkins said:

Yes, it's simple! I really think the issue is getting a reference to the control in the first place. What got me thinking about this was that on DNR this week, Carl Franklin mentions using FindControl for this. If you're doing FindControl, you're probably not wiring up events.. Or at least if you are, you're doing wayyy to much coding to get at it..
# October 6, 2004 6:39 AM

Paul Meyer said:

It's easy now after your great explanation. Thanks for the write up.
# October 6, 2004 3:24 PM

samir said:

awesome, saved my day. keep good articles coming!!!
# October 11, 2004 10:53 AM

Dan Poincelot said:

Not a lot of info available on this in print, found a few articles online after an exhausting search. Thanks, this seems to work much better the OnBubbleEvent which was causing strange behavior for me.
Thanks!
# November 17, 2004 8:46 AM

Jamie Sanders said:

Ditto to all the above really. Thanks for this article, it really helped me out especially after the msdn proved useless. Although being a VB programmer this line baffled me for ages:

this.MyControl.TabClicked +=new EventHandler(MyControl_TabClicked);

The equivalent in VB is:

AddHandler Me.MyControl.TabClicked, AddressOf MyControl_TabClicked

Cheers!
# November 23, 2004 3:58 AM

Max said:

THANK YOU THANK YOU THANK YOU JAMIE SANDERS!!!
Thank you for the VB AddHandler translation !!!
# November 26, 2004 3:16 AM

John Terrill said:

Thanks. That is very clear and helpful. But how do I keep the event handler for the user control active in the containing class when using OutputCache in the user control?
# December 2, 2004 6:52 AM

Adamz said:

I am attempting to use this technique with dynamically loaded user control. I am not finding that the event is wired up correctly. Any thoughts?
# December 14, 2004 7:32 AM

Brendan Tompkins said:

Make sure you add your control in the appropriate event, before the event is handled. You probably want to use the OnInit method, or CreateChildControls for this.

# December 14, 2004 7:46 AM

bill said:

did you find out
# January 1, 2005 5:18 PM

igor.dubrovinsky@gmail.com said:

thanks for great post! however, my problem goes beyond containing a single instance of the child control. i have a placeholder which contains children in its Controls collection. The problem arises when i'm trying to do the following:
a. maintain the state of the parent control
b. raise events from children controls to the parent

i assume the right way to approach this problem would be

a. to store the child control(s) in the viewstate (in order to persist its state between roundtrips) and recreate it on every
page_load event handler.

and

b. iterate through the Controls collection of the parent's control's place holder object, dynamically identify its type, and only then, we'll be able to "sign up" for events of the child controls...

am i making any sense?

gents, a good explanation or a link would be appreciated. be well!

Igor Dubrovinsky
# January 20, 2005 8:31 AM

Dev said:

Everytime I change something in the front , the tabClick event gets removed from the Web Form Designer generated code.How to keep it there?
# January 23, 2005 5:06 PM

devin.goble@gmail.com said:

I did everything just like was in the article (thanks for being one of the only places I could find that explains this in a clear and easy to understand way, by the way). The problem is that I get a runtime error that says that the "object reference is not set to an instance of an object." What did I miss? I tried to create a reference to the object. Then what happens is that I get an error that says: The type 'System.Web.UI.UserControl' has no event named 'itemCommandEvent'. This is on the line that has the even wired up to the proper method. This seems so straight forward I must be screwing up somehow. Thanks for the article though :D
# January 24, 2005 7:10 PM

Suni Karumathil said:

The article is great , explained well. Thanks for that.

Reply to Devin:

Declare the control as "Protected ".



# March 9, 2005 7:44 AM

Erik Lane said:

Great post...getting to this stuff now and it really does help with re-use.

Thanks.
# March 13, 2005 1:53 PM

Nathalie said:

merci !
# June 16, 2005 7:55 AM

praveen said:

It just saved my day. Million Thanks for posting this article
# July 22, 2005 8:50 AM

BillH said:

This article really turned on a lot of lightbulbs. I was trying to do a pretty complex project with session variables. This is way too easy and just in the nick of time. Thanks a bunch.

....BillH
# August 27, 2005 3:10 PM

DS said:

All my ASCX inherit from a base class (ControlBase) which inherits from UserControl.

These controls are dynamically loaded into a place holder as such:

ControlBase c = (ControlBase)Page.LoadControl("~/Controls/" + _control);

phContent.Controls.Add(c);

Having trouble implementing delegates / events without having to do:

switch(_controlName)
{
case "ControlA":
ControlBase item = (ControlA)LoadControl("Controls/ControlA.ascx");
((ControlA)item).MyControlEvent += new ControlA.MyControlEventHandler(this.DynamicControlEvent);
break;
.
.
.}

Hope that makes sense.
# September 13, 2005 1:53 PM

DS said:

Got it. This article saved me big time!!
# September 13, 2005 2:05 PM

Jino said:

Hi there,

I have been looking for a solution to get my user control events fired. This blog has helped many people and I think it is gonna help me too. But I am a newbie and also I develop in Vb.net. Even though there is a translation in one of the posts, it didnt make things very clear to me. I understand the concept of creating the instance of the control in the parent control and transferring the handling of the event to the parent.

The name of the control mentioned in the article above - 'MyControl1' - is this the 'ID' of the user control inside the <form> tag of the Parent Control HTML ?

Being a newbie, I couldnt figure out which parts of code goes where. There are too many places to put the code snippets in - User Control HTML, USer Control Code Behind, Parent Control HTML and Parent Control Code Behind. Can someone take the pain of explaining this article a little further so that I can also try this magic nd make my user control events work?

I am sure a lot of newbies would appreciate this effort. I see the same questions raised in many other forums ending up in unsatisfactory or not so detailed answers.

Thanks very much in advance. Someone please take the time. We are looking upto u guys for ur expertise.

Jino
# December 7, 2005 2:33 PM

Walt Sully said:

An excellent article. No need to ask "where's the beef?" For the poor soul out there, who ends up with the kiss of death <i>
"Object reference not set to an instance of an object"</i> after doing everything Brendan outlined, well, in Visual Studio 2003 the code gen feature can clobbler lines you've entered manually. MS warns about diddling in this area and that's too bad. The symptoms are -- it works for awhile and then breaks, usually when the code gen needs to register a new handler automatically in the container class.

I suggest a little defensive coding to yield a SAVE ME FROM NEEDING TO DEBUG THIS PROBLEM AGAIN WHEN I'VE FORGOTTEN THAT THIS CAN HAPPEN ...

// Raise the tabclicked event
<b>if(this.TabClicked != null)</b> throw new Exception("missing Event Handler registration in container class InitializeComponent() - Visual Studio may have removed it!");

this.TabClicked(this, new EventArgs());

regards,
# February 20, 2006 6:46 PM

Walt Sully said:

ERRATA - the if-test should be for equality. That is, if the EventHandler == null
# February 20, 2006 7:00 PM

Walt Sully said:

More specifically

if (this.TabClicked == null)
# February 20, 2006 7:02 PM

Venerable Bede said:

Very good article.  Its amazing how few clear and concise articles there are out there.

Thanks
# March 22, 2006 11:57 AM

StephK said:

I was so glad to see this article posted.  It was easy to follow and I thought that I was seeing the light at the end of the tunnel but then ran my app...I got the object not set to an instance error when declaring my control on my page's code behind.

I am inserting my control into a template column of a gridview.  So there are x instances of this control on my page all with the same id.  Does anyone have any ideas about how I could take the info from this article one step further and allow me to implement custom event handling on controls within a gridview?
# May 8, 2006 5:39 PM

Greg Park said:

Write a book, you were the first person to explain this clearly.
# May 24, 2006 9:38 AM

FSulistio said:

When I ran the code in debug mode why this line
if(this.TabClicked != null) always evaluates to null (undefined value).  So the event could not be raised.  What did I miss?
# July 20, 2006 10:23 AM

David Flamini said:

 
Walt Sully: Can you explain how to solve the problem you comment above? Please! Regards!
# August 24, 2006 2:41 PM

Jeff said:

So you put the "protected MyControl MyControl1;" line in the page that you drug the user contron onto.  Seems to me this merely declares a variable MyControl1 as type MyControl.  This MyControl1 variable will be set to null.  

Normally to use a class you must instanciate it.  So you would write the line "MyControl1 = new MyControl();".  If you do this, the reference that is shoved into variable MyControl1 is for an entirely new instance of the control (not the one you want.  You want a reference to the user control that you drug onto your page.).  How do you do this?

cjrice@tva.gov

# August 29, 2006 10:45 AM

Brendan Tompkins said:

Jeff,

You no longer need to do this with ASP.NET 2.0, in 1.1 this was necessary to get access to the instance of MyControl that was created by the runtime.

# August 29, 2006 1:25 PM

Dr Feet said:

Awesome! Thank you.

Saved at least a days work

# September 19, 2006 8:13 PM

Pritam said:

Article is too good.. Only the mistake is in wiring the event it should be

this.MyControl1.TabClicked +=new EventHandler(MyControl_TabClicked);

Thanks Brendan for nice article !

# October 5, 2006 8:18 AM

Brendan Tompkins said:

Pritam, you are correct sir!

# October 5, 2006 8:37 AM

tadhg88 said:

so simple and yet so effective this saved me a lot of reading/searching

thanks alot brendan very much appreciated

# October 17, 2006 11:52 AM

Anil said:

Hi Btompkins

I will again reapeat what Jino has said, and will be thankfull and appreciate if you or somebody take some take time and explain it in more detail so that the newbie like me and others can also used this. Thanks in Advance

Anil.

***********Jino said **********

Jino said:

Hi there,

I have been looking for a solution to get my user control events fired. This blog has helped many people and I think it is gonna help me too. But I am a newbie and also I develop in Vb.net. Even though there is a translation in one of the posts, it didnt make things very clear to me. I understand the concept of creating the instance of the control in the parent control and transferring the handling of the event to the parent.

The name of the control mentioned in the article above - 'MyControl1' - is this the 'ID' of the user control inside the <form> tag of the Parent Control HTML ?

Being a newbie, I couldnt figure out which parts of code goes where. There are too many places to put the code snippets in - User Control HTML, USer Control Code Behind, Parent Control HTML and Parent Control Code Behind. Can someone take the pain of explaining this article a little further so that I can also try this magic nd make my user control events work?

I am sure a lot of newbies would appreciate this effort. I see the same questions raised in many other forums ending up in unsatisfactory or not so detailed answers.

Thanks very much in advance. Someone please take the time. We are looking upto u guys for ur expertise.

Jino

# November 5, 2006 12:16 PM

Gopala said:

Hi

i am unable to register the event. do i need to use InitializeComponent() to register the event below? (I am using vs2005)

I used "this.MyControl1.TabClicked +=new EventHandler(MyControl_TabClicked)" but

the  TabClicked always giving me null.

am i doing anything wrong? anybody who experienced this please reply back.

-Gopala

# December 7, 2006 9:21 AM

Gopala said:

Hi, I figured it out.

Use OnInit method on your containingpage and initialize the user control and event there. Just like below

protected override void OnInit(EventArgs e)

       {

          mypager.ItemClicked += new ItemClickEventHandler(this.HandlePagerEvent);

          mypager.EnableViewState = true;

          base.OnInit(e);

       }

The problem i faced before is on postback, the Properties of control were becoming null. so is the event and unable to raise it. OnInit takes care of this.....

thanks

-Gopal

# December 8, 2006 12:21 AM

Jenssi said:

Awesome! Incredibly simple and infinitely useful. Thanks for posting this!

# December 15, 2006 2:12 PM

stm said:

Nice article, I'm using this since asp.net 1.1, but I've some comment for asp.net 2.0 / vs2005, maybe it helps.

... "Visual studio does not automatically add a protected member variable for UserControls that are dropped onto and ASCX or ASPX control" ...

I think this is true only for the "web site" project model. In the wap ("web application project model"), we get the protected member automatically in the "aspx.designer.cs/aspx.designer.vb" partial class.

With WAP:

protected  MyControl MyControl1;

not needed in the code behind, because already declared in the partial class!

But there is a small problem with this automation:

What if my page is inherited from a base class and I want to declare MyControl1 in the base class? If I simply put the declaration in the base class, we could get warning CS0108! In that case we can delete the declaration in the designer file, and the IDE will not add declaration again (until the same declaration remains in the codebehind class, or in a base class).

What do you think?

# January 4, 2007 11:08 AM

stm said:

Some explanation for Jino :) (only .net 2.0)

We have an aspx page with a codebehind file.

And we have a usercontrol: an ascx file with also a codebehind file.

You drop your usercontrol to the aspx in design view or manually (I prefer this) add it in source view.

a) MyControl1 is the id of the usercontrol in the asPx file.

b) protected  MyControl MyControl1; needs to move in the asPx's codebehind. In WAP you doesn't need this see the previous comment.

c) public event System.EventHandler TabClicked; go to the asCx's codebehind.

d)

if(this.TabClicked != null)

this.TabClicked(this, new EventArgs());

also could be in any function in asCx's codebehind. This raises the event.

(In fact by the .net event declaring "rules", this should be called from a protected virtual function OnTabClicked( EventArgs e ) { ... }, but doesn't care about this now.)

e) this.MyControl1.TabClicked +=new EventHandler(MyControl_TabClicked);

should be in the asPx's codebehind that way:

protected override void OnInit(EventArgs e)

{

this.MyControl1.TabClicked +=new EventHandler(MyControl_TabClicked);

base.OnInit(e);

}

f) private void MyControl_TabClicked(object sender, EventArgs e){} should be in the asPx's codebehind, this handles the usercontrol event.

g) The rest of the article show you how to fine tune your event (sending additional info) in the usercontrol, by declaring a custom delegate.

hope this helps.

# January 4, 2007 11:50 AM

Mark said:

You don't need to declare the child control as ASP.NET does this for you when you include the control in the page - you will already have a local object called MyControl1 if you give it that id, eg:

<MyTagPrefix:MyControl id="MyControl1" runat="server" />

Then you can just use the public properties of your control when the event fires:

   private void MyControl1_EventFired(object sender, EventArgs e)

   {

       //set the value of a text box as an example

       ATextBoxOnTheContainingPage.Text = MyControl1.PublicProperty;

   }

# March 12, 2007 5:14 AM

T8R said:

Ok...I have a series of user controls which inherit from a base class that has an event defined in it.  Each of these is sitting on a page (5 of them right now).  When a change is made in a datagrid in any one of the user controls the page needs to respond.  The user control raises the event but I can't get the page to see the event.  Likewise, there is an event that the page raises when a selection is made in a dropdown or two but I cannot get the user controls to see that page level event.

How and where should these events be defined (I'm using VB.NET) so that UC's and Page can see the others events and live happily ever after?  Any ideas?

# March 14, 2007 3:17 PM

Leif said:

Great article! Thanks for the straight forward explanation.

In my case I used an event that was published from a User Control via a Master Page to a Page.

- Leif

# April 10, 2007 4:27 AM

Parwej said:

Gr8..!!!!

Thanx 4 saving lots of my time and showing me good way to use events in user control.

# May 28, 2007 7:04 AM

Bhushan Deo said:

Dear Sir,

I want to fire the custom event from user control (c#) so , please if u have any sample code then send me it on this mail id deo_bhushan@yahoo.com

# May 31, 2007 3:15 AM

Cenon Del Rosario said:

I have been doing this before but have a question...

How come I cannot do the event subscription from the code front (i.e. aspx) like what you can do with server controls?

It would be nice if you can do TabClicked="MyControl_TabClicked" in the aspx rather than code.

BTW, I found a work around to this problem ;)

Email: cenon@optusnet.com.au

# July 27, 2007 5:37 AM

Ian said:

My thanks join the many others. Jsut to make it even easier for some, here is all you need to do in ASP.NET 2.0 with VB.net:

in control:

Public Event MyEvent As System.EventHandler

(do this in whatever sub or func as needed)

RaiseEvent MyEvent(Me, New EventArgs())

and in container:

Protected Sub MyControl_MyEvent (ByVal sender As Object, ByVal e As System.EventArgs) Handles MyControl.MyEvent

'do something

End Sub

# August 1, 2007 8:41 AM

Bob’s World of Code » Blog Archive » Create A User Control said:

Pingback from  Bob&#8217;s World of Code  &raquo; Blog Archive   &raquo; Create A User Control

# November 13, 2007 6:29 PM

James said:

Thanks - this described just enough to get me started - now I'm going to apply this technique across my entire project (well, at least where it's applicable). Thanks again!

# December 20, 2007 3:51 PM

Tom Pester said:

You can also use another approach by overriding the accessors of the event :

   public event EventHandler onButSavClicked

   {

       add { butSave.Click += value; }

       remove { butSave.Click -= value; }

   }

This saves some typing since you dont have to implement the event in the user control that just throws it again, don't have to check for null and don't have to add fe. Onclick event on the usercontrol.

# February 27, 2008 9:55 AM

Griegs said:

Tom Pester, you are a legend.  Cheers dude.  So glad I scrolled to the bottom to check for sample code.  :)

# April 17, 2008 1:56 AM

Pablo said:

Thank you for the write up. It's nice to see it done so simple.

# July 23, 2008 11:38 AM

Realist said:

thank you!

# August 25, 2008 10:38 AM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Brendan Tompkins

Brendan has been programming with .NET since the first public beta and is owner and operator of Port Technology Services, a consultancy company providing .NET application development services to the Maritime industry. In July, 2007, he was awarded the Microsoft MVP award for ASP.NET. He's also a proud co-founder of failed .COM startup Intrinsigo, and has had a hand in the failure of numerous other businesses. He currently runs CodeBetter.Com and Devlicio.us, and lives in Norfolk, Virgina with his wife Tiara and son Ian.

View Brendan's profile on LinkedIn

Check out Devlicio.us!

Our Sponsors

Free Tech Publications