Using the Notifications Framework in a SharePoint 2010 AJAX enabled Web Part

There’s quite a lot of decent content out there about creating AJAX-enabled Visual Web parts in SharePoint 2010. Such as http://msdn.microsoft.com/en-us/gg620536. However, when it comes to creating a standard web part with AJAX, content is a bit more sparse. (Who needs the pain of manually constructing UI’s I hear you say! There are some legitimate reasons for creating an old school web part, the main one being to subclass a built in web part). Anyway, you could be forgiven for thinking that there isn’t much difference between the two, but there are a few gotchas that I thought I’d mention. In addition to that I also wanted to look at how we can use the Notifications Framework with an AJAX-enabled web part.

What is the Notifications framework?

It sounds pretty impressive, as though there are all kinds of whistles and bells that we can use to notify users of stuff. In reality we’ve got one whistle and a flashing light (and the flashing light doesn’t flash).

The Whistle – If you’ve used SharePoint for more than 10 minutes you’ve definitely see the whistle. On the home page of a standard team site, select the Page tab in the ribbon and click the Edit button.

image

You’ll see a Loading.. message appearing in the right corner of the page under the ribbon. This is a notification.

The Flashing Light (that doesn’t flash) – Even been to SharePoint Central Admin? You’ve seen the flashing light (which is most probably red)

image

Believe it or not, that red bar isn’t meant to be there all the time! This is a status message.

These two elements are the core of the Notifications framework and both are used extensively throughout the SharePoint UI. The functions and properties required to use these methods are defined in SP.js which is referenced on the default master page and is therefore omnipresent (unless you’re using a custom master page for publishing purposes or whatever). The documentation for these gizmos can be found at http://msdn.microsoft.com/en-us/library/ee552096.aspx. But they’re pretty easy to use and we’ll see an example later.

Why use the Notifications framework in an AJAX web part?

Where we’re using AJAX to perform time consuming activities we really need to give the user some feedback to let them know that something is happening. Otherwise they’ll click the hell out of the ‘Do Something’ button and whatever activity we’re trying to perform will never complete. Before you know it you’ve got a ton of emails from users complaining that the ‘Do Something’ button is inappropriately named. When developing AJAX based pages, a common solution to this problem is the use the UpdateProgress control. This control allows us to dynamically display content is an AJAX call-back is taking longer than a predefined amount of time (500ms be default). So why re-invent the wheel you may be saying, just use the UpdateProgress control and be done with it! For the record, I have nothing against the UpdateProgress control, we’ve had many happy outings together in the past and I’m sure we’ll continue to do so in future. However, one of the hallmarks of a good user experience is consistency. Since everything else in SharePoint uses the Notifications framework, in the interests of consistency it makes sense that our custom web parts use it as well. (That’s not to say that we cant also use the UpdateProgress control, just that we should always make use of the Notifications framework as well)

A basic AJAX web part

Here’s some code for a basic AJAX-enabled web part:

using System;
using System.ComponentModel;
using System.Text;
using System.Threading;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;

namespace AjaxWebPart.MyWebPart
{
    [ToolboxItem(false)]
    public class MyWebPart : WebPart
    {
        protected Label TheLabel;

        protected override void CreateChildControls()
        {
            var panel = new UpdatePanel {ID = "thePanel"};

            //Important: UpdateMode MUST be Conditional in a webpart to prevent conflicts with the ribbon.
            panel.UpdateMode = UpdatePanelUpdateMode.Conditional;
            var button = new Button {ID = "theButton", Text = "Click Me"};

            button.Click += ButtonClick;
            TheLabel = new Label {ID = "theLabel"};
            Control template = panel.ContentTemplateContainer;
            template.Controls.Add(button);
            template.Controls.Add(TheLabel);
            Controls.Add(panel);
        }

        private void ButtonClick(object sender, EventArgs e)
        {
            TheLabel.Text = string.Format("Clicked {0:F}", DateTime.Now);
            Thread.Sleep(2000);
        }
    }
}

Like all good AJAX examples, this one updates a label with the current time and waits a while before returning. The code is pretty straightforward but there are a few things to point out:

  • The UpdateMode MUST be set to Conditional. The default is Always and if you create a web part in the default mode and manage to add it to page, you’ll find that whenever you click anything in the ribbon that causes an AJAX call-back, you’re web part will be filled with nonsense and the ribbon won’t work any more. All in all, best avoided!
  • Any buttons, dropdowns or whatever else that you want to handle events for, MUST have their control ID’s explicitly set. If you don’t do this the update panel won’t work properly and your controls will trigger regular post backs instead. Naturally this isn’t an issue for Visual Web Parts because the Visual Syudio designer gives all controls an ID when they’re dropped onto the page.

Incorporating a ‘Loading’ Notification

If we add the following code the our web part we can render some script to make the necessary calls to SP.UI.Notify……

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

    if (!Page.ClientScript.IsClientScriptBlockRegistered("UseNotifications"))
    {
        var sb = new StringBuilder();

        int wait = 500;

        sb.AppendLine("Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(CueMessage);");
        sb.AppendLine("Sys.WebForms.PageRequestManager.getInstance().add_endRequest(RemoveNotification);");
        sb.AppendLine("function CueMessage(sender, args){");
        sb.AppendLine("var prm = Sys.WebForms.PageRequestManager.getInstance();");
        sb.AppendLine("var element=args.get_postBackElement();");
        sb.AppendLine("if (!element.theMessage) return;");
        sb.Append("var nq=setTimeout("ShowNotification('" + element.id + "')", ").Append(wait.ToString()).
            AppendLine(");");
        sb.AppendLine("element.nq=nq;");
        sb.AppendLine("args.get_request().element=element;");
        sb.AppendLine("}");
        sb.AppendLine("function ShowNotification(elementId){");
        sb.AppendLine("var element=$get(elementId);");
        sb.AppendLine("if (!element) return;");
        sb.AppendLine("if (element.theMessage){");
        sb.AppendLine("var nid=SP.UI.Notify.addNotification(element.theMessage,true);");
        sb.AppendLine("element.nid=nid;");
        sb.AppendLine("}}");
        sb.AppendLine("function RemoveNotification(sender,args){");
        sb.AppendLine("var element=args.get_response().get_webRequest().element;");
        sb.AppendLine("if(!element) return;");
        sb.AppendLine("if(element.nq)clearTimeout(element.nq);");
        sb.AppendLine("if(element.nid)SP.UI.Notify.removeNotification(element.nid);");
        sb.AppendLine("}");

        Page.ClientScript.RegisterClientScriptBlock(typeof (MyWebPart), "UseNotifications", sb.ToString(), true);
    }
}

The important parts here are:

var nid=SP.UI.Notify.addNotification(element.theMessage,true);

This line renders the notification. The last parameter is set to true because we want the notification to stay there until we remove it. (Since our web part is still doing it’s funky thang). So that we can refer to the notification, the addNotification method returns a notification ID. This is basically a string that uniquely identifies the notification.

SP.UI.Notify.removeNotification(element.nid);

As you’ve probably guessed, this line removes the notification with the id referenced by element.nid

Before any of this will work we need to specify a message to be displayed. To make this easily configurable and to make it possible to have multiple webparts of the same type on the same page, I’ve elected to store the message (and other transient state data) as attributes on the element that causes the callback. In this example that’s the button control. Add the following line to CreateChildControls (after the button has been defined of course!):

button.Attributes["theMessage"] = "Loading" + ID + ". Please Wait...";

That’s about it. Another exciting adventure in SharePoint concludes.

This entry was posted in SharePoint. Bookmark the permalink.