Archive for July, 2012

Render fields on form programmatically

07/08/2012

For a recent project I was faced with the problem of having to render fields programmatically in a dynamic fashion and then save the posted values of these fields in a listitem.

Rendering the fields where pretty trivial once you figure out how the BaseFieldRenderingControl property works.

I ended up with the following code for rendering a user field and returning the validation script of the user control. The only thing the validation script covers is the required field option. It does not take the validity of users into account!


/// <summary>
/// Renders a SharePoint User or UserMulti field.
/// </summary>
/// <param name="Title">HtmlTableCell for title element</param>
/// <param name="Body">HtmlTableCell for control and description element</param>
/// <param name="Field">SPField object to render</param>
/// <returns>Validation script as string</returns>
public static string RenderUserField(HtmlTableCell Title, HtmlTableCell Body, SPField Field)
{
string validationScript = string.Empty;

if ((Field != null) && (Field.FieldRenderingControl != null))
{
Title.Attributes.Add("class", "ms-formlabel");
Title.Attributes.Add("nowrap", "true");
Title.Controls.Add(new LiteralControl("<h3>" + Field.Title));

Body.Attributes.Add("class", "ms-formbody");
Body.EnableViewState = true;

if ((Field.TypeAsString == "User") || (Field.TypeAsString == "UserMulti"))
{
PeopleEditor pplEdit = new PeopleEditor();
pplEdit.ID = Field.InternalName;
pplEdit.AllowTypeIn = false;
pplEdit.ValidatorEnabled = true;
pplEdit.PlaceButtonsUnderEntityEditor = false;
pplEdit.MultiSelect = (Field.TypeAsString == "UserMulti");
pplEdit.Rows = 1;
pplEdit.PreferContentEditableDiv = true;
pplEdit.EnableViewState = true;
pplEdit.AllowEmpty = !Field.Required;

Body.Controls.Add(pplEdit);

validationScript = "var obj_" + Field.InternalName + " = document.getElementById('ctl00_PlaceHolderMain_" + Field.InternalName + "_checkNames'); if(obj_" + Field.InternalName + " != null) { obj_" + Field.InternalName + ".style.display = 'none'; }";

if (Field.Required)
{
Title.Controls.Add(new LiteralControl("<span style='color:red;'> *</span>"));
Body.Attributes.Add("req", "user");
}
}
Title.Controls.Add(new LiteralControl("</h3>"));

if (!string.IsNullOrEmpty(Field.Description))
{
Body.Controls.Add(new LiteralControl("<br /><span>" + Field.Description + "</span>"));
}
}

return validationScript;
}

And the following code for rendering all other fields including taxonomy fields. Same this here with the validation of course. If the field is required, the javascript checks the field for an empty value before posting.


/// <summary>
/// Renders a SharePoint field.
/// </summary>
/// <param name="Title">HtmlTableCell for title element</param>
/// <param name="Body">HtmlTableCell for control and description element</param>
/// <param name="Field">SPField object to render</param>
/// <param name="currentContext">SPContext object based on item context</param>
/// <returns>Validation script as string</returns>
public static string RenderBaseField(HtmlTableCell Title, HtmlTableCell Body, SPField Field, SPContext currentContext)
{
string validationScript = string.Empty;

if ((Field != null) && (Field.FieldRenderingControl != null))
{
Title.Attributes.Add("class", "ms-formlabel");
Title.Attributes.Add("nowrap", "true");
Title.Controls.Add(new LiteralControl("<h3>" + Field.Title));

Body.Attributes.Add("class", "ms-formbody");
Body.EnableViewState = true;

BaseFieldControl fldCtrl = Field.FieldRenderingControl;
fldCtrl.ListId = Field.ParentList.ID;
fldCtrl.ControlMode = SPControlMode.New;
fldCtrl.ID = Field.InternalName;
fldCtrl.ItemId = 0;
fldCtrl.FieldName = Field.InternalName;
fldCtrl.EnableViewState = true;
fldCtrl.RenderContext = currentContext;
fldCtrl.ItemContext = currentContext;

Body.Controls.Add(fldCtrl);

if (Field.TypeAsString == "Choice")
{
SPFieldChoice fldChoice = new SPFieldChoice(Field.ParentList.Fields, Field.InternalName, Field.Title);

if (fldChoice.EditFormat == SPChoiceFormatType.Dropdown)
{
validationScript = "var obj_" + Field.InternalName + " = document.getElementById('ctl00_PlaceHolderMain_" + Field.InternalName + "_DropDownChoice'); if(obj_" + Field.InternalName + " != null) { insertEmptyOption(obj_" + Field.InternalName + "); }";
}
}

if (Field.Required)
{
Title.Controls.Add(new LiteralControl("<span style='color:red;'> *</span>"));

if (Field.TypeAsString == "URL")
Body.Attributes.Add("req", "url");
else if (Field.TypeAsString == "MultiChoice")
Body.Attributes.Add("req", "multichoice");
else if (Field.TypeAsString == "Choice")
Body.Attributes.Add("req", "choice");
else if (Field.TypeAsString == "LookupMulti")
Body.Attributes.Add("req", "lookupmulti");
else if (Field.TypeAsString == "Number")
Body.Attributes.Add("req", "number");
else if (Field.TypeAsString == "Currency")
Body.Attributes.Add("req", "currency");
else
Body.Attributes.Add("req", "field");
}

Title.Controls.Add(new LiteralControl("</h3>"));

if (!string.IsNullOrEmpty(Field.Description))
{
Body.Controls.Add(new LiteralControl("<br /><span>" + Field.Description + "</span>"));
}
}

return validationScript;
}

Since I’m adding these values to a non existing list item I need to send a context object that isn’t based on the item ID. Here’s how i call the functions:


/// <summary>
/// Render form fields for a list or content type.
/// </summary>
/// <param name="List">SPList to render fields for.</param>
/// <param name="ContentType">Selected contenttype to render fields for or null.</param>
/// <param name="FormControls">asp:Panel to contain fields table.</param>
/// <param name="Scripts">asp:Panel to contain validation scripts.</param>
public static void RenderFormFields(SPList List, SPContentType ContentType, Panel FormControls, Panel Scripts)
{
SPContext ctx = SPContext.GetContext(HttpContext.Current, List.DefaultView.ID, List.ID, List.ParentWeb);
ctx.FormContext.FormMode = SPControlMode.New;
SPFieldCollection FieldsToRender = List.Fields;
FormControls.Controls.Clear();
Scripts.Controls.Clear();

if (ContentType != null)
{
FieldsToRender = List.ContentTypes[ContentType.Id].Fields;
}
foreach (SPField fld in FieldsToRender)
{
if (!fld.Hidden)
{
if (
(!Constants.ExcludedFieldTypes.Contains(fld.TypeAsString)) &&
(!Constants.ExcludedFieldInternalNames.Contains(fld.InternalName))
)
{
HtmlTableRow row = new HtmlTableRow();
HtmlTableCell cellTitle = new HtmlTableCell();
HtmlTableCell cellControl = new HtmlTableCell();

if ((fld.TypeAsString == "User") || (fld.TypeAsString == "UserMulti"))
{
Scripts.Controls.Add(new LiteralControl(SharePointManager.RenderUserField(cellTitle, cellControl, fld)));
}
else
{
Scripts.Controls.Add(new LiteralControl(SharePointManager.RenderBaseField(cellTitle, cellControl, fld, ctx)));
}

row.Cells.Add(cellTitle);
row.Cells.Add(cellControl);

FormControls.Controls.Add(row);
}
}
}
}