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.
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
- <Elements xmlns="https://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 + '&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:
Code Snippet
- 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.
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.
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.
Comments
Anonymous
November 24, 2009
Hello, can you join the code of your ASPX file ? Actually, I tried to do what you explain. Finally, I use the fonction OpenPopUpPage (lass customizable) but I have an "issue" with my application page. The master page of this one is default.master but my problem is that ALL the design appear, but on your screen, it seems that you don't have it and I would like to so the same... I don't need to see the Quick launch for this applicative page, so can you join or post the code of yours ? Thanks a lotAnonymous
November 24, 2009
The comment has been removedAnonymous
February 15, 2010
Hi - Thanks for this great demo. I followed your instructions and have my modal dialog pop up nicely. I was wondering thou - What script should i now call to pass information back to the ribbon? The idea is that once the dialog pops up, a user selects something from an asp.net listview and this is then inserted into the content they are editing.. Any suggestions? Thanks for the great blog!Anonymous
May 26, 2010
Hi - Thanks for this demo. What is the way to implement the ribbon in the dialog too ? I wish my dialog looks like your first screenshot. Thanks for the great blog!!!Anonymous
June 28, 2010
Hi, Thanks for posting this wonderful information. this really helped me for one of our requirements. i can able to see all the sceens which you have mentioned here. But my final requirment is after selecting some items from the library i can able to see the selected items. After that i want to move the selected files to some other library.can you please tell me how can we do that part? Thanks, AnandAnonymous
August 26, 2010
Have a look a good explanation of Model dialog... www.a2zmenu.com/.../SharePoint%202010%20Model%20Dialog.aspxAnonymous
September 21, 2010
Is there any possible method to return the selected value from the modal dialogue to the previous parent window and is it possible to populate it to a particular textbox of the parent window. Thanks, JoharaAnonymous
October 03, 2010
Very nice example and explanation. This is exactly what i am looking for.Anonymous
March 17, 2011
Hi Jonathon, may i know does there any possible that sharepoint 2010 perform batch approval? and can you send some sample code to me? email: curvehen@hotmail.com thanks, sunnyAnonymous
October 07, 2011
It is excellent.. I am learning sharepoint development would it be possible to see the entire code (move documents to another library / record center) and try for learning purpose. I am trying to move selected documnets from doc library to record center. Can you post it..Anonymous
October 08, 2011
Nice post J. praveenbattula.blogspot.com/.../close-sharepoint-2010-dialog-through.htmlAnonymous
November 28, 2011
The comment has been removedAnonymous
November 28, 2011
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %> <%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %> <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %> <%@ Import Namespace="Microsoft.SharePoint" %> <%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="showitems.aspx.cs" Inherits="Skynet.CustomRibbonButton.DemoPage" DynamicMasterPageFile="~masterurl/default.master" %> <asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server"> </asp:Content> <asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server"> <asp:Label id="LabelItems" runat ="server"/> </asp:Content> <asp:Content ID="PageTitle" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server"> Application Page </asp:Content> <asp:Content ID="PageTitleInTitleArea" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server" > My Application Page </asp:Content>Anonymous
November 28, 2011
//Layout codebehind without line number 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 partial 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 } }Anonymous
November 28, 2011
<Elements xmlns="schemas.microsoft.com/.../"> <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/testCustomActionRibbonButton/ShowItems.aspx?items=' + myItems + '&source=' + SP.ListOperation.Selection.getSelectedList(), tite: 'Move Documents', allowMaximize: true, showClose: true, width: 800, height: 600, dialogReturnValueCallback: demoCallback }; SP.UI.ModalDialog.showModalDialog(options);" /> </CommandUIHandlers> </CommandUIExtension> </CustomAction> </Elements>Anonymous
December 12, 2011
I am looking for how to move the documents to record center can you post your code under 'more stuff here section' 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 simplicityAnonymous
July 21, 2012
The comment has been removedAnonymous
December 11, 2012
I have followed your example. The popup window opens but comes up blank. I then need to move the selected documents selected to the record center. Thanks MonaAnonymous
March 04, 2013
Chanra, in the options section, if you replace this: url: '/_layouts/Skynet/ShowItems.aspx?items=' + myItems + '&source=' + SP.ListOperation.Selection.getSelectedList(), with this: url: '{SiteUrl}/_layouts/Skynet/ShowItems.aspx?items=' + myItems + '&source=' + SP.ListOperation.Selection.getSelectedList(), Then the page will open up with its context being your current web, rather than the site collection.Anonymous
May 02, 2013
Can anybody please post the code that was omitted for simplicity?Anonymous
June 13, 2013
When I try to add a button for moving the files and I put an OnClick to it the pop-up returns an error.Anonymous
November 06, 2014
Very good article. Thanks. It worked fine for me. Could you help me through my web part(eg: list 2 web part) how can i open another list and get selected items from that sharepoint list (eg: List1). Thanks in Advance.Anonymous
May 04, 2015
i used your code for receiving querystring but when i open application page i receive error [NullReferenceException: Object reference not set to an instance of an object.] RibbonCopyMove.Layouts.RibbonCopyMove.CopyMovePage.OnLoad(EventArgs e) +846 System.Web.UI.Control.LoadRecursive() +65 System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2427 i don't receive querystring parameters items and source from java script url: '/_layouts/RibbonCopyMove/CopyMovePage.aspx?items=' + myItems + '&source=' + SP.ListOperation.Selection.getSelectedList() how that i solve this problemAnonymous
July 19, 2015
Hi - Thanks for this great demo. I followed your instructions and have my modal dialog pop up nicely.and also showing alert (u have selected the follwoing items to move) Plz explain how to move from one document library to another. I am trying to move selected documnets from doc library to another doc library . Can you post it..