Updating a control in a MasterPage from a UserControl

Problem: You have a control, for example a Label, in a MasterPage that you want to update from a UserControl added to a page that resides in the MasterPage’s ContentPlaceHolder control (see figure 1).

Figure 1: From MasterPage to UserControl

I was facing this problem a few months back on a project, where I had many different UserControls that was added to a limited number of pages, and all the pages had the same master page. I wanted an elegant way of updating the status label in my MasterPage while limiting the amount of redundant code. After googling around a bit, I ended up combining a few different solutions into the following.

Solution: The idea is to use the event system in .NET and give each UserControl an event that the UserControl can fire whenever it wants to update the status label. Pages that host the UserControl can then subscribe to the event and update the status label on the MasterPage when the event fires.

Here’s an overview of the solution

  1. Declare a new event in the UserControl.
  2. Declare a new event argument.
  3. On the ASPX page, subscribe to the UserControls event and make an event handler that updates the status label on the master page.
  4. Create a public property on the MasterPage for each access to the status label.

1: Declare a new event in the UserControl
To avoid declaring the event each time I added a new UserControl to my project, I created a new class UserControlBase, which extends System.Web.UI.UserControl, and made all UserControls extend this class, instead of the default System.Web.UI.UserControl. This way I could put all the redundant UserControl code in one file, and be done with it.

// Declare the event handler

public event EventHandler<UpdateStatusEventArgs> StatusLabelUpdateEvent;

/// <summary>

/// Method for triggering the UpdateStatusEventArgs.

/// </summary>

/// <param name="message">Status message to pass to the subscribers</param>

protected void UpdateStatusLabel(string message)

{

    // Copy to a temporary variable to be thread-safe.

    EventHandler<UpdateStatusEventArgs> temp = StatusLabelUpdateEvent;

    if (temp != null)

        temp(this, new UpdateStatusEventArgs(message));

}

And here is the code for the UserControl (notice the new : UserControlBase in the class declaration):

public partial class UserControls_UpdateStatusUserControl : UserControlBase

{

    protected void Page_Load(object sender, EventArgs e) { }

    protected void submitButton_Click(object sender, EventArgs e)

    {

        base.UpdateStatusLabel(messageTextBox.Text);

    }

}

2: Declare a new event argument
I also needed to create an new class – UpdateStatusEventArgs, which extends System.EventArgs, so the UserControls would be able to pass an message back to the ASPX page that can then update the status label.

/// <summary>

/// A status label event args class.

/// </summary>

public class UpdateStatusEventArgs : EventArgs

{

    // private fields

    private string _message;

    /// <summary>

    /// Create an Status Label Event Args with a string

    /// message and default type is StatusType.OK.

    /// </summary>

    public UpdateStatusEventArgs() { }

    /// <summary>

    /// Create an Status Label Event Args with a

    /// string message and default type is StatusType.OK.

    /// </summary>

    /// <param name="message">Update message</param>

    public UpdateStatusEventArgs(string message)

    {

        _message = message;

    }

    /// <summary>

    /// Get/set status message

    /// </summary>

    public string Message

    {

        get { return _message; }

        set { _message = value; }

    }

}

3: On the ASPX page, subscribe to the UserControls event.
On my ASPX page I do two simple things. First I declare my master page through the MasterPage directive, just below Page directive, like so:

<%@ MasterType VirtualPath=”~/MasterPage.master” %>

This provides me with a strongly typed reference to my master page, which makes it a breeze to access properties and methods on it.

In the code behind file I subscribe to the user controls update event, and in my event handler update the status label on the master page.

protected void Page_Load(object sender, EventArgs e)

{

    LoadUserControl();

}

/// <summary>

/// Loads user control in to the ContentPlaceHolder.

/// </summary>

private void LoadUserControl()

{

    // Declare usercontrol

    UserControlBase userControl;

    // Load correct usercontrol

    userControl =

    Page.LoadControl("~/UserControls/UpdateStatusUserControl.ascx")

    as UserControlBase;

    // Subscribe to StatusLabelUpdateEvent

    userControl.StatusLabelUpdateEvent +=

    new EventHandler<UpdateStatusEventArgs>(UpdateStatusLabelEvent);

    // Add it to the page

    PlaceHolder.Controls.Add(userControl);

}

// Event handler, used when a status label

// update request is being triggered

protected void UpdateStatusLabelEvent(object sender, UpdateStatusEventArgs e)

{

    Master.StatusLabel.Text = e.Message;

}

4: Create a public property on the MasterPage for each access to the status label.
The last thing I needed to make was a public property on the master page that returns a reference to the status label.

public Label StatusLabel

{

    get { return statusLabel; }

}

That’s it!

Please excuse the horrible source code in this blog post, I haven’t found a good plugin to do this probably for me yet. I have created a Visual Studio example project, it might be easier to read the code if you just download that and view the source code from Visual Studio.

Leave a Reply