Using a generic ascx control to display messages to the user

I have many times been involved in projects where the customer wants the application (web/SharePoint) to inform the user when something is successfully completed or for that matter when the app fails.
Since I usually implement things using ascx controls (usercontrols) this has in most cases been implemented using a method on the masterpage/page that the ascx control can talk to and then the page/masterpage takes care of displaying the message to the user.
The downfall of this approach is of course that the ascx control has to know on which page/masterpage it resides.

Another solution is using an event on the ascx control that the page/masterpage can subscribe to which is much better but still, there´s usually a lot of duplicate code to handle the same sort of event subscription (if we have multiple usercontrols). One for each ascx control on the page/masterpage.

The generic usercontrol solution

Note: The solution for this situation isn´t really based on a “generic” usercontrol but rather on a base class for a usercontrol that has a generic event.

Let´s look at an example where we can imagine an ascx usercontrol with a button called SaveButton. When the user clicks the button we attempt to save the contents of the control. That might look something like this.

 public partial class HelloWorldUserControl : CustomUserControl
 {
protected void SaveButton_Click(object sender, EventArgs e)
{
if (this.Page.IsValid)
{
try
{
// perform save operation
DisplayInformation("Save event handled successfully");
}
catch (Exception ex)
{
RaiseError(”Save event was not handled successfully”);
}

}

}

As you might see the control derives from a base class called CustomUserControl which has the methods DisplayInformation and RaiseError. That base class for an ascx usercontrol might look something like this:

public abstract class CustomUserControl : System.Web.UI.UserControl
 {

 public event EventHandler<EventArgs<string>> DisplayingError = delegate { };
 public event EventHandler<EventArgs<string>> DisplayingInformation = delegate { };

 protected override void OnError(EventArgs e)
 {
 Exception exception = Server.GetLastError();
 RaiseError(exception.Message);
 }

 public void RaiseError(string message)
 {
 DisplayingError(this, new EventArgs<string>(message));
 }

 public void DisplayInformation(string message)
 {
 DisplayingInformation(this, new EventArgs<string>(message));
 }

At the top you can see two events exposed for handling the error and information events. So when the “child” ascx control calls either RaiseError or DisplayInformation these events are used to inform whoever is listening that we wan´t to show the user some visual feedback.
As you might see I have used a sort of generic event handler for passing string messages (EventArgs<string>) in the event. That is a flavor I like to use not needing to create a custom eventargs class just for passing strings.
The generic event handler class has the following look:

 public class EventArgs<T> : EventArgs
 {
 public EventArgs(T value)
 {
 m_value = value;
 }

 private T m_value;

 public T Value
 {
 get { return m_value; }
 }

 public override string ToString()
 {
 return m_value.ToString();
 }
 }

Note: The inspiration for this code comes from Matthew Cochran.

So now on to the masterpage, in this case, that will ultimately contain this (or these) types of CustomUserControl instances. What I would like to do is to let the masterpage determine how many CustomUserControl instances there are on the page and subscribe to the events exposed from those controls. So in the OnLoad event I have code that look like this:

protected override void OnLoad(EventArgs e)
 {
 base.OnLoad(e);

 var CustomUserControls = this.Controls.FindControlsOfType<CustomUserControl>();

 foreach (var ctrl in CustomUserControls)
 {
 ctrl.DisplayingError += (caller, args) =>
 {
 ShowError(args.Value);
 };
 ctrl.DisplayingInformation += (caller, args) =>
 {
 ShowInformation(args.Value);
 };
 }


So, first I will find all the controls that are of type CustomUserControl and then add event handlers for the two events exposed from them. I have constructed an extension method, FindControlsOfType, that will do this for me. It has the following look:

public static IEnumerable<T> FindControlsOfType<T>(this ControlCollection Controls) where T : class
 {

 T control;

 foreach (Control ctrl in Controls)
 {
 if ((control = ctrl as T) != null)
 {
 yield return control;
 }

 foreach (T child in FindControlsOfType<T>(ctrl.Controls))
 {
 yield return child;
 }
 }

 }

Note: The inspiration for this code comes from David Findley.

The last thing to do is implement a mechanism to display messages to the user. This can of course be done in numerous ways but I got my inspiration from Matt Berseth that uses the ScriptManager´s RegisterDataItem method to send information to the client during async postbacks. I have also made it possible for standard postbacks to use the same method. Ok, let’s look at the code:

 public void ShowInformation(string message)
 {
 if (ScriptManager.IsInAsyncPostBack)
 {
 try
 {
 ScriptManager.RegisterDataItem(informationPane, message);
 }
 catch
 {
 LiteralControl info = new LiteralControl(message);
 informationPane.Controls.Add(info);
 informationPane.CssClass += " fullpost";
 }
 }
 else
 {
 LiteralControl info = new LiteralControl(message);
 informationPane.Controls.Add(info);
 informationPane.CssClass += " fullpost";
 }
 }



ShowError has the same look but uses a different panel called errorPane to send messages to.

In the markup for the masterpage I will handle the messages sent during async postbacks and I also handle the ones that are not. The markup looks something like this (with some markup removed for clarity):

<script type="text/javascript">


 var prm = Sys.WebForms.PageRequestManager.getInstance();


 prm.add_pageLoaded(function(sender, args) {


 var infoPane = '<%= this.informationPane.ClientID %>';
 var errorPane = '<%= this.errorPane.ClientID %>';
 var info = true;
 var text = args.get_dataItems()[infoPane];

 if (text === undefined) {
 text = args.get_dataItems()[errorPane];
 if (text) {
 info = false;
 }
 }

 if (text) {

 var elem = $("#<%= this.informationPane.ClientID %>");
 if (info == false) {
 elem = $("#<%= this.errorPane.ClientID %>");
 }
 elem.center({ 'absolute': true });
 elem.html(text);
 elem.fadeIn(2500, function() {
 setTimeout(function() {
 elem.fadeOut();
 }, 2500);
 });
 }
 });


 $(".fullpost").center({ 'absolute': true }).fadeIn(2500, function() {
 setTimeout(function() {
 $(".fullpost").fadeOut();
 }, 2500);
 });

 });


 </script>

 <asp:Panel ID="informationPane" CssClass="informationPane" runat="server" Style="display: none;"
 EnableViewState="false" />
 <asp:Panel ID="errorPane" CssClass="errorPane" runat="server" Style="display: none;"
 EnableViewState="false" />


Please note that the code uses both the MS Ajax library as well as the JQuery library.

What happens is that the code checks if the message sent during async postbacks is sent to either the information panel or the error panel and injects the message into the appropriate control. Then it fades the control in and shows it for 2.5 seconds before it fades it out again. I also use a Jquery extension for positioning the element (panel) in the center of the page which has the effect of displaying the message on top of the contents of the page.

I also have support for finding every element with the “fullpost” css class attached to it (normal postbacks) and displaying those in the same manner.

And the end result might have the following look when a user has clicked the button:

Note: I have used this approach both in SharePoint and in a custom asp.net app, it doesn’t really matter since SharePoint is of course based upon asp.net…

, , , ,

  1. #1 by Giancarlo on June 24, 2010 - 14:27

    Hi,
    i implemented your solution in a ASP.NET project to handle the warning/error messages. it worked very well and the graphic effect (fadein-fadeout) is really cool ;).
    Thanks for this post , i learned a lot …

    • #2 by Johan Leino on June 24, 2010 - 14:59

      Great that you liked it! I have an upcoming variation of this post that uses a lot more JQuery stuff. Hoping to post it ASAP…

      • #3 by cerasela on May 6, 2013 - 21:03

        Very useful post ! I was able to implement it successfully on server-side but had some issues on client-side. Therefore I changed the client-side code and used the jquery-ui dialog widget to display the message. However I am curious about your jquery approach !!

Leave a reply to Johan Leino Cancel reply