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; }
}

Friday, January 2, 2009

DIV based CSS vs. Tables - CSS Tricks and Performance Review

After initially failing to find an easy, efficient, and cross-browser compatible way of writing CSS for complex forms, I came across a layout foundation called YUI Grids CSS at the Yahoo! Development Network. I would highly recommend using the YUI framework for page layout, since it covers many scenarios including cross-browser compliance. However, if you are going to be doing a complex form layout with dozens of layout cells, the performance is slower than with using a traditional table. In my informal tests, I found load times to be 20~30% slower when using YUI than a simple table. You can see an example of the form I used in my previous blog post labeled Using CSS - Complex Forms Without Tables? Anyone? Anyone?. Ultimately, I will be using these concepts to create a form generation tool that will create forms on the fly with all the general event handling.

Fortunately, I was able to adopt some of the ideas within the YUI CSS framework and simplify it for form layout developments and make the DIV and CSS based layout perform 25% better. I created examples of forms that include up to 10 columns. The container DIV will always have the class divContainer and the items inside will always have a class called divItem[ColumnNumber]. The following example will have content divided into 3 equal parts.

<div class="divContainer">
    <div class="divItem3">Column 1 Content</div>
    <div class="divItem3">Column 2 Content</div>
    <div class="divItem3">Column 3 Content</div>
</div>

You can also mix and match assuming the item fractions add up properly. (For example: 1/3 + 1/3 + 1/6 + 1/6 = 1)

<div class="divContainer">
    <div class="divItem3">Column 1 Content</div>
    <div class="divItem3">Column 2 Content</div>
    <div class="divItem6">Column 3 Content</div>
    <div class="divItem6">Column 4 Content</div>
</div>

You can also add your own custom css based on existing ones. In this example, I wanted to tweak the 4 column layout so that the first two columns were a bit different. Since the original two column widths would have added up to 48%, I just had to make sure they were calculated appropriately.

        .divItem4
        {
            float: left;
            width: 24%;
            margin-left: 1%;
        }      
       .divItem4Custom1
        {
            float: left;
            width: 9%;
            margin-left: 1%;
        }
        .divItem4Custom2
        {
            float: left;
            width: 35%;
            margin-left: 1%;
        }

<div class="divContainer">
    <div class="divItem4Custom1">Column 1 Custom Content</div>
    <div class="divItem4Custom2">Column 2 Custom Content</div>
    <div class="divItem4">Column 3 Content</div>
    <div class="divItem4">Column 4 Content</div>
</div>

I have included the code for the table based and CSS based and correlating performance results below. As I mentioned earlier, the columns are based on equal sizes, so you can tweak these to make them variable. Also note that if you change the margins are padding, the width will need to be tweaked. I have set the left margin to 1px by default.

As far as centering is concerned, IE doesn't recognize auto margins, so you have to make sure that the body has the text-align set to center and container DIV's to have text-align set to left. All container DIV's also need to include the pseudo element "after" which allows you to include content after the closing tag.

Performance Results

The following tests were performed using Google Chrome's Javascript Console (Ctrl+Shift+J) which allows you to see the load time in milliseconds. I loaded each page 10 times, removed the highest outlier, and then got the average. As you can see below, the CSS Load Time was 25.5814% faster. You can see the full code I used below.

DIV CSS Load Time (ms) Table Load Time (ms)
32 43
37 80 (Outlier)
43 43
76 (Outlier) 55
32 41
31 55
40 36
32 42
48 36
32 40
Average: 36.33333 Average: 43.44444

Code

Compatible Browsers:  Microsoft IE: 7.0.5730.13CO / Google Chrome: 1.0.154.36 / Firefox 3.0.1

DIV Based CSS HTML:

<html>
  <head>
    <style type="text/css">
        body
        {
            text-align: center;
        }
        .divContainer
        {
            border-left-style: solid;
            border-left-width: 1px;
            border-left-color: black;
            border-right-style: solid;
            border-right-width: 1px;
            border-right-color: black;
            border-top-style: solid;
            border-top-width: 0px;
            border-top-color: black;
            border-bottom-style: solid;
            border-bottom-width: 1px;
            border-bottom-color: black;
            width: 950px;
            margin-left: auto;
            margin-right: auto;
            text-align: left;
        }
        .divContainerFirst
        {
            border-left-style: solid;
            border-left-width: 1px;
            border-left-color: black;
            border-right-style: solid;
            border-right-width: 1px;
            border-right-color: black;
            border-top-style: solid;
            border-top-width: 1px;
            border-top-color: black;
            border-bottom-style: solid;
            border-bottom-width: 1px;
            border-bottom-color: black;
            width: 950px;
            margin-left: auto;
            margin-right: auto;
            text-align: left;
        }
        .divContainer:after
        {
            content: ".";
            display: block;
            height: 0;
            clear: both;
            visibility: hidden;
        }
        .divContainerFirst:after
        {
            content: ".";
            display: block;
            height: 0;
            clear: both;
            visibility: hidden;
        }

