Expand Minimize

Opening a SharePoint 2010 Modal Dialog Box from an InfoPath Form

SharePoint 2010

Summary:  Learn how to create, deploy, and test a Microsoft SharePoint 2010 application that opens a SharePoint modal dialog box from a Microsoft InfoPath 2010 form.

Many page activities in Microsoft SharePoint 2010 open a modal dialog box that lets you complete a task without navigating away from the current page. When the dialog box opens, the background darkens (the so-called lightbox effect), which indicates that the page is inactive. You must deal with the dialog box before you can return to the page.

Good examples are the modal dialog boxes that open when you create, view, or edit an item in a SharePoint list. Each of these dialog boxes loads a SharePoint application page (NewForm.aspx, DispForm.aspx, or EditForm.aspx) in a pop-up window. Although the pop-up window displays what is, technically speaking, a new page, the new page appears to open in the context of the page that you were on (see Figure 1). It feels like you are zooming in on the page instead of navigating away.

Figure 1. SharePoint New Item dialog box

SharePoint New Item dialog box

When SharePoint 2010 creates a modal dialog box, it employs a new client-side dialog box framework that you can also use. For the most part, the functionality is encapsulated in the SP.UI.Dialog Class and SP.UI.ModalDialog Class of the JavaScript (ECMAScript) Class Library. The dialog box classes are implemented in the SP.UI.Dialog.js file, which is located in the LAYOUTS folder of your SharePoint 2010 installation. (The source code is minified for performance; if you want to browse, get the more readable companion file, SP.UI.Dialog.debug.js.)

It is not difficult to imagine situations where you might want to invoke a modal dialog box from a Microsoft InfoPath 2010 form. A classic example is a data-picker control that opens a calendar so that the person who is filling out the form can browse to and select a date. Another example is when the form requires information that is not readily available to the person who is filling it out, such as a link to an item in a SharePoint list. In this case, you might want to open a dialog box that enables the user to navigate to or search for the information that the form requires without leaving the context of the form.

In this article, you develop a sample application that demonstrates how to open a SharePoint modal dialog box from an InfoPath form. You learn how to create and test an application page and the modal dialog box that displays it. Then, you finish the application by designing a form template and creating a Web Part to host the form. When the application is complete, you deploy and test it.

In the scenario for this sample, Human Resources at Contoso requested a browser-based form that each department can fill out as a first step in planning for the coming fiscal year. The form will be used to gather information about the department's current resources and forecast needs. One field in the proposed form asks for a link to a department organization chart.

To fulfill this request, you decide to design an InfoPath form that is hosted on an internal SharePoint website. As you design the form, you realize that to complete the field that asks for a link to the organization chart, the user must navigate away from the form to obtain the URL. You want to avoid any situation that causes a user to leave the form, so you decide to give the form the ability to open a modal dialog box that lets the user search for the selected file and select it.

InfoPath Form Hosted on a SharePoint Site Sample Application Architecture

The sample application has the following components:

  • Browser-enabled InfoPath form  The title of the form is "Contoso Resource Planning." It contains a label ("Organization Chart"), a hyperlink field ("URL"), and a button ("Search"). The button click event is handled by form code that raises the NotifyHost event and passes an XPath expression for the field.

  • Web Part that contains an XMLFormView control  The InfoPath form is hosted by an XmlFormView control inside a custom Web Part. The XMLFormView control subscribes to the NotifyHost event and handles it by invoking a modal search dialog box. When the dialog box closes, the Web Part updates the field in the form.

  • Modal dialog box  The modal search dialog box is opened by a client script that executes in the context of the Web Parts page. The script calls a method in the SP.UI.ModalDialog Class to open a modal dialog box and display a search application page. When the modal dialog box closes, the result of the search is passed to a callback function that also executes in the context of the Web Parts page.

  • SharePoint application page  The user interface (UI) and the logic for the search application are implemented in a SharePoint application page. For this demonstration, the application page implements a simple search among the items listed in a document library on the SharePoint website.

The relationship among the components is shown in Figure 2.

Figure 2. Sample application components and relationships

Sample application components and relationships

You will develop the example application in six steps.

  • Step 1: Create a Search Application Page In this step, you design the UI for the page that the modal dialog box displays, and you write code to perform the search.

  • Step 2: Add JavaScript for the modal dialog box In this step, you create an ECMAScript (JavaScript, JScript) file with code to open the search application page in a pop-up modal dialog box. The function that opens the modal dialog box specifies a callback function that returns information about the document that the user has selected to the page that opened the dialog box.

  • Step 3: Test the Modal Dialog Box In this step, you do a quick test of what you have developed so far. You use the SharePoint Content Editor Web Part (CEWP) for this purpose.

  • Step 4: Design the InfoPath Form In this step, you create a form template that includes a Search button, and you write form code that notifies the form's host when a user clicks the button.

  • Step 5: Create a Web Part to Host the Form In this step, you write a custom Web Part that uses an XmlFormView control to host the InfoPath form.

  • Step 6: Connect the Components In this step, you put everything together. You write Web Part code that handles the NotifyHost event, code that invokes the modal dialog box, and code that uses the dialog box result to update the InfoPath form.

Application Prerequisites

You will learn more if you follow along on your own development website as you construct the sample application. To complete the application, you must have the following:

  • Microsoft Visual Studio 2010

  • InfoPath Designer 2010

  • Microsoft Visual Studio Tools for Applications

  • A server that is running Microsoft SharePoint Server 2010

You must also do some preparation on your development website. First, make sure that the root web of your SharePoint site collection has a Shared Documents library. Then populate the library with test documents that can be found by the search application. Use document names relevant to our scenario, such as "IT Department Org Chart FY11.docx," "IT Department Policies.docx," and so on.

Although not required reading, the following articles contain background information that may help explain some of the techniques that are used:

Step 1: Create a Search Application Page

Begin by creating a search application page that allows a user to search for and select a document. Our project is not about implementing search, so the application that we create simply enumerates over the Shared Documents library. You can extend it later by implementing full search.

You will build the search application page in stages. First you create the UI on the page. Then you implement the search logic in code behind the page. Finally, you deploy the application page and test it.

