Welcome to MSDN Blogs Sign in | Join | Help

How to Display a SharePoint Dialog from Ribbon Button and Get Selected Item Context

Overview

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.

Before I proceed I really must give a shout out to my colleague Elisabeth Olson at Microsoft.  Thanks to her awesome demos and presentations from recent events and conferences, I got a great head start on learning how the client-side dialoging object model worked.

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.

image

Screen shot showing the dialog framework in action for edit list item properties. It’s draggable too!

 

 

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, I have a recent blog post on this topic.

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.

 

Code Snippet
  1. <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  2.  
  3.   <CustomAction
  4.     
  5.     Id="SkynetCustomRibbonButton"
  6.     RegistrationId="101"
  7.     RegistrationType="List"
  8.     Location="CommandUI.Ribbon"
  9.     Sequence="5"
  10.     Title="Move Documents">
  11.  
  12.     <CommandUIExtension>
  13.       <CommandUIDefinitions>
  14.         <CommandUIDefinition Location="Ribbon.Documents.Manage.Controls._children">
  15.           <Button
  16.               Id="Ribbon.Documents.New.SkynetTestButton"
  17.               Alt="Move Documents"
  18.               Sequence="5"
  19.               Command="Skynet_Test_Button"
  20.               Image32by32="/_layouts/images/Skynet/WinEarth32x32.png"
  21.               Image16by16="/_layouts/images/Skynet/WinEarth16x16.png"
  22.               LabelText="Move Documents"
  23.               TemplateAlias="o1" />
  24.         </CommandUIDefinition>
  25.       </CommandUIDefinitions>
  26.  
  27.       <CommandUIHandlers>
  28.         <CommandUIHandler
  29.           Command="Skynet_Test_Button"
  30.           CommandAction="javascript:
  31.           
  32.               function demoCallback(dialogResult, returnValue)
  33.               {
  34.                 SP.UI.Notify.addNotification('Operation Successful!');
  35.                 
  36.                 SP.UI.ModalDialog.RefreshPage(SP.UI.DialogResult.OK);
  37.               }
  38.               
  39.               var ctx = SP.ClientContext.get_current();
  40.               var items = SP.ListOperation.Selection.getSelectedItems(ctx);
  41.               var myItems = '';
  42.               var k;
  43.               
  44.               for (k in items)
  45.               {
  46.                 myItems += '|' + items[k].id;
  47.               }
  48.               
  49.               var options = {              
  50.                 url: '/_layouts/Skynet/ShowItems.aspx?items=' + myItems + '&amp;source=' + SP.ListOperation.Selection.getSelectedList(),
  51.                 tite: 'Move Documents',
  52.                 allowMaximize: false,
  53.                 showClose: false,
  54.                 width: 800,
  55.                 height: 600,
  56.                 dialogReturnValueCallback: demoCallback };
  57.               
  58.               SP.UI.ModalDialog.showModalDialog(options);" />
  59.         
  60.       </CommandUIHandlers>
  61.  
  62.     </CommandUIExtension>
  63.   </CustomAction>
  64.   
  65. </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:

 

Code Snippet
  1. using System;
  2. using System.Web;
  3. using System.Web.UI;
  4. using System.Web.UI.WebControls;
  5. using System.Collections.Generic;
  6. using Microsoft.SharePoint;
  7. using Microsoft.SharePoint.Client;
  8. using Microsoft.SharePoint.WebControls;
  9.  
  10. namespace Skynet.CustomRibbonButton
  11. {
  12.     public class DemoPage : LayoutsPageBase
  13.     {
  14.         protected Label LabelItems;
  15.         protected TextBox TextDestination;
  16.         
  17.         protected System.Collections.Generic.List<SPListItem> ListItems;
  18.         
  19.         protected override void OnLoad(EventArgs e)
  20.         {
  21.             if (Request.QueryString["items"] != null && Request.QueryString["source"] != null)
  22.             {
  23.                 string source = Request.QueryString["source"];
  24.                 string[] items = Request.QueryString["items"].ToString().Split('|');
  25.                 
  26.                 LabelItems.Text = "You have selected the following items to move:<br><br>";
  27.  
  28.                 source = source.Substring(1, source.Length - 2).ToLower();
  29.  
  30.                 Guid sourceID = new Guid(source);
  31.  
  32.                 SPDocumentLibrary sourceDocLib = (SPDocumentLibrary)SPContext.Current.Web.Lists[sourceID];
  33.  
  34.                 ListItems = new System.Collections.Generic.List<SPListItem>();
  35.  
  36.                 for (int i = 1; i < items.Length; i++)
  37.                 {
  38.                     SPListItem currentListItem = sourceDocLib.GetItemById(int.Parse(items[i]));
  39.  
  40.                     ListItems.Add(currentListItem);
  41.                     LabelItems.Text += currentListItem.Name + "<br>";
  42.                 }
  43.             }
  44.         }
  45.  
  46.         // more stuff here, omitted for simplicity
  47.     }
  48. }

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.

 

