Creating a Custom Editor Control by Using the IFieldEditor Interface in SharePoint 2010

Summary:  Learn how to create a custom editor control in SharePoint 2010 that calculates the interest for a given amount and displays the sum of the principal and interest.

Applies to: Business Connectivity Services | Open XML | SharePoint Designer 2010 | SharePoint Foundation 2010 | SharePoint Online | SharePoint Server 2010 | Visual Studio

Provided by:  Aravind Cheziyan, Microsoft Corporation

Contents

  • Overview of Creating a Custom Editor Control

  • Setting up the Visual Studio Project

  • Creating the Custom Field Class

  • Creating the Field Rendering Control

  • Creating the Field Rendering Template

  • Creating the Field Type Definition

  • Creating the XSLT Style Sheet

  • Building and Testing the Custom Field Type

  • Conclusion

  • Additional Resources

Click to get code Download the code sample from the MSDN Code Gallery

Overview of Creating a Custom Editor Control

This article describes how to create a custom editor control by using the IFieldEditor interface in Microsoft SharePoint 2010. The custom editor control calculates the interest for a given amount and displays the sum of principal and interest for that amount.

Setting up the Visual Studio Project

In this section, you create and initialize the Microsoft Visual Studio 2010 project.

To set up the custom field project

  1. In Visual Studio 2010, create a SharePoint project by clicking File, pointing to New, and then clicking Project. In the Installed Templates tree, click Visual C#, click SharePoint, and then click Empty SharePoint Project. Name the project InterestCalculatorFieldType, and make it a farm solution.

  2. In Solution Explorer, right-click the project name, and select Properties.

  3. On the Application tab of the Properties dialog box, enter SharePoint.CustomControls.InterestCalculator as the Assembly name, and enter SharePoint.CustomControls as the Default namespace. Leave the Target framework set to .NET Framework 3.5.

  4. Click Save all files on the toolbar.

  5. In Solution Explorer, right-click the project name, point to Add, and then click New Item.

  6. In the Add New Item dialog box, in the Installed Templates tree, click Visual C#, and then click Code.

  7. Select Class in the Templates box, and enter InterestCalculatorField.cs in the Name box. Click Add.

  8. Repeat the previous step to create a second class named InterestCalculatorFieldControl.cs. Click Add.

  9. In Solution Explorer, right-click the project name, point to Add, and then click SharePoint Mapped Folder.

  10. Use the tree control that opens to map the folder to TEMPLATE\ControlTemplates, and then click OK.

  11. In Solution Explorer, right-click the new ControlTemplates folder (not the project name), point to Add, and then click New Item.

  12. In the Add New Item dialog box, in the Installed Templates tree, click SharePoint, and then click 2010.

  13. In the Templates box, select a SharePoint User Control, and name the .ascx file InterestCalculatorEditor.ascx. Click Add.

    Visual Studio automatically adds the file to the SharePoint solution manifest and sets it to be deployed to %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\ControlTemplates. It also adds the assembly to the manifest and sets it to be deployed to the global assembly cache (GAC).

  14. In Solution Explorer, right-click the project name, point to Add, and then click SharePoint Mapped Folder.

  15. Use the tree control that opens to map the folder to TEMPLATE\XML, and then click OK.

  16. In Solution Explorer, right-click the new XML folder (not the project name), point to Add, and then click New Item.

  17. In the Add New Item dialog box, in the Templates window, click Visual C#, click Data, and then click XML File.

  18. In the Name box, enter fldtypes_InterestCalculatorField.xml, and then click Add.

  19. In Solution Explorer, right-click the project name, point to Add, and then click SharePoint Mapped Folder.

  20. Use the tree control that opens to map the folder to TEMPLATE\LAYOUTS\XSL, and then click OK.

  21. In Solution Explorer, right-click the new XSL folder (not the project name), point to Add, and then click New Item.

  22. In the Add New Item dialog box, in the Templates window, click Visual C#, click Data, and then click XSLT File.

  23. In the Name box, enter fldtypes_InterestCalculatorField.xsl, and then click Add.

    Note

    This is very similar to the name of the previous file that you created. The two files have different purposes and are deployed to different folders. Keep them distinct as you work through this article.

    The project structure should appear as shown in Figure 1.

    Figure 1. Project structure

    Project structure