To create the user interface for the search application page

  1. In Visual Studio 2010, create a new project.

  2. In the New Project dialog box, select SharePoint 2010 and the Empty SharePoint Project template.

  3. Name the project ModalHost. ApplicationPages, and then click OK.

  4. In the SharePoint Customization Wizard, click Deploy as a farm solution. Then click Finish.

  5. In Solution Explorer, right-click the project node, point to Add, then click New Item.

  6. In the Add New Item dialog box, select the Application Page template. Name the page ModalHost.aspx, and then click Add.

  7. In the ModalHost.aspx application page markup, locate ContentPlaceHolderID="PlaceHolderMain". Place the cursor on a blank line above the closing </asp:Content> tag. Then copy the following markup and paste it at the insertion point.

        <asp:TextBox runat="server" ID="Dialogvalue" CssClass="modalhiddenfield"></asp:TextBox>
        <div style="font-family: Calibri; font-size: 16px">
            <div>
                <div>
                    <span style="vertical-align: middle;">Enter document name:</span>&nbsp;
                    <asp:TextBox ID="SearchBox" runat="server" Width="242px" ToolTip="Search.."></asp:TextBox>
                    <asp:ImageButton ID="Dosearch" runat="server" ImageUrl="~/_layouts/images/searchlogo.png"
                        ImageAlign="Top" OnClick="Dosearch_Click" Width="20px" AlternateText="search"
                        ToolTip="Search" />
                </div>
                <div style="padding-top: 10px">
                    <div style="padding-bottom: 10px">
                        <asp:Label runat="server" Visible="false" ID="ResultCount"></asp:Label>
                    </div>
                    <SharePoint:SPGridView ID="ResultGrid" runat="server" AutoGenerateColumns="false"
                        ShowHeader="true" RowStyle-BackColor="#EBEBEB" HeaderStyle-BackColor="#C3C3C3"
                        AlternatingRowStyle-BackColor="#F6F6F6" EnableTheming="true" ShowHeaderWhenEmpty="true"
                        AutoGenerateSelectButton="true" SelectedRowStyle-BackColor="#EDE275" AllowSorting="true">
                        <Columns>
                            <asp:HyperLinkField HeaderText="Title" DataNavigateUrlFields="Url" 
                            DataTextField="Title"
                                ShowHeader="true" HeaderStyle-BackColor="#C3C3C3" 
                                HeaderStyle-ForeColor="#000000" />
                            <asp:BoundField HeaderText="Created By" DataField="CreatedBy" ShowHeader="true" 
                                HeaderStyle-BackColor="#C3C3C3" HeaderStyle-ForeColor="#000000" />
                        </Columns>
                    </SharePoint:SPGridView>
                </div>
            </div>
            <!-- Insert the modal dialog box OK and Cancel buttons here--->
        </div>
    
    
    

This markup creates a UI that has the controls shown in the Figure 3.

Figure 3. User interface controls

User interface controls

Now you must make the controls do something. Basically, you have to handle two events: the Click event raised by the ImageButton control, and the SelectedIndexChanged event raised by the SPGridView control. When the ImageButton is clicked, the handler must do the following:

  • Get a reference to the Shared Documents library on the current website.

  • Query the library for items with titles that contain the search terms.

  • Display the result count in the Label control.

  • Display the result set in the SPGridView control.

When the user selects an item in the SPGridView control, the handler for the SelectedIndexChanged event must do the following:

  • Extract information that you can later use to create a hyperlink from the selected document's metadata.

  • Store the document metadata in the DialogValue text box.

Later in this article, you will write code that passes the document metadata to a callback function that executes in the context of the search application's consumer, a Web Part.

To add code for the search application page

  1. Click anywhere on the markup for ModalHost.aspx. Then press F7 to view the page code.

  2. At the top of the file, add the following using statements.

    using System.Web.UI;
    using System.Linq;
    using System.Collections.Generic;
    using System.Web.UI.WebControls;
    using Microsoft.SharePoint.Utilities;
    
    
  3. Delete contents of the ModalHost class body. Then copy the following code and paste it into the class body.

    protected void Page_Load(object sender, EventArgs e)
    {
        // Register the SPGridView SelectedIndexChanged event.
        ResultGrid.SelectedIndexChanged += new EventHandler(ResultGrid_SelectedIndexChanged);
    }
    
    // Fires when the search button is clicked.
    protected void Dosearch_Click(object sender, ImageClickEventArgs e)
    {
        // Declare a variable to store the website's URL.
        string url = string.Empty;
    
        // Reset the hidden text box to empty.
        Dialogvalue.Text = string.Empty;
    
        try
        {
            // Get the document library object along with the site URL.
            SPList documents = this.GetDocumentLibrary(out url);
            if (documents != null && documents.Items.Count > 0)
            {
                if (!string.IsNullOrEmpty(SearchBox.Text.Trim()))
                {
    
                    // Create a collection of the custom class Result by enumerating over library items.
                    List<Result> results = (from document in documents.Items.OfType<SPListItem>()
                                            where document.Name.ToLower().Contains(SearchBox.Text.ToLower())
                                            select new Result
                                            {
                                                Title = document.Name,
    
                                                // Create a complete URL for the document.
                                                Url = string.Concat(url, "/", document.Url),
    
                                                // Capture 'Created By' from document metadata and trim off id.
                                                CreatedBy = this.GetUserName(document["Created By"].ToString()),
                                            }).ToList();
    
                    // Display the result count.
                    ResultCount.Text = string.Concat(results.Count.ToString(), " items found.");
                    ResultCount.Visible = true;
    
                    // Bind the gridview with the result object collection.
                    ResultGrid.DataSource = results;
                    ResultGrid.DataBind();
    
                    // Reset the selected gridview row from previous search.
                    ResultGrid.SelectedIndex = -1;
    
                    // Display the gridview is there are results to display.
                    if (results.Count > 0)
                    {
                        ResultGrid.Visible = true;
                    }
                    else
                    {
                        ResultGrid.Visible = false;
                    }
                }
                else
                {
                    // Ask the user to enter a search term. 
                    ResultCount.Text = "Please enter one or more search words.";
                    ResultCount.Visible = true;
    
                    // Reset the gridview datasource if any of the required parameters are absent.
                    ResultGrid.DataSource = null;
                    ResultGrid.DataBind();
                    ResultGrid.Visible = false;
                }
            }
            else
            {
                // Tell the user that the search cannot connect to the content source. 
                ResultCount.Text = "Cannot connect to content source or the source is empty.";
                ResultCount.Visible = true;
    
                // Reset the gridview data source if any of the required parameters are absent.
                ResultGrid.DataSource = null;
                ResultGrid.DataBind();
                ResultGrid.Visible = false;
            }
        }
        catch
        {
            throw;
        }
    }
    
    // Fires on a row that is selected in the gridview.
    void ResultGrid_SelectedIndexChanged(object sender, EventArgs e)
    {
        try
        {
            // Determine whether an items has been selected.
            if (ResultGrid.SelectedIndex != -1)
            {
                // Get the selected row from the gridview.
                SPGridViewRow row = ResultGrid.SelectedRow as SPGridViewRow;
                if (row != null)
                {
                    // Create a hyperlink object to retrieve the selected document details.
                    HyperLink hyperlink = row.Cells[1].Controls[0] as HyperLink;
     
                   // Get the document title by removing the file extension.
                    string docname = hyperlink.Text.Remove(hyperlink.Text.LastIndexOf("."));
    
                    // Encode the string hyperlink to URL.
                    string docurl = SPEncode.UrlEncodeAsUrl(hyperlink.NavigateUrl);
    
                    // Create a semicolon (;) separated string of docname and docurl and assign to 
                    // a hidden text box.
                    Dialogvalue.Text = string.Concat(docurl, ";", docname);
                }
            }
        }
        catch
        {
            throw;
        }
    }
    
    // Gets the list object for the default Shared Documents library.
    private SPList GetDocumentLibrary(out string siteurl)
    {
        try
        {
            // Read the URL from the query string.
            siteurl = this.GetQueryStringValue();
            if (!string.IsNullOrEmpty(siteurl))
            {
                // Create the site object based on the received URL.
                using (SPSite site = new SPSite(siteurl))
                {
                    using (SPWeb web = site.OpenWeb())
                    {
                        // Look for the Shared Documents library on the web. 
                        SPList list = web.Lists.TryGetList("Shared Documents");
                        if (list != null)
                        {
                            return list;
                        }
                    }
                }
            }
        }
        catch
        {
            throw;
        }
    
        return null;
    }
    
    // Retrieves the URL from the page query string.
    private string GetQueryStringValue()
    {
        if (Page.Request.QueryString.Count > 0)
        {
            // Check if the page query string contains a url key.
            if (!string.IsNullOrEmpty(Page.Request.QueryString["url"]))
            {
                return Page.Request.QueryString["url"].ToString();
            }
        }
    
        return null;
    }
    
    // Trims the user name string by removing the ;#.
    private string GetUserName(string spusername)
    {
        int index = spusername.LastIndexOf("#");
        if (index != -1)
        {
            // Remove the id and # from the username string.
            spusername = spusername.Remove(0, index + 1); //"9;#UserName" 
        }
    
        return spusername;
    }
    
    // Custom class to store required document metadata.
    public class Result
    {
        public string Title { get; set; }
        public string Url { get; set; }
        public string CreatedBy { get; set; }
    }
    
    
    