image

 

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.

 

image

 

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.

Posted by jfrost | 2 Comments

Adding Custom Button to the SharePoint 2010 Ribbon

In this blog post I will walk through how to add a custom button to the new SharePoint 2010 ribbon.  One very big and noticeable update in SharePoint 2010 is that we now have an awesome, Office client-like ribbon the provides most of the controls and buttons now in SharePoint 2010.  A question that has come up on my current project is, how can we add new buttons to it that do cool stuff, like popup dialogs and so forth?  In this post I will walk through just adding a custom button, but in an upcoming post I will walk through how to make a popup dialog render when a custom button is clicked.

For reference, the version of SharePoint 2010 I am using is 14.0.4605.1000.  This version is newer than Beta 1 (also called Technical Preview) but not quite full Beta 2.  So I unfortunately can’t guaranty that these steps will work in full Beta 2 or RTM.  But nevertheless, I hope the process I use and my notes along the way can be useful!

Step 1: Create Feature Definition File

The first step to adding a custom button is to create a new feature definition file.  This schema has changed very little since SharePoint 2007.  In fact, I have been using old SharePoint 2007 samples as my base and not changing anything and they work great!

Code Snippet
  1. <Feature xmlns="http://schemas.microsoft.com/sharepoint/"
  2.  
  3.          Id="afe52f83-a20e-42d4-8aa7-35d8c5fdfb39"
  4.          Title="Skynet Custom Ribbon Button"
  5.          Scope="Web"
  6.          Description="Skynet Custom Ribbon Button"
  7.          ImageUrl="Skynet/WinEarth32x32.png">
  8.  
  9.   <ElementManifests>
  10.  
  11.     <ElementManifest Location="RibbonButton.xml"/>
  12.  
  13.   </ElementManifests>
  14.  
  15. </Feature>

Step 2: Create Feature Elements File

Just like back in SharePoint 2007, we still need to create feature “Elements.xml” files.  In this case I am calling mine “RibbonButton.xml”.  This file contains the real information that dictates what kind of button is shown, what happens when you click on it and so forth.

There are a number of important things to point out about the XML snippet below.  First is that in contrast to current documentation, I still needed to add lines 6 and 7 below where I am specifying that this CustomAction is geared for a Document Library list type – hence “101”, the code for Document Library which hasn’t changed since the last version of SharePoint and even the one before that if I am not mistaken.

Another item to note is line 8, the “Location” attribute.  In Beta 1 we had to use “ViewToolbar” this has now been changed to “CommandUI.Ribbon”.

The attribute “Sequence” below on line 9, just as in the past, specifies the ordering of the button, now in terms of left to right as we are in the context of the horizontally aligned ribbon.  The value “5” in this case is pretty low, so in my example this makes my custom button the first in its grouping (shown in screen shot below).