.divItem1
{
    float: left;
    width: 99%;  /*Total: 99%*/
    margin-left: 1%;
}
.divItem2
{
    float: left;
    width: 48%;  /*Total: 96%*/
    margin-left: 1%;
}
.divItem3
{
    float: left;
    width: 32%; /*Total: 96%*/
    margin-left: 1%;
}
.divItem4
{
    float: left;
    width: 24%;  /*Total: 96%*/
    margin-left: 1%;
}
.divItem5
{
    float: left;
    width: 19%;  /*Total: 95%*/
    margin-left: 1%;
}
.divItem6
{
    float: left;
    width: 15.5%;  /*Total: 93%*/
    margin-left: 1%;
}
.divItem7
{
    float: left;
    width: 13.25%;  /*Total: 92.75%*/
    margin-left: 1%;
}
.divItem8
{
    float: left;
    width: 11.4%;  /*Total: 91.2%*/
    margin-left: 1%;
}
.divItem9
{
    float: left;
    width: 10.0%;  /*Total: 90%*/
    margin-left: 1%;
}
.divItem10
{
    float: left;
    width: 8.9%;  /*Total: 89%*/
    margin-left: 1%;
}

  </style>
  </head>
  <body>    <div class="divContainerFirst">
        <div class="divItem1">
            1 Column: Imagination is more important than knowledge. Albert Einstein</div>
    </div>
    <div class="divContainer">
        <div class="divItem2">
            2 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem2">
            2 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
    </div>
    <div class="divContainer">
        <div class="divItem3">
            3 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem3">
            3 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem3">
            3 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
    </div>
    <div class="divContainer">
        <div class="divItem4">
            4 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem4">
            4 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem4">
            4 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem4">
            4 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
    </div>
    <div class="divContainer">
        <div class="divItem5">
            5 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem5">
            5 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem5">
            5 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem5">
            5 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem5">
            5 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
    </div>
    <div class="divContainer">
        <div class="divItem6">
            6 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem6">
            6 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem6">
            6 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem6">
            6 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem6">
            6 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem6">
            6 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
    </div>
    <div class="divContainer">
        <div class="divItem7">
            7 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem7">
            7 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem7">
            7 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem7">
            7 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem7">
            7 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem7">
            7 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem7">
            7 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
    </div>
    <div class="divContainer">
        <div class="divItem8">
            8 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem8">
            8 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem8">
            8 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem8">
            8 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem8">
            8 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem8">
            8 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem8">
            8 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem8">
            8 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
    </div>
    <div class="divContainer">
        <div class="divItem9">
            9 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem9">
            9 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem9">
            9 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem9">
            9 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem9">
            9 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem9">
            9 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem9">
            9 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem9">
            9 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem9">
            9 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
    </div>
    <div class="divContainer">
        <div class="divItem10">
            10 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem10">
            10 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem10">
            10 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem10">
            10 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem10">
            10 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem10">
            10 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem10">
            10 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem10">
            10 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem10">
            10 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
        <div class="divItem10">
            10 Columns: Imagination is more important than knowledge. Albert Einstein
        </div>
</body>
</html>

Table Based HTML:

<html>
  <head>
    <style type="text/css">
        body
        {
            text-align: center;
        }
        .divContainer
        {
            border-left-style: solid;
            border-left-width: 1px;
            border-left-color: black;
            border-right-style: solid;
            border-right-width: 1px;
            border-right-color: black;
            border-top-style: solid;
            border-top-width: 0px;
            border-top-color: black;
            border-bottom-style: solid;
            border-bottom-width: 1px;
            border-bottom-color: black;
            width: 950px;
            margin-left: auto;
            margin-right: auto;
            text-align: left;
        }
        .divContainerFirst
        {
            border-left-style: solid;
            border-left-width: 1px;
            border-left-color: black;
            border-right-style: solid;
            border-right-width: 1px;
            border-right-color: black;
            border-top-style: solid;
            border-top-width: 1px;
            border-top-color: black;
            border-bottom-style: solid;
            border-bottom-width: 1px;
            border-bottom-color: black;
            width: 950px;
            margin-left: auto;
            margin-right: auto;
            text-align: left;
        }
        .divContainer:after
        {
            content: ".";
            display: block;
            height: 0;
            clear: both;
            visibility: hidden;
        }
        .divContainerFirst:after
        {
            content: ".";
            display: block;
            height: 0;
            clear: both;
            visibility: hidden;
        }
    </style>
  </head><body>
    <table class="divContainerFirst">
        <tr>
            <td>
                1 Column: Imagination is more important than intelligence. Albert Einstein.
            </td>
        </tr>
    </table>
    <table class="divContainer">
        <tr>
            <td>
                2 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                2 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
        </tr>
    </table>
    <table class="divContainer">
        <tr>
            <td>
                3 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                3 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                3 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
        </tr>
    </table>
    <table class="divContainer">
        <tr>
            <td>
                4 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                4 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                4 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                4 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
        </tr>
    </table>
    <table class="divContainer">
        <tr>
            <td>
                5 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                5 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                5 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                5 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                5 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
        </tr>
    </table>
    <table class="divContainer">
        <tr>
            <td>
                6 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                6 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                6 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                6 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                6 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                6 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
        </tr>
    </table>
    <table class="divContainer">
        <tr>
            <td>
                7 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                7 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                7 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                7 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                7 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                7 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                7 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
        </tr>
    </table>
    <table class="divContainer">
        <tr>
            <td>
                8 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                8 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                8 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                8 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                8 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                8 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                8 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                8 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
        </tr>
    </table>
    <table class="divContainer">
        <tr>
            <td>
                9 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                9 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                9 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                9 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                9 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                9 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                9 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                9 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                9 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
        </tr>
    </table>
    <table class="divContainer">
        <tr>
            <td>
                10 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                10 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                10 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                10 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                10 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                10 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                10 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                10 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                10 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
            <td>
                10 Columns: Imagination is more important than intelligence. Albert Einstein.
            </td>
        </tr>
    </table>