Take a moment to examine the code that you just pasted into your project.

  • GetDocumentLibrary This method returns the SPList object that represents the Shared Documents library and uses an output parameter to return the URL of the current SharePoint site collection.

    The GetDocumentLibrary method gets a reference to the list by instantiating an SPSite object to represent the site collection, getting the SPWeb object for the root website, and then querying the list collection for the SPList object that represents the Shared Documents library. This is all fairly standard practice for programming against SharePoint data.

    Notably, the code uses the site collection URL to instantiate an SPSite object, but it does not get the URL from SPContext.Current.Web.Url, as it might if it were running in the context of an ordinary content page or a Web Part. This code runs in the context of a SharePoint application page, where context information is not available. The standard practice in this case is for the consumer of the application page to pass required context information in parameters of a query string that is appended to the page URL.

    The GetDocumentLibrary method gets the value of the query string parameter by calling the GetQueryStringValue method.

  • GetQueryStringValue This method returns the value of the url parameter in the query string that is appended to the page URL. For example, given a page URL like this:

    http://teamsite/_layouts/ModalHost.ApplicationPages/ModalHost.aspx?url=http://teamsite

    The method returns the following:

    http://teamsite

  • Result This custom class contains three properties to hold document metadata (Title, URL and Created By) that are relevant here. A collection of Result objects is bound to the GridView control to display the search results.

  • Dosearch_Click This event handler enumerates over the items in the document library and generates a collection of Result objects that match the search criteria.

  • ResultGrid_SelectedIndexChanged This event handler is invoked when a user selects a row inside the gridview. It creates a semicolon-delimited string by using the selected document's title and URL. It then stores this string as the value of the Dialogvalue text box so that it can be read by the consumer application.

Now that you have a UI and code behind it, you can test the search application page.

To test the search application page

  1. In Solution Explorer, right-click the ModalHost.ApplicationPages project node, and then click Deploy.

  2. Navigate to application page using the following URL:

    http://YOUR_SITE_NAME/_layouts/ModalHost.ApplicationPages/ModalHost.aspx?url=http://YOUR_SITE_NAME

    The application page that appears should resemble Figure 4.

    Figure 4. Search application page

    Search application page
  3. Type a search term in the Enter document name text box. Then click the Search button (magnifying glass).

    The search results are displayed in the grid, as shown in Figure 5.

    Figure 5. Search results displayed in a grid

    Search results displayed in a grid
  4. Select a document by clicking Select in any row of the results, as shown in Figure 6.

    The DialogValue text box displays a semicolon-delimited string with metadata for the selected document, as shown in Figure 6.

    Figure 6. Selecting a document in the search results

    Selecting a document in search results

Step 2: Add JavaScript for the Modal Dialog Box

The next development task is to write JavaScript to open the search application page in a pop-up modal dialog box and, when the dialog box closes, return information about the selected document to the page that opened the modal dialog box.

For this task, you use the SP.UI.ModalDialog Class in the SharePoint 2010 JavaScript Class Library. You also reference the jQuery library version jquery-1.5.min.js. You can download jquery-1.5.min.js from the jQuery site.

Note Note

The example code references a particular version of the jQuery library. You can use a later version as long as you change all references in the example code to the version of the library that you are using. A simpler alternative might be to rename the file that you have downloaded to jquery-1.5.min.js.

