Click here to Skip to main content
15,860,972 members
Articles / Web Development / ASP.NET
Article

How to invoke events across User Controls in ASP.NET

Rate me:
Please Sign up or sign in to vote.
4.29/5 (11 votes)
11 Sep 2006CPOL2 min read 84K   791   44   14
How to use self-registering objects and Context.Items to invoke events across User Controls.

The Problem

I have on several occasions experienced problems when events were raised in one User Control that must be received in another User Control in a different layer.

Handling events from controls on a page is easy, but it gets harder once we refactor common code into user controls. And the difficulties increase with each added User Control layer. One scenario is sending an event from a User Control to the page, another is sending an event from a User Control inside of a User Control to a User Control inside of a different User Control.

Just to complicate things even more, we can't assign to events in C#. So a naive solution would be to add event handlers to raise new events. To do that, we need to duplicate the event declaration plus the event handler code for every User Control the event has to pass through. That smells like The Middle Man anti-pattern.

I am not too fond of bubbled events, either. I find them too hard to discover, and too implicit. There is nothing in the code to reveal their existence or usage. I prefer a more explicit solution.

One Possible Solution

I want my code to avoid all the middle men, to be foolproof with clear and visible usage, and to be simple to use. That suggests we should declare our events the usual way, all listeners should register themselves the usual way, there should be no more than one line of code to forward the event, and the forwarding should happen once.

So we need something to expose the events and methods to invoke them:

  • Events and notification methods come in pairs.
  • Notifiers and listeners use the same object.
  • The object will keep track of the event handler delegates in Context.Items.

In the provided code, I have one User Control with two buttons to invoke events, and another User Control to receive them. I used a regular Button and an ImageButton to demonstrate event handlers with different argument types.

The sender control uses the following code to invoke the events:

C#
private ButtonEvents events;
        
private void Page_Load (object sender, EventArgs e)
{
    events = new ButtonEvents(Context.Items);
}

private void uxButton_Click (object sender, EventArgs e)
{
    events.ClickButton(sender, e);
}

private void uxImage_Click (object sender, ImageClickEventArgs e)
{
    events.ClickImageButton(sender, e);
}

The receiver control uses this code to register to those events:

C#
private void Page_Load (object sender, EventArgs e)
{
    ButtonEvents events = new ButtonEvents(Context.Items);
    events.ImageButtonClicked += 
             new ImageClickEventHandler(events_ImageButtonClicked);
    events.ButtonClicked += new EventHandler(events_ButtonClicked);
}

The ButtonEvents class needs a little more work than usual to implement, but not much:

C#
internal class ButtonEvents
{
    private CachedEvent buttonEvent;
    private CachedEvent imageButtonEvent;

    public ButtonEvents (IDictionary items)
    {
        buttonEvent = new CachedEvent(items, "Button");
        imageButtonEvent = new CachedEvent(items, "ImageButton");
    }

    public event ImageClickEventHandler ImageButtonClicked
    {
        add { imageButtonEvent.Add(value);}
        remove { imageButtonEvent.Remove(value);}
    }

    public event EventHandler ButtonClicked
    {
        add { buttonEvent.Add(value);}
        remove { buttonEvent.Remove(value);}
    }

    public void ClickButton (object sender, EventArgs e)
    {
        buttonEvent.Invoke(sender, e);
    }

    public void ClickImageButton (object sender, ImageClickEventArgs e)
    {
        imageButtonEvent.Invoke(sender, e);
    }
}

The ButtonEvents class makes use of the CachedEvent helper object. This is the object which implements the real solution. The CachedEvents takes a dictionary and a key in its constructor, so it knows where to store the event handlers. The event handlers are delegates, and are stored as such:

C#
public CachedEvent (IDictionary items, object key)
{
    this.items = items;
    this.key = key;
}

private Delegate Listeners
{
    set { items [key] = value; }
    get { return (Delegate) items [key]; }
}

There are two helper methods to register and unregister event listeners:

C#
public void Add (MulticastDelegate newListener)
{
    Listeners = Delegate.Combine(Listeners, newListener);
}

public void Remove (MulticastDelegate formerListener)
{
    Listeners = Delegate.Remove(Listeners, formerListener);
}

And finally, the method to invoke the event:

C#
public void Invoke (object sender, EventArgs arguments)
{
    Delegate listeners = Listeners;
    if (listeners != null)
        listeners.DynamicInvoke(new object[] {sender, arguments});
}

Points of Interest

History

  • Article submitted - Sep 12, 2006.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Norway Norway
I have programmed applications in ASP.NET from the very beginning. My favourite .NET language is C#, and I am a fan of Test Driven Design. I was an experienced VB6 coder before the .NET era. I have also done some Transacted SQL programming in Sql Server. Although I have made a career on Microsoft technology, I have browsed enough Java code to read it fluently, and I find Smalltalk and Ruby very interesting.

I live a few kilometers away from Oslo.

My email is thomas.eyde@gmail.com.

Comments and Discussions

 
QuestionHow to do it interchangeably Pin
Kris Kristensen9-Jun-09 5:19
Kris Kristensen9-Jun-09 5:19 
AnswerRe: How to do it interchangeably Pin
Thomas Eyde9-Jun-09 6:58
Thomas Eyde9-Jun-09 6:58 
GeneralRe: How to do it interchangeably Pin
Kris Kristensen9-Jun-09 22:17
Kris Kristensen9-Jun-09 22:17 
GeneralGreat applicable solution Pin
LeRoy@CodeProject29-Aug-08 10:34
LeRoy@CodeProject29-Aug-08 10:34 
QuestionMoving objects Pin
Hellacious6-Nov-07 14:34
Hellacious6-Nov-07 14:34 
AnswerRe: Moving objects Pin
Thomas Eyde7-Nov-07 8:55
Thomas Eyde7-Nov-07 8:55 
Questionone question Pin
okcjh22-Dec-06 4:04
okcjh22-Dec-06 4:04 
very helpful thanks.
but i have one question.how to get ReturnValue from ReceiverControl.
AnswerRe: one question Pin
Thomas Eyde22-Dec-06 12:02
Thomas Eyde22-Dec-06 12:02 
GeneralRe: one question Pin
okcjh22-Dec-06 23:49
okcjh22-Dec-06 23:49 
GeneralVery helpful, thanks. Pin
steve.cadwallader6-Nov-06 14:39
steve.cadwallader6-Nov-06 14:39 
Generalyou got my 5. Pin
Hatchet120-Sep-06 23:28
Hatchet120-Sep-06 23:28 
GeneralRe: you got my 5. Pin
Thomas Eyde21-Sep-06 2:56
Thomas Eyde21-Sep-06 2:56 
Generalsequence of Page_Load... Pin
LucyLight18-Sep-06 22:43
LucyLight18-Sep-06 22:43 
AnswerRe: sequence of Page_Load... [modified] Pin
Thomas Eyde18-Sep-06 23:25
Thomas Eyde18-Sep-06 23:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.