What for ?

Sharepoint’s API provides some standard form controls to render each column. This is the controls used to render the standard add and edit forms. And they all inherit the BaseFieldControl class.

In other word: In any SPList, you have some SPField fields and each of these SPField has the super power to create a BaseFieldControl. Each BaseFieldControl is a CompositeControl containing ASP.Net controls.

For a single line field, you will just have a wrapped TextBox. But for some more advanced fields like a multi-select lookup field (SPFieldLookup) or rich text field, it can generate some “complex” controls and their related javascript code.

The BaseFieldControl can be directly connected to your Sharepoint’s SPListItem and the BaseFieldControl.Value will match the format required to fill the SPListItem.

You can create your own BaseFieldControl controls, and you can directly mess up the BaseFieldControl.Controls property if you like to (that can be useful). I personally had to create a variation of the MultipleLookupField (that’s the BaseFieldControl used for a SPLookupField with the AllowMultipleValues property enabled) to create a CheckBoxList of selected items (that what the client wanted).

How to use them to display data ?

You can get it by accessing the PField.FieldRenderingControl property. It gives you everything you need. Each BaseFieldControl has a ControlMode property (SPControlMode enum) which can be set to New, Edit, Display or Invalid (I don’t know why this third one exists).

Here is a stripped down version of the necessary code :

protected override CreateChildControls() {
 
     // The web used
     var web = SPContext.Current.Web;
 
     // This is the code you could have easily guessed :
     var list = web.Lists["MyList"];
     var item = list.GetItemById( 1 );
     var field = item["DateOfEvent"];
     var bfc = field.FieldRenderingControl;
 
     // This is when it becomes tricky :
     var renderContext = SPContext.GetContext( this.Context, , list.ID, web );
 
     bfc.ListId = list.ID;
     bfc.FieldName = field.InternalName;
     bfc.ID = field.InternalName;
     bfc.ControlMode = SPControlMode.Edit;
     bfc.RenderContext = renderContext;
     bfc.ItemContext = renderContext;
     bfc.EnableViewState = true;
     bfc.Visible = true;
 
     Controls.Add( bfc );
}

And if you want to use it as an input form, you have to get a non existing SPListItem:

var item = list.GetItemById( 1 );
var field = item["DateOfEvent"];

By this:

var item = list.Items.Add();
var field = list.Fields["DateOfEvent"];

And then later add this reflection code to force the render context to take into account the temporary created item:

var fiContextItem = ( typeof( SPContext ) ).GetField( "m_item", BindingFlags.Instance | BindingFlags.NonPublic );
fiContextItem.SetValue( renderContext, item );

If you use multiple SPWebs

In your Sharepoint code, you might need to use more than one SPWeb. In that case, you must absolutely take care of creating the right SPContext (using the SPWeb of the SPList of the source SPField) for each BaseFieldControl used.

And if the SPWeb used doesn’t have the same language as your current site, you can change it easily by doing something like that:

web.Locale = SPContext.Current.Web.Locale;

If you create your own BaseFieldControl

If you create your own BaseFieldControl, you should really take a look on the disassembled code of the default BaseFieldControls. It could make you save a lot of time and efforts. And please note that the LookupField (which directly inherits BaseFieldControl) isn’t sealed. Inheriting from it might be a good way to do your own custom lookup BaseFieldControl.

If you enable the ControlMode = SPControlMode.Display

If you want to use your BFC with the Display ControlMode and want to set a value, you must take care of setting the Value BEFORE setting the ControlMode. If you don’t do that it will work on the first render but fail on the first PostBack: Your value will not be used (and displayed) by the BFC.

If you check the value of the BFC

If you check the value of the BFC, you have to remember that each BFC create its value for the SPField from which it has been created. For instance, empty values can be returned as String.Empty, null or even the 0 integer.