Customizing the User Experience of SharePoint: Custom fields deep dive (Part 5 of 6)

Part 5 of the “Customizing the User Experience of SharePoint” series. This day we also celebrate the third day of SharePoint User Experience week here on SharePoint Magazine. One full week, focusing only on understanding SharePoint architecture from a user experience point of view. If it could be any better it would have to be covered in chocolate.

The Customizing the User Experience of SharePoint series aims to explain how the user experience works, from how the interface is built down to details on how columns of lists  get created.

The series is also an exclusive preview of the topics from my upcoming book, “Building the SharePoint User Experience”, which deals with SharePoint user experience for 350 pages. Actually, as with this series, the book explains the SharePoint architecture from a user experience perspective.

Here is an article outline for the six parts:

Part 1: Overview of the default SharePoint interface from a technical point of view

In the first article we will look at how the default SharePoint interface is built. We will look at a site, going from top-down, explore some of the the default lists, the fields used to create the basic field types, which content types are available, and how list forms are rendered.

Part 2: Modifying the default experience

This article will show you which options are available for you to modify and improve the default setup. Learn how to override the default rendering of fields or forms without voiding your supported state.

Part 3: Lists and custom list forms

The third article will cover the basics of customizing lists using different views, custom list forms, and fields.

Part 4: Content types user interface

The next article will explore how you can utilize content types to display different input forms and display forms.

Part 5: Custom fields deep dive

Ever wanted to create a new field type? SharePoint enables you to do this and it is a very powerful tool for customizing the user experience.

Part 6: Fast track to feature generation

Writing custom lists with content types by hand can take a massive amount of time. In the final installment I will share with you some tools and techniques that makes list, field, and content type generation very fast.

Don’t fear if you think the previous parts have contain too little code, however, for this time we are creating custom field types. But first, we need to understand how columns work in SharePoint

Columns or fields?

Before we go any further I’d like to clarify the use of the names fields, field types, and columns.

In most cases columns and fields refer to the same thing, namely the names of the individual items we use to store data in a SharePoint list item. The confusion may come from the use of the name Column in the web interface and the name Field in code. when you go to add a new column/field to a list or a content type the term ‘column’ is used, but when you write a feature to deploy a new site column or a content type that has columns you suddenly use the name Field.

To confuse even further there’s the concept of field types. Field types are types of data. Compare this to SQL you may think of nvarchar, bigint, and datetime. However, field types are more complex as they also include the visual rendering of the data, both for input and display.

To make sure we are clear in this article I’ll use the name Column and not Field while referring to the columns of a list.

Site columns and list columns

You can define columns on two levels, either on the site level or on the list level. Site columns are useful if you intend to use a column in multiple lists. Rather than having to define all the settings for every list, you can define a site column with all the settings and add that site column to the lists.

Site columns are your only option if you intend to use your columns in content types. List columns are available only for a particular list. For lists, you can use either site columns or list columns. Or, actually, you can use list columns only. Or, actually, you can add site columns, but they become list columns. Oh, I know this may be confusing. Let me explain.

On the list or library settings page, you have the option to either create a new column or add an existing site column. If you choose the latter, a copy of the site column is made using the same field ID as the site column. The copy becomes a list column.

SiteToListColumn

The use of similar ID values for both the site column and the list column maintains the parent-child relationship. This relationship means you can make changes to the site column and optionally have all child columns inherit those changes.

This is a good thing, because it means that the list column is detached from the site column when you want it to be, but it can still maintain its relationship to the parent site column. An example might clarify this.

Let’s say you have a site column named Recipient used to store who will receive reports from different departments. You add that field to a new list for a new department, but then the department head comes to you and says that the column needs to be named “Target manager” instead.

In this situation, you can simply go to the list settings and change the name of the column on that list. Had you changed the site columns, every occurrence of the Recipient column would be changed, and other departments might not like that.

Site Columns in Content Types

What might spin your head a bit later is when we start to consider how content types work in this scenario. As I mentioned in the previous part of the series, content types use only site columns, so if we want to change a column for a content type on a list, we might be in trouble. And, if content types are so cool and we should use them as often as we can, how can we have such a limitation?

The answer is that content types also create list column copies of their respective site columns. When you add a content type to a list or library, all the site columns of that content type are copied into list columns on a list.

Problem solved.

Site Columns in CAML

To add site columns using the web interface, you simply go to the site where you want to column, then go to the Site Settings page and finally select the site columns. Create your site column by clicking Create, fill in the form, submit it, and live happily ever after. You can do this on any site.

Creating a basic field using a feature is slightly more complex, but not much. First, create a new feature, and make sure your feature scope is set to site, not web. With WSPBuilder, this is as easy as creating a new WSPBuilder project and adding a new item of the Blank Feature. You’ll learn plenty more about WSPBuilder in the next part of this series.

As with content types, site column features are site scoped, and of course you remember that site in this feature context means site collection. The ID of the field needs to be unique to the entire site collection to support content type inheritance and to allow site columns to be used in child sites.

Yes, that’s correct. Columns have scope just like content types. If you define a site column in one site, all child sites will also be able to use that column. Site columns and content types are closely connected.

Let’s get back to your feature.  now—In your elements file, add a Field element as such:
<?xml version=”1.0″ encoding=”utf-8″ ?>
<Elements xmlns=”http://schemas.microsoft.com/sharepoint/”>
<Field Name=”Recipient” DisplayName=”Recipient”
ID=”{053EC00E-D451-4733-8CA0-31532C974E91}” Type=”Text” >
</Field>
</Elements>

Now, before you take even a sip of your coffee or even draw another breath, pay very close attention to the following: Unlike other GUID examples, you must use brackets in the GUID ID value when defining site columns. Do not forget this.

When you build and deploy your new feature, and of course remember to activate from the Site Collection Features page you should see your new site column on the Site Columns page. This also means that your site column is now ready to use in any content type or list as you see fit.

SiteColumnDeployed

To limit the scope and ask you to notice the Type which is Text as this is important to our next step when we look at field types.

Field Types

Columns in SharePoint may be analogous to columns in a SQL table. If so, field types are analogous to the data types in SQL such as ntext, bigint, datetime, and bit. Actually, the correct way of thinking this is that a field type value is approximately the same as a SQL data type, while the field type itself is a bit more complex. Bear with me a second if you’re confused, I’ll try to clear things up a bit.

A SharePoint field type comprises several elements that handle the storage and the rendering of the field type. The easiest thing is to break these elements into three classes:

Field Type

This is the code element, defined as a .NET class, which ties together the value and visual representation of the field.

Field Type Control

This element is responsible for the visual rendering of the field, also a .NET class, and with an optional ASCX user control to render the output.

Field Type Value

Finally, the field type value handles storage of the data in columns based on the field type, again, this is a .NET class. For simple string values this class is optional.

When you first start to look at custom field types you may be very confused from how these elements interact, but once you start looking at what happens in each element, everything clears up a lot. We will get to that in a moment.

In addition to the three elements defined above you also have a Field Type Definition. This is really the simplest element as it is a plain XML file that only describes the field type to SharePoint. These field type definitions are stored in the [12]\TEMPLATE\XML folder inside files that are named fldtypes_XXXXXX.xml, where XXXXXX is an arbitrary string. The built-in field types are stored in the file called fldtypes.xml.

Let’s start out with this field type definition and work our way from there.

Before we being, however, note that field types are not deployed as features. Field types are global to the farm and as such should be deployed as a solution.

Building your first custom field type

Start by creating a new WSPBuilder project. You have WSPBuilder, right? Call it whatever you like, I’ve called mine SPMType. WSPBulder automatically creates a signing key and sets up your project to be signed.

Next, add a TEMPLATE folder under the 12 folder, and then an XML folder under the TEMPLATE folder.

Inside the XML folder, add a new XML file, called ‘fldtypes_SPMType.xml’. Or, choose another name, as long as you adhere to the fldtypes_XXXXXX.xml format.

Your solution explorer should now look like this:

SolutionExplorerStart

Field Type Definition

Open the fldtypes_SPMType.xml file and add the following XML code. I’ll explain each element afterwards:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<FieldTypes>
<FieldType>
<Field Name=”TypeName”>SPMType</Field>
<Field Name=”ParentType”>Text</Field>
<Field Name=”TypeDisplayName”>SPMType</Field>
<Field Name=”TypeShortDescription”>Description for SPMType</Field>
<Field Name=”UserCreatable”>TRUE</Field>
<Field Name=”Sortable”>TRUE</Field>
<Field Name=”AllowBaseTypeRendering”>TRUE</Field>
<Field Name=”Filterable”>TRUE</Field>
<Field Name=”FieldTypeClass”>[5-PART STRONG NAME]</Field>
</FieldType>
</FieldTypes>

You need to modify the FieldTypeClass element to insert the 5-part strong name of your field type class which we will create in a moment.

Each of the Field elements has a Name that is used to define the field type we are creating. The TypeName is the internal name used when we create columns from this field type. In the site column we created above this would match the Type attribute, which in our example was Text.

The ParentType is also vital, as it defines one of several root types from which our custom field type will inherit. You must always inherit your custom field type from another field type.

The TypeDisplayName and the TypeShortDescription are only for your eyes’ enjoyment, you can enter whatever values you like here. the UserCreatable states if users should be able to create new columns from this type, while the Sortable and Filterable states if the columns created from this field type should be sortable and filterable, respectively.

The AllowBaseTypeRendering specifies whether the field type should fall back to its parent rendering in case of problems rendering the field type.

Now for that FieldTypeClass.

WSPBuilder will happily sign your assembly for you to create a strong-named assembly. However, this only produces the four-part strong name. We need to prefix this four-part name with the name of our class. Which class you ask? Ah, we get to that when we create the…

Field Type Class

First, make sure you have added a reference to the Microsoft.SharePoint.dll to your project.

In the solution explorer, add a new class file to the root of your project. Name it something like SPMFieldType.cs. If you like, put it in a folder, but in any case, open the .cs file and add the following code:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;

namespace SPMType
{
public class SPMFieldType : SPFieldText
{
public SPMFieldType(SPFieldCollection fields, string fieldName)
: base(fields, fieldName)
{
}

public SPMFieldType(SPFieldCollection fields, string typeName, string displayName)
: base(fields, typeName, displayName)
{
}
}
}

One small thing first, before I forget. Let’s grab that five-part strong name we needed for the field type definition. First, build your solution, you may use the the WSPBuilder->Build WSP. Next, find the four-part strong name of your assembly. Finally, add the namespace.classname to the front of the strong name to create a five-part strong name. In my assembly this looks like this:

“SPMType.SPMFieldType, SPMType, Version=1.0.0.0, Culture=neutral, PublicKeyToken=817b2cbd5ec31d67”

Your PublicKeyToken will be different, as may your assembly, class, and namespace names.

Add this string to the FieldTypeClass element in your fldtypes_SPMType.xml field definition file.

Back to the class, we are inheriting from the SPFieldText class which makes sense since our parent type is Text. However, we are not bound by this, we can use any of the other SPFieldXXXX classes as the parent class for our own field type class.

Next we need to implement the constructors for our class, and SPFieldText determines the signatures for these constructors. For our class we don’t actually need to do anything but call the base constructor, but you can add functionality here to implement custom property handling or add other features as you see fit.

At this point we have actually created all we need to get our custom field type up and running. Of course, we’ve just created the most useless custom field type there is, as all we have done is implement the SPFieldText type, which is quite well done already by the built-in field type Text. However, we have a frame into which we can put a beautiful picture, so for now, just build (WSPBuilder->Build WSP) and deploy (WSPBuilder->Deploy) and sit back and wait. No need to activate anything, remember that custom field types are not deployed in features, so your field type will be available once the solution is deployed. Check this out in the Create new column of a list or when you go to create a new site column:

FieldTypeDeployed1

If you add a column based on your new field type, it will behave exactly like a regular Text type, since we haven’t really added anything ‘Custom’ yet. Let’s add custom to the equation now.

Custom Field Type Control

Our first order of business is to create a control class to act as the rendering control for our new field type.

Start by adding a reference to System.Web since we will now deal with creating web controls.

Continue by adding yet another class file to the root of your project, this time called ‘SPMFieldControl.cs’ or something along those lines. To that class, add the following code:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;

namespace SPMType
{
public class SPMFieldControl : BaseTextField
{
}
}

Note that we are inheriting from the BaseTextField class. Again, this is nothing extraordinary since we are creating a text class field type. However, for other field type classes you might want to inherit from another control.Here is a list of some of the Field controls from which we can inherit:

AllDayEventField
AttachmentsField
AttendeeField
BaseChoiceField
BaseTextField
BooleanField
CalculatedField
ComputedField
CrossProjectLinkField
DateTimeField
FieldValue
FileField
FormField
LookupField
ParentInformationField
RatingScaleField
RecurrenceField
UrlField
UserField

In addition, several of these, such as the BaseTextField have child types from which you also can inherit. The BaseTextField for example has BaseNumberField, NoteField, and TextField as child classes, and even more grand-children classes.

For our purposes, however, I really just want to show you the anatomy of a custom field type control, so we’ll base our control on the BaseTextField class.

First we must tell the control which template to use for rendering the control. We do this by overriding the DefaultTemplateName property as such:

protected override string DefaultTemplateName
{
get
{
return “TextField”;
}
}

The TextField is the default rendering template used for the single line of text field types we are mimicking. If you recall from Part 2 we looked at the DefaultTemplates.ascx file in the [12]\TEMPLATE\CONTROLTEMPLATES folder. This file also contains the TextField rendering template. This template is incredibly simple, but will do for the moment.

TextFieldRenderingTemplate

At this point our control behaves like the default single line of text. Let’s add something to make sure we’re doing things right. Let’s override the RenderFieldForDisplay method which is use to, well, Render Field For Display. Bet you didn’t see that coming…

Add the following override to your SPMFieldControl class:

protected override void RenderFieldForDisplay(System.Web.UI.HtmlTextWriter output)
{
output.Write(“Custom displaymode: “);
base.RenderFieldForDisplay(output);
}

Again we’re simply relying on the underlying BaseTextField to do most of the manual labor, however adding the “Custom displaymode: “ enables us to see that we are in fact modifying the rendering of the field.

I’ll show you some more advanced samples in a moment, but for now, let’s connect our new field type control to the field type we created earlier. To do this, go back to your SPMFieldType.cs file, and add the following override to your class:

public override Microsoft.SharePoint.WebControls.BaseFieldControl FieldRenderingControl
{
get
{
SPMFieldControl c = new SPMFieldControl();
c.FieldName = this.InternalName;
return c;
}
}

You might want to add a ‘using Microsoft.SharePoint.WebControls;’ statement by the way, but that’s just for prettier code.

This code instantiates an object of our new control class and assigns the FieldName of that object to the field type internal name.

Finally, build your WSP as described above and redeploy. You may need to do an IISRESET to get your changes updated.

Try creating a new column based on your field type on a list and then add a new item. You will notice that the new column behaves exactly like a text field

CustomFieldTypeNewItem

I bet by now you’re going: “Aw, come one, we haven’t seen any custom rendering at all, only mimicking of the default single line of text”. Patience, young Skywalker.

Add your new item and the display that item. Let’s see if your 1 second of patience has paid off:

CustomFieldTypeDisplayItem

Wohoo! Congratulations, you’ve just made… Well, absolutely nothing if you ask your boss, but hey, you made it yourself, right? That’s got to be worth something. Let’s see if we can impress your boss as well.

Oh, yeah, the date. I got a bit delayed writing this article.

One Step Further

Ok, so we have wired up our field type and we have connected that field type to a field control. We might as well utilize that to our advantage.

We want to create our own rendering template and not rely on the DefaultTemplates.ascx template. After all, we might want to add a nice Ajax control, or perhaps even a Silverlight control? No, I wont do that for you, that’s a completely different show, but I’ll show you how to create your own rendering template.

First, in your solution explorer, add a folder named CONTROLTEMPLATES to the TEMPLATE folder under the 12 folder.

Inside the CONTROLTEMPLATES folder, still in solution explorer, add a new Text file, and name it MyCoolTextBox.ascx. Using a text file but naming it .ascx makes Visual Studio recognize the file as a web user control even if this is not a web project.

SolutionExplorerCustomControl

Next, open the MyCoolTextBox.ascx and add the following outline:

<%@ Control %>
<%@ Assembly Name=”Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c” %>
<%@ Register TagPrefix=”SharePoint” Assembly=”Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c”
Namespace=”Microsoft.SharePoint.WebControls” %>
<SharePoint:RenderingTemplate ID=”MyCoolTextBox” runat=”server”>
<Template>
</Template>
</SharePoint:RenderingTemplate>

Now, inside the Template tag, let your designers go bananas. Yes, I mean bananas, designers always love that. I’ll add one provision for now: Make sure you have an ASP:TextBox control with an Id of “TextField”. I’ll explain why in a moment. Besides that, do whatever you like.