To create the modal dialog box script

  1. In Solution Explorer, under the Layouts folder, right-click the ModalHost.ApplicationPages folder, point to Add, and then click New Folder. Name the new folder scripts.

  2. Right-click the scripts folder, point to Add, and then click Existing Item. Navigate to the downloaded jquery-1.5.min.js file, and then click Add.

  3. Right-click the scripts folder, point to Add, and then click New Item. Select Visual C#, and then click General. Select the Text File template, and name the file ModalHost.js. Then click Add.

  4. Copy the following code and paste it into ModalHost.js.

    //  When invoked open the modal dialog box.
    //  We will call this function from the Web Parts page.
    function popupmodalui(url) {
    
        // Set the required properties.
        var options = { autoSize: true,
                        title: "Search documents",
                        showClose: true,
                        allowMaximize: true 
                      };
        // Pop up the application page in the modal dialog box, 
        // and pass the site URL as a query string to the application page.
          SP.UI.ModalDialog.commonModalDialogOpen("../_layouts/ModalHost.ApplicationPages/ModalHost.aspx?url=" 
          + url, 
    
    options, closecallback, null);
    }
    
    // Handles the click event for OK button on the modal dialog box.
    // This function runs in the context of the application page.
    function ModalOk_click() {
    
        // Get the value of the hidden text box on the modal dialog box.
        var value = getValueByClass('.modalhiddenfield');
    
        // Pass the hidden text box value to the callback and close the modal dialog box.
        SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK, value);
    }
    
    // Handles the click event for the Cancel button on the modal dialog box.
    function ModalCancel_click() {
    
        // Set the dialog result property to Cancel and close the modal dialog box.
        SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.cancel, 'Cancel clicked');
    }
    
    // Executes when the modal dialog box is closed.
    // This function runs in the context of the Web Part's page.
    function closecallback(result, value) {
    
        // Determine whether the OK button was clicked.
        if (result === SP.UI.DialogResult.OK) {
    
            // Set the value of the hidden text box on the Web Part 
            // with the value passed by the OK button event.
            var ispostback = setValue('.webparthiddenfield', value);
            if (ispostback == true) {
    
                // Postback the page so the Web Part life cycle is reinitiated.
                postpage();
            }
        }
    }
    
    // Finds a control by CSS class name and retrieves its value.
    function getValueByClass(className) {
        formtextBox = $(className);
        if (formtextBox != null) {
            return formtextBox.val();
        }
    }
    
    // Finds a control by CSS class name and sets its value.
    // This function runs in the context of the Web Part's page.
    function setValue(className, value) {
        hiddenfieldid = $(className);
        if (hiddenfieldid != null) {
            hiddenfieldid.val(value);
            /*For testing the modal dialog box with the CEWP. Can be removed.*/
            if (hiddenfieldid.css('visibility') == "visible") {
                return false;
            }
            /*--*/
            return true;
        }
    }
    
    // Check if the hidden text box on the modal dialog box is empty.
    function checkTextChange() {
        value = jQuery.trim(getValueByClass('.modalhiddenfield'));
    
        // Enable the OK button on the modal window if the hidden text box has a value.
        if (value) {
            $('#btnModalOK').removeAttr('disabled');
        }
        // Disable the OK button on the modal window if the hidden text box does not have a value.
        else {
            $('#btnModalOK').attr("disabled", "true");
        }
    }
    
    // Postback the page to reinitiate the Web Part life cycle.
    function postpage() {
        document.forms[0].submit();
    }
    
    // Look for a change every time the page is loaded.
    $(document).ready(function () {
        checkTextChange();
    });
    
    

Let's review the code.

  • popupmodalui  Opens the modal dialog box that displays the search application page. It does this by calling the SP.UI.ModalDialog.commonModalDialogOpen(url, options, callback, args) Method function and passing in the relative URL of the application page with the website URL appended in a query string parameter. As you might recall, the code behind the application page has a method to retrieve the URL from the query string.

    Later in the project, you will write code that invokes the popupmodalui function from a Web Part that hosts an InfoPath form.

  • closecallback  This function is called after the modal dialog box closes and it executes in the context of the page that called commonModalDialogOpen. If the user clicks OK to close the dialog box, the value argument is passed to the "webparthiddenfield" text box on the parent page. Later in the project, you will create this hidden text box in the Web Part that invokes the popmodalui function.

  • checkTextChange  This function checks whether the "modalhiddenfield" text box on the application page has a value. (This is the text box whose ID is DialogValue.) If the text box does have a value, the function enables the OK button. Otherwise, it disables the button.

  • ModalOk_click  This function first retrieves the value of the "modalhiddenfield" text box. Then it closes the dialog box by calling SP.UI.ModalDialog.commonModalDialogClose(dialogResult, returnVal) Method with two arguments: a value that indicates that OK was clicked, and the value taken from the "modalhiddenfield" text box.

    Note Note

    The search application page does not yet have a button to raise the event that this function handles.

  • ModalCancel_click  This function closes the dialog box and passes a value that indicates that Cancel was clicked.

    Note Note

    The search application page does not yet have a button to raise the event that this function handles.

  • postpage  This function submits the parent page, causing a partial page postback. This reinitiates the page life cycle. Later, you will write code for a Web Part that reads the value in the hidden text box in its OnPreRender event handler.

Before you continue, you should understand how the application is using hidden text boxes. The completed project will have two of them, both used for temporary storage of metadata from the document that the user selects in the modal dialog box. So far, you have created just one hidden text box, the DialogValue control on the application page. (You have not yet actually made the text box invisible. You will do that very soon.) The markup attaches the CssClass "modalhiddenfield" to the first hidden text box. This is a dummy class used to make it easier to locate the element in the page's element tree. Later in the project, you create a second hidden text box, this one on a Web Part. It will be identified by the CssClass attribute "webparthiddenfield".

When the user makes a selection in the modal dialog box, code behind the search application page stores metadata for the document in the "modalhiddenfield" text box on the application page. The click event handler for the OK button in the modal dialog box passes the value that is stored in this text box to the callback function. The callback function executes in the context of the parent page, the Web Part, and stores the value that it receives in the Web Part's "webparthiddenfield" text box. Code for the Web Part reads the "webparthiddenfield" text box and uses its value to update a hyperlink field on the InfoPath form.

Now that you have written JavaScript for the modal dialog box, you must link to the script in the markup for the application page. While you are editing the page, you can also add markup to hide the "modalhiddenfield" text box that has, until now, been visible. And you can finish by adding OK and Cancel buttons to close the dialog box.

