Monday, February 23, 2009

SharePoint InfoPath Integration Basics

I recently had to quickly write up some simple documentation that will be used by an administrative assistant for drafting up some existing forms from Word documents. I have included basic steps for creating a Form Library, creating Site Columns, designing InfoPath Forms, and publishing them to the Form Library. These are fairly basic steps and don't cover all scenarios, but should provide some basic fundamentals and give you an idea of how easily it is to create forms using SharePoint and InfoPath.

 

Create Form Library

This is where the forms will be created, stored, and managed.

1. Navigate to the SharePoint web site that the Form Library will be added to.

2. Click on Site Settings item from the Site Actions menu and then click on Form Library link found under Libraries.

clip_image002

3. Type in a name and click Create.

4. To enable web-based forms and allow management of content types, do the following.

a. Click on Form Library Settings item from the Settings menu.

b. Click on Advanced settings link found under General Settings.

c. Click on the Yes option for Allow management of content types and select the Display as a Web page option for Browser-enabled Documents and then click the OK button.

clip_image004

Create Site Columns

Site Columns are used within SharePoint to define types that can be used across different Document Libraries, Form Libraries, Lists, etc. If a group of forms include common data, it’s a good idea to either use existing Site Columns or create new ones. (Hint: It isn’t necessary to create Site Columns for every input item on the form, only ones that are common to other forms or will be used for views or reporting.

1. Navigate to the SharePoint web site that the Site Columns will be added to.

2. Click on Site Settings item from the Site Actions menu and then click on Site columns link found under Galleries.

3. Click on the Create button to create the Site Column. If these will be grouped with others, pick an Existing group from the drop down menu or type in a New group.

clip_image006

Design InfoPath Forms

1. Import Existing Word Document

a. Open InfoPath and click on the Import a Form link from the Getting Started dialog.

clip_image008

(Hint: If the Getting Started dialog isn’t showing, click on Design a Form Template from the File menu and click Import.)

clip_image010

b. Click Import Form item from the File menu to open the Import Wizard.

c. Select InfoPath importer for Word documents item from the list and click the Next button.

clip_image012

d. Using the Browse button, navigate to the Word document and click the Open button.

clip_image014

e. Click the Finish button to import and then click the OK button once it is imported.

clip_image016

f. (Hint: If the form was imported from Word and has the message above, it is most likely referring features specific to Word. Therefore, if the message is similar to the one below in the Design Checker within InfoPath, it can be ignored.)

clip_image018

2. Add Form Controls

These are the controls that allow the user to enter data. All other text on the form will be read-only, so there will need to be a control for anything that requires input. (Hint: If controls were in the imported Word document, many of these will have already been applied. However, there are InfoPath controls that are not available in Word and should be taken advantage of. For example, Word does not have a Date Picker control.)

a. If it is not open already click on Design Tasks item from the View menu.

(Hint: This will be used a lot, so get used to how the navigation works. Use the Back left arrow button to return to Design Tasks.

clip_image020

b. Click the Controls link from the Design Tasks pane.

clip_image022

c. Click anywhere on the form and then click a control from the Controls pane to add it. The control properties can then be modified if necessary by right-clicking on the control and clicking the control properties.

(Hint: For more information, click on the Help with Controls link at the bottom of the controls pane.)

d. Important: Before continuing to the next step, always make sure to click the Preview item from the File menu to ensure the form behaves as expected.

3. Publish Form to InfoPath Library

a. Click Design Tasks from the View menu and click on the Change Compatibility Settings link.

b. In order to enable columns to be used with in SharePoint or eventually map the input values to Site Columns, do the following.

     i. Click on the Property Promotion item from the Category list, click the Add button, and add all input items that will be used.

clip_image024

c. In order to publish as a browser-enabled form instead of InfoPath client based form, do the following.

     i. Click the Compatibility item from the Category list and click on the Design a form template that can be opened in a browser or InfoPath    checkbox.

clip_image026

     ii. Enter the name of the SharePoint site URL.

d. Click the OK button.

e. Click the Back left arrow or Design Tasks link at the top of the Design Checker.

f. Click the Publish Form Template link.

g. If a Save File Dialog appears, copy and paste in the URL to the Forms Library where you want the template published using the following format: http://[SharePointSiteName]/[FormLibraryName]/Forms/[FormName].xsn

h. Click the To a SharePoint server with or without InfoPath Forms Services option and click the Next button.

clip_image028

i. Enter the location of your SharePoint site.

clip_image030

j. Click the Enable this form to be filled out by using a browser checkbox and Site Content Type option and click the Next button.

clip_image032

k. If you are creating a new content type, do the following.

     i. Click the Create a new content type option and click the Next button.

     ii. Type in a Name for the content type and click the Next button.

clip_image034

l. If you are updating an existing site content type, do the following.

     i. Click the Update and existing site content type option.

     ii. Click the appropriate content type in the list and click the Next button.

clip_image036

m. Type in the URL and file name to the URL that will be used to publish the template and then click the Next button.

clip_image038

n. In order to promote properties to SharePoint and map them to corresponding site columns, use the following steps. (Hint: On subsequent updates, these will be pre-populated.)

     i. Select the column item from the Column Name list and click the Modify button.

clip_image040

     ii. Select the corresponding site column group item from the Site column group drop down list, the column item from the Column name drop down list, and then the OK button. Repeat this for all properties that will be promoted and then click the Next button.

clip_image042

     iii. Click the Publish button.

clip_image044

     iv. Open a browser and navigate to the SharePoint Form Library where you have deployed the InfoPath form.

     v. Click on the Form Library Settings item from the Settings menu.

     vi. Under Content Types, click on the Add from existing site content types link.

clip_image046

     vii. Select the Site Content Type item from the list, click the Add button, and click the OK button.

clip_image048

 

viii. Navigate back to the Form Library view and verify that the Form shows up under the New menu.

image

Thursday, February 19, 2009

Use VBA macro to rename Word document with Guid when opened

One thing I don't like about SharePoint Document Libraries is that when you try to save a document from a template, it always uses the existing template name. This means that you must rename it if you don't want to overwrite an existing document. I have come up with a simple solution that checks to see if the document template name is being used and if so renames the document with a Guid. If an existing document is opened, the document is not renamed since it is not using the template name.

 

1.  From the Developer tag click on the Visual Basic button.

2.  Click on ThisDocument under Project --> Microsoft Word Objects and add the following code:

Private Sub Document_Open()

    If ActiveDocument.Name = "Doc1.docm" Then 'Change this name to whatever the template name is.

        ActiveDocument.SaveAs FileName:=GetGUID() & ".docm"

    End If

End Sub

3.  Right-click on Project and Insert Module (not Class Module) and add the following code:

(Note: Taken from the following:  http://support.microsoft.com/kb/176790)

Private Type GUID

Data1 As Long

Data2 As Integer

Data3 As Integer

Data4(7) As Byte

End Type

Private Declare Function CoCreateGuid Lib "OLE32.DLL" (pGuid As GUID) As Long

Public Function GetGUID() As String

Dim udtGUID As GUID

If (CoCreateGuid(udtGUID) = 0) Then

GetGUID = _

String(8 - Len(Hex$(udtGUID.Data1)), "0") & Hex$(udtGUID.Data1) & _

String(4 - Len(Hex$(udtGUID.Data2)), "0") & Hex$(udtGUID.Data2) & _

String(4 - Len(Hex$(udtGUID.Data3)), "0") & Hex$(udtGUID.Data3) & _

IIf((udtGUID.Data4(0) < &H10), "0", "") & Hex$(udtGUID.Data4(0)) & _

IIf((udtGUID.Data4(1) < &H10), "0", "") & Hex$(udtGUID.Data4(1)) & _

IIf((udtGUID.Data4(2) < &H10), "0", "") & Hex$(udtGUID.Data4(2)) & _

IIf((udtGUID.Data4(3) < &H10), "0", "") & Hex$(udtGUID.Data4(3)) & _

IIf((udtGUID.Data4(4) < &H10), "0", "") & Hex$(udtGUID.Data4(4)) & _

IIf((udtGUID.Data4(5) < &H10), "0", "") & Hex$(udtGUID.Data4(5)) & _

IIf((udtGUID.Data4(6) < &H10), "0", "") & Hex$(udtGUID.Data4(6)) & _

IIf((udtGUID.Data4(7) < &H10), "0", "") & Hex$(udtGUID.Data4(7))

End If

End Function

 

Note: For an interesting discussion of Guid duplicate probability, see the following article.

Random UUID probability of duplicates

"...only after generating 1 billion UUIDs every second for the next 100 years, the probability of creating just one duplicate would be about 50%. The probability of one duplicate would be about 50% if every person on earth owns 600 million UUIDs."

Tuesday, February 10, 2009

ASP.NET GridView With Simple Collapse and Expand Functionality

I recently needed an expandable and collapsible GridView in a project I was working on, but couldn't find any simple solutions that met my criteria. Fortunately, it was fairly easy to write up. I have included the basic source code below, so it should be fairly easy to implement into other projects. The only thing you will need to do is find a collapse and expand icon and then update the image names accordingly in the code behind. (For SharePoint, you can use "_layouts\images\TPMin2.gif" for collapse and "_layouts\images\TPMax2.gif" for expand.)

Note: I have not included code for paging. This will require a little more work.

Requirements

  • Use a standard ASP.NET GridView control.
  • Ability to collapse or expand the whole GridView or selective parts of it.
  • Use client side Javascript to ensure better performance and no browser refresh.
  • Only show expand and collapse buttons and grouping value ("Company" in my case) if the next row has the same grouping value.
  • Provide the ability to have the GridView collapsed initially. This is handled in the OnRowDataBound event handler.

Smoke Tests

  • Collapse / Expand link works as expected and the text is updated accordingly. - PASS
  • Collapse and expand button icons are updated properly when clicked and when using the Collapse / Expand link. - PASS
  • Rows without expand and collapse behave properly, especially the first and last rows. - PASS

Screenshots

All Expanded

image

All Collapsed

image

Some Expanded and Some Collapsed

image

ASPX Code

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>

    <script type="text/javascript">
        function ExpandCollapse(index, imageName) {
            var continueLoop = new Boolean(true);
            var imageMax = "TPMax2.gif";
            var imageMin = "TPMin2.gif";
            var rows = document.getElementById('<%= GridView1.ClientID %>').getElementsByTagName('tbody')[0].getElementsByTagName('tr');
            var image = document.getElementsByName(imageName);
            var imageSrc = image[0].src;
            if (imageSrc.match(imageMax) != null) {
                image[0].src = imageMin;
                index++;
                while (continueLoop) {
                    if (rows[index].cells[1].innerHTML == "") {
                        rows[index].style.display = "block";
                        index++;
                        if (index >= rows.length) {
                            continueLoop = false;
                        }
                    }
                    else {
                        continueLoop = false;
                    }
                }
            }
            else {
                image[0].src = imageMax;
                index++
                while (continueLoop) {
                    if (rows[index].cells[1].innerHTML == "") {
                        rows[index].style.display = "none";
                        index++;
                        if (index >= rows.length) {
                            continueLoop = false;
                        }
                    }
                    else {
                        continueLoop = false;
                    }
                }
            }
        }

        function ExpandCollapseAll(linkId) {
            var link = document.getElementById(linkId);
            var imageMax = "TPMax2.gif";
            var imageMin = "TPMin2.gif";
            var rows = document.getElementById('<%= GridView1.ClientID %>').getElementsByTagName('tbody')[0].getElementsByTagName('tr');
            if (link.innerHTML == 'Expand All') {
                link.innerHTML = 'Collapse All';
                for (var i = 1; i < rows.length; i++) {
                    if (rows[i].cells[1].innerHTML != "") {
                        if (rows[i].cells[0].childNodes[0] != undefined) {
                            var img = rows[i].cells[0].childNodes[0];
                            img.src = imageMin;
                        }
                    }
                    else {
                        rows[i].style.display = "block";
                    }
                }
            }
            else {
                link.innerHTML = 'Expand All';
                for (var i = 1; i < rows.length; i++) {
                    if (rows[i].cells[1].innerHTML != "") {
                        if (rows[i].cells[0].childNodes[0] != undefined) {
                            var img = rows[i].cells[0].childNodes[0];
                            img.src = imageMax;
                        }
                    }
                    else {
                        rows[i].style.display = "none";
                    }
                }
            }
        }
    </script>

</head>
<body>
    <form id="form1" runat="server">
    <div>
        <div onclick="javascript:ExpandCollapseAll(this.id);" id="CollapseExpandAllLink"
            style="color: Blue; text-decoration: underline; cursor: pointer" runat="server">
            Collapse All</div>
        <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false" OnRowDataBound="GridView1_OnRowDataBound"
            OnDataBound="GridView1_DataBound">
            <Columns>
                <asp:ImageField DataImageUrlField="ImageUrl">
                </asp:ImageField>
                <asp:BoundField DataField="Company" HeaderText="Company" />
                <asp:BoundField DataField="FirstName" HeaderText="First Name" />
                <asp:BoundField DataField="LastName" HeaderText="Last Name" />
            </Columns>
        </asp:GridView>
    </div>
    </form>
</body>
</html>

ASPX Code Behind

using System;
using System.Collections.Generic;
using System.Data;
using System.Web.UI.WebControls;

public partial class _Default : System.Web.UI.Page
{
    public string PreviousGroupingValue { get; set; }
    public string ImageMin = "TPMin2.gif";
    public string ImageMax = "TPMax2.gif";
    protected void Page_Load(object sender, EventArgs e)
    {       
        DataTable dt = new DataTable();
        dt.Columns.Add("ImageUrl");
        dt.Columns.Add("Company");
        dt.Columns.Add("FirstName");
        dt.Columns.Add("LastName");
        DataRow dr = dt.NewRow();
        foreach (Employee employee in GetEmployees())
        {
            dr = dt.NewRow();
            dr["ImageUrl"] = ImageMin;
            dr["Company"] = employee.Company;
            dr["FirstName"] = employee.FirstName;
            dr["LastName"] = employee.LastName;
            dt.Rows.Add(dr);
        }
        GridView1.DataSource = dt;
        GridView1.DataBind();
    }
    protected void GridView1_OnRowDataBound(object sender, GridViewRowEventArgs e)
    {
        string imageName = string.Empty;
        bool collapsedInitially = true;
        bool populateImageCtrl = false;
        if (collapsedInitially)
        {
            CollapseExpandAllLink.InnerText = "Expand All";
        }
        else
        {
            CollapseExpandAllLink.InnerText = "Collapse All";
        }
        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            imageName = "ImageExpand" + e.Row.RowIndex.ToString();
            if (e.Row.RowIndex != 0)
            {
                if (GridView1.Rows[e.Row.RowIndex - 1].RowType == DataControlRowType.DataRow)
                {
                    if (e.Row.Cells[1].Text == PreviousGroupingValue)
                    {
                        PreviousGroupingValue = e.Row.Cells[1].Text;
                        e.Row.Cells[1].Text = "";
                        ((Image)e.Row.Cells[0].Controls[0]).Visible = false;
                        if (collapsedInitially)
                        {
                            e.Row.Style.Add("display", "none");
                        }
                    }
                    else
                    {
                        ((Image)GridView1.Rows[e.Row.RowIndex - 1].Cells[0].Controls[0]).Visible = false;
                        populateImageCtrl = true;
                    }
                }
            }
            else
            {
                populateImageCtrl = true;
            }
            if (populateImageCtrl)
            {
                ((Image)e.Row.Cells[0].Controls[0]).Attributes.Add("name", imageName);
                ((Image)e.Row.Cells[0].Controls[0]).Attributes.Add("onclick", "javascript:ExpandCollapse(" + (Convert.ToInt32(e.Row.RowIndex + 1)).ToString() + ", '" + imageName + "');");
                ((Image)e.Row.Cells[0].Controls[0]).ImageUrl = ImageMax;
                PreviousGroupingValue = e.Row.Cells[1].Text;
            }
        }
    }

    protected void GridView1_DataBound(object sender, EventArgs e)
    {
        if (GridView1.Rows.Count > 1)
        {
            if (GridView1.Rows[GridView1.Rows.Count - 1].Cells[1].Text != GridView1.Rows[GridView1.Rows.Count - 2].Cells[1].Text)
            {
                ((Image)GridView1.Rows[GridView1.Rows.Count - 1].Cells[0].Controls[0]).Visible = false;
            }
        }
    }
    public List<Employee> GetEmployees()
    {
        List<Employee> employees = new List<Employee>();
        employees.Add(new Employee { Company = "C Company", FirstName = "Helen", LastName = "Hamaguchi" });
        employees.Add(new Employee { Company = "A Company", FirstName = "Ira", LastName = "Ivanovich" });
        employees.Add(new Employee { Company = "D Company", FirstName = "Greg", LastName = "Good" });
        employees.Add(new Employee { Company = "F Company", FirstName = "Adam", LastName = "Apple" });       
        employees.Add(new Employee { Company = "F Company", FirstName = "Carl", LastName = "Castle" });
        employees.Add(new Employee { Company = "D Company", FirstName = "Delia", LastName = "Dunken" });      
        employees.Add(new Employee { Company = "D Company", FirstName = "Francis", LastName = "Frantz" });      
        employees.Add(new Employee { Company = "E Company", FirstName = "Katsuhiro", LastName = "Kawasaki" });
        employees.Add(new Employee { Company = "D Company", FirstName = "Evan", LastName = "Erlich" });
        employees.Add(new Employee { Company = "E Company", FirstName = "Leslie", LastName = "Lang" });
        employees.Add(new Employee { Company = "F Company", FirstName = "Beverly", LastName = "Beard" });
        employees.Add(new Employee { Company = "E Company", FirstName = "Marvin", LastName = "Marx" });
        employees.Add(new Employee { Company = "Z Company", FirstName = "Nelson", LastName = "Nellis" });
        employees.Add(new Employee { Company = "E Company", FirstName = "Jasmine", LastName = "Jobs" });
        employees.Sort(delegate(Employee e1, Employee e2) { return e1.Company.CompareTo(e2.Company); });
        return employees;
    }
}

public class Employee
{
    public string Company { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}