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 BaseFieldControl
s. 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.