To complete the search application page

  1. In Visual Studio, open ModalHost.aspx.

  2. Locate ContentPlaceHolderID="PlaceHolderAdditionalPageHead". Place the cursor on a blank line above the closing </asp:Content> tag. Then copy the following markup and paste it at the insertion point.

    <script src="/_layouts/ModalHost.ApplicationPages/scripts/jquery-1.5.min.js" type="text/javascript"></script>
    <script src="/_layouts/ModalHost.ApplicationPages/scripts/ModalHost.js" 
    type="text/javascript"></script>
    
    

    This markup registers the jQuery library and the modal dialog box script on the page.

  3. Locate the following markup.

    <asp:TextBox runat="server" ID="Dialogvalue" CssClass="modalhiddenfield"></asp:TextBox>
    
    

    Replace it with the following markup.

    <asp:TextBox runat="server" ID="Dialogvalue" CssClass="modalhiddenfield" 
        onchange="checkTextChange();" Style="display: none; visibility: hidden;"></asp:TextBox>
    
    

    The revised markup adds a handler for the onchange event and hides the text box.

  4. Locate the following comment:

    <!-- Insert the modal dialog OK and Cancel buttons here--->

    Replace it with the following markup.

    <div style="padding-top: 10px">
        <input type="button" name="BtnOK" id="btnModalOK" value="OK" onclick="ModalOk_click();" />&nbsp;&nbsp;
        <input type="button" name="BtnCancel" id="btnModalCancel" value="Cancel" 
        onclick="ModalCancel_click();" />
    </div>
    
    

    This markup defines the OK and Cancel buttons and attaches event handlers to both.

  5. In Solution Explorer, right-click the solution's node, and then click Deploy.

Step 3: Test the Modal Dialog Box

At this point, you can do a quick test of what you have developed so far. You can use the SharePoint CEWP for this purpose.

To test the modal dialog box

  1. Open your web browser and navigate to your SharePoint site.

  2. On the ribbon, click Site Actions, and then click More Options.

  3. Under Filter By, select Page. Then select Web Part Page, and click Create.

  4. Name the page ModalDialogSearchPage. Under Document Library, select Site Pages. Then click Create.

  5. Click Add a Web Part in any of the columns.

  6. Under Categories, select Media and Content. Under Web Parts, select Content Editor, and then click Add.

  7. In the Content Editor, click Click here to add new content, as shown in Figure 7.

    Figure 7. Add new content in Content Editor

    Add new content in Content Editor
  8. On the ribbon, click the Format Text tab. Select HTML, and then click Edit HTML Source.

    Figure 8. Edit HTML Source on the ribbon

    Edit HTML Source on the ribbon
  9. Copy the following markup and paste it into the HTML Source dialog box.

    <script src="../_layouts/ModalHost.ApplicationPages/scripts/jquery-1.5.min.js" 
    type="text/javascript"></script>
    <script src="../_layouts/ModalHost.ApplicationPages/scripts/ModalHost.js" type="text/javascript"></script>
    <div>
        <input class="webparthiddenfield" type="text" style="visibility: visible" />
        <input onclick="popupmodalui('http://teamsite');" type="button" value="Show Dialog" />
    </div>
    
    
  10. In the markup, locate the call to the popupmodalui function. Change the URL to the URL for your website. Then click OK to close the dialog box.

    The Web Part should resemble Figure 9.

    Figure 9. Web Part with Show Dialog button

    Web Part with Show Dialog button
  11. Click Show Dialog.

    The modal dialog box should appear. Notice that the OK button is not enabled.

    Figure 10. Modal dialog box with OK button disabled

    Modal dialog box with OK button disabled
  12. Type a search term, such as IT. Then click the Search button.

    Search results appear in the results grid, as shown in Figure 11.

    Figure 11. Search results as they appear in results grid

    Search results as they appear in results grid
  13. Select any row in the results grid.

    The selected row is highlighted, and the OK button is enabled, as shown in Figure 12.

    Figure 12. Selected row highlighted in search results grid

    Selected row highlighted in search results
  14. Click OK to close the modal dialog box.

  15. Examine the string in the text box. It should consist of the URL for the selected document plus a semicolon, and then the name of the document, as in the following example.

    http://teamsite/Shared%20Documents/IT%20Department%20Annual%20Report.docx;IT Department Annual Report

Step 4: Design the InfoPath Form

At this point, you have a search application page, and you can display the page in a modal dialog box. Next, you create a form to invoke the dialog box.

For the sample application, you do not require anything very complex. A very simple form will do. It must have these three elements:

  • A label ("Organization Chart")

  • A hyperlink field ("URL")

  • A button ("Search")

You also want to create a handler for the button click event. In the handler, you call the NotifyHost(String) method, and in the method's notification parameter, you pass an XPath expression for the field. This expression is communicated via event arguments to whatever is handling the NotifyHost event on the host. (You implement the host's side of the conversation later, in Step 6.)

Finally, finish Step 4 by publishing the form to a website that is running SharePoint Server 2010.

To design the form

  1. In InfoPath Designer 2010, click the File tab, and then click New.

  2. Under Available Form Templates, select Blank Form. Then click Design Form.

  3. Click Click to add title, and then type Contoso Resource Planning.

  4. In the body of the form, click Add tables. Click the Insert tab, and then click Custom Table. Insert a 3x1 table (three columns and one row).

  5. Click in the first column of the table, and type Organization Chart.

  6. Click anywhere in the second column of the table. Then click the Home tab, and in the Controls group under Objects, click Hyperlink.

    A new field1 and an attribute field2 appear in the Fields task pane.

  7. In the Fields task pane, right-click field1, and select Properties.

  8. On the Data tab, change the Name property to URL. Click OK.

  9. Repeat the two previous steps, this time renaming field2 to Title.

  10. In the second column of the table, right-click the hyperlink control, and then select Hyperlink Properties. In the Hyperlink Properties dialog box (see Figure 13), select the Read-Only check box. Then click OK.

    This step prevents the user from editing the hyperlink manually.

    Figure 13. Hyperlink Properties dialog box

    Hyperlink Properties dialog box
  11. Click anywhere in the third column of the table. Then click the Home tab, and in the Controls group under Objects, click Button.

  12. Right-click the Button control, and then select Button Properties.

  13. In the Button Properties dialog box, change the Label property to Search, and change the ID property to InvokePopup. Then click OK.

  14. (Optional) Customize the page layout and form style as you want.

  15. On the File tab, click Save. Name the file ContosoResourcePlanning.xsn (see Figure 14).

    Figure 14. Contoso Resource Planning form

    Contoso Resource Planning form

To add a handler for the button click event

  1. In InfoPath Designer, click the Developer tab, and then click Language.

  2. In the Form Options dialog box, under Form template code language, select C#, and then click OK.

  3. On the Developer tab, click Code Editor to start Microsoft Visual Studio Tools for Applications.

  4. In InfoPath Designer, on the form design surface, select the Search button. On the ribbon, click the Properties tab. Click Custom Code to generate the click event handler for the button.

  5. In Visual Studio Tools for Applications, locate the InvokePopup_Clicked method. Paste the following code after the comment.

    
    NotifyHost("/my:myFields/my:URL");
    
    

    This line of code serves two purposes. First, it notifies the host that a user has clicked the Search button. Second, it passes the XPath for the field that should receive the result of the search.

    NoteNote

    You can obtain the XPath of the URL field by right-clicking the URL node in the Fields task pane and selecting Copy XPath.

  6. On the File menu, click Save FormCode.cs. Then exit Visual Studio Tools for Applications.

Now you are ready to publish the form template.

To publish the form template

  1. In InfoPath Designer, click the File tab.

  2. In the actions column, click Publish, and then click SharePoint Server, as shown in Figure 15.

    Figure 15. Publishing form to SharePoint Server

    Publishing form to SharePoint Server
  3. In the Publishing Wizard, type the URL of the target SharePoint website, and then click Next.

  4. Verify that the Enable this form to be filled out by using a browser check box is selected, and that Form Library is selected, as shown in Figure 16.

    Figure 16. SharePoint Publishing Wizard

    SharePoint Publishing Wizard
  5. Click Next twice.

  6. Click Publish.

    InfoPath Designer first creates a new form library on the website and then publishes the resource planning form in the library.

  7. In the Publishing Wizard, click Open this form in the browser.

    The form opens in the default browser.

  8. Copy the entire contents of the browser's address bar, and paste the URL in a text file.

    The address bar contents should resemble the following.

    http://teamsite/_layouts/FormServer.aspx?XsnLocation=http://teamsite/ContosoResourcePlanning/forms/template.xsn&OpenIn=browser&SaveLocation= http://teamsite/ContosoResourcePlanning&Source=http://teamsite/ContosoResourcePlanning

  9. Delete everything up to and including the first equal sign (=). Then delete everything after "template.xsn".

    The result should resemble the following:

    http://teamsite/ContosoResourcePlanning/forms/template.xsn

    This is the absolute URL to the form template file. You will need this information when you configure a Web Part to host the form.

  10. Save the text file.

Step 5: Create a Web Part to Host the Form

In the previous step, you created an InfoPath form that notifies its host whenever the Search button is clicked. Now it is time to create the host.

In this step, we create a custom Web Part that includes an XmlFormView control that can be configured to host a browser-enabled InfoPath form. In addition, the Web Part has a text box control for temporary storage of the value that is returned by the modal Search dialog box. Because the text box is used only for storage, it is styled in a way that prevents it from rendering on the page. Finally, you want the Web Part to be reusable, so you will give it a custom property that accepts the URL to a form template file.

After the Web Part is created, you redeploy the solution and add the Web Part to a page.

To create the custom Web Part

  1. In Visual Studio, open the ModalHost.ApplicationPages solution.

  2. In Solution Explorer, right-click the solution node, point to Add, and then click New Project. Select SharePoint, select 2010, and then select Empty Project. Name the project ModalHost.WebParts. Click OK.

    The SharePoint Customization Wizard appears.

  3. Select Deploy as a farm solution, and then click Finish.

  4. In Solution Explorer, right-click the ModalHost.WebParts project node, point to Add, and then click New Item. Select SharePoint, select 2010, and then select Web Part. Name the Web Part InfoPathFormHost, and then click OK.

  5. In the ModalHost.WebParts project, add a reference to Microsoft.Office.InfoPath.Server.dll by browsing to the folder <Drive Letter>:\Program Files\Microsoft Office Servers\14.0\Bin.

    If Visual Studio displays a warning message, click Yes to continue adding the reference to the project.

  6. At the top of InfoPathFormHost.css, add the following using statements:

    using Microsoft.Office.InfoPath.Server.Controls;
    using System.Text;
    
  7. The InfoPathFormHost class contains an empty CreateChildControls method. Delete this method, and then paste the following code in the body of the InfoPathFormHost class.

    // XmlFormView control object to render the browser-enabled InfoPath form.
    private XmlFormView xmlFormView = null;
    
    // Field to hold the return value from the modal dialog box.
    private TextBox hiddenText = null;
    private string receivedValue = null;
    
    // Property to store the InfoPath XSN URL.
    [WebBrowsable(true)]
    [WebDisplayName("Form XSN URL")]
    [Description("Relative URL of the InfoPath XSN file.")]
    [Category("Configurations")]
    [Personalizable(PersonalizationScope.Shared)]
    public string FormXSNURL { get; set; }
    
    protected override void CreateChildControls()
    {
        try
        {
            base.CreateChildControls();
    
            // Instantiate the XMlFormView object.
            this.xmlFormView = new XmlFormView();
    
            // Set the editing status to init on initial load.
            this.xmlFormView.EditingStatus = XmlFormView.EditingState.Init;
    
            // Add to the Web Part controls collection.
            this.Controls.Add(this.xmlFormView);
    
            this.hiddenText = new TextBox();
    
            // Hide the text box.
            this.hiddenText.Style.Add("display", "none");
            this.hiddenText.Style.Add("visibility", "hidden");
     
           // Assign a dummy class to the control so it can be found through jQuery functions.
            this.hiddenText.CssClass = "webparthiddenfield";
    
            // Add to the Web Part controls collection.
            this.Controls.Add(this.hiddenText);
        }
        catch
        {
            throw;
        }
    }
    
    
    

    This code declares the FormXSNURL property and makes it available through the Web Part's tool pane so that the user can set it with the URL of the published InfoPath form's XSN file.The code also initializes an XMLFormView control and a text box control, and adds them to the controls collection. The text box is explicitly marked as hidden on the page. It is later used to store the value that is returned by the modal dialog box.

  8. Just below the CreateChildControls method, add the following code.

    protected override void OnPreRender(EventArgs e)
    {
        try
        {
            // Determine whether the user has set the FormXSNURL property with InfoPath XSN location.
            if (!string.IsNullOrEmpty(this.FormXSNURL))
            {
                // Configure the XmlFormView control to host the form.
                this.xmlFormView.XsnLocation = this.FormXSNURL;
    
                // Set the editing status to ensure the control displays the form.
                this.xmlFormView.EditingStatus = XmlFormView.EditingState.Editing;
            }
        }
        catch
        {
            throw;
        }
    }
    
    
    

    This code overrides the OnPreRender() event handler to configure the XmlFormView control to host the InfoPath form that the Web Part's FormXSNURL property points to, and to set the control's EditingStatus property to Editing. Doing this enables the XMLFormView control to access the InfoPath form's underlying XML data source.

  9. In Solution Explorer, right-click the ModalHost.WebParts project node, and then click Deploy.

  10. Navigate to the page that you created while you were testing the modal dialog box.

  11. Remove the previously added CEWP from the page.

  12. Add the InfoPathFormHost Web Part. (Look for it under the Custom category on the ribbon.)

  13. On the InfoPathFormHost Web Part menu, click Edit Web Part to open the tool pane.

  14. Locate the Configurations category at the bottom of the tool pane, and set Form XSN URL to point to the location where you published the InfoPath form, as shown in Figure 17. (You saved the URL in a text file.)

    Figure 17. Tool pane showing the Configurations category

    Tool pane showing the Configurations category
  15. Click OK in the tool pane to close it.

  16. On the ribbon, click Stop Editing button to see the InfoPath form in the browser.

