Custom field type for SharePoint survey

10/12/2015 10:54

We needed custom field type for SharePoint surveys. When doing it according to different steps found on web sites, it was working fine for SharePoint lists, but we had problems in surveys.
Once we used branching in survey, values were not saved, only in case our custom field is on the last page. Finally we found out, that 'Next' button doesn't call Save handler, but calls getter.
That's why we overrided also Value property. Then our field worked as expected in a list and also in surveys with branching.

Here is example for our custom field type - it's dropdown list, which fetches data from SourceList on Rootweb. Data in field are stored as a text - field is derived from SPFieldText.
Originally we wanted just to use JSLink to modify edit mode as dropdown an to fill it client side, but SharePoint surveys don't support JSLink. That's why we decided to create custom field type instead.

How to create Custom Field Type

We need 3 files for field type definition and one for registration

Definition files

MyTestRegionField.cs
MyTestRegionFieldControl.ascx - must be stored directly in ControlTemplates mapped folder - create user control in Visual Studio, move file from folder to root and remove codebehind .cs file, then create new one
MyTestRegionFieldControl.cs

Registration files

fldtypes_MyTestRegionFieldControl.xml - must be stored directly in Template\XML mapped folder, name must start with fldtypes_...


MyTestRegionField.cs

using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using MyProject.Code;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyProject
{
    public class MyTestRegionField : SPFieldText
    {
     
        public MyTestRegionField(SPFieldCollection fields, string fieldName)
            : base(fields, fieldName)
        {
        }


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

        public override object GetFieldValue(string value)
        {
            return base.GetFieldValue(value);
        }

     

        public override string GetValidatedString(object value)
        {
            return base.GetValidatedString(value);
        }

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

    }
}

MyTestRegionFieldControl.ascx


<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.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=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Control Language="C#" %>


<SharePoint:RenderingTemplate runat="server">
    <Template>
        <div>
           <asp:DropDownList runat="server" ID="ddRegion"></asp:DropDownList>
        </div>
    </Template>
</SharePoint:RenderingTemplate>

<SharePoint:RenderingTemplate runat="server">
    <Template>
        <div>
            <asp:Label runat="server" ID="lblRegion"></asp:Label>
        </div>
    </Template>
</SharePoint:RenderingTemplate>

MyTestRegionFieldControl.cs


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

using System.Web.UI.WebControls;

namespace MyProject.Code
{
    public class MyTestRegionFieldControl: BaseFieldControl
    {
        protected DropDownList dropdownRegion;
        protected Label labelRegion;

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

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

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            dropdownRegion = (DropDownList)TemplateContainer.FindControl("ddRegion");
            labelRegion = (Label)TemplateContainer.FindControl("lblRegion");

            if (this.ControlMode != SPControlMode.Display)
            {
                if (dropdownRegion != null)
                {
                    dropdownRegion.DataSource = LoadDropdownValues();
                    dropdownRegion.DataBind();
                    if (this.ItemFieldValue != null)
                    {
                        dropdownRegion.SelectedValue = this.ItemFieldValue.ToString();
                    }

                    if (this.ControlMode == SPControlMode.New)
                    {
                        //New mode
                        dropdownRegion.SelectedValue = dropdownRegion.Items[0].Value;
                    }
                }
            }
            else //Display mode
            {
                if (labelRegion != null && this.ItemFieldValue != null)
                {
                    labelRegion.Text = this.ItemFieldValue.ToString();
                }
            }
        }

        public override object Value
        {
            get
            {
                this.EnsureChildControls();

                dropdownRegion = (DropDownList)TemplateContainer.FindControl("ddRegion");
                if (dropdownRegion != null)
                {
                    return dropdownRegion.SelectedItem.Value;
                }
                else
                {
                    labelRegion = (Label)TemplateContainer.FindControl("lblRegion");
                    if (labelRegion != null)
                    {
                        return labelRegion.Text;
                    }
                }
                return "Can't find control";
            }
            set
            {
                this.EnsureChildControls();
            }
        }

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

            if ((SPContext.Current.FormContext.FormMode == SPControlMode.New) '' (SPContext.Current.FormContext.FormMode == SPControlMode.Edit))
            {
                SPContext.Current.FormContext.OnSaveHandler += new EventHandler(MyTestRegionSaveHandler);
            }
        }

        protected void MyTestRegionSaveHandler(object sender, EventArgs e)
        {
            Page.Validate();
            if (Page.IsValid)
            {
                this.ItemFieldValue = dropdownRegion.SelectedItem.Value;
                SPContext.Current.ListItem.Update();
            }
        }

        protected List<string> LoadDropdownValues()
        {
            List<string> ddValues = new List<string>();
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                using (SPSite site = new SPSite(SPContext.Current.Site.RootWeb.Url))
                {
                    using (SPWeb web = site.OpenWeb())
                    {
                        SPList list = web.Lists.TryGetList("MySourceList");
                        if (list != null)
                        {
                            SPQuery query = new SPQuery();
                            query.ViewXml = "<View><Query><Where></Where></Query></View>";

                            SPListItemCollection items = list.GetItems(query);
                            if (items.Count != 0)
                            {
                                foreach (SPListItem item in items)
                                {
                                    ddValues.Add(item["MySourceColumn"] as string);
                                }
                                ddValues = ddValues.Distinct().ToList();
                            }
                        }
                    }
                }
            });

            return ddValues;
        }
    }
}

fldtypes_MyTestRegionFieldControl.xml