How to create Custom ListForm WebPart
- by DipeshBhanani
Mostly all who works extensively on SharePoint (including meJ) don’t like to use out-of-box list forms (DispForm.aspx, EditForm.aspx, NewForm.aspx) as interface. Actually these OOB list forms bind hands of developers for the customization. It gives headache to developers to add just one post back event, for a dropdown field and to populate other fields in NewForm.aspx or EditForm.aspx. On top of that clients always ask such stuff. So here I am going to give you guys a flight for SharePoint Customization world. In this blog, I will explain, how to create CustomListForm WebPart. In my next blogs, I am going to explain easy deployment of List Forms through features and last, guidance on using SharePoint web controls.
1. First thing, create a class library project through Visual Studio and inherit the class with WebPart class.
public class CustomListForm : WebPart
2. Declare the public variables and properties which we are going to use throughout the class. You will get to know these once you see them in use.
#region "Variable Declaration"
Table spTableCntl;
FormToolBar formToolBar;
Literal ltAlertMessage;
Guid SiteId;
Guid ListId;
int ItemId;
string ListName;
#endregion
#region "Properties"
SPControlMode _ControlMode = SPControlMode.New;
[Personalizable(PersonalizationScope.Shared),
WebBrowsable(true),
WebDisplayName("Control Mode"),
WebDescription("Set Control Mode"),
DefaultValue(""),
Category("Miscellaneous")]
public SPControlMode ControlMode
{
get { return _ControlMode; }
set { _ControlMode = value; }
}
#endregion
The property “ControlMode” is used to identify the mode of the List Form. The property is of type SPControlMode which is an enum type with values (Display, Edit, New and Invalid). When we will add this WebPart to DispForm.aspx, EditForm.aspx and NewForm.aspx, we will set the WebPart property “ControlMode” to Display, Edit and New respectively.
3. Now, we need to override the CreateChildControl method and write code to manually add SharePoint Web Controls related to each list fields as well as ToolBar controls.
protected override void CreateChildControls()
{
base.CreateChildControls();
try
{
SiteId = SPContext.Current.Site.ID;
ListId = SPContext.Current.ListId;
ListName = SPContext.Current.List.Title;
if (_ControlMode == SPControlMode.Display || _ControlMode == SPControlMode.Edit)
ItemId = SPContext.Current.ItemId;
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite site = new SPSite(SiteId))
{
//creating a new SPSite with credentials of System Account
using (SPWeb web = site.OpenWeb())
{
//<Custom Code for creating form controls>
}
}
});
}
catch (Exception ex)
{
ShowError(ex, "CreateChildControls");
}
}
Here we are assuming that we are developing this WebPart to plug into List Forms. Hence we will get the List Id and List Name from the current context. We can have Item Id only in case of Display and Edit Mode. We are putting our code into “RunWithElevatedPrivileges” to elevate privileges to System Account.
Now, let’s get deep down into the main code and expand “//<Custom Code for creating form controls>”. Before initiating any SharePoint control, we need to set context of SharePoint web controls explicitly so that it will be instantiated with elevated System Account user. Following line does the job.
//To create SharePoint controls with new web object and System Account credentials
SPControl.SetContextWeb(Context, web);
First thing, let’s add main table as container for all controls.
//Table to render webpart
Table spTableMain = new Table();
spTableMain.CellPadding = 0;
spTableMain.CellSpacing = 0;
spTableMain.Width = new Unit(100, UnitType.Percentage);
this.Controls.Add(spTableMain);
Now we need to add Top toolbar with Save and Cancel button at top as you see in the below screen shot.
// Add Row and Cell for Top ToolBar
TableRow spRowTopToolBar = new TableRow();
spTableMain.Rows.Add(spRowTopToolBar);
TableCell spCellTopToolBar = new TableCell();
spRowTopToolBar.Cells.Add(spCellTopToolBar);
spCellTopToolBar.Width = new Unit(100, UnitType.Percentage);
ToolBar toolBarTop = (ToolBar)Page.LoadControl("/_controltemplates/ToolBar.ascx");
toolBarTop.CssClass = "ms-formtoolbar";
toolBarTop.ID = "toolBarTbltop";
toolBarTop.RightButtons.SeparatorHtml = "<td class=ms-separator> </td>";
if (_ControlMode != SPControlMode.Display)
{
SaveButton btnSave = new SaveButton();
btnSave.ControlMode = _ControlMode;
btnSave.ListId = ListId;
if (_ControlMode == SPControlMode.New)
btnSave.RenderContext = SPContext.GetContext(web);
else
{
btnSave.RenderContext = SPContext.GetContext(this.Context, ItemId, ListId, web);
btnSave.ItemContext = SPContext.GetContext(this.Context, ItemId, ListId, web);
btnSave.ItemId = ItemId;
}
toolBarTop.RightButtons.Controls.Add(btnSave);
}
GoBackButton goBackButtonTop = new GoBackButton();
toolBarTop.RightButtons.Controls.Add(goBackButtonTop);
goBackButtonTop.ControlMode = SPControlMode.Display;
spCellTopToolBar.Controls.Add(toolBarTop);
Here we have use “SaveButton” and “GoBackButton” which are internal SharePoint web controls for save and cancel functionality. I have set some of the properties of Save Button with if-else condition because we will not have Item Id in case of New Mode. Item Id property is used to identify which SharePoint List Item need to be saved.
Now, add Form Toolbar to the page which contains “Attach File”, “Delete Item” etc buttons.
// Add Row and Cell for FormToolBar
TableRow spRowFormToolBar = new TableRow();
spTableMain.Rows.Add(spRowFormToolBar);
TableCell spCellFormToolBar = new TableCell();
spRowFormToolBar.Cells.Add(spCellFormToolBar);
spCellFormToolBar.Width = new Unit(100, UnitType.Percentage);
FormToolBar formToolBar = new FormToolBar();
formToolBar.ID = "formToolBar";
formToolBar.ListId = ListId;
if (_ControlMode == SPControlMode.New)
formToolBar.RenderContext = SPContext.GetContext(web);
else
{
formToolBar.RenderContext = SPContext.GetContext(this.Context, ItemId, ListId, web);
formToolBar.ItemContext = SPContext.GetContext(this.Context, ItemId, ListId, web);
formToolBar.ItemId = ItemId;
}
formToolBar.ControlMode = _ControlMode;
formToolBar.EnableViewState = true;
spCellFormToolBar.Controls.Add(formToolBar);
The ControlMode property will take care of which button to be displayed on the toolbar. E.g. “Attach files”, “Delete Item” in new/edit forms and “New Item”, “Edit Item”, “Delete Item”, “Manage Permissions” etc in display forms.
Now add main section which contains form field controls.
//Create Form Field controls and add them in Table "spCellCntl"
CreateFieldControls(web);
//Add public variable "spCellCntl" containing all form controls to the page
spRowCntl.Cells.Add(spCellCntl);
spCellCntl.Width = new Unit(100, UnitType.Percentage);
spCellCntl.Controls.Add(spTableCntl);
//Add a Blank Row with height of 5px to render space between ToolBar table and Control table
TableRow spRowLine1 = new TableRow();
spTableMain.Rows.Add(spRowLine1);
TableCell spCellLine1 = new TableCell();
spRowLine1.Cells.Add(spCellLine1);
spCellLine1.Height = new Unit(5, UnitType.Pixel);
spCellLine1.Controls.Add(new LiteralControl("<IMG SRC='/_layouts/images/blank.gif' width=1 height=1 alt=''>"));
//Add Row and Cell for Form Controls Section
TableRow spRowCntl = new TableRow();
spTableMain.Rows.Add(spRowCntl);
TableCell spCellCntl = new TableCell();
//Create Form Field controls and add them in Table "spCellCntl"
CreateFieldControls(web);
//Add public variable "spCellCntl" containing all form controls to the page
spRowCntl.Cells.Add(spCellCntl);
spCellCntl.Width = new Unit(100, UnitType.Percentage);
spCellCntl.Controls.Add(spTableCntl);
TableRow spRowLine2 = new TableRow();
spTableMain.Rows.Add(spRowLine2);
TableCell spCellLine2 = new TableCell();
spRowLine2.Cells.Add(spCellLine2);
spCellLine2.CssClass = "ms-formline";
spCellLine2.Controls.Add(new LiteralControl("<IMG SRC='/_layouts/images/blank.gif' width=1 height=1 alt=''>"));
// Add Blank row with height of 5 pixel
TableRow spRowLine3 = new TableRow();
spTableMain.Rows.Add(spRowLine3);
TableCell spCellLine3 = new TableCell();
spRowLine3.Cells.Add(spCellLine3);
spCellLine3.Height = new Unit(5, UnitType.Pixel);
spCellLine3.Controls.Add(new LiteralControl("<IMG SRC='/_layouts/images/blank.gif' width=1 height=1 alt=''>"));
You can add bottom toolbar also to get same look and feel as OOB forms. I am not adding here as the blog will be much lengthy.
At last, you need to write following lines to allow unsafe updates for Save and Delete button.
// Allow unsafe update on web for save button and delete button
if (this.Page.IsPostBack && this.Page.Request["__EventTarget"] != null
&& (this.Page.Request["__EventTarget"].Contains("IOSaveItem")
|| this.Page.Request["__EventTarget"].Contains("IODeleteItem")))
{
SPContext.Current.Web.AllowUnsafeUpdates = true;
}
So that’s all. We have finished writing Custom Code for adding field control. But something most important is skipped. In above code, I have called function “CreateFieldControls(web);” to add SharePoint field controls to the page.
Let’s see the implementation of the function:
private void CreateFieldControls(SPWeb pWeb)
{
SPList listMain = pWeb.Lists[ListId];
SPFieldCollection fields = listMain.Fields;
//Main Table to render all fields
spTableCntl = new Table();
spTableCntl.BorderWidth = new Unit(0);
spTableCntl.CellPadding = 0;
spTableCntl.CellSpacing = 0;
spTableCntl.Width = new Unit(100, UnitType.Percentage);
spTableCntl.CssClass = "ms-formtable";
SPContext controlContext = SPContext.GetContext(this.Context, ItemId, ListId, pWeb);
foreach (SPField listField in fields)
{
string fieldDisplayName = listField.Title;
string fieldInternalName = listField.InternalName;
//Skip if the field is system field or hidden
if (listField.Hidden || listField.ShowInVersionHistory == false)
continue;
//Skip if the control mode is display and field is read-only
if (_ControlMode != SPControlMode.Display && listField.ReadOnlyField == true)
continue;
FieldLabel fieldLabel = new FieldLabel();
fieldLabel.FieldName = listField.InternalName;
fieldLabel.ListId = ListId;
BaseFieldControl fieldControl = listField.FieldRenderingControl;
fieldControl.ListId = ListId;
//Assign unique id using Field Internal Name
fieldControl.ID = string.Format("Field_{0}", fieldInternalName);
fieldControl.EnableViewState = true;
//Assign control mode
fieldLabel.ControlMode = _ControlMode;
fieldControl.ControlMode = _ControlMode;
switch (_ControlMode)
{
case SPControlMode.New:
fieldLabel.RenderContext = SPContext.GetContext(pWeb);
fieldControl.RenderContext = SPContext.GetContext(pWeb);
break;
case SPControlMode.Edit:
case SPControlMode.Display:
fieldLabel.RenderContext = controlContext;
fieldLabel.ItemContext = controlContext;
fieldLabel.ItemId = ItemId;
fieldControl.RenderContext = controlContext;
fieldControl.ItemContext = controlContext;
fieldControl.ItemId = ItemId;
break;
}
//Add row to display a field row
TableRow spCntlRow = new TableRow();
spTableCntl.Rows.Add(spCntlRow);
//Add the cells for containing field lable and control
TableCell spCellLabel = new TableCell();
spCellLabel.Width = new Unit(30, UnitType.Percentage);
spCellLabel.CssClass = "ms-formlabel";
spCntlRow.Cells.Add(spCellLabel);
TableCell spCellControl = new TableCell();
spCellControl.Width = new Unit(70, UnitType.Percentage);
spCellControl.CssClass = "ms-formbody";
spCntlRow.Cells.Add(spCellControl);
//Add the control to the table cells
spCellLabel.Controls.Add(fieldLabel);
spCellControl.Controls.Add(fieldControl);
//Add description if there is any in case of New and Edit Mode
if (_ControlMode != SPControlMode.Display && listField.Description != string.Empty)
{
FieldDescription fieldDesc = new FieldDescription();
fieldDesc.FieldName = fieldInternalName;
fieldDesc.ListId = ListId;
spCellControl.Controls.Add(fieldDesc);
}
//Disable Name(Title) in Edit Mode
if (_ControlMode == SPControlMode.Edit && fieldDisplayName == "Name")
{
TextBox txtTitlefield = (TextBox)fieldControl.Controls[0].FindControl("TextField");
txtTitlefield.Enabled = false;
}
}
fields = null;
}
First of all, I have declared List object and got list fields in field collection object called “fields”. Then I have added a table for the container of all controls and assign CSS class as "ms-formtable" so that it gives consistent look and feel of SharePoint. Now it’s time to navigate through all fields and add them if required.
Here we don’t need to add hidden or system fields. We also don’t want to display read-only fields in new and edit forms. Following lines does this job.
//Skip if the field is system field or hidden
if (listField.Hidden || listField.ShowInVersionHistory == false)
continue;
//Skip if the control mode is display and field is read-only
if (_ControlMode != SPControlMode.Display && listField.ReadOnlyField == true)
continue;
Let’s move to the next line of code.
FieldLabel fieldLabel = new FieldLabel();
fieldLabel.FieldName = listField.InternalName;
fieldLabel.ListId = ListId;
BaseFieldControl fieldControl = listField.FieldRenderingControl;
fieldControl.ListId = ListId;
//Assign unique id using Field Internal Name
fieldControl.ID = string.Format("Field_{0}", fieldInternalName);
fieldControl.EnableViewState = true;
//Assign control mode
fieldLabel.ControlMode = _ControlMode;
fieldControl.ControlMode = _ControlMode;
We have used “FieldLabel” control for displaying field title. The advantage of using Field Label is, SharePoint automatically adds red star besides field label to identify it as mandatory field if there is any.
Here is most important part to understand. The “BaseFieldControl”. It will render the respective web controls according to type of the field. For example, if it’s single line of text, then Textbox, if it’s look up then it renders dropdown. Additionally, the “ControlMode” property tells compiler that which mode (display/edit/new) controls need to be rendered with. In display mode, it will render label with field value. In edit mode, it will render respective control with item value and in new mode it will render respective control with empty value.
Please note that, it’s not always the case when dropdown field will be rendered for Lookup field or Choice field. You need to understand which controls are rendered for which list fields. I am planning to write a separate blog which I hope to publish it very soon.
Moreover, we also need to assign list field specific properties like List Id, Field Name etc to identify which SharePoint List field is attached with the control.
switch (_ControlMode)
{
case SPControlMode.New:
fieldLabel.RenderContext = SPContext.GetContext(pWeb);
fieldControl.RenderContext = SPContext.GetContext(pWeb);
break;
case SPControlMode.Edit:
case SPControlMode.Display:
fieldLabel.RenderContext = controlContext;
fieldLabel.ItemContext = controlContext;
fieldLabel.ItemId = ItemId;
fieldControl.RenderContext = controlContext;
fieldControl.ItemContext = controlContext;
fieldControl.ItemId = ItemId;
break;
}
Here, I have separate code for new mode and Edit/Display mode because we will not have Item Id to assign in New Mode. We also need to set CSS class for cell containing Label and Controls so that those controls get rendered with SharePoint theme.
spCellLabel.CssClass = "ms-formlabel";
spCellControl.CssClass = "ms-formbody";
“FieldDescription” control is used to add field description if there is any.
Now it’s time to add some more customization,
//Disable Name(Title) in Edit Mode
if (_ControlMode == SPControlMode.Edit && fieldDisplayName == "Name")
{
TextBox txtTitlefield = (TextBox)fieldControl.Controls[0].FindControl("TextField");
txtTitlefield.Enabled = false;
}
The above code will disable the title field in edit mode. You can add more code here to achieve more customization according to your requirement. Some of the examples are as follow:
//Adding post back event on UserField to auto populate some other dependent field
//in new mode and disable it in edit mode
if (_ControlMode != SPControlMode.Display && fieldDisplayName == "Manager")
{
if (fieldControl.Controls[0].FindControl("UserField") != null)
{
PeopleEditor pplEditor = (PeopleEditor)fieldControl.Controls[0].FindControl("UserField");
if (_ControlMode == SPControlMode.New)
pplEditor.AutoPostBack = true;
else
pplEditor.Enabled = false;
}
}
//Add JavaScript Event on Dropdown field. Don't forget to add the JavaScript function on the page.
if (_ControlMode == SPControlMode.Edit && fieldDisplayName == "Designation")
{
DropDownList ddlCategory = (DropDownList)fieldControl.Controls[0];
ddlCategory.Attributes.Add("onchange", string.Format("javascript:DropdownChangeEvent('{0}');return false;", ddlCategory.ClientID));
}
Following are the screenshots of my Custom ListForm WebPart. Let’s play a game, check out your OOB List forms of SharePoint, compare with these screens and find out differences.
DispForm.aspx:
EditForm.aspx:
NewForm.aspx:
Enjoy the SharePoint Soup!!!