ENTERPRISE

Managing SharePoint 2010 Data : Custom Field Types

9/17/2012 7:31:59 PM
When you add a new site column into the site columns gallery in a site collection, or you add a new column into a list, you base that column based on a bunch of preexisting field definitions. For example, you could choose to create the new column as a single line of text, multiple lines of text, be driven by Metadata, or even be driven by external data. When making that choice, SharePoint prompts you with a user interface with many radio buttons. This is shown in Figure 1.
Figure 1. Currently registered field types

Those various choices in those radio buttons are referred to as field types. You can write your own field type which is referred to as a custom field type.

The custom field type I intend to write will present the end user with a google auto suggest like experience. The values will be driven from a list, and to keep things simple you're going to create a list based on the Custom List definition. The name of the list would be "Songs, " and you will put some of my favorite songs in this list. When the user begins to type "Aer" in this textbox, the system will prompt the user with Aerosmith's "Dude looks like a lady" or "Crazy," and Jefferson Aeroplane's "Need somebody to love." Obviously, I have prepopulated these and other such songs in the "Song" list beforehand. Typing the first three characters in this textbox executes a CAML query on the "Songs" list, and these results are somehow shown as a list on the client side.

To achieve this, you will have to write a custom field type. In order to write this custom field type, you could take many approaches. You could use the ClientObject model and jQuery or you could write the JavaScript yourself.

Start by creating a new empty SharePoint project in Visual Studio 2010, call it "SongTypeField". In this project, add a reference to the AjaxControlToolKit.dll that you just downloaded from codeplex. Writing a custom field type requires you to deploy a file at SharePointRoot\Template\XML. This file must have a filename that starts with fldTypes_*.xml. Whenever SharePoint starts, or whenever the IIS application pool starts or restarts, all these xml files in this directory are read, and they contain the field type definitions for all available fields in SharePoint. In your project, add a mapped folder for TEMPLATE\XML and inside that folder drop in a file called fldTypes_AutoComplete.xml. The contents of this file can be seen in Listing 1.

Example 1. Contents of fldTypes_AutoComplete.xml
<?xml version="1.0" encoding="utf-8" ?>
<FieldTypes>
 <FieldType>
  <Field Name="TypeName">AutoCompleteField</Field>
  <Field Name="ParentType">Text</Field>
  <Field Name="TypeDisplayName">Auto Complete Song</Field>
  <Field Name="TypeShortDescription">Auto Complete Song</Field>
  <Field Name="UserCreatable">TRUE</Field>
  <Field Name="ShowInListCreate">TRUE</Field>
  <Field Name="ShowInSurveyCreate">TRUE</Field>
  <Field Name="ShowInDocumentLibraryCreate">TRUE</Field>
  <Field Name="ShowInColumnTemplateCreate">TRUE</Field>
  <Field Name="FieldTypeClass">
   SongTypeField.AutoCompleteField, SongTypeField, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=c1944463f56d857a
  </Field>
 </FieldType>
</FieldTypes>					  

You can view the full possible schema for this file on MSDN. In Listing 6-1, I had to find some basic attributes of the new custom field type I intended to code. Whenever I am authoring a custom field type, I have to specify a FieldTypeClass. This attribute tells SharePoint with the actual implementation of my custom field type is. The implementation for AutoCompleteField can be seen in Listing 2.

Example 2. AutoCompleteField Implementation
public class AutoCompleteField : SPFieldText
{
  public AutoCompleteField(
    SPFieldCollection fields, string fieldName) :
    base(fields, fieldName) { }
  public AutoCompleteField(
    SPFieldCollection fields,
    string typeName, string displayName) :
    base(fields, typeName, displayName) { }

  public override BaseFieldControl FieldRenderingControl
  {
    get
    {
      BaseFieldControl fieldControl = new AutoCompleteFieldControl();
      fieldControl.FieldName = InternalName;
      return fieldControl;
    }
  }
}

As you can see, I'm overriding the FieldRenderingControl property and I am returning an instance of the AutoCompleteFieldControl class. The AutoCompleteField control class is where the actual implementation of my field type goes. This can be a pure server control or it can be something that loads a user control. The AutoCompleteFieldControl class will directly or indirectly inherit from the BaseFieldControl base class.

In this particular implementation, since you intend to use the Ajax toolkit, it is a lot easier to write this code as a user control rather than a server control. Therefore, the implementation of the AutoCompleteFieldControl can be seen in Listing 3.

Example 3. Implementation for the AutoCompleteFieldControl
public class AutoCompleteFieldControl : BaseFieldControl
{
  protected TextBox autoCompleteField;

  public override object Value
  {
    get
    {
      EnsureChildControls();
      return autoCompleteField.Text;
    }
    set
    {
      EnsureChildControls();
      autoCompleteField.Text = this.ItemFieldValue as String;
    }
  }

protected override void CreateChildControls()
  {
    if (this.Field == null || this.ControlMode == SPControlMode.Display)
      return;
    base.CreateChildControls();

    autoCompleteField = TemplateContainer.FindControl("autoCompleteField") as TextBox;
  }

  protected override string DefaultTemplateName
  {
    get
    {
      return "AutoCompleteFieldTemplate";
    }
  }
}

					  

In Listing 3, there are a number of interesting things. Let's start from the bottom. There is a property DefaultTemplateName. This should return the ID of the SharePoint:RenderingTemplate control that contains the actual implementation of the user interface of my custom feed control. This SharePoint:RenderingTemplate is in any of the ASCX files in the SharePointRoot\ControlTemplates virtual directory.

Therefore, add the ControlTemplates SharePoint mapped folder in your project and add an AutoCompleteField.ascx file in this folder. When adding this as ASCX, there are two important things to watch out for. First, that this ASCX cannot have a code behind, and second, this ASCX needs to live in the ControlTemplates directory not in a sub directory under ControlTemplates. This new ASCX and the code in Listing 6-3 together provide the actual implementation of the custom field type. The code for the ASCX can be seen in Listing 4.

Example 4. AutoCompleteField.ascx Implementation
<SharePoint:RenderingTemplate ID="AutoCompleteFieldTemplate" runat="server">
  <Template>
    <asp:TextBox runat="server" ID="autoCompleteField" Width="100%" autocomplete="off" />
    <ajaxToolkit:AutoCompleteExtender
        runat="server"
        BehaviorID="AutoCompleteEx"
        ID="autoComplete1"
        TargetControlID="autoCompleteField"
        ServicePath="/_vti_bin/SongTypeField/AjaxToolkitMusic.svc"
        ServiceMethod="GetTopSongs"
        MinimumPrefixLength="1"
        CompletionInterval="1000"
        EnableCaching="true"
        CompletionSetCount="10"
        ShowOnlyCurrentWordInCompletionListItem="true"
        FirstRowSelected="true">
    </ajaxToolkit:AutoCompleteExtender>
  </Template>
</SharePoint:RenderingTemplate>					  

Do note that I omitted the assembly, register and import tags from Listing 4 for brevity. You can find those in the associated code download. Now to understand how the custom field definition works, compare Listing 4 and Listing 3 in tandem. Note that Listing 4 contains a textbox called autoCompleteField. You will find a protected textbox by the name of autoCompleteField in Listing 3 as well. Also, in the CreateChildControls method of the AutoCompleteFieldControl, you will see the value of this textbox being set as shown in the following:

autoCompleteField =
  TemplateContainer.FindControl("autoCompleteField") as TextBox;

Once this value has been set, in Listing 3 with in the AutoCompleteFieldControl, override the "Value" property to set and get the value of this autoCompleteField, which is also the value for this custom field type.

Now focus your attention on the rest of Listing 4. In Listing 4, you also see an entry for the AutoCompleteExtender. This is a standard control in the Ajax toolkit, which fetches various values as a string array from a WCF service, and shows them in a google suggest/autocomplete like behavior under the target textbox. As you can see from the various properties on the AutoCompleteExtender in Listing 4, you are querying a service located at /_vti_bin/SongTypeField/AjaxToolkitMusic.svc, and you are querying a method called GetTopSongs.

Therefore, you need to implement a WCF service in your project as well, which will be located at /_vti_bin/SongTypeField/AjaxToolkitMusic.svc and will have a method called GetTopSongs, which will return a string array. Add the new class in your project and call it AjaxToolkitSongQuery.cs. The code for the WCF service implemented in AjaxToolkitSongQuery.cs can be seen in Listing 5.

