Sharepoint : Using BaseFieldControl

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 SPField.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 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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, 0, 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 :

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

By this :

1
2
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 :

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

If you use multiple SPWeb…
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 :

1
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.

6 thoughts on “Sharepoint : Using BaseFieldControl”

  1. Hello Florent,

    I have read your post to dynamically generate from a list some fields which is on another site.

    To present properly the encoding form, I inserted the fields in a table.

    When I load the Webpart at the first time, everything is okay. But when I click the “OK” button, I get an error, and the event “On_click” is not fired. But the createDynamiqueControl () is executed.

    Here is the error message: (Domain is a field of the list)

    [SPException: Failed to get value of the “Domain” column from the “Choice” field type control. See details in log. Exception message: Object reference not set to an instance of an object..]
    Microsoft.SharePoint.WebControls.BaseFieldControl.OnLoad(EventArgs e) +22286585
    System.Web.UI.Control.LoadRecursive() +66
    System.Web.UI.Control.LoadRecursive() +191
    System.Web.UI.Control.LoadRecursive() +191
    System.Web.UI.Control.LoadRecursive() +191
    System.Web.UI.Control.LoadRecursive() +191
    System.Web.UI.Control.LoadRecursive() +191
    System.Web.UI.Control.LoadRecursive() +191
    System.Web.UI.Control.LoadRecursive() +191
    System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2428

    And here is the involved part of code in createDynamiqueControl () :

    using (SPSite SiteDirectory = new SPSite(SiteDirectoryURL))
    {
    using (SPWeb WebDirectory = SiteDirectory.OpenWeb())
    {
    SPList SiteList = WebDirectory.Lists[“Site”];

    SPListItem itemtemp = SiteList.AddItem();

    foreach (SPField field in itemtemp.Fields)
    {
    // dont take care about standard sharepoint field
    if (!SPBuiltInFieldId.Contains(field.Id))
    {

    // dont take care about field in the FieldNotUseFull
    if (!FieldNotUseFull.Contains(field.StaticName.ToString()))
    {

    SPContext renderContext = SPContext.GetContext(this.Context, 0, SiteList.ID, WebDirectory);

    BaseFieldControl webControl = field.FieldRenderingControl;

    webControl.ListId = SiteList.ID;
    webControl.FieldName = field.InternalName;
    webControl.ID = field.InternalName;
    webControl.ControlMode = SPControlMode.Edit;
    webControl.RenderContext = renderContext;
    webControl.ItemContext = renderContext;
    webControl.EnableViewState = true;
    webControl.Visible = true;

    this.Controls.Add(webControl);
    dynamiqueControl.Add(webControl);

    // add a row in the table | Title | control |
    AddFieldToTab(ref row, ref cell, field.Title.ToString(), webControl);

    }
    }
    }
    }
    }

    If you have an idea of what might generate the error, it would be help full.

    Regards,

    Tom

  2. Thank you for posting the most retarded comment of this blog. You should know that the “var” keyword is eXactly like writing the type of the variable except you don’t have to think about it (the compiled code and the IDE auto-completion works exactly the same) and it makes the future refactoring of your code a lot easier.

    But well, you can just keep writing your code the old way. There’s no need to brag about not being able to evolve as a software developer (or anything else).

    I think I will remove your comment in the future, nothing personal, I just don’t really like having negative (and/or stupid) people around me or on my blog. I hope you understand.

  3. As it is understood that using var is the faster and lazier way of doing things, it doesn’t mean that it makes for great code. I would beg to differ that it’s the new and better way of coding. Are you just throwing code readability out the window? By using only var, you are forcing the reader to have to know what each function, method, and/or property will return. When you are providing an example on a blog to help others, try using the appropriate classes so that people will better understand what you are doing.

    Example:

    var bfc = field.FieldRenderingControl;

    For those of us coming to this blog to find out about Base Field Controls.. and we read your provided example.. how do we know that bfc will be of type BaseFieldControl? I guess we’re supposed to know that the property .FieldRenderingControl returns an object of type BaseFieldControl. And, I suppose if the reader knew that much.. they wouldn’t be reading your blog in the first place. Just a little example of why it would be better to provide the actual class names in your helpful blog. Take the time and stop being lazy and blaming it on being some 1337 developer. I wouldn’t belittle someone else because you don’t want to “think” about what you are doing.

Leave a Reply

Your email address will not be published. Required fields are marked *