Dialogs
Another addition in SharePoint 2010 is the Dialog framework. For the most part, this is encapsulated in the functionality exposed by the SP.UI.Dialog and SP.UI.ModalDialog classes. Although the notifications and status bar functionality is included in the SP.js file, the dialog framework code is included in the SP.UI.Dialog.js file. To get IntelliSense support in Visual Studio when writing JavaScript that targets the dialog framework, add the following reference element to the JavaScript file:
Showing a modal dialog is a relatively straightforward affair. First, we create a DialogOptions object, and then we call the showModalDialog method of the SP.UI.ModalDialog object, as this sample shows:
1
/// <reference path="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\SP.UI.Dialog.debug.js" />
1
function ShowDialog_Click() {<br> var options = { url: 'http://www.chaholl.com',<br> title: 'My Dialog',<br> allowMaximize: false,<br> showClose: true,<br> width: 150, height: 150,<br> dialogReturnValueCallback: SendNotification_Click<br> };<br> var did = SP.UI.ModalDialog.showModalDialog(options); <br>}
In this sample, we’re redirecting to an Internet page for the sake of simplicity. In a real-world application, we’d create a custom page that implemented the required functionality. In Chapter 9, you’ll see examples of custom dialogs when we build settings pages for service applications.
The important thing to note about the DialogOptions object is the dialogReturnValueCallback property. This contains details of a callback method that will be called when the dialog is closed. In this callback, we can write code to pick up the dialog result and perform any necessary actions. In this simple example, we’ve generated a notification using our notification sample code.
While this provides a basic introduction to the dialog framework it skips a few broad strokes. This short post will take a look at the dialog framework in more detail and will hopefully fill in some of the gaps in the current MSDN documentation.
One of the first things we want to do when using the dialog framework is to create a new page that will form our dialog. As the short sample above shows, a dialog can be any web page. However, for the sake of consistency, we’ll create a dialog that uses the look and feel of other SharePoint 2010 dialogs.
Using Visual Studio 2010, create a new Empty SharePoint Project as shown.
Set the local site to whatever the url is for your SharePoint 2010 dev server and choose the Deploy as farm solution option.
Currently there is no SPI for creating dialog pages in Visual Studio. I intend to submit one to the CKSDev project in the near future so hopefully this will be resolved soon. In the meantime we can manually configure a dialog page using the Application Page SPI as shown. Add a new application page named MyTestDialog.aspx.
Visual Studio will automatically add a mapped folder for the %SPROOT%/Template/Layouts folder and will add our new application page in a folder named after our project. So far, so good!
In SharePoint 2010, all system generated dialogs are based on the dialog.master master page that can be found at %SPROOT%/Template/Layouts/Dialog.master. To ensure consistency with other dialogs we’ll use this master page as the basis for our test dialog.
In the MyTestDialog.aspx page, amend the Page tag as follows:
1
2
3
4
5
<%@ Page Language="C#"
AutoEventWireup="true"
CodeBehind="MyTestDialog.aspx.cs"
Inherits="ModalDialogSample.Layouts.ModalDialogSample.MyTestDialog"
MasterPageFile="~/_layouts/dialog.master" %>
Notice the use of the MasterPageFile attribute as opposed to the DynamicMasterPageFile attribute that was inserted by default as part of the application page template. The DynamicMasterPageFile attribute can only reference ~masterurl/default.master or ~masterurl/custom.master and provides a mechanism for SharePoint to dynamically alter the master pages that are applied to a page. Since we’re not using this functionality we must use the standard MasterPageFile attribute and a relative path to our dialog.master file.
Since we’ve changed the master page, the content tags present in the application page template will no longer tie up correctly to Placeholder tags in our dialog.master. Delete the default content tags and replace with the following markup:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<asp:Content ID="Content1"
ContentPlaceHolderID="PlaceHolderDialogHeaderPageTitle"
runat="server"> My Test Dialog Title </asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<SharePoint:CssRegistration ID="CssRegistration1" runat="server" Name="ows.css" />
<SharePoint:ScriptLink ID="ScriptLink1" Language="javascript" Name="core.js" runat="server" />
<SharePoint:FormDigest ID="FormDigest1" runat="server" />
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="PlaceHolderDialogImage" runat="server">
<img src="/_layouts/images/allcontent32.png" alt="" />
</asp:Content>
<asp:Content ID="Content5" ContentPlaceHolderID="PlaceHolderDialogBodyHeaderSection" runat="server">
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="PlaceHolderDialogDescription" runat="server">
<div id="selectTermDescription" class="none-wordbreak">
<table class="ms-dialogHeaderDescription">
<tbody>
<tr>
<td>This is description text for a custom dialog</td>
</tr>
</tbody>
</table>
</div>
</asp:Content>
<asp:Content ID="Content8" ContentPlaceHolderID="PlaceHolderHelpLink" runat="server">
<!-- Remove the default help link -->
</asp:Content>
<asp:Content ID="Content6" ContentPlaceHolderID="PlaceHolderDialogBodyMainSection" runat="server">
This is the body section of our test dialog
</asp:Content>
A closer look at Dialog.master will reveal that there are other placeholders that can be used and that there is considerable flexibility in how the finished page is constructed. This sample page shows how we can use a few of those placeholder to generate a dialog that fits in with the overall SharePoint 2010 look.
To see how this looks in the browser, deploy the project and then navigate to: http://your-server-url/_layouts/modaldialogsample/mytestdialog.aspx. If all is well, you’ll see something akin to this:
Now that we’ve created a new dialog page the next step is to render it as a custom dialog. In a real world scenario we may show the dialog in response to a control click on a page or a button on the ribbon or maybe an edit action on a custom field control as is the case for Managed Metadata fields. For the sake of simplicity we’ll make use of a custom action to add a link to the ribbon on all document libraries. CKSDev provides an SPI to make the step easier, download it now via the Extension Manager in the Tools menu of Visual Studio 2010. Trust me, it’s worth it!
With CKSDev installed, add a new Custom Action named ShowDialogAction as shown.
Complete the Custom Action Settings wizard as shown in these screenshots:
To create a button that will appear in the ribbon, add the following xml within the CustomAction tag in the Elements.xml file in the ShowDialogAction folder.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<CommandUIExtension>
<CommandUIDefinitions>
<CommandUIDefinition Location="Ribbon.Templates._children">
<GroupTemplate Id="ModalDialogSample.Templates.OneLargeControl">
<Layout Title="Large">
<Section Type="OneRow">
<Row>
<ControlRef TemplateAlias="c1" DisplayMode="Large" />
</Row>
</Section>
</Layout>
</GroupTemplate>
</CommandUIDefinition>
<CommandUIDefinition Location="Ribbon.Library.Scaling._children">
<MaxSize Id="ModalDialogSample.DocumentLibraryTab.CustomGroup.MaxSize"
Sequence="55" GroupId="ModalDialogSample.DocumentLibraryTab.CustomGroup" Size="Large" />
</CommandUIDefinition>
<CommandUIDefinition Location="Ribbon.Library.Groups._children">
<Group Id="ModalDialogSample.DocumentLibraryTab.CustomGroup" Sequence="55" Description="A test custom group"
Title="Test Group" Template="ModalDialogSample.Templates.OneLargeControl">
<Controls Id="ModalDialogSample.DocumentLibraryTab.CustomGroup.Controls">
<Button Id="ModalDialogSample.DocumentLibraryTab.CustomGroup.ShowDialog" Command="ModalDialogSample.ShowDialog"
Sequence="55" Image16by16="/_layouts/images/allcontent16.png" Image32by32="/_layouts/images/allcontent32.png"
Description="Show sample dialog" LabelText="Show Dialog" TemplateAlias="c1" />
</Controls>
</Group>
</CommandUIDefinition>
</CommandUIDefinitions>
<CommandUIHandlers>
<CommandUIHandler Command="ModalDialogSample.ShowDialog"
CommandAction="javascript:window.location='{SiteUrl}/_layouts/ModalDialogSample/MyTestDialog.aspx';" />
</CommandUIHandlers>
</CommandUIExtension>
I won’t get into the details of how this Xml is used to create a button in the ribbon. Chris O’Brien has written up an excellent series of posts on how this all works that can be found here, also for those fortunate enough to have my SP2010 dev book, full coverage can be found in Chapter 3
.
After deploying the project, all document libraries will now feature a new group in the Library tab including a Show Dialog button that looks a bit like this:
Clicking on the button will redirect to our custom dialog although it this point it will appear as a standard page.
Now that we have a button that we can use to show our dialog, lets move on to look at how we can make our page appear as a dialog rather than as a standard page. Looking at the Element.xml file in the ShowDialogAction folder, we can see some javascript in the CommandUIHandler node. In effect, this javascript is executed when our button is clicked. We can see that, at the moment, it’s simply redirecting the browser to our dialog page.
Replace the CommandUIHandler for the ModalDialogSample.ShowDialog command with the following xml:
1
2
3
4
5
6
<CommandUIHandler Command="ModalDialogSample.ShowDialog"
CommandAction="Javascript:
var options = {url: '{SiteUrl}/_layouts/ModalDialogSample/MyTestDialog.aspx',
allowMaximize: false,
showClose: true};
var did = SP.UI.ModalDialog.showModalDialog(options);" />
We can see that our revised JavaScript is making use of the showModalDialog method in the same way as shown in the example quoted from my book. The process for creating a dialog is: firstly, create an object of type SP.UI.DialogOptions and then pass that object to the showModalDialog method of the SP.UI.ModalDialog object.
This is where the first documentation weakness comes in – search for SP.UI.DialogOptions in MSDN and all you’ll find are references to the type in various methods. There are no details on exactly what the available options are and what they do. Let’s tackle that here:
Much of the JavaScript that implements the user interface functionality in SharePoint 2010 is built automatically using a tool known as Script#. This tool allows developers to write C# code and have it compiled into cross-platform Javascript. There are many benefits in this, not least in the fact that the development tooling for C# is light years ahead of anything for JavaScript. However there is a drawback, although the generated JavaScript is readable, it’s not exactly easy to understand and so getting to the bottom of simple questions such as “what are the members on the SP.UI.DialogOptions class?”, are not as simple as they could be if Microsoft was to provide us with the assemblies that they used to generate the JavaScript. Anyhow, I’ve saved you the pain – here’s the list of members and their functions to the best of my knowledge:
So we now have a button that will show our dialog correctly using the Dialog framework. Let’s be honest though, we haven’t really covered anything new here. The original code sample at the top of the page achieved the same result! Let’s move on to look at how we can pass some properties to this custom dialog.
As the list of properties for the SP.UI.DIalogOptions object indicates, we can use the args property to pass a user defined object that encapsulates any values that we need to pass. So to give an example, if we change our CommandUIHandler code as follows:
1
2
3
4
5
<CommandUIHandler Command="ModalDialogSample.ShowDialog"
CommandAction="Javascript: var anObj={objProp1:'red', objProp2:'fish'};
var myArgs = {aProperty:'foo', anotherProperty:'bar', yetAnother:23, objProperty:anObj};
var options = {url: '{SiteUrl}/_layouts/ModalDialogSample/MyTestDialog.aspx',args:myArgs};
var did = SP.UI.ModalDialog.showModalDialog(options);" />
We can see in this code snippet that we can pass practically anything using the args property. The example here is pretty trivial – a dynamically generated object with some values – but we could pass a reference to a DOM object for example, if we wanted to create some kind of builder control. There are many possibilities here. This is another area where the documentation is a bit ropey. At the time of writing I couldn’t find any documentation to explain how to read the value of the args property from the dialog form. Let’s address that here with an example.
Add the following script to the MyTestDialog.aspx page that we created earlier:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script type="text/javascript">
<!--
if (typeof (_spBodyOnLoadCalled) == 'undefined' || _spBodyOnLoadCalled) {
window.setTimeout(TestDialogSetup, 0); }
else {
_spBodyOnLoadFunctionNames.push("TestDialogSetup");
}
function TestDialogSetup() {
SP.SOD.executeFunc("sp.ui.dialog.js", null, showParameters);
}
function showParameters() {
var theDiv = $("#MyBodyDiv");
var args = SP.UI.ModalDialog.get_childDialog().get_args();
var html;
html = "<table><tr><td>Name</td><td>value</td></tr>";
$.each(args, function (i, n) {
html+="<tr><td>" + i + "</td><td>" + n + "</td></tr>";
});
html+="</table>";
theDiv.html(html);
}
-->
</script>
As you can see, this script makes use of JQuery. Add the following link in the PlaceHolderAdditionalPageHead content control:
1
<script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js" type="text/javascript"></script>
Note that this link makes use of the Microsoft Ajax Content Delivery Network. If you’re trying this on a machine with no internet connectivity you may need to download the JQuery library and reference it locally
Also, so that we can see the output of the script, add a Div in the PlaceHolderDialogBodyMainSection with the Id MyBodyDiv
With these changes in place we can now deploy the solution. This time, clicking on the button in the ribbon will show our dialog together with a table containing the properties that we passed to it.
One thing to note in the script above is the use of SP.SOD.executeFunc. Since SharePoint makes extensive use of JavaScript, a Script-On-Demand model exists, allowing scripts to be loaded as and when required as opposed to having everything loaded unnecessarily. The executeFunc method ensures that the referenced on-demand script is loaded before calling the referenced function. In effect, we’re ensuring that the SP.UI.ModalDialog namespace is available before attempting to call the get_childDialog method.
Now that we’ve seen how to send values to our custom dialog, the next logical step is to look at how the dialog can send back a response to our calling script. The clue here is again in the SP.UI.DialogOptions table. Notice the dialogReturnValueCallback property – this property allows us to specify a callback function. Once the dialog is closed, the callback function will be called with two parameters as we’ll see in the next code sample.
Amend the CommandUIHandler that you created earlier to include the following script:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CommandUIHandler Command="ModalDialogSample.ShowDialog"
CommandAction="Javascript:
var anObj={objProp1:'red', objProp2:'fish'};
var myArgs = {aProperty:'foo', anotherProperty:'bar', yetAnother:23, objProperty:anObj};
var options = {url: '{SiteUrl}/_layouts/ModalDialogSample/MyTestDialog.aspx',
args:myArgs,
dialogReturnValueCallback:myDialogCallback};
var did = SP.UI.ModalDialog.showModalDialog(options);
function myDialogCallback(result,response)
{
switch(result)
{
case SP.UI.DialogResult.cancel:
//do nothing
break;
case SP.UI.DialogResult.invalid:
//do nothing
break;
case SP.UI.DialogResult.ok:
var note=SP.UI.Notify.addNotification(response.message);
break;
}
}
" />
As you can see, we’ve defined a new function, myDialogCallback that accepts a result and a response. Result is a numeric value that corresponds to the results defined in the SP.UI.DialogResult enumeration, whereas response is an object and can contain any values that we allocate in much the same way as we did earlier with the args property. Notice that the dialogResultCallback property of our SP.UI.DialogOption object has been set to a reference to our new function.
For the sake of simplicity, we’re using the Notifications framework to display a message passed to our callback from our modal dialog. The Notifications framework is covered in Chapter 4 of my book and a good run through can be found on Waldek Mastykarz site.
With our callback function in place, the next thing we need to do is add the functionality to our dialog to pass back values and dismiss the dialog at the appropriate time. Since we’ve used the out-of-the-box Dialog.master master page, we already have an Ok and Cancel button on the page. Since we want to add some custom functionality when the Ok button is clicked we need to hook it up to a custom script on our page. In the MyTestDialog.aspx.cs code-behind file, add the following code:
01
02
03
04
05
06
07
08
09
10
public partial class MyTestDialog : LayoutsPageBase {
protected override void OnLoad(EventArgs e) {
if (Master is DialogMaster)
{
DialogMaster dm = Master as DialogMaster;
dm.OkButton.Attributes.Add("onclick", "return OkClicked();");
}
base.OnLoad(e);
}
}
All we’re doing here is hooking up the OkClicked script to the onclick event of the Ok button that's defined in the master page. With that in place, let’s add the script top the page. In MyTestDialog.aspx, add the following JavaScript after the showParameters function that you added earlier:
1
2
3
4
5
6
7
function OkClicked() {
var results = {
message: "A message from the dialog",
anothervalue: "Something else"
};
SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.ok, results);
}
In this short script we’re creating a results object containing a few arbitrary values and then calling teh commonModalDialogClose method of the SP.UI.ModalDialog object. We’re passing the result of SP.UI.DialogResult.ok. As part of it’s internal processing, commonModalDialogClose will call our callback function if one is defined and will pass these two parameters into it.
If we new build and deploy the project we can see the finished dialog. This time when we click on the Ok button, the dialog will be closed and a notification will appear on the screen containing the message text stored in our results object. Clicking the Cancel button will close the dialog and no message will appear.
The dialog framework is a useful tool and is used extensively throughout the SharePoint 2010 user interface. For developers building applications on the SharePoint platform, having the ability to easily leverage this functionality with a few lines of code saves much work and given what a lazy lot we all are, it also means that we’re more likely to design user interfaces that are consistent with the out-of-the-box user experience. This post covers the key functionality of the dialog framework, however one area that hasn’t been addressed is using an HTML DOM element as the dialog rather than a separate page. I’ll leave that for somebody else to document although it’s worthwhile pointing out that in the interests of separating concerns and ensuring maintainable code, unless there’s a really good reason to pass a DOM element rather than redirect to a new page (I can’t think of one at the moment!), I’d suggest that the approach documented above is the way to go.
Hope this save some time investigating and wading through .js files! As always comments/criticisms/complaints and corrections are welcome.