{"id":1214,"date":"2012-10-20T18:37:58","date_gmt":"2012-10-20T17:37:58","guid":{"rendered":"http:\/\/www.stuartroberts.net\/?p=1214"},"modified":"2012-12-10T21:13:31","modified_gmt":"2012-12-10T21:13:31","slug":"enhanced-lookup-field-part-2","status":"publish","type":"post","link":"http:\/\/www.stuartroberts.net\/index.php\/2012\/10\/20\/enhanced-lookup-field-part-2\/","title":{"rendered":"Enhanced Lookup Field &#8211; Part 2"},"content":{"rendered":"<p>This is part two of the post showing how to create a custom lookup field for SharePoint.<\/p>\n<p>Other pages in this post are:<\/p>\n<ul>\n<li>Part 1 &#8211; <a href=\"http:\/\/www.stuartroberts.net\/index.php\/2012\/10\/03\/enhanced-lookup-field-part-1\/\" title=\"Enhanced Lookup Field - Part 1\">Creating a custom lookup field type<\/a><\/li>\n<li>Part 3 &#8211; <a href=\"http:\/\/www.stuartroberts.net\/index.php\/2012\/11\/13\/enhanced-lookup-field-part-3\/\" title=\"Enhanced Lookup Field - Part 3\">Add field control class<\/a><\/li>\n<li>Part 4 &#8211; <a href=\"http:\/\/www.stuartroberts.net\/index.php\/2012\/12\/10\/enhanced-lookup-field-part-4\/\" title=\"Enhanced Lookup Field - Part 4\">Implement custom related field for controlling display within views<\/a><\/li>\n<\/ul>\n<p>This part will implement the field editor class.<\/p>\n<p><!--more--><\/p>\n<p>I won&#8217;t show every line of code required for this class, I&#8217;ll just be covering the important areas.  A Visual Studio project will be available for download later.<\/p>\n<p>We&#8217;ll start by creating a new class:<\/p>\n<pre lang=\"csharp\">\r\npublic class ExtendedLookupFieldEditor : UserControl, IFieldEditor\r\n{\r\n}\r\n<\/pre>\n<p>The important part here is to ensure the UserControl inherits from <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.sharepoint.webcontrols.ifieldeditor.aspx\" title=\"IFieldEditor Interface\" target=\"_blank\">IFieldEditor<\/a>.<\/p>\n<p>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.<\/p>\n<p>Now we&#8217;ll implement the IFieldEditor interface members:<\/p>\n<pre lang=\"csharp\">\r\npublic void InitializeWithField(SPField field)\r\n{\r\n    _extendedLookupFieldType = field as ExtendedLookupFieldType;\r\n\r\n    if (Page.IsPostBack)\r\n        return;\r\n\r\n    if (_extendedLookupFieldType != null)\r\n    {\r\n        chkAllowMultipleItems.Checked = _extendedLookupFieldType.AllowMultipleValues;\r\n\r\n        \/\/ Only allow editing for new columns, unless the list\\columns defined no longer exist\r\n        if (_extendedLookupFieldType.WebSourceId != Guid.Empty)\r\n        {\r\n            using (SPWeb web = SPContext.Current.Site.OpenWeb(_extendedLookupFieldType.WebSourceId))\r\n            {\r\n                SPList lookupList = (from list in web.Lists.Cast<SPList>()\r\n                                     where list.ID.Equals(_extendedLookupFieldType.LookupListId)\r\n                                     select list).SingleOrDefault();\r\n\r\n                if (lookupList != null)\r\n                {\r\n                    SetAsReadOnly(lblSelectedLookupList, lookupList.Title, ddlLookupList, validator_ddlLookupList);\r\n\r\n                    if (lookupList.Fields.Contains(_extendedLookupFieldType.DisplayColumnId))\r\n                    {\r\n                        SPField displayColumn = lookupList.Fields[_extendedLookupFieldType.DisplayColumnId];\r\n                        SetAsReadOnly(lblSelectedDisplayColumn, displayColumn.Title, ddlDisplayColumn, validator_ddlDisplayColumn);\r\n                    }\r\n                    else\r\n                    {\r\n                        SetAsNotFoundError(lblSelectedDisplayColumn, \"Previously configured display field was not found, please update.\", ddlDisplayColumn);\r\n                        BindDisplayColumns(lookupList);\r\n                    }\r\n                }\r\n                else\r\n                {\r\n                    SetAsNotFoundError(lblSelectedLookupList, \"Previously configured lookup list was not found, please update.\", ddlLookupList);\r\n                    BindLookupList();\r\n                }\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\npublic void OnSaveChange(SPField field, bool bNewField)\r\n{\r\n    ExtendedLookupFieldType extendedLookupFieldType = field as ExtendedLookupFieldType;\r\n\r\n    if (DropDownListHasSelectedItem(ddlWebForList))\r\n        extendedLookupFieldType.WebSourceId = new Guid(ddlWebForList.SelectedItem.Value);\r\n    if (DropDownListHasSelectedItem(ddlLookupList))\r\n        extendedLookupFieldType.LookupListId = new Guid(ddlLookupList.SelectedItem.Value);\r\n    if (DropDownListHasSelectedItem(ddlDisplayColumn))\r\n        extendedLookupFieldType.DisplayColumnId = new Guid(ddlDisplayColumn.SelectedItem.Value);\r\n\r\n    extendedLookupFieldType.AllowMultipleValues = chkAllowMultipleItems.Checked;\r\n}\r\n\r\npublic bool DisplayAsNewSection\r\n{\r\n    get\r\n    {\r\n        return true;\r\n    }\r\n}\r\n<\/pre>\n<p>The <em>InitializeWithField<\/em> 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.<\/p>\n<p>The <em>OnSaveChange<\/em> 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.<\/p>\n<p>The DropDownListHasSelectedItem method used by the OnSaveChange method is very simple:<\/p>\n<pre lang=\"csharp\">\r\nprivate bool DropDownListHasSelectedItem(DropDownList dropDownList)\r\n{\r\n    return dropDownList.SelectedItem != null && !string.IsNullOrEmpty(dropDownList.SelectedItem.Value);\r\n}\r\n<\/pre>\n<p>To get the list of webs, the following is implemented:<\/p>\n<pre lang=\"csharp\">\r\n\r\nprotected override void OnInit(EventArgs e)\r\n{\r\n    base.OnInit(e);\r\n\r\n    Dictionary<Guid, string> webs = GetListOfWebsFromWeb(SPContext.Current.Web);\r\n    BindList(ddlWebForList, webs);\r\n}\r\n\r\nprivate void BindList(DropDownList dropDownList, Dictionary<Guid, string> dataSource)\r\n{\r\n    dropDownList.DataSource = dataSource;\r\n    dropDownList.DataTextField = TEXT_FIELD;\r\n    dropDownList.DataValueField = VALUE_FIELD;\r\n    dropDownList.DataBind();\r\n}\r\n\r\npublic Dictionary<Guid, string> GetListOfWebsFromWeb(SPWeb web)\r\n{\r\n    Dictionary<Guid, string> webs = new Dictionary<Guid, string>();\r\n\r\n    if (web == null)\r\n        return webs;\r\n\r\n    SPWeb workingWeb = web;\r\n    while (!workingWeb.IsRootWeb)\r\n    {\r\n        webs.Add(workingWeb.ID, workingWeb.Title);\r\n        workingWeb = workingWeb.ParentWeb;\r\n    }\r\n    webs.Add(workingWeb.ID, workingWeb.Title);\r\n\r\n    return webs;\r\n}\r\n<\/pre>\n<p>Where every web up to and including the current web is added to the drop down list.<\/p>\n<p>To get the lists for a selected web, the following is added:<\/p>\n<pre lang=\"csharp\">\r\n\r\nprotected void ddlWebForListOnSelectedIndexChanged(object sender, EventArgs eventArgs)\r\n{\r\n    DropDownList dropDownList = sender as DropDownList;\r\n\r\n    if (dropDownList.SelectedItem != null && dropDownList.SelectedItem.Value.IsGuid())\r\n    {\r\n        Dictionary<Guid, string> lists = GetListsForWeb(SPContext.Current.Web, SPBaseType.GenericList);\r\n        BindList(ddlLookupList, lists);\r\n    }\r\n}\r\n\r\npublic Dictionary<Guid, string> GetListsForWeb(SPWeb web, SPBaseType listType)\r\n{\r\n    Dictionary<Guid, string> lists = new Dictionary<Guid, string>();\r\n\r\n    if (web == null)\r\n        return lists;\r\n\r\n    lists = (from list in web.Lists.Cast<SPList>()\r\n             where list.BaseType == listType\r\n             orderby list.Title\r\n             select list).ToDictionary(item => item.ID, item => item.Title);\r\n\r\n    return lists;\r\n}\r\n<\/pre>\n<p>Finally, to retrieve the available display fields:<\/p>\n<pre lang=\"csharp\">\r\n\r\nprotected void ddlLookupListOnSelectedIndexChanged(object sender, EventArgs eventArgs)\r\n{\r\n    DropDownList dropDownList = sender as DropDownList;\r\n\r\n    if (dropDownList.SelectedItem != null && dropDownList.SelectedItem.Value.IsGuid())\r\n    {\r\n        Guid webId = (DropDownListHasSelectedItem(ddlWebForList)) ? new Guid(ddlWebForList.SelectedItem.Value) : _extendedLookupFieldType.WebSourceId;\r\n        Guid listId = new Guid(dropDownList.SelectedItem.Value);\r\n\r\n        using (SPWeb web = SPContext.Current.Site.OpenWeb(webId))\r\n        {\r\n            SPList list = web.Lists.GetList(listId, false);\r\n\r\n            Dictionary<Guid, string> fields = GetFieldsForList(list, SPFieldType.Text);\r\n            BindList(ddlDisplayColumn, fields);\r\n        }\r\n    }\r\n}\r\n\r\npublic Dictionary<Guid, string> GetFieldsForList(SPList list, SPFieldType fieldType)\r\n{\r\n    Dictionary<Guid, string> columns = new Dictionary<Guid, string>();\r\n\r\n    if (list == null)\r\n        return columns;\r\n\r\n    columns = (from field in list.Fields.Cast<SPField>()\r\n               where field.Type == fieldType\r\n               orderby field.Title\r\n               select field).ToDictionary(item => item.Id, item => item.Title);\r\n\r\n    return columns;\r\n}\r\n<\/pre>\n<p>Part three of this post will continue with the creation of the custom lookup field.  Click <a href=\"http:\/\/www.stuartroberts.net\/index.php\/2012\/11\/13\/enhanced-lookup-field-part-3\/\" title=\"Enhanced Lookup Field - Part 3\">here<\/a> to view it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is part two of the post showing how to create a custom lookup field for SharePoint. Other pages in this post are: Part 1 &#8211; Creating a custom lookup field type Part 3 &#8211; Add field control class Part &hellip; <a href=\"http:\/\/www.stuartroberts.net\/index.php\/2012\/10\/20\/enhanced-lookup-field-part-2\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_mi_skip_tracking":false,"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":[]},"categories":[3],"tags":[54,81],"jetpack_publicize_connections":[],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/plx2I-jA","_links":{"self":[{"href":"http:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/posts\/1214"}],"collection":[{"href":"http:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/comments?post=1214"}],"version-history":[{"count":10,"href":"http:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/posts\/1214\/revisions"}],"predecessor-version":[{"id":1291,"href":"http:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/posts\/1214\/revisions\/1291"}],"wp:attachment":[{"href":"http:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/media?parent=1214"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/categories?post=1214"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/tags?post=1214"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}