Example 5. AjaxToolkitSongQuery WCF Service
[ServiceContract(Namespace = "winsmarts")]
[AspNetCompatibilityRequirements(RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed)]
public class AjaxToolkitSongQuery
{
  [OperationContract]
  public string[] GetTopSongs(string prefixText, int count)
  {
    List<string> songs = new List<string>();

    XmlDocument camlDocument = new XmlDocument();
    camlDocument.LoadXml(
      @"<Where>
        <Contains>
          <FieldRef Name='Title' />
          <Value Type='Text'>[prefixText]</Value>
        </Contains>
       </Where>".Replace("[prefixText]", prefixText));


    SPWeb web = SPContext.Current.Site.RootWeb;

    SPQuery query = new SPQuery();
    query.Query = camlDocument.InnerXml;

    SPListItemCollection items = web.Lists["Songs"].GetItems(query);

IEnumerable<string> sortedItems =
      from item in items.OfType<SPListItem>()
      orderby item.Title
      select item.Title;

    songs.AddRange(sortedItems);
    return songs.ToArray();
  }
}

As you can see from Listing 6-5, the WCF Service expects to run under the asp.net compatibility mode. It exposes a method called GetTopSongs, which returns a string array of all matched top songs. Internally, it executes a simple CAML query to fetch the actual objects. It then uses a linq query to create a projection of the data that you are interested in and then it then returns that as a string array.

Since this class is a part of your SharePoint project, and thus a part of the associated DLL, it will get deployed to the GAC. Now all you need to create is an endpoint so this service is accessible.

Create an endpoint for the WCF Service by adding a SharePoint map folder at ISAPI, and under the ISAPI\SongTypeField folder add two files.

First, the endpoint, call it AjaxToolkitMusic.svc (note that this name matches what you specified in Listing 4). The contents for this file are as follows:

<%@ Assembly Name="SongTypeField, Version=1.0.0.0,
Culture=neutral,PublicKeyToken=c1944463f56d857a"%>
<%@ ServiceHost Service="SongTypeField.AjaxToolkitSongQuery" %>

Obviously, the public key token you generate might be different.

Secondly, the web.config file which provides all the configuration information for this endpoint can be seen in Listing 6.

Example 6. Configuration Information for the AjaxToolkitMusic.svc Service Endpoint
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <system.serviceModel>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
  <bindings>
   <webHttpBinding>
    <binding name="customWebHttpBinding">
     <security mode="TransportCredentialOnly">
      <transport clientCredentialType="Ntlm"/>
     </security>
    </binding>
   </webHttpBinding>
  </bindings>
  <services>
   <service name="SongTypeField.AjaxToolkitSongQuery">
    <endpoint address=""
         behaviorConfiguration="SongQueryBehaviorAjaxToolkit"
         bindingConfiguration="customWebHttpBinding"
         binding="webHttpBinding"
         contract="SongTypeField.AjaxToolkitSongQuery"/>
   </service>
  </services>

					  

<behaviors>
   <endpointBehaviors>
    <behavior name="SongQueryBehaviorAjaxToolkit">
     <enableWebScript/>
    </behavior>
   </endpointBehaviors>
  </behaviors>
 </system.serviceModel>
</configuration>

As you can see from Listing 6-6, you're choosing to expose the service over webHttpBinding and are requiring the NTLM credential to be sent before the service can be run. Also, you have required aspNetCompatibilityEnabled to true.

There's one last thing that needs to be done. The Ajaxcontroltoolkit.dll that you add it towards the beginning of the project also needs to be deployed to the GAC. Double-click the package designer, click the advanced tab, and choose to "Add an Existing assembly". Deploy the Ajaxcontroltoolkit.dll assembly in the bin\debug or bin\release folder.

Your final project structure should look like the one shown in Figure 2.

Figure 2. Custom field type project structure

Next, build and deploy your SharePoint project.

Create a list based on the custom list template and call it "Songs". This list contains only the title column, so enter a few of your favorite songs. The list looks like the one in Figure 3.

Figure 3. The Songs list

Next, create another list based on the custom list template and call it "My Favorite Songs". In this list, choose to add a new column. You should see your "Auto Complete Song" custom field type as one of the kinds of columns you can create. This can be seen in Figure 4.

Figure 4. Your custom field type is now available for use.

After you have created the column, try creating a new list item in this "My favorite songs" list. You should see and autocomplete like user interface show up inside SharePoint, as shown in Figure 5.

Figure 5. Your custom field type in action

The same user interface will also surface when you're trying to edit an item. Obviously a custom field type has many other possibilities. Now that you understand how to write a basic custom field type, you can refer to MSDN and learn how to add things such as custom validations. Also, do you think you can write the same custom field type using jQuery and the Client Object Model ? Of course, you can! I'll give you a starter hint, the jQuery script registration and the ClientObject model ScriptLink control registration will go in the CreateChildControls method of the AutoCompleteFieldControl.

Also, check and see if you can use this custom field control in a site column. Can you use it in a site column ? Can you use it in a content type ? I think you'll find that the answers to these questions are yes. And while you can easily do this through the browser, it is useful to learn how you can do this as a .WSP as well.

Next, let's look at how you can create a site column and an associated content type using this field type that you just created through a Visual Studio project.

Other  
  •  Managing SharePoint 2010 Data : Content Types
  •  Active Directory Domain Services 2008 : Enable a Group Policy Object Link, Enforce a Group Policy Object Link, Remove the Enforcement of a Group Policy Object Link
  •  Active Directory Domain Services 2008 : Link a Group Policy Object, Remove a Group Policy Object Link, Disable a Group Policy Object Link
  •  Microsoft Dynamics AX 2009 : Building Lookups - Creating a lookup dynamically
  •  Microsoft Dynamics AX 2009 : Building Lookups - Creating an automatic lookup
  •  Introducing Our New Zero-Point
  •  Exchange Server 2010 Administration Overview (part 3) - Using the Graphical Administration Tools, Using the Command-Line Administration Tools
  •  Exchange Server 2010 Administration Overview (part 2) - Exchange Server and Windows,Exchange Server and Active Directory
  •  Exchange Server 2010 Administration Overview (part 1) - Exchange Server 2010 and Your Hardware, Exchange Server 2010 Editions
  •  Touch Screens Are Everywhere
  •  Intel Builds Bridge to Next - Generation Macs
  •  Microsoft vs. Google vs. Apple: Who will win?
  •  Phone Business: The Age Of Convergence
  •  Microsoft Visual Basic 2008 : Services That Listen - Allowing Multiple Connections
  •  Microsoft Visual Basic 2008 : Services That Listen - Listening with TCP/IP
  •  Seagate GoFlex Satellite Wireless
  •  Toshiba MQ01ABD100 1TB Hard Drive
  •  View Quest Retro WI-FI Radio
  •  Microsoft Enterprise Library : Non-Formatted Trace Listeners (part 3) - Adding Additional Context Information, Tracing and Correlating Activities
  •  Microsoft Enterprise Library : Non-Formatted Trace Listeners (part 2) - Logging to a Database, Testing Logging Filter Status
  •  
    Most View
    Group Test - Mid-Range Maestros (Part 1) : Sony Xperia T
    MSI HD 7850 1GB – It’s The One To Get
    The 3DS XL : Super-Sized!
    The 10 Things To Know Before Buying A Laptop (Part 2)
    Sony Xperia P Review (Part 2)
    Programming .NET Components : Serialization and Persistence - Serialization Formatters
    Samsung Ativ Smart PC Review (Part 1)
    Canon EF 100mm f/2.8L Macro IS USM
    Slingbox 350 And 500 - Sling Media Finally Upgrades Its Line Of Media Streamers (Part 1)
    The JauBird BlueBuds X - Wireless Comforts For Yuppies
    Top 10
    Windows Management and Maintenance : The Windows 7 Control Panel (part 11) - Region and Language, System
    Windows Management and Maintenance : The Windows 7 Control Panel (part 10) - Programs and Features
    Windows Management and Maintenance : The Windows 7 Control Panel (part 9) - Notification Area Icons, Performance Information and Tools
    Windows Management and Maintenance : The Windows 7 Control Panel (part 8) - Fonts
    Windows Management and Maintenance : The Windows 7 Control Panel (part 7) - Ease of Access Center
    Windows Management and Maintenance : The Windows 7 Control Panel (part 6) - Devices and Printers
    Windows Management and Maintenance : The Windows 7 Control Panel (part 5) - AutoPlay
    Windows Management and Maintenance : The Windows 7 Control Panel (part 4) - AutoPlay
    Windows Management and Maintenance : The Windows 7 Control Panel (part 3) - Action Center
    Windows Management and Maintenance : The Windows 7 Control Panel (part 2)