SharePoint 2010 Custom Fields Editor Control in Page Edit Mode

Author by Concurrency Blog

SharePoint allows you to create custom fields which can be associated to any content type. With the 'Page' content type, SharePoint makes it possible to for you to create 'Edit' fields for your custom fields. These fields are displayed when you put a page into 'Edit Mode'. Edit Mode is where you can add web parts to a page, change the page title (in a Publishing Site), etc. In this post, I am going to show you how to create these custom edit fields. Here's an example of an out of the box edit field for the 'Title' field in a pages edit mode:

Prerequisite's

This post will be using the 'Page' content type that is available in the Publishing features of SharePoint 2010. Follow the instructions provided here to enable the features. We need to create a site column and associate it to the 'Page' content type. If you already have the following prerequisites set up, you can skip to 'Creating the Custom Editor Control' section.

Site Column

  • From your site, click 'Site Actions' > 'Site Settings'
  • Under the galleries heading, click 'Site columns'
  • Click 'Create' and set the following fields
    • Column Name: CustomColumn1
    • Single line of text
    • Leave all other defaults
    • Click 'OK'

Associate Site Column

  • From your site, click 'Site Actions' > 'Site Settings'
  • Under the galleries heading, click 'Site content types'
  • Locate the content type named 'Page' and select it
  • Under the 'Columns' heading, click 'Add from existing site columns'
  • Select 'Custom Columns' group, and select 'CustomColumn1' by clicking 'Add>' to add it to the list of columns to add to the content type
  • Select the 'Yes' radio button for 'Update all content types inheriting from this type
  • Click OK

Create the test page

  • Navigate to the newly provisioned 'Pages' library (provisioned when you enabled the Publishing features).
  • Select 'Documents' from the ribbon
  • Select 'New Document'.
  • Select the type of 'Page', name it 'Sample Page 1' and pick any page layout you desire.
  • Click Save.
Now that we have our test page, we can see our custom column by selecting our page from our library, and click 'Edit Properties'. Now we're going to create an edit field for this custom property just like the 'Title' editor shown above.

Creating the Custom Editor Control

We will be using a development method of SharePoint called a 'Delegate Control'. We will be appending some markup into the 'AdditionalPageHead' of the sites master page by way of a feature.
  • Create a new Visual Studio 2010 'Empty SharePoint Project' targeting .NET Framework 3.5. Let's name it CustomColumnEditor
  • Right click on the project name, and select 'Add' > 'Class'. Name the class CustomEditorFields.cs
  • Add a few references to our project by right clicking 'References' and 'Add Reference'. Locate and add the following references:
    • System.Web
    • Microsoft.SharePoint.Publishing
  • Now we need to add some using statements for some classes we'll need later on
    using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts;using Microsoft.SharePoint; using Microsoft.SharePoint.Publishing; using Microsoft.SharePoint.WebControls;
  • Inherit our class from the Controlclass (which was included in our System.Web.UI reference).
    class CustomEditorFields : Control
  • Override the Control classes 'OnInit' method
    protected override void OnInit(EventArgs e) { base.OnInit(e); }
At this point, we have a class that inherits from the Control class, and we've overridden the OnInit method. Because this will be appended to the master page and loaded on every single page, we want it to be as efficient as possible. As an example, we don't want to try to render out our edit controls while on the Site Settings page.
  • The first step is to ensure that we have an SPContext, and whether or not the current item is a list item. Pages such as Site Settings aren't contained in a list (or library). Pages, such as the one in this example, do reside there. So let's perform our check. This code should be placed prior to base.OnInit(e);
    if (SPContext.Current != null && SPContext.Current.ListItem != null) { }
  • Our next step, within the last if statement, is to determine if the content type of our page is, or is a child of, our page content type (where we added our custom field).
    UPDATED 8/17/11 - As pointed out by Chris in the comments, the deleted code below will only return those content types in the current web. If you are within a sub site, the Page content type will not exist. By searching the AvailableContentTypes, you will be searching the current web, as well as any parent webs.
    SPContentTypeId pageCT = SPContext.Current.Web.ContentTypes["Page"].Id; SPContentTypeId pageCT = SPContext.Current.Web.AvailableContentTypes["Page"].Id; if (SPContext.Current.ListItem.ContentTypeId.Equals(pageCT) ||     SPContext.Current.ListItem.ContentTypeId.IsChildOf(pageCT)) { }
  • If we've made it this far, then we are on a page that exists in a list (i.e., A Site page), it's Content Type is either Page, or, it is a child of page. Now we need to determine if the current page is a Publishing Page, and if it is, check to see if the page is in Edit Mode, or is a New Page (when the edit options are displayed).
    // The current page is a publishing page and is in Edit or New mode if ((PublishingPage.IsPublishingPage(SPContext.Current.ListItem)) && (SPContext.Current.FormContext.FormMode == SPControlMode.Edit || SPContext.Current.FormContext.FormMode == SPControlMode.New)) { }
