Displaying a SharePoint Modal Dialog from Ribbon Button and Getting Selected Item Context

Another really cool new feature in SharePoint 2010 is the new dialog framework provided by the JavaScript client object model. Inside the new client object model is a JavaScript static class called “SP.UI.ModalDialog” that provides a number of helpful methods for working with the dialog framework. In this blog post, I will discuss the process for displaying a SharePoint Dialog using the client object model and wiring it up to a custom ribbon button and also showing how to get context to selected items in a list or library at the time the ribbon button was clicked.

Also I need to mention that I am using version 14.0.4605.1000 of SharePoint 2010, this version is newer than Beta 1 but not quite full Beta 2. I am hoping that as we get closer to RTM, upcoming versions still work with my examples in this post. If you do encounter any weirdness with the samples shown here, please let me know by leaving a comment on this post and I will do my best to investigate and see what’s up.

Displaying a SharePoint Modal Dialog from Ribbon Button and Getting Selected Item Context

Looking at "SP.UI.ModalDialog.showModalDialog()"

The method in the SP.UI.ModalDialog class that launches the dialog is SP.UI.ModalDialog.showModalDialog() and it takes a parameter of an object called “options”. The “options” parameter is really quite important because it communicates to the dialog all the settings for it such as the URL to be displayed inside the dialog, its dimensions and other settings. The pattern I’ve used and seen so far is just to declare a literal object called “options (could be any name you like though of course) with the correct members specified, then pass this object along to “showModalDialog()”. To see this in action, check out the custom ribbon button code sample below. In this code we are defining the the properties of a custom ribbon button. It is inside the “CommandAction” attribute that is inside the “CommandUIHandler” that we specify the JavaScript that is executed when the button is clicked. In our case, we are launching a dialog. If you would like to learn more about adding a custom button to the SharePoint 2010 ribbon, please have a look  How to add Custom Button to the SharePoint 2010 Ribbon

I should also mention that in order for the dialog to be completely useful, you will need to deploy a custom page such as an application page first to your SharePoint web application that you can use to render in your dialog.

Something else cool to mention is that you may notice that OOB in SharePoint the pages in the dialogs seem to have some smartness with their masterpage chrome. It turns out that now in SharePoint 2010, masterpages won’t show all the top and left nav stuff if they are being displayed inside a dialog launched using the showModalDialog() function. The masterpages do this because now if any content in SharePoint is marked up using the CSS class “s4-notdlg” it will render when viewed normally in a browser, but when rendered through a client object modal dialog, the content marked with this CSS class will not be displayed.

One last side note to point out is that because the “showModalDialog()” function is simply part of the SharePoint 2010 client object model we could actually call it from anywhere, such as a context menu item click or a button on the screen, it doesn’t necessarily have to be a custom ribbon button. But I am suspecting that launching dialogs from custom ribbon buttons might be a pretty common pattern as more custom applications are written on SharePoint 2010.

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

<CustomAction

Id="SkynetCustomRibbonButton"
RegistrationId="101"
RegistrationType="List"
Location="CommandUI.Ribbon"
Sequence="5"
Title="Move Documents">

<CommandUIExtension>
<CommandUIDefinitions>
<CommandUIDefinition Location="Ribbon.Documents.Manage.Controls._children">
<Button
Id="Ribbon.Documents.New.SkynetTestButton"
Alt="Move Documents"
Sequence="5"
Command="Skynet_Test_Button"
Image32by32="/_layouts/images/Skynet/WinEarth32x32.png"
Image16by16="/_layouts/images/Skynet/WinEarth16x16.png"
LabelText="Move Documents"
TemplateAlias="o1" />

</CommandUIDefinition>

</CommandUIDefinitions>
<CommandUIHandlers>
<CommandUIHandler
Command="Skynet_Test_Button"
CommandAction="javascript:

function demoCallback(dialogResult, returnValue)
{
SP.UI.Notify.addNotification('Operation Successful!');

SP.UI.ModalDialog.RefreshPage(SP.UI.DialogResult.OK);
}

var ctx = SP.ClientContext.get_current();
var items = SP.ListOperation.Selection.getSelectedItems(ctx);
var myItems = '';
var k;

for (k in items)
{
myItems += '|' + items[k].id;
}

var options = {
url: '/_layouts/Skynet/ShowItems.aspx?items=' + myItems + '&amp;source=' + SP.ListOperation.Selection.getSelectedList(),
tite: 'Move Documents',
allowMaximize: false,
showClose: false,
width: 800,
height: 600,
dialogReturnValueCallback: demoCallback };

SP.UI.ModalDialog.showModalDialog(options);"
/>

</CommandUIHandlers>

</CommandUIExtension>

</CustomAction>

</Elements>







Code Analysis



To delve a little deeper into the code sample above, on line 39 we are declaring our literal object called “options” that we create along with the required members. The code above covers a number of all the possible fields available to be used in the option class, but there are a few more too. If you are curious, and would like to dig in further to see all the possible fields for the options object, and how the options object is parsed inside the showModalDialog() function, check out the .js file named “SP.UI.Dialog.debug.js” located in the folder “{14 Hive}\TEMPLATE\LAYOUTS\” – on lines 24 to 93 you can see the actual code that is processing this object.



One of the members of the options object that is, I think, particularly important to point out is “dialogReturnValueCallback”. This field takes a user defined delegate function which subsequently will be called after the dialog is closed. This could be useful for a lot of reasons, one I’ve already ran into is wanting to refresh the originating list or library if the dialog is doing something that updates the items. Also, another cool new feature of the SharePoint 2010 client object mode and good use for the callback function is a function called “SP.UI.Notify.addNotification()”. This function is shown in my “demoCallback” delegate function above and when called displays a friendly, subtly fading message on the top right of the screen. This is an attractive way to display some kind of feedback to an end user after the dialog is closed.



I mentioned refreshing the list as a reason to use the callback function. The function I found for doing this (after digging around in the OOB .js files for inspiration) is “SP.UI.ModalDialog.RefreshPage()”. RefreshPage takes a couple of different parameters, but the one that I found works best to gracefully refresh the list without a full postback was to use the built-in enumeration of “SP.UI.DialogResult.OK” as seen above on line 36.







Getting Selected Item Context



The other big concept illustrated in the code sample above is getting context on the items selected in the list or library the user launched the dialog from. This was something that took me a little while to figure out because I could not find too many examples of it yet. In the end I actually ended up interrogating the OOB features of “Document Sets” and “In Place Records Management” to see inside their .js files how they accomplished getting item context when their dialogs are launched.



The key function I found I needed to use was “getSelectedItems()”, as shown on line 40 in the above code sample, which lives in the static JavaScript class “SP.ListOperation.Selection”. This function takes in a context object so first I had to get context using the “SP.ClientContext.get_current()” function then pass the resulting object as the single parameter of “getSelectedItems()”. The function “getSelectedItems()” then returns a loosely typed collection object (well, everything is loosely typed in JavaScript!) of the selected items in the list or library that I then iterate through using a “for” loop, where in each iteration of the loop I snag the ID of the item and build up a string of all the IDs using a pipe symbol as a delimiter (this will come in handy shortly).



From there, on line 50 in the above code sample, I append query string parameters onto my URL field of my options object of the built-up string of IDs, delimited with pipe symbols, and also the ID GUID of the current list. The key reason for employing the query string for this, as I am sure you’ve guessed by now, is that on my ShowItems.aspx application page, I parse these query string parameters server-side, performing a string.Split(‘|’) call on the string of IDs and using the ID GUID of the list to get context of the items that were selected client-side. Once you get to this point, your options are endless with our old friend the SharePoint server-side API. The code sample below is from the code behind of my ShowItems.aspx application page and illustrates the server-side aspect discussed:




using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections.Generic;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.WebControls;

namespace Skynet.CustomRibbonButton
{
public class DemoPage : LayoutsPageBase
{
protected Label LabelItems;
protected TextBox TextDestination;

protected System.Collections.Generic.List<SPListItem> ListItems;

protected override void OnLoad(EventArgs e)
{
if (Request.QueryString["items"] != null && Request.QueryString["source"] != null)
{
string source = Request.QueryString["source"];
string[] items = Request.QueryString["items"].ToString().Split('|');

LabelItems.Text = "You have selected the following items to move:<br><br>";

source = source.Substring(1, source.Length - 2).ToLower();

Guid sourceID = new Guid(source);

SPDocumentLibrary sourceDocLib = (SPDocumentLibrary)SPContext.Current.Web.Lists[sourceID];

ListItems = new System.Collections.Generic.List<SPListItem>();

for (int i = 1; i < items.Length; i++)
{
SPListItem currentListItem = sourceDocLib.GetItemById(int.Parse(items[i]));

ListItems.Add(currentListItem);
LabelItems.Text += currentListItem.Name + "<br>";
}
}
}

// more stuff here, omitted for simplicity
}
}







Some Further Visuals



After you add the the CommandAction code as shown in the code sample above and redeploy your feature, you can have a dialog appear that looks like the following below. As you can see we are hiding the maximize button and close button as specified in the options object, along with rendering the custom application page as the URL value. Also, due to getting context of the selected items from the originating library, I am then rendering the names of the files on the server-side code, using the IDs of each item to get context to the actual SPListItem object.



Displaying a SharePoint Modal Dialog from Ribbon Button and Getting Selected Item Context



As mentioned earlier, below is an example of what happens when you close the dialog. Because we make a call to “SP.UI.Notify.addNotification()” on the callback function delegate, the attractive yellow message appears on the top right of the page.



Displaying a SharePoint Modal Dialog from Ribbon Button and Getting Selected Item Context



Well that’s it for now. I hope to be posting more on the topic of the dialog framework and the SharePoint 2010 client object model overall since I find these topics really interesting and also because I am working with them for my current project and it’s nice to keep track of what I’ve been working on for future reference.