Code Snippet
  1. <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  2.  
  3.   <CustomAction
  4.    
  5.     Id="SkynetCustomRibbonButton"
  6.     RegistrationId="101"
  7.     RegistrationType="List"
  8.     Location="CommandUI.Ribbon"
  9.     Sequence="5"
  10.     Title="Move Documents">
  11.  
  12.     <CommandUIExtension>
  13.       <CommandUIDefinitions>
  14.         <CommandUIDefinition Location="Ribbon.Documents.Manage.Controls._children">
  15.           <Button
  16.               Id="Ribbon.Documents.New.SkynetTestButton"
  17.               Alt="Move Documents"
  18.               Sequence="5"
  19.               Command="Skynet_Test_Button"
  20.               Image32by32="/_layouts/images/Skynet/WinEarth32x32.png"
  21.               Image16by16="/_layouts/images/Skynet/WinEarth16x16.png"
  22.               LabelText="Move Documents"
  23.               TemplateAlias="o1" />
  24.         </CommandUIDefinition>
  25.       </CommandUIDefinitions>
  26.  
  27.       <CommandUIHandlers>
  28.         <CommandUIHandler
  29.           Command="Skynet_Test_Button"
  30.           CommandAction="javascript:alert('SharePoint 2010 Rocks!');" />
  31.       </CommandUIHandlers>
  32.  
  33.     </CommandUIExtension>
  34.   </CustomAction>
  35.  
  36. </Elements>

 

Looking down the XML snippet, we get to line 14.  Here we specify the “Location” of the button in the ribbon.

The schema here is basically: Ribbon.[Tab].[Group].Controls._children

Because the exact schema for these locations is not yet published, one learning tool I’ve been using is checking out the built-in features that come out of the box with SharePoint 2010 in the 14 Hive in the TEMPLATE\FEATURES folder.  There are a number of features in there like In Place Records Management or Document Sets where you can get ideas of where that feature’s custom buttons are placed and then snag that location.

Another attribute to talk on is “Command” on line 19.  Here we specify the name of the command which links together the “CommandUIDefinition” with its related “CommandUIHandler”.  The “CommandUIDefinition” defines where the button goes and how it looks while the “CommandUIHandler” specifies the action that takes place when the button is clicked.

On line 23 is “TemplateAlias”.  This was one I was not quite sure about until I messed around with it a bit and found that when I made the value “o2” it made the button into the smaller, 16 x 16 version of itself.

There is a lot more that you can do with this schema, such as specifying how the ribbon should scale on itself when the browser window is resized, such as which buttons take priority over others when ribbon space is reduced.  Also, there are other types of ribbon controls and buttons you can use.  One type you can get an idea from looking at the In Place Records Management feature schema is grouping multiple buttons under one parent drop down button.

Moving on to the “CommandUIHandler” section, there are some interesting items here to note as well.  On line 29 we have our “Command” attribute again that links this “CommandUIHandler” with our “CommandUIDefinition” above.

More importantly is the “CommandAction” attribute.  In Beta 1 this was called “CommandScript”, this has now been changed to “CommandAction” in this later build and I understand this will stay.  In this attribute we specify the JavaScript that is invoked when the button is clicked.  There are really an unlimited number of options of things you could do here.  Some options include navigating to a different page, popping up an AJAX dialog, or performing actions on selected items.  This is all possible now by using the new JavaScript SharePoint API.  In my next blog post, I will walk through showing how to pop up an AJAX dialog box using the new JavaScript SharePoint API.

And that’s it for coding!  The next step is just deploying the feature.

ribbon_button_screen_shot

Step 3: Deploy Feature to SharePoint

There are now a number of ways to deploy features to SharePoint 2010.  STSADM is still available, however its been deprecated in preference to PowerShell and the vast amount of commandlets that come with it now for SharePoint 2010.  Also, if you are using Visual Studio 2010, there have been significant improvements to its SharePoint development extensions that make it a first-class development tool now for SharePoint 2010.

In my case, I’ve been kicking it old school and using STSADM just to see if it still works, which it does!  Below is my sample deployment .bat file script:

Code Snippet
  1. stsadm -o deactivatefeature -name SkynetCustomRibbonButton -url http://spdev -force
  2.  
  3. stsadm -o uninstallfeature -name SkynetCustomRibbonButton -force
  4.  
  5. stsadm -o installfeature -name SkynetCustomRibbonButton -force
  6.  
  7. stsadm -o activatefeature -name SkynetCustomRibbonButton -url http://spdev -force
  8.  
  9. pause
 
Page view tracker