</body></html>

Tuesday, December 9, 2008

Using CSS - Complex Forms Without Tables? Anyone? Anyone?

I have spent the last couple weeks doing research on CSS form development for very complex forms that involve things like multiple sections and multiple columns. I often hear that "tables are evil" or that "tables should only use them for tabular data", and I was determined to find a way to incorporate CSS for my next form.

I decided to do some in depth research to determine if there was a standard or best practice for complex form development. Fortunately for me, there are thousands of examples on the web and others that can be found in various books. I decided to purchase The CSS Anthology - 101 Essential Tips, Tricks & Hacks by Rachel Andrews which has a number of great examples. I also downloaded a free book by the same publisher titled The Art & Science of CSS I also went to Borders bookstore and spent some more time perusing through about eight different CSS books. Another book by Rachel Andrew even had the promising title HTML Utopia: Designing Without Tables.

Unfortunately, I could not find a single example that would meet my requirements. Pretty much every example I encountered had a very simple form, usually a very standard one with name, address, and zip code. I could not find a single form that was more complex that involved things like multiple sections and multiple columns.

Ultimately, I have come to the conclusion that while there may be a solution for a complex form using CSS, it hasn't been documented or standardized. Until then, I plan to continue using tables to develop complex forms. If anyone out there in the blogosphere has any suggestions I am posting a screenshot of the form.

Example:


Friday, December 5, 2008

Quick and Dirty Object Sorting by Implementing IComparer<T>

Let's say you have a simple Person class with some properties of First Name and Last Name. You then populate them into a strongly typed List<T> and would like to sort them by Last Name. Fortunately there is a very easy solution.

1. Create a Person class:

public class Person
{
    public string FirstName {get; set;}
    public string LastName {get; set;}
    public Person
}

2. Create a PersonComparer class that implements IComparer<Person>:

public class PersonComparer : IComparer<Person>
{
    public int Compare(Person p1, Person p2)
    {
        return String.Compare(p1.LastName, p2.LastName);
    }
}

3. It was that easy. Now write some test code:

static void Main(string[] args)
{
    List<Person> personList = new List<Person>();
        personList.Add(new Person { FirstName = "George", LastName = "Washington"});
        personList.Add(new Person { FirstName = "Abraham", LastName = "Lincoln" });
        personList.Add(new Person { FirstName = "John", LastName = "Adams" });
        personList.Sort(new PersonComparer()); //Or use alternative below
        foreach (Person p in personList)
        {
            Console.WriteLine("{0}, {1}", p.LastName, p.FirstName);
        }
        Console.Read();
}

Alternative: If you need a one off, create a delegate that will sort the object on the fly. You will not need to implement the PersonComparer class, however it is not as reusable.

personList.Sort(delegate(Person p1, Person p2)
{
    return p1.LastName.CompareTo(p2.LastName);
}});

Thursday, November 13, 2008

Build StringBuilder Code from C#

With a little quick and dirty code, you can generate StringBuilder code from C# code. To use it, all you need is a WinForms project with two TextBoxes and a Button. This is very useful for creating code generation tools. My reason is that I am in the process of creating a dynamic form generation tool for SharePoint Lists and Document Libraries.

Form Example Screenshot:

C# Code (Not Optimized):

public string GetStringBuilderCodeFromCSharp(string[] lines)
{
    StringBuilder stringBuilder = new StringBuilder();
    string line = string.Empty;
    foreach (string s in lines)
    {
        line = s.Replace("    ", "\\t").TrimEnd();    //Replace four spaces with tabs. VS cs uses spaces
        if (line.StartsWith("\\t\\t"))                      //Eliminate double tabs. VS cs defaults with tab
            line = line.Substring(4, line.Length - 4);
        line = line.Replace("\"", "\\\"");               //Replace double quotes with escaped double quotes
        line = "stringBuilder.Append(\"" + line + "\\r\\n\");\r\n";   //StringBuilder Append Code
        stringBuilder.Append(line);
    }
    stringBuilder.Insert(0, "StringBuilder stringBuilder = new StringBuilder();\r\n");
    return stringBuilder.ToString();
}