Now that we are in new/edit mode, we need to locate the 'PlaceHolderMain' from the master page, get an instance of our custom SPField, then add it to the page.
  • Get a handle on the PageHolderMain from our master page
    ContentPlaceHolder placeHolderMain = (ContentPlaceHolder)this.Page.Master.FindControl("PlaceHolderMain");
  • Get our custom SPField
    // Check placeholderMain placeHolderMain for null SPField CustomColumn1; try { CustomColumn1 = SPContext.Current.ListItem.Fields["CustomColumn1"]; } catch { CustomColumn1 = null; }
  • Create an instance of the controls FieldRenderingControl, which inherits from BaseFieldControl. FieldRenderingControl contains the type of control that should be rendered (TextBox, Multiline Textbox, etc) based on the Field Type
    // Check CustomColumn1 for null BaseFieldControl CustomColumn1Control = CustomColumn1.FieldRenderingControl;
  • Now we just need to set the ID of the control to be rendered on the page, and write it out to the page (I'm wrapping the control within the "edit-mode-panel" class so that it's width and background are set to match the 'Title' field, as shown at the beginning of the post).
    CustomColumn1Control.ID = CustomColumn1.InternalName; placeHolderMain.Controls.Add(new LiteralControl(
    )); placeHolderMain.Controls.Add(CustomColumn1Control); placeHolderMain.Controls.Add(new LiteralControl(
    ));

Create the feature

Now that we have our class built, we need to create the feature which handles appending resultant markup to the page. This will also involve adding a Module and an EmptySharepointElement

Feature

  • In your CustomColumnEditor project, right click 'Features' and select 'Add Feature'
  • Rename the feature to CustomColumnEditor

Module

  • Right click the project, select 'Add' > 'New Item' > Module. Name it 'CustomColumnEditorModule'. Visual Studio will automatically add this Module into our feature. If it doesn't, open up the feature editor, and move the new Element to the 'Items in the Feature' pane.
  • Delete the Sample.txt file from the Solution Explorer. This should remove the element in your new Elements.xml file. If it doesn't, manually remove it.
  • Add a reference to our assembly, and target it to write out to our AdditionalPageHead of the master page.

Empty SharePoint Element

At this point, you could deploy your project, but it would never render out, and you won't be shown any errors. You would end up with an error in the 14LOGS that states.
Failed to create a control from assembly 'CustomColumnEditor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=52a9078ab164e700', class 'CustomColumnEditor.CustomEditorFields': The control with assembly name 'CustomColumnEditor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=52a9078ab164e700' class name 'CustomColumnEditor.CustomEditorFields' is not allowed for web at URL 'http://cncy-spdev1/sites/demos'. The type is not registered as safe.DelegateControl: Exception thrown while building custom control 'Microsoft.SharePoint.SPControlElement': The control with assembly name 'CustomColumnEditor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=52a9078ab164e700' class name 'CustomColumnEditor.CustomEditorFields' is not allowed for web at URL 'http://cncy-spdev1/sites/demos'. The type is not registered as safe.
This is where the Emtpy SharePoint Element comes in. It will allow us to register the assembly as safe without having to manually modify any of our web.configs, or cause us to programmatically do it upon Feature Activation. Under the covers, SharePoint will add a SafeControl element in the manifest for the package.
  • Add a new item to your project > Select 'Empty Element'. Name it SafeControlsElement
  • Select your new Element in the Solution Explorer (not the new generated Elements.xml)
  • In the properties window for your SafeControlsElement, click the ellipsis to modify the Safe Control Entries
  • In the Safe Control Entries window, click 'Add'. Modify the properties to match below safecontrol
  • Click OK
  • Deploy the project
The result is a custom editor control for our custom field! result
Author

Concurrency Blog

The latest about Concurrency