At this point, you have successfully created a browser-enabled InfoPath form, a Web Part to host the form, a search application page to find a document, and a modal dialog box to display the search page. The only task left is to put these components together.

Step 6: Connect the Components

To connect the components, you can start by writing code that makes the XmlFormView control respond to NotifyHost events raised by the form that it hosts. A NotifyHost event occurs when the form's Search button is clicked. You want the host to respond by showing the modal dialog box that displays the search application page. Then, after the dialog box closes and the result of the user's search is posted back to the Web Parts page, additional Web Part code should access the main data source for the InfoPath form and update the hyperlink field by setting its value to the URL of the document that the user selected.

Handling the NotifyHost event

When you created the InfoPath form, you gave it a Search button, and you wrote a button click event handler that calls the NotifyHost(String) method of the XmlForm object. The NotifyHost method accepts a string argument, and you used this argument to pass the XPath for the form field that you want to update.

Now you must prepare the form's host to listen for NotifyHost events and respond by displaying a Search dialog box. Under the hood, you also want to extract the XPath for the form field and put it aside for later use.

You will start by giving the XMLFormView control a NotifyHost event handler. In the handler, you want to do two things:

  • Extract the XPath from the Notification property of the NotifyHostEventArgs object and save it in the Web Part's view state.

  • Add JavaScript to the page so that the next time the page renders in the browser, your modal dialog box appears.

To add a handler for the NotifyHost event

  1. In Visual Studio, open the InfoPathFormHost.cs file in the ModalHost.WebParts project.

  2. Navigate to the CreateChildControls method. Immediately before the line of code that adds the XMLFormView control to the Web Part controls collection, add the following line of code.

    // Add a handler for the NotifyHost event.
    this.xmlFormView.NotifyHost += new EventHandler<NotifyHostEventArgs>(xmlFormView_NotifyHost);
    
    
    
  3. To define the xmlFormView_NotifyHost method, paste the following code just below the CreateChildControls method.

    // Handles the event that is raised when the button 
    // in the InfoPath form is clicked.
    void xmlFormView_NotifyHost(object sender, NotifyHostEventArgs e)
    {
        try
        {
            // Check if the argument contains the XPath.
            if (!string.IsNullOrEmpty(e.Notification))
            {
    
                // Save the InfoPath field XPath in the view state so it can be used later.
                ViewState["fieldXPath"] = e.Notification;
    
                // Construct a JavaScript function to invoke the modal dialog box.
                StringBuilder functionSyntax = new StringBuilder();
                functionSyntax.AppendLine("function popupparams() {");
    
                // Pass the current SharePoint web URL as an argument.
                functionSyntax.AppendLine("var url ='" + SPContext.Current.Web.Url + "';");
    
                // Call the JavaScript function to pop up the modal dialog box.
                functionSyntax.AppendLine("popupmodalui(url);}");
    
                // Ensure the function popupparams is called after the UI is finished loading.
                functionSyntax.AppendLine("_spBodyOnLoadFunctionNames.push('popupparams');");
    
                // Register the script on the page.
                Page.ClientScript.RegisterClientScriptBlock(typeof(Page), "ModalHostScript", functionSyntax.ToString(), true);
            }
        }
        catch
        {
            throw;
        }
    }
    
    
    

    In the event handler, you create and register a script block that defines the JavaScript function popupparams. This function calls the popupmodalui function that we defined earlier in the project (see Step 2: Add JavaScript for the Modal Dialog Box) and passes in the URL for the current page. Then the event handler ensures that popupparams executes when the page body loads by adding it to the _spBodyOnLoadFunctionNames array.

    NoteNote

    _spBodyOnLoadFunctionNames is a global variable that is defined in the INIT.js file as an array of functions that are registered when a SharePoint page loads. You cannot directly add a function to the onload event for the body of the page because content pages in SharePoint are based on a master page that contains the "body" element. To work around this limitation, SharePoint provides indirect access to the onload event through the _spBodyOnLoadFunctionNames array. When the body of the page is loaded, the onload event handler executes each function whose name is contained in this array.

To finish wiring up the event handler, you must register ModalHost.js on the page. ModalHost.js calls into the JQuery library, so you must also register that. A good place to do the registration is in the handler for the Web Part's OnLoad event.

To register the external script files

  • In the InfoPathFormHost class, just above the CreateChildControls method, paste the following code that overrides the OnLoad event handler.

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
     
       // Register scripts to load and display the modal dialog box.
        ScriptLink.Register(this.Page, "ModalHost.ApplicationPages/scripts/jquery-1.5.min.js", false);
        ScriptLink.Register(this.Page, "ModalHost.ApplicationPages/scripts/ModalHost.js", false);
    }
    
    
    

