The custom 'Edit web info' Application Page displayed in a SharePoint 2010 Modal Dialog.
SharePoint 2010 ships with the new Modal Dialog framework that allows you to display dialog windows in an unobtrusive and user friendly way. This is extremely important when creating user friendly solutions as it allows users to preserve their context while providing some additional information. While the new modal dialog framework is definitely a great idea, there is one thing missing in the implementation: support for communicating with custom application pages.

Custom Application Pages the 2007-way

SharePoint 2007 lacked any kind of support for dynamic User Experience. Standard Application Pages used postback to process information and in order to provide a seamless experience we had to do exactly the same in our custom Application Pages. Every Application Page had some logic attached to it and after completing its work, it redirected the user either to where he came from or to the current Site.

SharePoint 2010 Modal Dialogs

In SharePoint 2010 the situation looks a bit different. SharePoint 2010 ships with the new Modal Dialog framework which allows you to interact with users in an unobtrusive way using just a few lines of JavaScript.

The following JavaScript snippet is sample code that you could use to open a SharePoint page in a modal dialog:

var options = {
  url:'/_layouts/prjsetng.aspx',
  title:'Title, Description, and Icon',
  width:640,
  height:400
}

SP.UI.ModalDialog.showModalDialog(options);

The ‘Title, Description, and Icon’ page opened in a SharePoint 2010 Modal Dialog.

As you can see it’s really easy for us to benefit of the new capabilities in our custom solution. In the background SharePoint automagically removes the Ribbons, menus etc. from the page leaving only the content. This is a great behavior because it allows you to create interfaces that are both suitable for normal view and Modal Dialogs without a lot of extra effort! There is however one ‘however’…

The ‘Site Settings’ page opened in a SharePoint 2010 Modal Dialog.

This is exactly what happens when you click the OK button on the Title, Description, and Icon page. The page will use its default behavior and will redirect to the Site Settings page. In fact most of the standard SharePoint 2010 Application Pages use the ‘old’ behavior.

Custom Application Pages and Modal Dialogs: what’s in the box?

Developing custom Application Pages is a regular part of many SharePoint Solutions. Whether it’s for management purposes or for requesting user input, Application Pages are a great way to interact with users. The Modal Dialog framework in SharePoint 2010 makes it extra interesting since it allows you to use Application Pages in an unobtrusive way. Unfortunately out of the box there is very little standard support for leveraging Modal Dialogs with Application Pages. And although it seems like a really generic functionality, it is something you would have to do yourself for the biggest part. Sounds like a call to action.

Side note on the sample case

To help you better understand the examples in this article I chose to create a custom Application Page similar to the standard ‘Title, Description, and Icon’ page provided with SharePoint. I chose that particular page because it requires you to differently handle closing the Modal Dialog depending on the taken action, which is great for discussing the capabilities available in SharePoint 2010. To make it easier for you to distinguish that I’m talking about the custom page I called it the ‘Edit web info’ page.

Step 1: The Dialog Application Page base class

Support for Modal Dialogs in Application Pages is something generic. As it’s something you will be reusing in all your custom Application Pages it’s a good idea to create a base class that contains all the plumbing that you need to get the job done. You can then create your custom Application Pages and by inheriting from that class you can ensure that you have consistent baseline within the whole solution or even across solutions.

Defining the base class

/// <summary>
/// Modal Dialog-aware Application Page base class.
/// </summary>
public abstract class DialogLayoutsPageBage : LayoutsPageBase
{
    /// <summary>
    /// URL of the page to redirect to when not in Dialog mode.
    /// </summary>
    protected string PageToRedirectOnOK { get; set; }
    /// <summary>
    /// Returns true if the Application Page is displayed in Modal Dialog.
    /// </summary>
    protected bool IsPopUI
    {
        get
        {
            return !String.IsNullOrEmpty(base.Request.QueryString["IsDlg"]);
        }
    }

    /// <summary>
    /// Call after completing custom logic in the Application Page.
    /// Returns the OK response.
    /// </summary>
    protected void EndOperation()
    {
        EndOperation(1);
    }

    /// <summary>
    /// Call after completing custom logic in the Application Page.
    /// </summary>
    /// <param name="result">Result code to pass to the output. Available results: -1 = invalid; 0 = cancel; 1 = OK</param>
    protected void EndOperation(int result)
    {
        EndOperation(result, PageToRedirectOnOK);
    }

    /// <summary>
    /// Call after completing custom logic in the Application Page.
    /// </summary>
    /// <param name="result">Result code to pass to the output. Available results: -1 = invalid; 0 = cancel; 1 = OK</param>
    /// <param name="returnValue">Value to pass to the callback method defined when opening the Modal Dialog.</param>
    protected void EndOperation(int result, string returnValue)
    {
        if (IsPopUI)
        {
            Page.Response.Clear();
            Page.Response.Write(String.Format(CultureInfo.InvariantCulture, "<script type=\"text/javascript\">window.frameElement.commonModalDialogClose({0}, {1});</script>", new object[] { result, String.IsNullOrEmpty(returnValue) ? "null" : String.Format("\"{0}\"", returnValue) }));
            Page.Response.End();
        }
        else
        {
            RedirectOnOK();
        }
    }

