Thursday, June 24, 2010

Object model code for SharePoint List forms

If we ever need to customize SharePoint list forms which a different look and feel then, this can be done with SharePoint designer. But if we have a custom functionality requires us to write SharePoint object model code? Yes, writing SharePoint object model code for List forms is possible in SharePoint. Let's look into it.


All the SharePoint list forms (NewForm.aspx, EditForm.aspx, DisplayForm.aspx) uses rendering template called "ListForm" which is present in the file "DefaultTemplates.ascx" in the following path.


C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\TEMPLATE\CONTROLTEMPLATES\DefaultTemplates.ascx


This is the code which I am referring to


<SharePoint:RenderingTemplate ID="ListForm" runat="server">


<Template>


<SPAN id='part1'>


<SharePoint:InformationBar runat="server"/>


<wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbltop" RightButtonSeparator="&nbsp;"
runat
="server">


<Template_RightButtons>


<SharePoint:NextPageButton runat="server"/>


<SharePoint:SaveButton runat="server"/>


<SharePoint:GoBackButton runat="server"/>


</Template_RightButtons>


</wssuc:ToolBar>


<SharePoint:FormToolBar runat="server"/>


<TABLE class="ms-formtable" style="margin-top: 8px;" border=0 cellpadding=0 cellspacing=0 width=100%>


<SharePoint:ChangeContentType runat="server"/>


<SharePoint:FolderFormFields runat="server"/>


<SharePoint:ListFieldIterator runat="server"/>


<SharePoint:ApprovalStatus runat="server"/>


<SharePoint:FormComponent TemplateName="AttachmentRows" runat="server"/>


</TABLE>


<table cellpadding=0 cellspacing=0 width=100%>


<tr>


<td class="ms-formline"><IMG SRC="/_layouts/images/blank.gif" width=1 height=1
alt="">


</td>


</tr>


</table>


<TABLE cellpadding=0 cellspacing=0 width=100% style="padding-top: 7px">


<tr>


<td width=100%>


<SharePoint:ItemHiddenVersion runat="server"/>


<SharePoint:ParentInformationField runat="server"/>


<SharePoint:InitContentType runat="server"/>


<wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbl" RightButtonSeparator="&nbsp;"
runat
="server">


<Template_Buttons>


<SharePoint:CreatedModifiedInfo runat="server"/>


</Template_Buttons>


<Template_RightButtons>


<SharePoint:SaveButton runat="server"/>


<SharePoint:GoBackButton runat="server"/>


</Template_RightButtons>


</wssuc:ToolBar>


</td>


</tr>


</TABLE>


</SPAN>


<SharePoint:AttachmentUpload runat="server"/>


</Template>


</SharePoint:RenderingTemplate>


If we look more closely into the above code we see the outline structure of the list form which includes Toolbar, Save Button, Cancel Button Attachments etc.. So how are the actual list data columns being generated? They get generated on the fly based in the columns in the list by one more rendering template called "ListFieldIterator" with this code of line


<SharePoint:ListFieldIterator runat="server"/>


Now let's assume that you want to completely customize the list form with custom functionality. To do that we have to follow the below steps



  • Create new rendering template and please it in the ControlTemplates folder

  • Design a usercontrol as per the requirement and write object model code to achieve the required operation

  • Register this template in the SharePoint custom list definition schema

Create new rendering template and please it in the ControlTemplates folder:


Since we should not modify the out of box template we should create a new rendering template an user control (.ascx) file and place it in the control templates folder. Lets name the user control as "CaymanTicketsNewForm.ascx". Below is the code that should be present in the CaymanTicketsNewForm.ascx user control.


<%@ Control Language="C#" AutoEventWireup="false" %>


<%@ Assembly
Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
%>


<%@ Register TagPrefix="SharePoint" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" Namespace="Microsoft.SharePoint.WebControls"
%>


<%@ Register TagPrefix="SPHttpUtility" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
Namespace="Microsoft.SharePoint.Utilities" %>


<%@ Register TagPrefix="wssuc" TagName="ToolBar" Src="~/_controltemplates/ToolBar.ascx"


%>


<%@ Register TagPrefix="wssuc" TagName="ToolBarButton" Src="~/_controltemplates/ToolBarButton.ascx" %>


<%@ Register TagPrefix="CaymanTicketsNewForm" TagName="CaymanTicketsNewFormFields"
Src="~/_controltemplates/Cayman/CaymanTicketsNewForm.ascx"
%>


<sharepoint:renderingtemplate id="CaymanTicketsNewForm" runat="server">


<Template>


<CaymanTicketsNewForm:CaymanTicketsNewFormFields runat="server"/>


</Template>


</sharepoint:renderingtemplate>


Design a usercontrol as per the requirement and write object model code to achieve the required operation:


The above code references a file called "CaymanTicketsNewForm.ascx" which contains the actual design and implementation of the custom list form requirement.Shown below is the code sample which is present in the file "CaymanTicketsNewForm.ascx"


<%@ Control Language="C#" Inherits="Btmu.Cayman.TicketsNewForm,Btmu.Cayman,Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc3c6db4e0e41def" AutoEventWireup="true" EnableViewState="true" %>


<%@ Assembly
Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
%>


<%@ Register TagPrefix="SharePoint" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
Namespace="Microsoft.SharePoint.WebControls" %>


<style type="text/css">


.ms-input


{


width: 100px;


}


.tableStyle1


{


background-color: #FFF2E6;


border:solid 1px #CD5C5E;


}


.tableStyle2


{


background-color: #F2FFF2;


border:solid 1px #008080;


}


</style>


<asp:Panel ID="panelCustomerSelection" runat="server">
<div style="padding-left: 10px;">
<sharepoint:scriptlink id="ScriptLink1" language="javascript" name="Cayman/Cayman.js" runat="server" />
<table border="0" cellpadding="0" cellspacing="0" style="width: 100%;">
<tr>
<td style="font-size: 14px; font-weight: bold">
<asp:Label ID="labelTransactionMessage" Text="" runat="server" ForeColor="Red"
Visible="false"></asp:Label
>
</td>
</tr>
<tr>
<td style="font-size: 14px; font-weight: bold; padding-top: 20px;">
<asp:Label ID="labelTransactionDate" Text="" runat="server" Visible="false"></asp:Label>
</td>
</tr>
<tr>
<td style="font-size: 14px; font-weight: bold; padding-top: 30px;">
<asp:Label ID="labelNextTransaction" Text="Next Transaction:" runat="server"


Font-Underline="true" Visible="false"></asp:Label>
</td>
</tr>
<tr>
<td style="font-size: 14px; font-weight: bold; padding-top: 20px;">


Select Customer for the form
</td>
</tr>
<tr>
<td style="padding-top: 10px; font-size: 14px; font-weight: bold">
<asp:Label ID="labelPrintDate" Text="" runat="server"></asp:Label>
<div style='width: 100%; border-top-color: Gray; border-top-width: 2px; border-top-style: double;


height: 1px; margin-top: 7px'>
</div>
</td>
</tr>
<tr>
<td style="padding-top: 20px; font-size: 11px; font-weight: bold">


Customer Name<br />
<asp:DropDownList ID="dropDownListCustomerName" runat="server" AutoPostBack="false"
ToolTip="Customer Name">
</asp:DropDownList>
</td>
</tr>
<tr>
<td style="padding-top: 20px">
<asp:Button ID="buttonProcess" runat="server" Text="Process" OnClientClick="return processValidate()" />
</td>
</tr>
</table>
</div>


</asp:Panel>


<script type="text/javascript">


_spBodyOnLoadFunctionNames.push("fillDefaultValuesForTickets");


</script>


And the Code file…


using System;


using System.Collections.Generic;


using System.Text;


using System.Web;


using System.Web.UI;


using System.Web.UI.WebControls;


using System.Web.UI.HtmlControls;


using System.Data;


using Microsoft.SharePoint;


using Microsoft.SharePoint.Utilities;


using Microsoft.SharePoint.WebControls;


using Microsoft.Office.Server.Diagnostics;


namespace Btmu.Cayman


{
public class TicketsNewForm : UserControl


{


#region Variable Decleration
//Variables declaration which includes control names in the .ascx page template
protected Label labelPrintDate;
protected Label labelTransactionMessage;
protected Label labelTransactionDate;
protected Label labelNextTransaction;
protected DropDownList dropDownListCustomerName;
protected Button buttonProcess;
protected Panel panelCustomerSelection;
private Utility _oUtility;


#endregion


#region Constants
const string customersListURL = "/Lists/Customers";
const string portfolioListURL = "/Lists/Portfolio";
const string typeListURL = "/Lists/Type";
const string currencyListURL = "/Lists/Currency";
const string ticketsListURL = "/Lists/Tickets";
const string transactionNumberColumnName = "TransactionNumber";


#endregion
/// </summary>
/// <param name="e"></param>
protected override void OnLoad(EventArgs e)


{
try


{
if (!Page.IsPostBack)


{


labelPrintDate.Text = "Print Date: "+DateTime.Today.ToShortDateString();


createUtilityObject();
//Load Customer Names
DataTable dtCustomerNames = null;
string query = "<Where><Eq><FieldRef Name='ActiveProfile' /><Value Type='Choice'>Active</Value></Eq></Where><OrderBy><FieldRef Name='Title' Ascending='True' /></OrderBy>";
string viewFields = "ID,Title";
using (SPWeb web = SPControl.GetContextSite(Context).OpenWeb())


{


dtCustomerNames = _oUtility.getListItemsDataTable(web, customersListURL, query, 0, viewFields);


}
if (dtCustomerNames != null && dtCustomerNames.DefaultView.Count > 0)


{


dropDownListCustomerName.DataValueField = "ID";


dropDownListCustomerName.DataTextField = "Title";


dropDownListCustomerName.DataSource = dtCustomerNames;


dropDownListCustomerName.DataBind();


dropDownListCustomerName.Items.Insert(0, new
ListItem("-Select-", "0"));


}


panelTicketsForm.Visible = false;


}


}
catch (System.Exception ex)


{
PortalLog.LogString(ex.StackTrace);
throw new SPException(ex.Message);


}


}


#region Event Handlers
/// <summary>
/// lists all the eventhandlesr
/// </summary>
/// <param name="e"></param>
protected override void OnInit(EventArgs e)


{


buttonProcess.Click += new EventHandler(buttonProcess_Click);


}
protected void buttonProcess_Click(object sender, EventArgs e)


{
try


{


}
catch (System.Exception ex)


{
PortalLog.LogString(ex.StackTrace);
throw new SPException(ex.Message);


}


}


#endregion


#region Methods
private void createUtilityObject()


{
if (_oUtility == null)


{


_oUtility = new Utility();


}


}


#endregion


}


}


Register this template in the SharePoint custom list definition schema:


Now once we complete our development we need to register this custom rendering template which we have developed in our custom list definition as follows


</FieldRefs>


<XmlDocuments>


<XmlDocument
NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms"
>


<FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms"&gt;


<Display>CaymanTicketsDisplayForm</Display>
<Edit>CaymanTicketsEditForm</Edit>


<New>CaymanTicketsNewForm</New>


</FormTemplates>


</XmlDocument>


</XmlDocuments>


</ContentType>


So the final folder structure looks like this…




Override SharePoint Save Button to Provide Custom Functionality

Let's assume a business scenario that we want to redirect the user to "DisplayForm.aspx" instead of "allItems.aspx" once the user clicks on the SharePoint "NewForm.aspx" save button or if we want to kickoff a Nintex custom workflow. This can as well with the help of the SharePoint Designer, but the client, I work for does not want to use SharePoint Designer. In the case we had to go for SharePoint object model coding.


