Introduction
Parent-child relationship pattern is very common in the real world. In SharePoint, it is represented with two SharePoint lists, linked with a Lookup Field contained in the child list. That way, the Parent-Child relationship is established between the Lists.
We need a way to present child items in Parent List Forms (DisplayForm.aspx, EditForm.aspx, NewForm.aspx). For that purpose, I created a SharePoint Custom Field Type: "ParentChildrenField
".
Description
To make a custom tab-dialog navigation instead of the standard SharePoint site top navigation, I created two SharePoint lists: Tabs and Sub-Tabs. I created the site column "Parent Tab", type Lookup on the table Tabs, showing the column Title. That column is added to the "Sub-Tabs" list.
Create "My Child Items" Field
To present child items (sub-tabs) in the Tab Forms (Display, Edit, and New), I created a new SharePoint Site Column "My Child Items", type of the previously defined "ParentChildrenField
". That column is added to the parent list "Tabs". Generally, it is much better to add columns to the list Content Types instead of directly to the List.
Source Code
public class ParentChildrenFieldControl : TextField
{
int NumOfChildren = 0;
LiteralControl literalCtrl;
SPList childList = null;
string listID = SPContext.Current.List.ID.ToString();
HyperLink linkAddNew = new HyperLink();
protected override void CreateChildControls()
{
base.CreateChildControls();
if (this.ControlMode == SPControlMode.Display ||
this.ControlMode == SPControlMode.Edit ||
this.ControlMode == SPControlMode.New)
{
literalCtrl = new LiteralControl();
base.Controls.Add(literalCtrl);
base.Controls.Add(linkAddNew);
}
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
string childListname = this.Field.GetCustomProperty("ChildListName").ToString();
childList = SPContext.Current.Web.Lists[childListname];
StringBuilder sb = new StringBuilder();
SPQuery query = new SPQuery(childList.DefaultView);
query.Query = string.Format(@"
<Where>
<Eq>
<FieldRef Name='{0}' LookupId='true' />
<Value Type='Integer'>{1}</Value>
</Eq>
</Where>", LookupField.InternalName, this.Item.ID.ToString());
sb.Append(childList.RenderAsHtml(query));
literalCtrl.Text = sb.ToString();
linkAddNew.Text = "Add New";
string navigateUrl = string.Format("/NewForm.aspx?{0}={1}&Source={2}",
SPEncode.UrlEncode(LookupField.Title),
SPContext.Current.ListItem.ID.ToString(),
SPEncode.UrlEncode(this.Page.Request.Url.ToString()));
linkAddNew.NavigateUrl = childList.RootFolder.ServerRelativeUrl + navigateUrl;
}
SPField _LookupField = null;
SPField LookupField
{
get {
if (_LookupField == null)
{
foreach (SPField field in childList.Fields)
{
if (field is SPFieldLookup &&
((SPFieldLookup)field).LookupList == listID)
{
_LookupField = field;
break;
}
}
}
return _LookupField;
}
}
protected override void Render(HtmlTextWriter output)
{
literalCtrl.RenderControl(output);
if (this.ControlMode == SPControlMode.Display ||
this.ControlMode == SPControlMode.Edit)
linkAddNew.RenderControl(output);
}
public override void UpdateFieldValueInItem()
{
this.EnsureChildControls();
this.Value = NumOfChildren;
this.ItemFieldValue = this.Value;
}
}
Add a New Child Item
When the user adds new child items, we want ot automatically select the item of the "Parent Tab" dropdown list in the NewForm.aspx form. That's why we add the Lookup Field Name and Value in the request query string: "NewForm.aspx?Parent%20Tab=1&Source=..." .
Put the following JavaScript block to the Child List NewForm.aspx. The best place is in "PlaceHolderBodyAreaClass
".
<script type="text/javascript">
var qs = location.search.substring(1, location.search.length);
var nameVal = qs.split("&")[0].split("=");
SetLookupFieldValue(unescape(nameVal[0]), nameVal[1]);
function SetLookupFieldValue(fieldName, val) {
var theSelect = getTagFromIdentifierAndTitle("select",
"Lookup", fieldName);
if (theSelect != null) {
theSelect.value = val;
return;
}
var theInput = getTagFromIdentifierAndTitle("input",
"", fieldName);
theInput.value = val;
}
function getTagFromIdentifierAndTitle(tagName, identifier, title) {
var len = identifier.length;
var tags = document.getElementsByTagName(tagName);
for (var i = 0; i < tags.length; i++) {
var tempString = tags[i].id;
if (tags[i].title == title && (identifier == "" ||
tempString.indexOf(identifier) == tempString.length - len))
return tags[i];
}
return null;
}
</script>
Installation
The Solution Package "ParentChildListRelationship.wsp" was developed using Visual Studio Extensions VSeWSS v1.3. Open "Setup.bat" and set your Site and Web URLs:
set DefaultWebUrl=http://YourWeb
set DefaultSiteUrl=http://YourSite
To install, run setup.bat. To uninstall, run setup.bat -u.