Every SharePoint power user or administrator knows the power of the Custom List. In just a few clicks, you can create small applications for collecting information of varying data types. In addition, you can easily create views to empower your users to make effective usage of this data. The problem is that the custom list eventually reaches a point where the out of the box functionality, simply isn't sufficient. In this column, I will demonstrate how to use the SharePoint UI to build a custom list, and then enhance this custom list to use a custom form and receiver with the help of VSeWSS.

Objective

We would like to use a custom SharePoint lists to store information about email contacts. The list will store the following information:

  1. Name
  2. Email Address
  3. Domain Name

The list must also contain a view that groups all contacts by domain name of the email address of the contact.

Start Basic

SharePoint lists involve a lot of XML, so this is a very compelling reason to NOT start from scratch. We will use the SharePoint Web UI to create a custom list as shown below:

 

 

Now that we have a good starting point, we'll use the SharePoint Solution Generator tool that is included with VSeWSS to create a Visual Studio project from our custom list. Our project should resemble what you see to the right. Note that the project also includes the .aspx file used for our custom view.

Less Data Entry

As you may have noticed from the objective of this column, we collect the domain name and the email address. Our application would be the most efficient if we extracted the domain name from the email address, rather than rely on the user to key in this information. The best way to do this is to use a regular expression to evaluate the email address and populate the Domain Name automatically in an event receiver. After adding the event receiver to the project, place the following code into the ItemAdding and ItemUpdating event handler:

public override void ItemAdding(SPItemEventProperties properties)
{
    this.DisableEventFiring();

    this.SetDomainName(properties);

    this.EnableEventFiring();
}

public override void ItemUpdating(SPItemEventProperties properties)
{
    this.DisableEventFiring();

    this.SetDomainName(properties);

    this.EnableEventFiring();            
}

The methods that are invoked by SetDomainName are shown below:

private string MatchDomain(string emailAddress)
{
    string retVal   = string.Empty;
    string matchExp = @"\w+([-+.']\w+)*@(?<DomainName>\w+([-.]\w+)*\.\w+([-.]\w+)*)";

    /* */
    if (!string.IsNullOrEmpty(emailAddress))
    {
        if (Regex.IsMatch(emailAddress, matchExp, RegexOptions.IgnorePatternWhitespace))
        {
            retVal = Regex.Match(emailAddress, matchExp, RegexOptions.IgnorePatternWhitespace)
                          .Groups["DomainName"]
                          .Value;
        }
    }

    return retVal;
}

private void SetDomainName(SPItemEventProperties properties)
{
    string domainName = string.Empty;

    domainName = this.MatchDomain(properties.AfterProperties["EmailAddress"].ToString());

    if (!string.IsNullOrEmpty(domainName))
    {
        properties.AfterProperties["DomainName"] = domainName.ToLowerInvariant();
    }
    else
    {
        properties.Cancel       = true;
        properties.ErrorMessage = "Invalid email address format";
    }
}
  

Taking the form one step further

SharePoint Designer provides us with a nice way to insert a custom list form; however this method is not very redistributable. SharePoint uses control templates for all sorts of common controls -including list forms- and many of the templates reside in the 12\TEMPLATE\CONTROLTEMPLATES\DefaultTemplates.ascx file. The ListForm template in this file is used to render the fields for a custom list. By using the techniques in the post Bringing jQuery to SharePoint, we can create a custom form that will highlight the textboxes in our list form when the textbox receives focus. The control template below demonstrates how to create a new rendering template for our list that will invoke the default ListForm template.

  
<%@ Control Language="C#" AutoEventWireup="false" %>

<%@ Register 
  TagPrefix="SharePoint" 
  Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
  Namespace="Microsoft.SharePoint.WebControls"%>

<SharePoint:RenderingTemplate ID="EmailContactsForm" runat="server">
  <Template>
    <script type="text/javascript">
      $(function() {
        var textBoxes = $('input[type=text]', $('#onetIDListForm'));
        $(textBoxes)
          .focus(function(e) {
            $(this).css('background-color', 'yellow');
          })
          .blur(function(e) {
            $(this).css('background-color', '#fff');
          });
        /* Initiate on page load */
        textBoxes[0].blur();
        textBoxes[0].focus();
      });
    </script>
    <SharePoint:FormComponent TemplateName="ListForm" runat="server"/>
  </Template>
</SharePoint:RenderingTemplate>
  

Since we're automatically collecting the domain name from our event receiver, we don't really need to display this field on new or edit forms. The SharePoint list schema provides us with a means to hide this field on the new and edit forms as well as associate the form with the actions of the list content type. This is accomplished by modifying the XML in the schema.xml file as shown below:

...
<ContentType ID="0x0100C6AF3ADC45CECF46BA9F5DA4DB3624F8" 
             Name="Item" 
             Group="List Content Types"
             Description="Create a new list item." 
             Version="2"
             FeatureId="{695b6570-a48b-4a8e-8ea5-26ea7fc1d162}">
  <FieldRefs>
    <FieldRef ID="{c042a256-787d-4a6f-8a8a-cf6ab767f12d}" 
              Name="ContentType" />
    <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" 
              Name="Title" 
              Required="TRUE" />
    <FieldRef ID="{077b0df1-b155-428e-a601-eada42231139}" 
              Name="EmailAddress" 
              Required="FALSE" />
    <FieldRef ID="{0c9c2a85-d8b5-4d93-a872-fa4a077ecc6f}" 
              Name="DomainName" 
              Required="FALSE" 
              ShowInNewForm="FALSE" ShowInEditForm="FALSE" />
  </FieldRefs>
  <XmlDocuments>
    <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
      <FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
        <Display>ListForm</Display>
        <Edit>EmailContactsForm</Edit> <New>EmailContactsForm</New>
      </FormTemplates>
    </XmlDocument>
  </XmlDocuments>
  <Folder TargetName="Item" />
</ContentType>
...

Using these techniques enables us to deliver a custom list that delivers far and above the out-of-the-box functionality. The sample code from this post is available for download. The solution also contains a feature for jQuery that is an activation dependency of the custom list.