    /// <summary>
    /// Redirects to the URL specified in the PageToRedirectOnOK property.
    /// </summary>
    private void RedirectOnOK()
    {
        SPUtility.Redirect(PageToRedirectOnOK ?? SPContext.Current.Web.Url, SPRedirectFlags.UseSource, Context);
    }
}

The above snippet presents a base class for a Modal Dialog-aware Application Page. All the different pieces are explained in XML comments, but probably the best way to understand how things work is to see it in action.

Behind the scenes of a Modal Dialog-aware Application Page

The ‘Edit web info’ custom Application Page.

The following code is the code behind of our custom ‘Edit web info’ page.

public partial class EditWebInfo : DialogLayoutsPageBage
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            WebTitle.Text = Web.Title;
            WebDescription.Text = Web.Description;
            WebName.Text = Web.Name;
        }
    }

    protected void BtnUpdateWeb_Click(object sender, EventArgs e)
    {
        if (IsValid)
        {
            Web.Title = WebTitle.Text;
            Web.Description = WebDescription.Text;
            bool urlChanged = !String.IsNullOrEmpty(WebName.Text) && !Web.Name.Equals(WebName.Text);
            if (urlChanged)
            {
                string source = Request.QueryString["Source"];
                if (!String.IsNullOrEmpty(source))
                {
                    PageToRedirectOnOK = source.Replace(Web.Name, WebName.Text);
                }

                Web.Name = WebName.Text;
            }

            Web.Update();

            EndOperation(1);
        }
    }
}

As you can see the code is fairly simple. In the Page_Load method we retrieve some values from the current Site and fill the controls on the page with them. In the BtnUpdateWeb_Click event handler we do the opposite: we take the new values and update the current Site with them.

Following the User Experience

Because we want the User Experience to be as good as possible we have a few different cases to handle.

First of all there is the scenario when user either closes the Modal Dialog using the Close button or clicks the Cancel button. In this scenario nothing has changed and we don’t have to do anything. The good news is that is already being handled by SharePoint out of the box and we don’t have to worry about it.

The next scenario is when the user changes the Title and/or the Description of the Site. What we need to do is to save the new values and refresh the Site so that the user can see the values being updated.

The last scenario is when the user changes the URL of the Site. In this case we have to get the URL of the current page, replace the URL part of the Site with the new value and redirect the user to the new URL.

From the Application Page point of view the only difference between the last two cases is the redirection. In the second scenario the value of the PageToRedirectOnOK property is being set to the new URL what allows the base class to redirect the user to the current page but then at changed URL. Finally, we call the simple variant of the EndOperation method with the OK result. If the Application Page has been opened in a Modal Dialog a JavaScript snippet will be sent to the output. This snippet will contain the OK result and depending on the action the new URL of the site.

Step 2: Handling the response on the client-side

Good User Experience around Application Pages consists of two pieces: not only we need to cover correct actions on the server, but also we have to take care that there is as little disruption for the users as possible. In other words: if there is no need to reload the whole page – don’t do it.

Earlier in this article I showed you a simple JavaScript snippet that can be used to open a SharePoint 2010 Modal Dialog. That sample was pretty straight forward and didn’t contain any logic for handling the response from the Application Page. Let’s extend it a little and add some handling routines to it:

var options = {
    url: SP.Utilities.Utility.getLayoutsPageUrl('DialogApplicationPages/EditWebInfo.aspx?Source=' + location.href),
    title: 'Edit web info',
    allowMaximize: false,
    showClose: true,
    width: 800,
    height: 330,
    dialogReturnValueCallback: Function.createDelegate(null, function (result, returnValue) {
        if (result == SP.UI.DialogResult.OK) {
            if (returnValue == null) {
                SP.UI.Notify.addNotification('Operation successful');
                SP.UI.ModalDialog.RefreshPage(SP.UI.DialogResult.OK);
            }
            else {
                location.href = returnValue;
            }
        }
    })
};

SP.UI.ModalDialog.showModalDialog(options);

The general idea is the same: we create a new object with all the options for our dialog and pass it to the SP.UI.ModalDialog.showModalDialog function. The difference is in the dialogReturnValueCallback property that now contains a delegate to the function that will be executed after the Modal Dialog window has been closed.

The delegate function executed after closing a Modal Dialog takes two parameters: result, which is an integer and can have one of the three values: invalid (-1), cancel (0) or OK (1), and returnValue which is an optional value that can contain some data from the Application Page. Getting back to our three scenarios…

In the first scenario, where user clicks the Cancel button, the result parameter will be equal to SP.UI.DialogResult.cancel or 0. So by checking for the value of the result parameter we can ensure that we will execute code only if the Modal Dialog has been closed by clicking the OK button.

In the second scenario, where only the title and/or description of the Site have been changed, the returnValue parameter is empty. In this case we don’t need to reload the whole page. The Modal Dialog framework allows us to quickly refresh the page by calling the SP.UI.ModalDialog.RefreshPage function, which has definitely a better experience than reloading the whole page.

Finally, in the last scenario, where the URL of the Site has been changed, the new URL is passed in the returnValue parameter and we use it to redirect the user to the new URL.

And that’s really it. The above framework provides you with the basic functionality for communicating with Application Pages and passing the results to the JavaScript in the originating page. From here you can easily extend the implementation to fit the specific needs of your application.

Technorati Tags: