Monday, July 11, 2011

Creating Custom Field in SharePoint 2010

In this post basically I tried to answer following questions:
A. What is Custom Field in SharePoint?
B. What are the steps to implement Custom Field in SharePoint?
C. How to override Save button while creating New Item in SharePoint's List?
D. How to consume Custom Field?


I explained the above questions with following scenario:
1. Created a column based on custom field.
2. Override the Save button of New Item page.
3. In Save button click, beside performing normal operation, I am setting the another column value with concated value of custom field column and current TimeStamp.


A. What is Custom Field in SharePoint?
=============================================
Exceprt from MSDN site-
Although site columns provide users and developers with new capabilities in reuse, you can define a reusable column definition that is even more powerful. With Microsoft SharePoint Foundation 2010, you can drop down to a lower level by creating custom field types.
Reference: Creating Custom SharePoint 2010 Field Types


Exceprt from Chak's blog-
In SharePoint, a site column is a reusable column definition, or template, that you can assign to multiple lists across multiple SharePoint sites. Site columns are associated to a particular Data Type. This Data Type is called a Field. For example, when you create a site column, you are also prompted to select the type of information, which is nothing but a Data Type or a Field.

Sometimes, it is necessary for us to create our own custom field that does not conform to the field types included in SharePoint by default.

All you have to do is – create a custom field which would result in creating a Field Class, Field Control and Field Type Definitions.
1. The Field Class should inherit from any of the SPField classes.
2. The Field Control should have Field Rendering Control and a Field Rendering Template.
3. The Field Type Definitions is a XML file which contains some information about the custom field.


Reference: SharePoint: Creating a custom field




B. What are the steps to implement Custom Field in SharePoint?
=====================================================================

Following Components/Files are required for creating Custom Field:

1. Add Assembly reference of PresentationFramework
2. UserControl/ASCX in SharePoint Mapped CONTROLTEMPLATES folder of 14 hive
3. XML in SharePoint Mapped XML folder of 14 hive
4. XSL in SharePoint Mapped XSL folder of 14 hive [Optional]
5. C# class for CustomField
6. C# class for CustomFieldControl
7. C# class for CustomFieldValidationRule [Optional]



1. Steps to Add PresentationFramework reference:
=======================================================

Solution Explorer > Project > References > .Net tab > Select PresentationFramework


2. Steps to add UserControl:
==================================

a. Add CONTROLTEMPLATES Mapped Folder:

Solution Explorer > Project > Right-Click > Add > SharePoint Mapped Folder...

Select desired folder in opened pop-up window.


b. Add UserControl in Project:



UserControl File:

Installed Templated > Visual C# > SharePoint > 2010 > User Control [Middle Pane] > Enter desired name


Code snippet for User Control [e.g. AviCustomNameField.ascx]:

<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, 
PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" 
Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, 
PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" 
Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, 
PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="asp" Namespace="System.Web.UI" 
Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
PublicKeyToken=31bf3856ad364e35" %>
<%@ Import Namespace="Microsoft.SharePoint" %> 
<%@ Register Tagprefix="WebPartPages" 
Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, 
Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Control Language="C#" %>
<SharePoint:RenderingTemplate ID="AviCustomNameFieldControl" runat="server">
    <Template>
        <table>
            <tr>
                <td>
                    <asp:TextBox ID="MyCustomNameText" runat="server" 
                                    Width="385px" />
                </td>
            </tr>
        </table>
    </Template>
</SharePoint:RenderingTemplate>
<SharePoint:RenderingTemplate ID="AviCustomNameFieldControlForDisplay" 
                                 runat="server">
    <Template>
        <table>
            <tr>
                <td>
                    <asp:Label ID="MyCustomNameLabel" runat="server" 
                                  Width="385px" />
                </td>
            </tr>
        </table>
    </Template>
</SharePoint:RenderingTemplate>




3. Steps to add XML in SharePoint Mapped XML folder of 14 hive:
==================================================================
a. Add SharePoint Mapped Folder for XML [as mentioned in earlier steps]
b. Add new XML file [as mentioned in earlier steps]
c. Code snippet for XML [e.g. fldtypes_AviCustomNameField.xml]

<?xml version="1.0" encoding="utf-8" ?>
<FieldTypes>
  <FieldType>
    <Field Name="TypeName">AviCustomNameField</Field>
    <Field Name="ParentType">Text</Field>
    <Field Name="TypeDisplayName">Avi Custom Name Field</Field>
    <Field Name="TypeShortDescription">Avi Custom Name Field</Field>
    <Field Name="UserCreatable">TRUE</Field>
    <Field Name="ShowOnListCreate">TRUE</Field>
    <Field Name="ShowOnSurveyCreate">TRUE</Field>
    <Field Name="ShowOnDocumentLibraryCreate">TRUE</Field>
    <Field Name="ShowOnColumnTemplateCreate">TRUE</Field>
    <Field Name="FieldTypeClass">Avi.AviCustomNameField,$SharePoint.Project.AssemblyFullName$</Field>
  </FieldType>
</FieldTypes>


Note: Prefix the file name(s) for XML/XSL file as fldtypes_


4. Steps to add XSL in SharePoint Mapped XSL folder of 14 hive:
==================================================================
a. Add SharePoint Mapped Folder for XSL [as mentioned in earlier steps]
b. Add new XSL file [as mentioned in earlier steps]
c. Code snippet for XSL [e.g. fldtypes_AviCustomNameField.xsl]

<xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema"
                xmlns:d="http://schemas.microsoft.com/sharepoint/dsp"
                version="1.0"
                exclude-result-prefixes="xsl msxsl ddwrt"
                xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime"
                xmlns:asp="http://schemas.microsoft.com/ASPNET/20"
                xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt"
                xmlns:SharePoint="Microsoft.SharePoint.WebControls"
                xmlns:ddwrt2="urn:frontpage:internal">
  <xsl:template match="FieldRef[@Name = 'AviCustomNameField']" mode="Text_body">
    <xsl:param name="thisNode" select="." />
    <span style="background-color:lightgreen;font-weight:bold">
      <xsl:value-of select="$thisNode/@*[name()=current()/@Name]" />
    </span>
  </xsl:template >
</xsl:stylesheet>


Note: Prefix the file name(s) for XML/XSL file as fldtypes_


5. Steps to add C# class for CustomField:
================================================
a. Add new C# file as per following figure:


Installed Templated > Visual C# > Class [Middle Pane] > Enter desired name

b. Code snippet for CustomField [e.g. AviCustomNameField.cs]

using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Security;
using System.Windows.Controls;
using System.Globalization;
using System.Security.Permissions;

namespace Avi
{
    class AviCustomNameField : SPFieldText
    {
        public AviCustomNameField(SPFieldCollection fields, string fieldName)
            : base(fields, fieldName)
        {
        }

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

        public override BaseFieldControl FieldRenderingControl
        {
            [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
            get
            {
                BaseFieldControl fieldControl = new AviCustomNameFieldControl();
                fieldControl.FieldName = this.InternalName;
                return fieldControl;
            }
        }

        public override string GetValidatedString(object value)
        {
            if ((this.Required == true) && ((value == null) 
               || ((String)value == "")))
            {
                throw new SPFieldValidationException(this.Title + 
                          " must have a value.");
            }
            else
            {
                AviCustomNameFieldValidationRule rule 
                   = new AviCustomNameFieldValidationRule();
                ValidationResult result 
                   = rule.Validate(value, CultureInfo.InvariantCulture);
                if (!result.IsValid)
                {
                    throw new
                          SPFieldValidationException((String)result.ErrorContent);
                }
                else
                {
                    return base.GetValidatedString(value);
                }
            }
        }
    }
}


6. Steps to add C# class for CustomFieldControl:
================================================
a. Add new C# file [as mentioned in earlier steps]
b. Code snippet for CustomFieldControl [e.g. AviCustomNameFieldControl.cs]

using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.IO;
using System.Collections;

namespace Avi
{
    class AviCustomNameFieldControl : BaseFieldControl
    {
        protected TextBox MyCustomNameText;
        protected Label MyCustomNameLabel;

        protected override string DefaultTemplateName
        {
            get
            {
                if (this.ControlMode == SPControlMode.Display)
                {
                    return this.DisplayTemplateName;
                }
                else
                {
                    return "AviCustomNameFieldControl";
                }
            }
        }

        public override string DisplayTemplateName
        {
            get
            {
                return "AviCustomNameFieldControlForDisplay";
            }
            set
            {
                base.DisplayTemplateName = value;
            }
        }

        protected override void CreateChildControls()
        {

            base.CreateChildControls();

            this.MyCustomNameText = 
               (TextBox)TemplateContainer.FindControl("MyCustomNameText");
            this.MyCustomNameLabel =
               (Label)TemplateContainer.FindControl("MyCustomNameLabel");


            if (this.ControlMode != SPControlMode.Display)
            {
                if (!this.Page.IsPostBack)
                {
                    if (this.ControlMode == SPControlMode.New)
                    {

                    }
                }
            }
            else
            {
                if (this.MyCustomNameLabel != null)
                {
                    MyCustomNameLabel.Text = (String)this.ItemFieldValue;
                    //this.MyCustomNameLabel.Text =
                      SPContext.Current.ListItem["MyName"].ToString();
                }
            }
        }

        public override object Value
        {
            get
            {
                EnsureChildControls();
                return base.Value;
            }
            set
            {
                EnsureChildControls();
                base.Value = (String)value;
            }
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            // add save handler only in New and Edit modes
            if ((SPContext.Current.FormContext.FormMode == SPControlMode.New)
                || (SPContext.Current.FormContext.FormMode == SPControlMode.Edit))
            {
                SPContext.Current.FormContext.OnSaveHandler
                    += new EventHandler(MyCustomSaveHandler);
            }
        }

        protected void MyCustomSaveHandler(object sender, EventArgs e)
        {
            Page.Validate();
            if (Page.IsValid)
            {
                SPContext.Current.ListItem["SampleNameWithTimeStamp"]
                    = MyCustomNameText.Text
                    + " - " + DateTime.Today.ToShortDateString();

                SPContext.Current.ListItem["MyName"] = MyCustomNameText.Text;
                
                SPContext.Current.ListItem.Update();
            }
            else
            {
                // do actions instead of save
            }
        }

    }
}



7. Steps to add C# class for CustomFieldValidationRule:
================================================
a. Add new C# file [as mentioned in earlier steps]
b. Code snippet for CustomFieldValidationRule[e.g. AviCustomNameFieldValidationRule.cs]

using System;
using System.Windows.Controls;
using System.Globalization;

namespace Avi
{
    public class AviCustomNameFieldValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, 
                                                  CultureInfo cultureInfo)
        {
            String CustomName = (String)value;
            bool result = isValidCustomName(CustomName);
            if (result != true)
            {
                return new ValidationResult(false, "Enter valid Name.");
            }
            else
            {
                return new ValidationResult(true, "Name Entered is correct.");
            }
        }
        public static bool isValidCustomName(string CustomName)
        {
            //Logic to validate data
            return true;
        }
    }
}




C. How to override Save button while creating New Item in SharePoint's List?
===================================================================================
a. Register custom method to Save button click in OnInit page event
Excerpt code snippet:
protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            // add save handler only in New and Edit modes
            if ((SPContext.Current.FormContext.FormMode == SPControlMode.New)
                || (SPContext.Current.FormContext.FormMode == SPControlMode.Edit))
            {
                SPContext.Current.FormContext.OnSaveHandler
                    += new EventHandler(MyCustomSaveHandler);
            }
        }

b. Implement custom method [e.g. MyCustomSaveHandler]
Excerpt code snippet:
protected void MyCustomSaveHandler(object sender, EventArgs e)
        {
            Page.Validate();
            if (Page.IsValid)
            {
                // do actions in case of save
            }
            else
            {
                // do actions instead of save
            }
        }

Note: For complete code refer AviCustomNameFieldControl.cs class [mentioned earlier].



After adding all the mentioned files, this will be Final View of Solution/Project Explorer:



Miscellaneous:
Steps to change Assembly name and Default namespace:
===========================================================
Solution Explorer > Project > Right-Click > Properties > Change Assembly name and Default namespace as per requirement


D. How to consume Custom Field?
======================================
1. Create a Custom List

2. Create a new column and choose already created custom field [as per following figure]:

3. Following would be list structure without any data:

4. Add a new item to List:

5. List with data:


References:
==================
Creating a Custom Field Type for SharePoint 2010 (Email Validation Field)

How to override the default item save event using custom fields

5 comments:

Sharad said...

Really good example for creating custom fields..

have you tried creating a custom field which defaults to the current User for SP 2010.. like this

http://www.fftf.org/news/Oct06feed/Current_user_as_default_value_for_a_SharePoint_Person_or_Group_field.rss.html

It would be really helpful

Avinash said...

Hi Sharad,

That's the nice usage of custom field.
You can change the code of AviCustomNameField.cs class with above mentioned link's C# code.

If there is a heavy UI, then AviCustomNameField.ascx is best place to create control [specially people picker] and refer it in AviCustomNameField.cs class.

Anonymous said...

Hello,

Thanks for the example.

But I don't understand how the connection is made between "AviCustomNameField.ascx" and "AviCustomNameField.cs"

Ramesh Kumar said...

Interesting post! I enjoyed reading it!
Thanks for sharing this useful info.keep updating same way.

Cheers,
Ramesh Roy
Sharepoint Custom Development

Roth French said...

With SharePoint 2013 Review, we can change the demonstration and approval of a customized area on any type in SharePoint as well as in ideas basically via JavaScript.This is an particularly highly effective ability.

Google