Finally we need to tell our control to use the new rendering template. Go back into the SPMFieldControl and update the DefaultTemplateName override as such:

protected override string DefaultTemplateName
{
get
{
return “MyCoolTextBox”;
}
}

By the way, if you ever get the error ‘Corrupted control template. Please check RenderingTemplate of “TextField” in the control template ascx file.’ while working with the BaseTextField class, don’t bother checking your TextField template, as the name ‘TextField’ is hardcoded in the Microsoft.SharePoint.dll. I just spent two hours chasing after the wrong rendering template. Now you don’t have to.

Back to our fancy new rendering template. Before you go and deploy your new WSP, uninstall the old one using WSPBuilder->Uninstall. The reason is that by default WSPBuilder will upgrade your solution. Upgrading a solution does not add new files, and since we just added our new ascx control we need to make a clean install. After the uninstall completes, go ahead and deploy as normal.

Depending on what you decided to put inside the Template tag of your control you will see your changes when you refresh your Edit or New form. I’ve added

<asp:TextBox ID=”TextField” runat=”server” BackColor=”Cyan” BorderStyle=”None”></asp:TextBox>

to my control and get this:

CustomFieldCustomTemplate

For some reason I never got a job offer as a designer. Go figure, I think this looks beautiful.

Before I wrap up with some final thoughts and some further resources for you, I’d like to explain a bit of the magic that goes on here. You see, when we inherited our control class from the BaseTextField class we got a ton of free functionality. The BaseTextField searches for a TextBox control with an Id of “TextField” inside whatever renderingtemplate we send it. If the BaseTextField finds such a TextBox it gladly hooks up all the required functionality for us, including populating the TextBox with the right values when editing. This happens in the CreateChildControls() method of the BaseTextField class.

By not overriding the CreateChildControls() in our own class we basically get that functionality for free. See why having a TextBox with an Id of “TextField” makes sense now? Of course, you can override the CreateChildControls() if you want absolute and complete control over the building of your control, but I think it makes sense to let Microsoft work a bit for all the money we send them, and letting them handle as much of the nitty-gritty details as possible is at least sensible to me.

Wrap Up and More Resources

Ok, before you all go yelling at me for not including details on writing your own field type value, remember that this is a user experience series, and I hardly see how managing data internally is part of the user experience. Also, this article is quite long enough as it is, so instead I’ll point you to another article here no SharePoint Magazine: Developing a SharePoint Custom Field Type for Displaying CRM 4.0 Data. This article also shows you how you can use a code-behind file as part of your control.

I do hope I have conveyed a bit of knowledge, however, both on columns and on custom field types, and that you are left with a bit more confidence when facing one of the most complex tasks a SharePoint developer will face.

Tomorrow I’ll take you on a fast-track to feature generation and show you how you can speed up your SharePoint feature development by using the right tools and utilizing the web interface to create a lot of the nitty-gritty CAML required for features.

Don’t forget to leave feedback. If you have questions or comments I love to hear about it!

.b

Twitter Digg Delicious Stumbleupon Technorati Facebook Email
  • rmpatel1981
    Hi,
    This is really a nice article about site columns, fields and field types. Thank you.

    I wanted to know if there is any way to limit the scope of the field types. So they they don't appear on every list of entire farm. In my company, some department may see this custom field control and would like to use it but, I have coded it in such a way (it connects to some predefined lists etc) it will always fail for them. So I wanted to know if we can hide these custom columns.

    Regards,
    Ravi
  • aisha
    Thanks for this great article , its very informative and explains step by step. I am just facing a small problem, after adding the .ascx file the page is complainig abt 'Corrupted control template', seems as if it cannot find the ID 'TextField' in sharepoint.dll. My code in ascx file is written below:

    thanks:9
    aisha
    <%@ Control %>
    <%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@ Register TagPrefix="SharePoint" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
    Namespace="Microsoft.SharePoint.WebControls" %>
    <SharePoint:RenderingTemplate ID="MyCoolTextBox" runat="server">
    <Template>
    <asp:TextBox ID="TextField" runat="server" BackColor="Cyan" BorderStyle="None"></asp:TextBox>
    </Template>
    </SharePoint:RenderingTemplate>
blog comments powered by Disqus