Updating the Field in the InfoPath Form

The final task is to set the value of the URL field in the form to the URL of the document that the user selects before closing the modal Search dialog box.It may help if you first review the sequence of events:

  • A user clicks the Search button on the InfoPath form, raising the NotifyHost event on the host Web Part.

  • Code for the Web Part responds to the event by storing the XPath for the form field in the view state. It then invokes JavaScript that causes the modal dialog box to appear.

  • The modal dialog box displays an application page with an interface that lets the user search for and select a document.

  • When the user selects a document, logic in the code behind the search application page places the URL for the selected document in a hidden text box on the application page and enables the OK button in the dialog box.

  • The user clicks OK. The handler for the button click event (ModalOk_click in ModalHost.js) extracts the URL from the hidden text box and passes it as an argument in a call to the SP.UI.ModalDialog.commonModalDialogClose(dialogResult, returnVal) Method.

  • The commonModalDialogClose method closes the dialog box and passes the URL along to the dialog box's callback function (closecallback in ModalHost.js).

  • The callback function stores the URL in a hidden text box on the Web Part.

Therefore, to complete the final task, you need code that retrieves the URL from the hidden text box on the Web Part, retrieves the XPath for the field from the view state, and uses both pieces of information to update the value of the field. If the user exits the modal dialog box by clicking OK, the callback function forces a postback that refreshes the page that hosts the form. Therefore, a good place to put code that updates the form is in the Web Part's OnPreRender method.

To add code that updates the URL field

  1. In Solution Explorer, right-click the ModalHost.WebParts project node, and then click Add Reference.

  2. In the Add Reference dialog box, browse to Drive Letter:\Program Files\Microsoft Office Servers\14.0\Bin\Microsoft.Office.InfoPath.dll, and then click OK. If Visual Studio displays a warning message, click Yes, and continue adding the reference to the project.

  3. Open InfoPathFormHost.cs, and add the following using statements at the top of the file.

    using System.Xml;
    using System.Xml.XPath;
    
    
  4. Navigate to the OnPreRender method. Add the following code immediately after the line that sets the EditingStatus property of the XMLFormView control.

    // Retrieve the return value from the modal dialog box.
    this.receivedValue = this.hiddenText.Text;
    
    // Determine whether the received value is not empty.
    if (!string.IsNullOrEmpty(this.receivedValue))
    {
        // Update the form data source with the new value.
        this.UpdateFormMainDataSource();
    }
    
    
    
  5. Define the UpdateFormMainDataSource method by adding the following code after the OnPreRender method.

    // Updates the form's datasource with the received value.
    private void UpdateFormMainDataSource()
    {
        if (ViewState["fieldXPath"] != null && ViewState["fieldXPath"].ToString() != string.Empty && !string.IsNullOrEmpty(this.receivedValue))
        {
            // Ensure the XMlFormView has access to the form's underlying data source.
            this.xmlFormView.DataBind();
    
            // Retrieve values from the semicolon (;) separated string.
            int index = this.receivedValue.IndexOf(";");
            if (index != -1)
            {
                // Get the document URL.
                string url = this.receivedValue.Substring(0, index);
    
                // Get the document name.
                string text = this.receivedValue.Substring(index + 1);
    
                // Pass the target InfoPath field XPath and the received values.
                this.SetFormFieldValue(ViewState["fieldXPath"].ToString(), url, text);
            }
        }
    }
    
    
  6. Define the SetFormFieldValue method by pasting the following code after the UpdateFormMainDataSource method.

    // Sets the target InfoPath form field value with the received value.
    private void SetFormFieldValue(string xpath, string url, string value)
    {
        // Create an XPathNavigator positioned at the root of the form's main data source.
        XPathNavigator xNavMain = this.xmlFormView.XmlForm.MainDataSource.CreateNavigator();
    
        // Create an XmlNamespaceManager.
        XmlNamespaceManager xNameSpace = new XmlNamespaceManager(new NameTable());
    
        // Add the "my" namespace alias from the form's main data source.
        // Note: Replace the second argument with the correct namespace 
        // from the form template that you are using.
        xNameSpace.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2011-03-29T15:01:42");
    
        // Create an XPathNavigator positioned on the target form field.
        XPathNavigator formfield = xNavMain.SelectSingleNode(xpath, xNameSpace);
    
        // Set the form's hyperlink field to the received document URL.
        formfield.SetValue(url);
        if (formfield.HasAttributes)
        {
            // Set the hyperlink's display text with document title.
            formfield.MoveToFirstAttribute();
            formfield.SetValue(value);
        }
    }
    
    
  7. Replace the second argument passed to the AddNamespace method with the correct namespace for your form template. To get the correct value, open the form template in Design mode, and click Show Fields on the ribbon to display the Fields task pane. Right-click myFields, and then click Details.

  8. Press F5 to build and deploy the solution.

This concludes the last phase of application development.

Now it is time to make sure that everything works as expected.

To test the application

  1. Navigate to your SharePoint website in your web browser, and open the Web Parts page where you previously added the InfoPathFormHost Web Part. The form appears as shown in Figure 18.

    Figure 18. Contoso Resource Planning form

    Contoso Resource Planning form
  2. On the Contoso Resource Planning form, click Search. A modal dialog box should open. The OK button should be disabled, as shown in Figure 19.

    Figure 19. Modal dialog box with OK button disabled

    Modal dialog box with OK button disabled
  3. Type a search term in the text box, and then click the Search button (magnifying glass). The names of any matching documents should fill the results grid, as shown in Figure 20.

    Figure 20. Search results in results grid

    Search results in results grid
  4. Select a document by clicking Select. The row that contains the selected document should be bold, and the OK button should be enabled, as shown in Figure 21.

    Figure 21. Selected document in search results grid

    Selected document in search results
  5. Click OK. The dialog box closes, and a link to the selected document appears in the URL field of the form, as shown in Figure 22.

    Figure 22. Contoso Resource Planning form with URL added

    Contoso Resource Planning form with URL added

This article demonstrates how to create a custom Web Part that hosts an InfoPath 2010 browser-enabled form. The article also shows you how to create a handler for the NotifyHost event raised by the form, and how to use the handler to invoke client-side code that opens a SharePoint 2010 modal dialog box. In addition, the article demonstrates how to return a value from a modal dialog box and then use the returned value to update the hosted form.

Show:
© 2014 Microsoft