Creating the Custom Field Class

In this section, you create the custom field InterestCalculatorField class.

To create a custom field class

  1. Open the InterestCalculatorField.cs file.

  2. Add the following statements to the top of the file.

    using System;
    using System.Security.Permissions;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Security;
    using Microsoft.SharePoint.WebControls;
    
  3. Add the following statement. This enables your class implementation to reference other classes that you create in later steps. Until you create those classes, you may see compiler warnings about this statement.

    using SharePoint.CustomControls.WebControls;
    
  4. Ensure that the namespace is SharePoint.CustomControls.

  5. Be sure that the class is named InterestCalculatorField, and change its declaration to specify that it inherits from SPFieldText.

    public class InterestCalculatorField: SPFieldText
    {
    }
    
  6. Add the following private variables to the class.

    private string _interestRate;
    private static string _interestRateValue;
    
  7. Add the following required constructors for the class.

    public InterestCalculatorField(SPFieldCollection fields, string fieldName)
                : base(fields, fieldName)
            {
                this.Init();
            }
    
            public InterestCalculatorField(SPFieldCollection fields, string typeName, string displayName)
                : base(fields, typeName, displayName)
            {
                this.Init();
            }
    
  8. Add the following property to the class. This property will be used to set the value from the control editor.

    public string InterestRate
            {
                get
                {
                    if (_interestRateValue == string.Empty || _interestRateValue == null)
                    {
                        return _interestRate;
                    }
                    else
                    {
                        return _interestRateValue;
                    }
                }
                set
                {
                    this._interestRate = value;
                }
            }
    
  9. Add the following override of FieldRenderingControl to the class. InterestCalculatorFieldControl is a class that you create in a later step.

    public override BaseFieldControl FieldRenderingControl
            {
                [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
                get
                {
                    BaseFieldControl fieldControl = new InterestCalculatorFieldControl();
                    fieldControl.FieldName = this.InternalName;
                    return fieldControl;
                }
            }
    
  10. Add the following override of the GetValidatedString(Object) method to the InterestCalculatorField class.

    public override string GetValidatedString(object value)
            {
                if ((this.Required == true) && (value == null || value.ToString().Equals(string.Empty)))
                {
                    throw new SPFieldValidationException(this.Title + " must have a value.");
                }
                else
                {
                    Decimal tempValue;
                    if (Decimal.TryParse(value.ToString(), out tempValue))
                    {
                        Decimal interest = Decimal.Parse(this.InterestRate);
                        Decimal principal = tempValue;
                        decimal interestPaid = principal * (interest / 100);
                        decimal totalPI = principal + interestPaid;
                        return base.GetValidatedString(totalPI);
                    }
                    else
                    {
                        throw new SPFieldValidationException("Enter only integer value");
                    }
                }
            }
    

    This override illustrates a common pattern for overrides of GetValidatedString(Object):

    • Overrides of the GetValidatedString(Object) method check whether the field is required; if it is required, the overridden method throws an SPFieldValidationException exception when the value is null or an empty String. This exception is caught by the New Item and Edit Item pages if the user tries to save the list item that is being created or edited. In this case, the page remains open, and the Message property of the exception causes an error message to appear underneath the empty field.

    • Overrides of GetValidatedString throw an SPFieldValidationException exception when the value is not valid, causing an error message to appear underneath the invalid field.

    • Overrides of GetValidatedString(Object) then call the base GetValidatedString, if the value passes the custom validation.

  11. Add the following methods to the InterestCalculatorField class.

    public void UpdateCustomProperty(string interestRateValue)
            {
                _interestRateValue = interestRateValue;
            }
    
      private void Init()
            {
                this.InterestRate = this.GetCustomProperty("InterestRate") == null ? null : this.GetCustomProperty("InterestRate").ToString();
            }
    
       public override void Update()
            {
                this.SetCustomProperty("InterestRate", this.InterestRate);
                base.Update();
                _interestRateValue = string.Empty;
    
    public override void OnAdded(SPAddFieldOptions op)
            {
                base.OnAdded(op);
                Update();
            }
    
  12. Save and close the file.

Creating the Field Rendering Control

In this section, you create the control that renders the field.

To create the field rendering control

  1. Open the InterestCalculatorFieldControl.cs file.

  2. Add the following statements.

    using System;
    using System.Web.UI.WebControls;
    using Microsoft.SharePoint.WebControls;
    
  3. Change the namespace to SharePoint.CustomControls.WebControls.

  4. Be sure that the class is named InterestCalculatorFieldControl, and change its declaration to specify that it inherits from TextField.

    public class InterestCalculatorFieldControl : TextField
        {
        }
    
  5. Add a protected field for an ASP.NET Label web control that will render the current value of the field in Display mode.

    protected Label InterestCalculatorFieldForDisplay;
    
  6. Next add the following override of the DefaultTemplateName property. The String that you are assigning to this property is the ID of a RenderingTemplate object that you will add, in a later step, to the .ascx file that you created earlier. (When your project is finished, that file is deployed to the folder %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\CONTROLTEMPLATES.) If ControlTemplate, Template, or TemplateName are not overridden, the RenderingTemplate is called as follows: ControlTemplate returns Template, which, in turn, returns the Template property of whatever RenderingTemplate is named by TemplateName. Finally, the get accessor of TemplateName returns DefaultTemplateName. In a more complex case in which, for example, you have separate templates for the New and Edit modes, you would have to override one or more of the preceding properties in addition to probably the AlternateTemplateName property or the DefaultAlternateTemplateName property.

    protected override string DefaultTemplateName
            {
                get
                {
                    if (this.ControlMode == SPControlMode.Display)
                    {
                        return this.DisplayTemplateName;
                    }
                    else
                    {
                        return "InterestCalculatorFieldForEdit";
                    }
                }
            }
    
  7. Add the following override of DisplayTemplateName. The String that you are assigning to this property is the ID of a RenderingTemplate object that you will add, in a later step, to the .ascx file that you created earlier. This object renders the field's value in Display mode.

      public override string DisplayTemplateName
            {
                get
                {
                    return "InterestCalculatorFieldForDisplay";
                }
                set
                {
                    base.DisplayTemplateName = value;
                }
            }
    
  8. Add the following override of the CreateChildControls() method. The override does not perform any function if the underlying InterestCalculatorField is null. (It might be null if the InterestCalculatorFieldControl is created independently of the set accessor for the FieldRenderingControl property of the InterestCalculatorField. See the override of FieldRenderingControl in InterestCalculatorField.cs.)

      protected override void CreateChildControls()
            {
                if (this.Field != null)
                {
    
    
                }
            }
    
  9. Add the following call to the base method as the first line of the conditional. Such a call is usually necessary to ensure that the inherited child controls are created, in case they are rendered entirely or partially by the base CreateChildControls instead of by a template. For example, the TextField template in DefaultTemplates.ascx (in %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\ControlTemplates) renders the child TextBox, but the CreateChildControls method adjusts the maximum size of the TextBox to match the maximum size of the underlying SPFieldText field. The base CreateChildControls may also create dynamic BaseValidator controls. However, ordinarily you do not have access to the source code of the base method, so experimentation is needed to determine whether it has to be called and, if so, where it should be called in your override.

    base.CreateChildControls();
    
  10. Add the following lines to associate the child controls in the rendering templates with the child control fields declared in your custom field control (or inherited from its parent). You must do this here because the call to the base CreateChildControls will associate the inherited child controls to the rendering templates used by the parent of your custom field class, not to the custom rendering templates, so the base association has to be replaced by a new one.

      this.InterestCalculatorFieldForDisplay = (Label)TemplateContainer.FindControl("InterestValueForDisplay");
    
  11. Add the following structure below the control association code. Your field uses a different rendering template (that you create in a later step of this topic) in Display mode from the one it uses in New and Edit modes. Therefore, different child controls are initialized depending on the mode.

    if (this.ControlMode != SPControlMode.Display)
    {
    
    }
    
  12. Inside the if condition block that runs when the control mode is Display, add the following code to initialize the field to its current value from the content database.

    InterestCalculatorFieldForDisplay.Text = (String)this.ItemFieldValue;
    
  13. At this point, your override of CreateChildControls should look like the following.

        protected override void CreateChildControls()
            {
                if (this.Field != null)
                {
                    base.CreateChildControls();
                    this.InterestCalculatorFieldForDisplay = (Label)TemplateContainer.FindControl("InterestValueForDisplay");
    
                    if (this.ControlMode == SPControlMode.Display)
                    {
                        InterestCalculatorFieldForDisplay.Text = (String)this.ItemFieldValue;
                    }
                }
            }
    
  14. Add the following override of the Value property, which is the value of the field in the user interface. If the end user has changed the value and has not yet saved, the Value property is not necessarily the actual value of the underlying InterestCalculatorField (derived from SPFieldText) object or the value of the field in the content database. Both the get accessor and set accessor begin by calling EnsureChildControls() (which calls CreateChildControls() as needed). Calling EnsureChildControls is mandatory unless (1) you call the base property first, and (2) you know that the set and get accessors of the base property call EnsureChildControls. If you were replacing the underlying TextBox child control inherited from TextField with a completely different type of control, such as a drop-down list box, the set accessor and the get accessor of your override of the Value property would have to set this control directly rather than call a base property. To ensure that the control initially loads with the value in the underlying InterestCalculatorField object, the OnLoad method sets InterestCalculatorFieldControl.Value to the value of ItemFieldValue, which is the value of the underlying InterestCalculatorField object.

        public override object Value
            {
                get
                {
                    EnsureChildControls();
                    return base.Value;
                }
                set
                {
                    EnsureChildControls();
                    base.Value = (String)value;
                }
            }
    
  15. Save and close the file.

Creating the Field Rendering Template

In this section, you create the template.

To create the rendering template

  1. Open the InterestCalculatorEditor.ascx file.

  2. Make sure the markup <%@ Control directive matches the following line.

    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="InterestCalculatorEditor.ascx.cs" Inherits="SharePoint.CustomControls.WebControls.InterestCalculatorEditor" %>
    
  3. Below the directives, add the following markup.

    <SharePoint:RenderingTemplate ID="InterestCalculatorFieldForEdit" runat="server">
      <Template>
        <asp:Label ID="PrincipalAmount" Text="Enter principal amount" runat="server" />   
           &nbsp;
        <asp:TextBox ID="TextField" runat="server"  />
      </Template>
    </SharePoint:RenderingTemplate>
    <SharePoint:RenderingTemplate ID="InterestCalculatorFieldForDisplay" runat="server">
      <Template>
        <asp:Label ID="InterestValueForDisplay" runat="server" />
      </Template>
    </SharePoint:RenderingTemplate>
    <wssuc:InputFormControl runat="server"LabelText="Enter Intereset Rate %">
        <Template_Control>
           <asp:TextBox id="InterestRate" runat="server"/>
        </Template_Control>
    </wssuc:InputFormControl>
    

    Note the following facts about this markup:

    • The ID of the RenderingTemplate must be identical to the string that you used in your override of the DefaultTemplateName property.

    • The Text attribute of the Label control is set here in the template because it never changes.

    • An HTML &nbsp; element comes between the two controls.

    • The TextBox definition is identical to the one in the TextField RenderingTemplate that is defined in %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\CONTROLTEMPLATES\DefaultTemplates.ascx. But the definition must be repeated here because your override of DefaultTemplateName points to this custom template—not the TextField template. The same ID is used in the custom template because the base CreateChildControls method (see earlier in the article) might refer to the control by this ID.

  4. Add the following markup just below the RenderingTemplate. This renders the text box to enter the interest rate while you are creating a new custom field in the list.

    <wssuc:InputFormControl runat="server" LabelText="Enter Interest Rate %">
        <Template_Control>
            <asp:TextBox id="InterestRate" runat="server"/>
        </Template_Control>
    </wssuc:InputFormControl>
    
  5. Open InterestCalculatorEditor.ascx.cs file.

  6. Add the following statements.

    using System.Web.UI;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.WebControls;
    
  7. Change the namespace to SharePoint.CustomControls.WebControls.

  8. Be sure that the class is named InterestCalculatorEditor, and change its declaration to specify that it implements the IFieldEditor interface.

    public partial class InterestCalculatorEditor : UserControl, IFieldEditor
    {
    }
    
  9. Add a field for an InterestCalculatorField control.

    InterestCalculatorField _interestCalculatorField = null;
    
  10. Add the following DisplayAsNewSection property of the IFieldEditor interface. This property gets a value that indicates whether the field property editor should be in a special section on the page.

    public bool DisplayAsNewSection
            {
                get { return false; }
            }
    
  11. Add the following InitializeWithField(SPField) method of the IFieldEditor interface. This method initializes the field property editor when the page loads.

    void IFieldEditor.InitializeWithField(SPField field)
            {
                _interestCalculatorField = field as InterestCalculatorField;
                if (Page.IsPostBack)
                {
                    return;
                }
                if (_interestCalculatorField != null && _interestCalculatorField.InterestRate != null)
                {
                    InterestRate.Text = _interestCalculatorField.InterestRate;
                }
            }
    
  12. Add the following OnSaveChange(SPField, Boolean) method of the IFieldEditor interface. This method validates and saves the changes the user has made in the field property editor control.

    public void OnSaveChange(Microsoft.SharePoint.SPField field, bool isNewField)
            {
                InterestCalculatorField interestCalculatorField = (InterestCalculatorField)field;
                string interestRate = InterestRate.Text == string.Empty ? "0" : InterestRate.Text;
                if (isNewField)
                {
                    interestCalculatorField.UpdateCustomProperty(interestRate);
                }
                else
                {
                    interestCalculatorField.InterestRate = interestRate;
                }
            }
    
  13. Save and close the file.

Creating the Field Type Definition

In this section, you create the field type definition.

To create the field type definition

  1. In Visual Studio 2010, build the project by clicking Build Solution on the Build menu.

  2. Open the fldtypes_InterestCalculatorField.xml file, and replace its contents with the following markup.

    <?xml version="1.0" encoding="utf-8" ?>
    <FieldTypes>
      <FieldType>
        <Field Name="TypeName">InterestCalculator</Field>
        <Field Name="ParentType">Text</Field>
        <Field Name="TypeDisplayName">Interest Calculator</Field>
        <Field Name="TypeShortDescription">Interest Calculator</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">SharePoint.CustomControls.InterestCalculatorField, $SharePoint.Project.AssemblyFullName$</Field>
        <Field Name="FieldEditorUserControl">/_controltemplates/InterestCalculatorEditor.ascx</Field>
        <PropertySchema>
          <Fields>
            <Field Name="InterestRate" DisplayName="Interest Rate %" Type="Text" Hidden="TRUE">
            </Field>
          </Fields>
        </PropertySchema>
      </FieldType>
    </FieldTypes>
    

    This file defines the custom field type for Microsoft SharePoint Foundation. For details about the purpose and meaning of its elements, see the following articles:

    Note

    The <Field Name="FieldTypeClass"> element must be entirely on one line.

    The value of the <Field Name="FieldTypeClass"> element is the fully qualified name of your custom field class followed by a comma and then a Visual Studio 2010 token ($SharePoint.Project.AssemblyFullName$). When you compile the project, a copy of this file is created, and the token is replaced by the full four-part name of the assembly. It is that copy that is deployed when you select Deploy Solution on the Build menu of Visual Studio 2010. If you are not using Visual Studio 2010, you will have to compile the project at this point, even though it is not finished, to generate a Public Key Token. You can then use the tool described at How to: Create a Tool to Get the Full Name of an Assembly to obtain the full four-part name and paste it in manually to replace the token.

Creating the XSLT Style Sheet

In this section, you create the style sheet.

To create the XSLT style sheet

  • Open the fldtypes_InterestCalculatorField.xslt file, and replace everything below the <?xml version="1.0" encoding="utf-8"?> tag with the following markup.

    <xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema"
                    xmlns:d="https://schemas.microsoft.com/sharepoint/dsp"
                    version="1.0"
                    exclude-result-prefixes="xsl msxsl ddwrt"
                    xmlns:ddwrt="https://schemas.microsoft.com/WebParts/v2/DataView/runtime"
                    xmlns:asp="https://schemas.microsoft.com/ASPNET/20"
                    xmlns:__designer="https://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 = 'TotalPrincipalInterest']" 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>
    

    The XSLT style sheet renders the field on list views. The cells in the InterestCalculator column of list views have a light green background with the InterestCalculator value in bold.

    The column header in list view mode is rendered by another XSLT style sheet that is supplied in the built-in file fldtypes.xsl in %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATES\LAYOUTS\XSL.

Building and Testing the Custom Field Type

In this section, you build and test the project.

To build and test the custom field type

  1. On the Build menu, select Deploy. This automatically rebuilds the assembly, deploys the assembly to the GAC, deploys the .ascx file to %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\ControlTemplates, deploys the fldtypes*.xml file to %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\XML, deploys the fldtypes*.xsl file to %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\LAYOUTS\XSL, and recycles the web application.

  2. Open a website in your SharePoint web application, and create a list named Loan.

  3. Add a new column to the list. On the Create Column page, enter TotalPrincipalInterest as the column name.

  4. Click the radio button for Calculate Interest.

  5. Click the No radio button to make the field required.

  6. Under the Additional Column Settings, type a value into the Enter Interest Rate % box (For example: 10).

  7. Leave the Add to default view check box selected.

  8. The custom column should appear as shown in Figure 2. Click OK.

    Figure 2. Add new custom column with IFieldEditor

    Add new custom column with IFieldEditor

  9. Add an item to the list.

  10. On the New Item page, enter values into the Title and TotalPrincipalInterest boxes, and then click Save.

  11. The Add New Item dialog box should appear similar to Figure 3.

    Figure 3. Add New Item

    Add New Item

  12. Confirm that the value on the list view is the sum of the principal amount and the interest amount, and it is bold against an orange background, as shown in Figure 4.

    Figure 4. List View

    List View

  13. Click the item title to open the Display page. Confirm that the field renders with its current value in the content database, as shown in Figure 5.

    Figure 5. Display Item

    Display Item

  14. Click Edit Item to edit the field. Confirm that the field is initially set to its current value, as shown in Figure 6.

    Figure 6. Edit Item

    Edit Item

Conclusion

In this article, you saw how to create a custom editor control by overriding the members of the IFieldEditor interface in SharePoint 2010. By applying these techniques, you can adapt these procedures to your own applications.

Additional Resources

For more information about the topics discussed here, see the following: