Pre-Requisites
The pre-requisites for the AJAX dependent custom fields
are,
1. ASP.NET 2.0 AJAX Extensions
1.0.
a. This component to be installed
and configured as per article Installing ASP.NET
2.0 AJAX Extensions 1.0 in Windows SharePoint Services Version 3.0.
b. This component has to be
configured for all the Virtual directories which host the custom fields.
AJAX Dependent Custom Fields
Moving forward, we will see how a custom field can be
developed using ASP.Net Ajax framework. As I said earlier, I will use the
dependant dropdown as an example and will help you implementing it as custom
field for SharePoint.
Components
The component that has the dependent custom Fields
are,
1. Parent Custom Field. (e.g.
Category)
2. Child Custom Field. (e.g.
Name)
The following sections will take the Category and Name
as an example to create the custom field.
User Control (ASCX File)
This file holds the rendering template for the custom
field. For simplicity purposes, I’ve added both of the rendering templates into
single file (EnigmaFieldControl.ascx). To deploy this file, please refer to the
Deployment Section of this article.
1. Category User Control
a) Following is the
Rendering template for the Category Field Control which will be used in
displaying the field control for Category Field Control.
<SharePoint:RenderingTemplate ID="CategoryFieldControl" runat="server">
<Template>
<asp:DropDownList ID="ddlCategory" runat="server" />
</Template>
</SharePoint:RenderingTemplate>
b) Only one DropDownList is
added which will be the parent control to trigger the Asynchronous postback.
2. Name User Control
a) Following is the Rendering
template for the Name Field Control which will be used in displaying the field
control for Name Field Control.
<SharePoint:RenderingTemplate
ID="NameFieldControl" runat="server">
<Template>
<AJAX:ScriptManager ID="scriptManager" runat="server"/>
<AJAX:UpdatePanel ID="updatePanel" UpdateMode="Conditional" runat="server">
<ContentTemplate>
<asp:DropDownList ID="ddlNames" runat="server">
<asp:ListItem>--Select--</asp:ListItem>
</asp:DropDownList>
<AJAX:UpdateProgress AssociatedUpdatePanelID="updatePanel" runat="server" DynamicLayout="true" ID="updateProgress">
<ProgressTemplate>
<div id="divTag" runat="server">Loading...</div>
</ProgressTemplate>
</AJAX:UpdateProgress>
</ContentTemplate>
</AJAX:UpdatePanel>
</Template>
</SharePoint:RenderingTemplate>
b) It contains the ScriptManager,
UpdatePanel, and UpdateProgess inside which all the controls are embedded.
c) The child DropDownList
ddlNames is embedded inside the ContentTemplate of the UpdatePanel, so that it
is enabled for Asynchronous updates (partial page loading).
d) Also, reference to the
System.Web.Extentions (required for ScriptManager, UpdateProgress and
UpdatePanel) is done on the ASCX control.
Category Custom Field
1. Custom Field
Type
a) The “CategoryFieldType”
class here will have to inherit from the “SPFieldText”
class since we are going to store the value as a single line of text here.
b) It has a standard layout of
coding with two constructors which are mandatory.
//These constructors are
mandatory.
public CategoryFieldType(SPFieldCollection fields, string fieldName)
:
base(fields, fieldName)
{
}
public CategoryFieldType(Microsoft.SharePoint.SPFieldCollection fields, string typeName, string displayName)
:
base(fields, typeName, displayName)
{
}
c) This function is used to hold
any validations, massaging values before it is stored in the list/document
library.
//GetFieldValue
public override
object GetFieldValue(string value)
{
if (String.IsNullOrEmpty(value))
return null;
//Any other code before
saving the value for this field
return value;
}
d) FieldRenderingControl property
takes care of actual loading of the field control when the list edit page or
Add new page is loaded.
public override
BaseFieldControl FieldRenderingControl
{
get
{
//Render the BaseField Control
BaseFieldControl objCategoryFieldControl= new CategoryFieldControl();
objCategoryFieldControl.FieldName
= InternalName;
return objCategoryFieldControl;
}
}
2. User Control Code File
a) DefaultTemplateName property
tells us which Rendering Template should be loaded when the control gets loaded.
This Rendering template is available in the ASCX file which i have created
earlier.
//Default Template
Name
protected override string
DefaultTemplateName
{
get
{
//Give the rendering
template id from the .ascx file
return
"CategoryFieldControl";
}
}
b) The Value Property handles
setting and getting the value to and from the control on Edit and Add Pages of
List/Document Library respectively.
//To
Get and set the items in the ListBox
public override object
Value
{
//To
Get the Value from the Control and Store in ListItem
get
{
EnsureChildControls();
ddlCategory =
(DropDownList)TemplateContainer.FindControl("ddlCategory");
return
ddlCategory.SelectedValue;
}
//To
Set the value to the control after getting the value from ListItem
set
{
EnsureChildControls();
ddlCategory =
(DropDownList)TemplateContainer.FindControl("ddlCategory");
ddlCategory.SelectedValue
= (string)this.Value;
}
}
c) In CreateChildControls
function,
1. Call the EnsurePanelFix
function (justification given later in this article).
2. Get the references to the
DropDownList, one using TemplateContainer.FindControl (as the ddlCategory is
within the same control) and the other using FindControlRecursive (justification
given later in this article)
3. Add EventHandler and values for
the ddlCategory DropDownList.
4. Get the ScriptManager reference
and register ddlCategory as an AsnychronousPostControl.
protected override void
CreateChildControls()
{
try
{
if
(Field == null)
return;
base.CreateChildControls();
//To
make sure Asynchronous Postback works with out any issues
EnsurePanelFix();
//If
the user control is in display mode then return
if
(ControlMode == Microsoft.SharePoint.WebControls.SPControlMode.Display)
return;
//Use
TemplateContainer.FindControl as its inside same control
ddlCategory =
(DropDownList)TemplateContainer.FindControl("ddlCategory");
//Use
FindControlRecursive as its outside the control
ddlNames =
(DropDownList)FindControlRecursive(this.Page, "ddlNames");
//Add
the Selected Index Changed Event Handler, and some values to Category
Control
ddlCategory.AutoPostBack =
true;
ddlCategory.SelectedIndexChanged
+= new
EventHandler(ddlCategory_SelectedIndexChanged);
ddlCategory.Items.Add("Fruits");
ddlCategory.Items.Add("Vegetables");
ddlCategory.Items.Add("Trees");
//Get
existing Script Mananger
ScriptManager sm =
ScriptManager.GetCurrent(this.Page);
//If
there is no script Manager, add one
if (sm
== null)
{
//create new ScriptManager
and EnablePartialRendering
sm =
new ScriptManager();
sm.EnablePartialRendering =
true;
sm.EnableScriptLocalization
= true;
//add
the ScriptManager as the first control in the Page.Form
this.Controls.AddAt(0,
sm);
}
//Register the Asnychronous
Post back Control as ddlCategory with script Manager
sm.RegisterAsyncPostBackControl(ddlCategory);
}
catch
(Exception ex)
{
throw
new Exception(ex.Message);
}
}
d) EnsurePanelFix method is used
to resolve an issue in the AJAX Framework and for Asynchronous post back to
work.
e) FindControlRecursive method is
used to find a control which is outside the current rendering template.
f) The SelectedIndexChanged
eventhandler for the ddlCategory gets the reference of the UpdatePanel and the
ddlNames DropDownList, updates the values in the ddlNames according to the value
selected in the ddlCategory DropDownList and calls the update method of the
UpdatePanel so that the controls inside it are updated.
//ddlCategory Selected
Index Changed
void
ddlCategory_SelectedIndexChanged(object sender, EventArgs e)
{
//get references to the
controls
updatePanel =
(UpdatePanel)FindControlRecursive(this.Page, "updatePanel");
ddlCategory =
(DropDownList)TemplateContainer.FindControl("ddlCategory");
ddlNames =
(DropDownList)FindControlRecursive(this.Page, "ddlNames");
ddlNames.Items.Clear();
//Load the Names
dropdownlist based on the Category
switch
(ddlCategory.SelectedValue)
{
case
"Fruits":
ddlNames.Items.Add("Peach");
ddlNames.Items.Add("Strawberry");
ddlNames.Items.Add("Blueberry");
break;
case
"Vegetables":
ddlNames.Items.Add("Carrot");
ddlNames.Items.Add("Radish");
ddlNames.Items.Add("Beetroot");
break;
case
"Trees":
ddlNames.Items.Add("Banyan");
ddlNames.Items.Add("Neem");
break;
default:
break;
}
//refresh the update
panel
updatePanel.Update();
}
|