To override the SharePoint Save button follow the below steps:



  • Create new rendering template and please it in the ControlTemplates folder

  • Override the "SaveButton" Class which is present in Microsoft.SharePoint.WebControls

  • Register this template in the SharePoint custom list definition schema

Create new rendering template and please it in the ControlTemplates folder:


All the SharePoint list forms (NewForm.aspx, EditForm.aspx, DisplayForm.aspx) uses rendering template called "ListForm" which is present in the file "DefaultTemplates.ascx" in the following path.


C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\TEMPLATE\CONTROLTEMPLATES\DefaultTemplates.ascx


This is the code which I am referring to


<SharePoint:RenderingTemplate ID="ListForm" runat="server">


<Template>


<SPAN id='part1'>


<SharePoint:InformationBar runat="server"/>


<wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbltop" RightButtonSeparator="&nbsp;" runat="server">


<Template_RightButtons>


<SharePoint:NextPageButton runat="server"/>


<SharePoint:SaveButton runat="server"/>


<SharePoint:GoBackButton runat="server"/>


</Template_RightButtons>


</wssuc:ToolBar>


<SharePoint:FormToolBar runat="server"/>


<TABLE class="ms-formtable" style="margin-top: 8px;" border=0 cellpadding=0 cellspacing=0
width
=100%>


<SharePoint:ChangeContentType runat="server"/>


<SharePoint:FolderFormFields runat="server"/>


<SharePoint:ListFieldIterator runat="server"/>


<SharePoint:ApprovalStatus runat="server"/>


<SharePoint:FormComponent TemplateName="AttachmentRows" runat="server"/>


</TABLE>


<table cellpadding=0 cellspacing=0 width=100%>


<tr>


<td class="ms-formline"><IMG SRC="/_layouts/images/blank.gif" width=1 height=1 alt="">


</td>


</tr>


</table>


<TABLE cellpadding=0 cellspacing=0 width=100% style="padding-top: 7px">


<tr>


<td width=100%>


<SharePoint:ItemHiddenVersion runat="server"/>


<SharePoint:ParentInformationField runat="server"/>


<SharePoint:InitContentType runat="server"/>


<wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbl" RightButtonSeparator="&nbsp;"
runat
="server">


<Template_Buttons>


<SharePoint:CreatedModifiedInfo runat="server"/>


</Template_Buttons>


<Template_RightButtons>


<SharePoint:SaveButton runat="server"/>


<SharePoint:GoBackButton runat="server"/>


</Template_RightButtons>


</wssuc:ToolBar>


</td>


</tr>


</TABLE>


</SPAN>


<SharePoint:AttachmentUpload runat="server"/>


</Template>


</SharePoint:RenderingTemplate>


If we look more closely into the above code we see the outline structure of the list form which includes Toolbar, Save Button, Cancel Button Attachments etc.. So how are the actual list data columns being generated? They get generated on the fly based in the columns in the list by one more rendering template called "ListFieldIterator" with this code of line


<SharePoint:ListFieldIterator runat="server"/>


Since we should not modify the out of box template we should create a new rendering template an user control (.ascx) file and place it in the control templates folder. Let's name the user control as "BtmuMTDFaxLogNewFormTemplate.ascx". Below is the code that should be present in the BtmuMTDFaxLogNewFormTemplate.ascx user control.


<%@ Control Language="C#" AutoEventWireup="false" %>


<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>


<%@ Register TagPrefix="SharePoint" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" Namespace="Microsoft.SharePoint.WebControls"
%>


<%@ Register TagPrefix="SPHttpUtility" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" Namespace="Microsoft.SharePoint.Utilities"
%>


<%@ Register TagPrefix="wssuc" TagName="ToolBar" Src="~/_controltemplates/ToolBar.ascx"
%>


<%@ Register TagPrefix="wssuc" TagName="ToolBarButton" Src="~/_controltemplates/ToolBarButton.ascx" %>


<%@ Register TagPrefix="MTDFaxLogSaveButton" Namespace="Btmu.MTDFaxLog"
Assembly="Btmu.MTDFaxLog, Version=1.0.0.0, Culture=neutral, PublicKeyToken=53432c81e5329871"%>


<sharepoint:renderingtemplate id="MTDFaxLogNewForm" runat="server">


<Template>


<SPAN id='part1'> <SharePoint:InformationBar runat="server"/>


<wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbltop" RightButtonSeparator="&nbsp;"
runat
="server">


<Template_RightButtons>


<SharePoint:NextPageButton runat="server"/> <MTDFaxLogSaveButton:FaxLogSaveButton runat="server" />


<SharePoint:GoBackButton runat="server"/>


</Template_RightButtons>


</wssuc:ToolBar>


<SharePoint:FormToolBar runat="server"/>


<TABLE class="ms-formtable" style="margin-top: 8px;" border=0 cellpadding=0 cellspacing=0 width=100%>


<SharePoint:ChangeContentType runat="server"/>


<SharePoint:FolderFormFields runat="server"/>


<SharePoint:ListFieldIterator runat="server"/>


<SharePoint:ApprovalStatus runat="server"/>


<SharePoint:FormComponent TemplateName="AttachmentRows" runat="server"/>


</TABLE>


<table cellpadding=0 cellspacing=0 width=100%>


<tr>


<td class="ms-formline"><IMG SRC="/_layouts/images/blank.gif" width=1 height=1
alt="">


</td>


</tr>


</table>


<TABLE cellpadding=0 cellspacing=0 width=100% style="padding-top: 7px">


<tr>


<td width=100%>


<SharePoint:ItemHiddenVersion runat="server"/>


<SharePoint:ParentInformationField runat="server"/>


<SharePoint:InitContentType runat="server"/>


<wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbl" RightButtonSeparator="&nbsp;"
runat
="server">


<Template_Buttons>


<SharePoint:CreatedModifiedInfo runat="server"/>


</Template_Buttons>


<Template_RightButtons>


<MTDFaxLogSaveButton:FaxLogSaveButton runat="server" />


<SharePoint:GoBackButton runat="server"/>


</Template_RightButtons>


</wssuc:ToolBar>


</td>


</tr>


</TABLE>


</SPAN>


<SharePoint:AttachmentUpload runat="server"/>


</Template>


</sharepoint:renderingtemplate>


If we observed the above highlited code we removed the actual SharePoint Sabe Button and replaced it with our own webcontrol which we have developed. Below shows the Save Button overriding code


Override the "SaveButton" Class which is present in Microsoft.SharePoint.WebControls:


using System;


using System.Collections.Generic;


using System.Linq;


using System.Text;


using Microsoft.SharePoint;


using Microsoft.SharePoint.WebControls;


namespace Btmu.MTDFaxLog


{
public class FaxLogSaveButton : SaveButton


{
protected override bool SaveItem()


{
bool success = base.SaveItem();
//Dont forget to remove the source querystring parameter from the NewForm.aspx URL, otherwise this setting will not work.


RedirectUrl = String.Concat(List.ParentWeb.ServerRelativeUrl, "/", List.Forms[PAGETYPE.PAGE_DISPLAYFORM].Url, @"?ID=", ListItem.ID, @"&Source=", ListItem.ParentList.DefaultViewUrl);
return success;


}


}


}


Register this template in the SharePoint custom list definition schema:


Now once we complete our development we need to register this custom rendering template which we have developed in our custom list definition as follows


<XmlDocuments>


<XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">


<FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms"&gt;


<New>MTDFaxLogNewForm</New>


</FormTemplates>


</XmlDocument>


</XmlDocuments>


So the final folder structure looks like this…