Enhanced Lookup Field – Part 2

This is part two of the post showing how to create a custom lookup field for SharePoint.

Other pages in this post are:

This part will implement the field editor class.

I won’t show every line of code required for this class, I’ll just be covering the important areas. A Visual Studio project will be available for download later.

We’ll start by creating a new class:

public class ExtendedLookupFieldEditor : UserControl, IFieldEditor
{
}

The important part here is to ensure the UserControl inherits from IFieldEditor.

The editor will allow users to select a source Web, list, the display field to use and specify if the field should allow multiple items. In the previous post, we added the field type XML, which referenced an ascx file (FieldEditorUserControl), this class implements that.

Now we’ll implement the IFieldEditor interface members:

public void InitializeWithField(SPField field)
{
    _extendedLookupFieldType = field as ExtendedLookupFieldType;
 
    if (Page.IsPostBack)
        return;
 
    if (_extendedLookupFieldType != null)
    {
        chkAllowMultipleItems.Checked = _extendedLookupFieldType.AllowMultipleValues;
 
        // Only allow editing for new columns, unless the list\columns defined no longer exist
        if (_extendedLookupFieldType.WebSourceId != Guid.Empty)
        {
            using (SPWeb web = SPContext.Current.Site.OpenWeb(_extendedLookupFieldType.WebSourceId))
            {
                SPList lookupList = (from list in web.Lists.Cast<SPList>()
                                     where list.ID.Equals(_extendedLookupFieldType.LookupListId)
                                     select list).SingleOrDefault();
 
                if (lookupList != null)
                {
                    SetAsReadOnly(lblSelectedLookupList, lookupList.Title, ddlLookupList, validator_ddlLookupList);
 
                    if (lookupList.Fields.Contains(_extendedLookupFieldType.DisplayColumnId))
                    {
                        SPField displayColumn = lookupList.Fields[_extendedLookupFieldType.DisplayColumnId];
                        SetAsReadOnly(lblSelectedDisplayColumn, displayColumn.Title, ddlDisplayColumn, validator_ddlDisplayColumn);
                    }
                    else
                    {
                        SetAsNotFoundError(lblSelectedDisplayColumn, "Previously configured display field was not found, please update.", ddlDisplayColumn);
                        BindDisplayColumns(lookupList);
                    }
                }
                else
                {
                    SetAsNotFoundError(lblSelectedLookupList, "Previously configured lookup list was not found, please update.", ddlLookupList);
                    BindLookupList();
                }
            }
        }
    }
}
 
public void OnSaveChange(SPField field, bool bNewField)
{
    ExtendedLookupFieldType extendedLookupFieldType = field as ExtendedLookupFieldType;
 
    if (DropDownListHasSelectedItem(ddlWebForList))
        extendedLookupFieldType.WebSourceId = new Guid(ddlWebForList.SelectedItem.Value);
    if (DropDownListHasSelectedItem(ddlLookupList))
        extendedLookupFieldType.LookupListId = new Guid(ddlLookupList.SelectedItem.Value);
    if (DropDownListHasSelectedItem(ddlDisplayColumn))
        extendedLookupFieldType.DisplayColumnId = new Guid(ddlDisplayColumn.SelectedItem.Value);
 
    extendedLookupFieldType.AllowMultipleValues = chkAllowMultipleItems.Checked;
}
 
public bool DisplayAsNewSection
{
    get
    {
        return true;
    }
}

The InitializeWithField method casts the field to the custom lookup field type and updates the drop down list controls (for specifying the web, list and display column to use) to be read-only when the field is being edited and the source list\field exist. For new fields (the field parameter will be null) and editing when the list or display field is missing, we allow editing.

The OnSaveChange method also casts the field to the custom lookup field type and then sets the appropriate properties with the values specified by the user creating the new field.

The DropDownListHasSelectedItem method used by the OnSaveChange method is very simple:

private bool DropDownListHasSelectedItem(DropDownList dropDownList)
{
    return dropDownList.SelectedItem != null && !string.IsNullOrEmpty(dropDownList.SelectedItem.Value);
}

To get the list of webs, the following is implemented:

 
protected override void OnInit(EventArgs e)
{
    base.OnInit(e);
 
    Dictionary<Guid, string> webs = GetListOfWebsFromWeb(SPContext.Current.Web);
    BindList(ddlWebForList, webs);
}
 
private void BindList(DropDownList dropDownList, Dictionary<Guid, string> dataSource)
{
    dropDownList.DataSource = dataSource;
    dropDownList.DataTextField = TEXT_FIELD;
    dropDownList.DataValueField = VALUE_FIELD;
    dropDownList.DataBind();
}
 
public Dictionary<Guid, string> GetListOfWebsFromWeb(SPWeb web)
{
    Dictionary<Guid, string> webs = new Dictionary<Guid, string>();
 
    if (web == null)
        return webs;
 
    SPWeb workingWeb = web;
    while (!workingWeb.IsRootWeb)
    {
        webs.Add(workingWeb.ID, workingWeb.Title);
        workingWeb = workingWeb.ParentWeb;
    }
    webs.Add(workingWeb.ID, workingWeb.Title);
 
    return webs;
}

Where every web up to and including the current web is added to the drop down list.

To get the lists for a selected web, the following is added:

 
protected void ddlWebForListOnSelectedIndexChanged(object sender, EventArgs eventArgs)
{
    DropDownList dropDownList = sender as DropDownList;
 
    if (dropDownList.SelectedItem != null && dropDownList.SelectedItem.Value.IsGuid())
    {
        Dictionary<Guid, string> lists = GetListsForWeb(SPContext.Current.Web, SPBaseType.GenericList);
        BindList(ddlLookupList, lists);
    }
}
 
public Dictionary<Guid, string> GetListsForWeb(SPWeb web, SPBaseType listType)
{
    Dictionary<Guid, string> lists = new Dictionary<Guid, string>();
 
    if (web == null)
        return lists;
 
    lists = (from list in web.Lists.Cast<SPList>()
             where list.BaseType == listType
             orderby list.Title
             select list).ToDictionary(item => item.ID, item => item.Title);
 
    return lists;
}

Finally, to retrieve the available display fields:

 
protected void ddlLookupListOnSelectedIndexChanged(object sender, EventArgs eventArgs)
{
    DropDownList dropDownList = sender as DropDownList;
 
    if (dropDownList.SelectedItem != null && dropDownList.SelectedItem.Value.IsGuid())
    {
        Guid webId = (DropDownListHasSelectedItem(ddlWebForList)) ? new Guid(ddlWebForList.SelectedItem.Value) : _extendedLookupFieldType.WebSourceId;
        Guid listId = new Guid(dropDownList.SelectedItem.Value);
 
        using (SPWeb web = SPContext.Current.Site.OpenWeb(webId))
        {
            SPList list = web.Lists.GetList(listId, false);
 
            Dictionary<Guid, string> fields = GetFieldsForList(list, SPFieldType.Text);
            BindList(ddlDisplayColumn, fields);
        }
    }
}
 
public Dictionary<Guid, string> GetFieldsForList(SPList list, SPFieldType fieldType)
{
    Dictionary<Guid, string> columns = new Dictionary<Guid, string>();
 
    if (list == null)
        return columns;
 
    columns = (from field in list.Fields.Cast<SPField>()
               where field.Type == fieldType
               orderby field.Title
               select field).ToDictionary(item => item.Id, item => item.Title);
 
    return columns;
}

Part three of this post will continue with the creation of the custom lookup field. Click here to view it.

This entry was posted in SharePoint and tagged , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

Solve the maths problem shown below before posting: *