{"id":1232,"date":"2012-11-13T22:04:28","date_gmt":"2012-11-13T22:04:28","guid":{"rendered":"http:\/\/www.stuartroberts.net\/?p=1232"},"modified":"2012-12-13T10:14:14","modified_gmt":"2012-12-13T10:14:14","slug":"enhanced-lookup-field-part-3","status":"publish","type":"post","link":"http:\/\/www.stuartroberts.net\/index.php\/2012\/11\/13\/enhanced-lookup-field-part-3\/","title":{"rendered":"Enhanced Lookup Field &#8211; Part 3"},"content":{"rendered":"<p>This is part three of the post on 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 2 &#8211; <a href=\"http:\/\/www.stuartroberts.net\/index.php\/2012\/10\/20\/enhanced-lookup-field-part-2\/\" title=\"Enhanced Lookup Field - Part 2\">Add field editor 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>For this post, I&#8217;ll describe the implementation of the field control class.<\/p>\n<p><!--more--><\/p>\n<p>As previously mentioned, a Visual Studio project will be available for download once this series is finished.<\/p>\n<p>For this class, we&#8217;ll inherit from the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.sharepoint.webcontrols.basefieldcontrol(v=office.12).aspx\" title=\"BaseFieldControl Class\" target=\"_blank\">BaseFieldControl<\/a> class.<\/p>\n<pre lang=\"csharp\">\r\npublic class ExtendedLookupFieldControl : BaseFieldControl\r\n{\r\n    public ExtendedLookupFieldControl(ExtendedLookupFieldType extendedLookupFieldType)\r\n    {\r\n        if (extendedLookupFieldType == null)\r\n            throw new ArgumentNullException(\"extendedLookupFieldType\");\r\n\r\n        _isInitialised = false;\r\n        _webSourceId = extendedLookupFieldType.LookupWebId;\r\n        _lookupListId = new Guid(extendedLookupFieldType.LookupList);\r\n        _displayColumnId = new Guid(extendedLookupFieldType.LookupField);\r\n        _valueColumnId = extendedLookupFieldType.ValueColumnId;\r\n        _allowMultipleItemSelection = extendedLookupFieldType.AllowMultipleValues;\r\n        _relatedFieldName = extendedLookupFieldType.RelatedFieldName;\r\n        _isRequired = extendedLookupFieldType.Required;\r\n    }\r\n}\r\n<\/pre>\n<p>In the constructor for this class we&#8217;re taking the settings defined in the field&#8217;s definition and storing them for use later on.<\/p>\n<p>The first thing I&#8217;ll describe is how the UI is implemented.  This covers the creating\\editing and displaying of field data.<\/p>\n<pre lang=\"csharp\">\r\nprotected override void CreateChildControls()\r\n{\r\n    if (Field == null) return;\r\n\r\n    base.CreateChildControls();\r\n            \r\n    switch (ControlMode)\r\n    {\r\n        case SPControlMode.Display:\r\n            CreateControlsForDisplay();\r\n            break;\r\n        case SPControlMode.Edit:\r\n        case SPControlMode.New:\r\n            CreateControlsForNewEdit();\r\n            break;\r\n    }\r\n}\r\n\r\nprotected override void OnLoad(EventArgs e)\r\n{\r\n    base.OnLoad(e);\r\n\r\n    InitialiseControls();\r\n}\r\n\r\nprivate void InitialiseControls()\r\n{\r\n    if (_isInitialised)\r\n        return;\r\n\r\n    string data = Item[_relatedFieldName] == null ? string.Empty : Item[_relatedFieldName].ToString();\r\n\r\n    _lookupData = !string.IsNullOrEmpty(data) ? Serialization<ExtendedLookupData>.DeserializeObject(data) : new ExtendedLookupData();\r\n\r\n    switch (ControlMode)\r\n    {\r\n        case SPControlMode.Display:\r\n            InitialiseForDisplay();\r\n            break;\r\n        case SPControlMode.Edit:\r\n        case SPControlMode.New:\r\n            InitialiseForNewEdit();\r\n            break;\r\n    }\r\n\r\n    _isInitialised = true;\r\n}\r\n<\/pre>\n<p>During the control creation stage, the control mode is queried and the class either creates controls for displaying or for editing\\creation.  Similar logic is applied during the load phase of the class.<\/p>\n<p>One thing worth noting here is the deserialization of an ExtendedLookupData object.  This object is stored in the related field (see the <a href=\"http:\/\/www.stuartroberts.net\/index.php\/2012\/12\/10\/enhanced-lookup-field-part-4\/\" title=\"Enhanced Lookup Field - Part 4\">fourth post<\/a> for the implementation of this) and is used to ensure the selected lookup values are displayed in the event the source list\\items being deleted.<\/p>\n<p>Let&#8217;s have a look at the display mode implementation:<\/p>\n<pre lang=\"csharp\">\r\nprivate void CreateControlsForDisplay()\r\n{\r\n    ltlDisplayItems = new Literal { ID = \"ltlDisplayItems\" };\r\n    Controls.Add(ltlDisplayItems);\r\n}\r\n\r\nprivate void InitialiseForDisplay()\r\n{\r\n    ltlDisplayItems.Text = string.Empty;\r\n\r\n    SPList lookupList = GetLookupList();\r\n    List<SharePointHelper.ExtendedLookupColumnValues> currentValues = GetSelectedLookupColumns(lookupList, _lookupData, false);\r\n\r\n    if (currentValues != null && currentValues.Count > 0)\r\n    {\r\n        string displayLinks = GetFieldValuesAsLinks(_lookupData.WebId, _lookupData.ListId, _lookupData.ValueId, currentValues);\r\n\r\n        if (string.IsNullOrEmpty(displayLinks))\r\n        {\r\n            foreach (SharePointHelper.ExtendedLookupColumnValues value in currentValues)\r\n            {\r\n                ltlDisplayItems.Text += value.Display + DisplayDelimeter;\r\n            }\r\n\r\n            ltlDisplayItems.Text = ltlDisplayItems.Text.Substring(0, ltlDisplayItems.Text.Length - DisplayDelimeter.Length);\r\n        }\r\n        else\r\n        {\r\n            ltlDisplayItems.Text = displayLinks;\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>The <em>CreateControlsForDisplay<\/em> method is pretty self explanatory.<\/p>\n<p><em>InitialiseForDisplay<\/em> starts by retrieving the list object containing the lookup items by calling <em>GetLookupList()<\/em>.<\/p>\n<p>The next stage calls <em>GetSelectedLookupColumns<\/em> to retrieve (see next code block) the current display values and updating any items that no longer exist (because they&#8217;ve been deleted) with the original value that was set during the item creation.  Again, for further details on the <em>ExtendedLookupColumnValues<\/em> and the hidden related field, have a look at the <a href=\"http:\/\/www.stuartroberts.net\/index.php\/2012\/12\/10\/enhanced-lookup-field-part-4\/\" title=\"Enhanced Lookup Field - Part 4\">fourth post<\/a> for this series (coming soon).<\/p>\n<pre lang=\"csharp\">\r\nprivate List<SharePointHelper.ExtendedLookupColumnValues> GetSelectedLookupColumns(SPList lookupList, ExtendedLookupData lookupData, bool removeInvalidEntries)\r\n{\r\n    List<SharePointHelper.ExtendedLookupColumnValues> columns = new List<SharePointHelper.ExtendedLookupColumnValues>();\r\n\r\n    if (lookupData != null && lookupData.SavedDisplayValues != null && lookupData.SavedDisplayValues.Count > 0)\r\n    {\r\n        foreach (KeyValuePair<int, string> value in lookupData.SavedDisplayValues)\r\n        {\r\n            string currentDisplayValue = null;\r\n            if (lookupList != null)\r\n            {\r\n                currentDisplayValue = (from item in lookupList.Items.Cast<SPListItem>()\r\n                                        where DoesEqual(item[_valueColumnId], value.Key)\r\n                                        select item.Title).FirstOrDefault();\r\n            }\r\n\r\n            if (string.IsNullOrEmpty(currentDisplayValue) && !removeInvalidEntries)\r\n            {\r\n                currentDisplayValue = value.Value;\r\n            }\r\n\r\n            if (!string.IsNullOrEmpty(currentDisplayValue))\r\n            {\r\n                columns.Add(new SharePointHelper.ExtendedLookupColumnValues { Value = value.Key, Display = currentDisplayValue });\r\n            }\r\n        }\r\n    }\r\n\r\n    return columns;\r\n}\r\n<\/pre>\n<p>Next, the <em>InitialiseForDisplay<\/em> method calls <em>GetFieldValuesAsLinks()<\/em> to get the display values in the form of hyperlinks to the corresponding lookup items.  Where all of the lookup items no longer exist and it&#8217;s not possible to create hyperlinks the display names from the hidden related field are used to display static titles of the lookup items.<\/p>\n<p>Lastly, for the display implementation, we render the literal control by overriding the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.sharepoint.webcontrols.basefieldcontrol.renderfieldfordisplay(v=office.12).aspx\" title=\"BaseFieldControl.RenderFieldForDisplay Method\" target=\"_blank\">RenderFieldForDisplay<\/a> method.<\/p>\n<pre lang=\"csharp\">\r\nprotected override void RenderFieldForDisplay(HtmlTextWriter output)\r\n{\r\n    ltlDisplayItems.RenderControl(output);\r\n}\r\n<\/pre>\n<p><strong>Create\\Edit Implmentation<\/strong><\/p>\n<p>Now onto the creation\\editing implementation.<\/p>\n<pre lang=\"csharp\">\r\nprivate void CreateControlsForNewEdit()\r\n{\r\n    if (_allowMultipleItemSelection)\r\n    {\r\n        CreateMultipleItemSelectionControls();\r\n    }\r\n    else\r\n    {\r\n        ddlLookupItems = new DropDownList {ID = \"ddlLookupItems\"};\r\n        if (!Field.Required)\r\n        {\r\n            ddlLookupItems.DataBound += DropDownList_OnDataBound;\r\n        }\r\n        Controls.Add(ddlLookupItems);\r\n    }\r\n}\r\n<\/pre>\n<p>During the control creation, a simple drop down list is created when the field definition specifies that only single selection is permitted.  When multiple items are allowed, <em>CreateMultipleItemSelectionControls<\/em> is called which creates the controls required to allow the user to select more than one lookup item.<\/p>\n<pre lang=\"csharp\">\r\nprivate void CreateMultipleItemSelectionControls()\r\n{\r\n    const string SEPARATOR_CELL_STYLE = \"padding-left: 10px;\";\r\n    const string CSS_MSLONG = \"ms-long\";\r\n    const string CSS_MSINPUT = \"ms-input\";\r\n    const string CSS_MSBUTTONHEIGHTWIDTH = \"ms-ButtonHeightWidth\";\r\n\r\n    gipLookupItems = new GroupedItemPicker\r\n                            {\r\n                                ID = \"gipLookupItems\",\r\n                                CandidateControlId = \"SelectCandidate\",\r\n                                ResultControlId = \"SelectResult\",\r\n                                AddButtonId = \"btnAdd\",\r\n                                RemoveButtonId = \"btnRemove\"\r\n                            };\r\n    Controls.Add(gipLookupItems);\r\n\r\n    Table tblMultipleValue = new Table { ID = \"tblMultipleValue\", CellPadding = 0, CellSpacing = 0, CssClass = CSS_MSLONG };\r\n\r\n    TableRow row = new TableRow();\r\n    TableCell cell = new TableCell { CssClass = CSS_MSINPUT };\r\n    SPHtmlSelect selectCandidate = new SPHtmlSelect {ID = \"SelectCandidate\", Width = 143, Height = 125, Multiple = true };\r\n    cell.Controls.Add(selectCandidate);\r\n    row.Cells.Add(cell);\r\n    tblMultipleValue.Rows.Add(row);\r\n\r\n    cell = new TableCell();\r\n    cell.Attributes.Add(\"style\", SEPARATOR_CELL_STYLE);\r\n    row.Cells.Add(cell);\r\n    tblMultipleValue.Rows.Add(row);\r\n\r\n    cell = new TableCell { CssClass = CSS_MSINPUT, HorizontalAlign = HorizontalAlign.Center, VerticalAlign = VerticalAlign.Middle };\r\n    HtmlButton btnAdd = new HtmlButton { ID = \"btnAdd\", InnerText = \"Add\" };\r\n    btnAdd.Attributes.Add(\"class\", CSS_MSBUTTONHEIGHTWIDTH);\r\n    cell.Controls.Add(btnAdd);\r\n    Literal litBreak = new Literal();\r\n    litBreak.Text = \"<br \/><br \/>\";\r\n    cell.Controls.Add(litBreak);\r\n    HtmlButton btnRemove = new HtmlButton { ID = \"btnRemove\", InnerText = \"Remove\" };\r\n    btnRemove.Attributes.Add(\"class\", CSS_MSBUTTONHEIGHTWIDTH);\r\n    cell.Controls.Add(btnRemove);\r\n    row.Cells.Add(cell);\r\n    tblMultipleValue.Rows.Add(row);\r\n\r\n    cell = new TableCell();\r\n    cell.Attributes.Add(\"style\", SEPARATOR_CELL_STYLE);\r\n    row.Cells.Add(cell);\r\n    tblMultipleValue.Rows.Add(row);\r\n\r\n    cell = new TableCell { CssClass = CSS_MSINPUT };\r\n    SPHtmlSelect selectResult = new SPHtmlSelect { ID = \"SelectResult\", Width = 143, Height = 125, Multiple = true };\r\n    cell.Controls.Add(selectResult);\r\n    row.Cells.Add(cell);\r\n    tblMultipleValue.Rows.Add(row);\r\n\r\n    Controls.Add(tblMultipleValue);\r\n}\r\n<\/pre>\n<p>The control I chose to use to allow users to select multiple items was the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.sharepoint.webcontrols.groupeditempicker(v=office.12).aspx\" title=\"GroupedItemPicker Class\" target=\"_blank\">GroupedItemPicker<\/a> control, which is part of the <em>Microsoft.SharePoint<\/em> assembly.<\/p>\n<p>The rest of this method simply creates a table to contain the group picker and the associated controls it requires, such as the add\\remove buttons and the candidate and selection list boxes.<\/p>\n<p>Now onto the loading phase:<\/p>\n<pre lang=\"csharp\">\r\nprivate void InitialiseForNewEdit()\r\n{\r\n    if (_allowMultipleItemSelection)\r\n    {\r\n        InitialiseMultipleItemSelectionControls();\r\n    }\r\n    else\r\n    {\r\n        if (!Page.IsPostBack)\r\n        {\r\n            SPList lookupList = GetLookupList();\r\n            if (lookupList != null)\r\n            {\r\n                List<SharePointHelper.ExtendedLookupColumnValues> values = SharePointHelper.GetColumnValues(lookupList, _displayColumnId, _valueColumnId);\r\n                        \r\n                BindLookupColumnValues(ddlLookupItems, values);\r\n\r\n                if (ddlLookupItems.Items.Count > 0)\r\n                {\r\n                    if (IsEditModeValid(_lookupData, lookupList.ID))\r\n                    {\r\n                        List<SharePointHelper.ExtendedLookupColumnValues> selectedValues = GetSelectedLookupColumns(lookupList, _lookupData);\r\n                        ListItem currentItem = null;\r\n                        if (selectedValues.Count > 0)\r\n                        {\r\n                            \/\/ Select the first item in the selectedValues list, this may happen if the field was configured for multiple items\r\n                            \/\/ and was then changed to a single selection.\r\n                            currentItem = ddlLookupItems.Items.FindByValue(selectedValues[0].Value.ToString());\r\n                        }\r\n                        ddlLookupItems.SelectedIndex = (currentItem != null) ? ddlLookupItems.Items.IndexOf(currentItem) : 0;\r\n                    }\r\n                    else \/\/ Not edit mode, so select the first item in the list\r\n                    {\r\n                        ddlLookupItems.SelectedIndex = 0;    \r\n                    }\r\n                }\r\n            }\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>As with the <em>CreateControlsForNewEdit<\/em> method, the above method checks if the field allows multiple item selection and processes the initialisation accordingly.<\/p>\n<p>For single selection (using the drop down list) similar logic to initialising the display mode is implemented.  The difference here is that lookup items that no longer exist are removed from the available options.  Subsequently, saving changes when this is the case will remove the deleted item from the stored values and it will no longer appear in display mode.<\/p>\n<p>For multiple item selection the following is implemented:<\/p>\n<pre lang=\"csharp\">\r\nprivate void InitialiseMultipleItemSelectionControls()\r\n{\r\n    if (Page.IsPostBack)\r\n        return;\r\n\r\n    SPList lookupList = GetLookupList();\r\n    if (lookupList != null)\r\n    {\r\n        List<SharePointHelper.ExtendedLookupColumnValues> values = SharePointHelper.GetColumnValues(lookupList, _displayColumnId, _valueColumnId);\r\n\r\n        _itemDetails = new Dictionary<int, string>();\r\n        foreach (SharePointHelper.ExtendedLookupColumnValues value in values)\r\n        {\r\n            _itemDetails.Add(Convert.ToInt32(value.Value), value.Display);\r\n        }\r\n\r\n        if (IsEditModeValid(_lookupData, lookupList.ID))\r\n        {\r\n            List<SharePointHelper.ExtendedLookupColumnValues> selectionValues = GetSelectedLookupColumns(lookupList, _lookupData);\r\n\r\n            foreach (SharePointHelper.ExtendedLookupColumnValues value in selectionValues)\r\n            {\r\n                gipLookupItems.AddSelectedItem(value.Value.ToString(), value.Display);\r\n            }\r\n\r\n            UpdateCandidateColumns(values, selectionValues);\r\n        }\r\n\r\n        foreach (SharePointHelper.ExtendedLookupColumnValues value in values)\r\n        {\r\n            gipLookupItems.AddItem(value.Value.ToString(), value.Display, string.Empty, string.Empty);\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>This method has to fill two list boxes (part of the GroupItemPicker control) one for the available lookup items and the other for the currently selected items.<\/p>\n<p>If the mode is set to edit, the latter drop down list is bound to the item&#8217;s selected lookup items, minus those where their source has been deleted.<\/p>\n<p>The candidate drop down list is then bound to the complete list of available lookup items.  Where the mode is edit any selected items are removed from the candidate list by calling <em>UpdateCandidateColumns<\/em>:<\/p>\n<p>An important part of the above method is the setting of the _itemDetails  variable.  Here, the lookup item&#8217;s id and display values are being stored into a Dictionary object.  The reason for doing this is that the GroupItemPicker control only exposes a property that allows you to read the selected item&#8217;s ids.  The property that returns the display values is set to internal, so this is a workaround to allow the field control to save the selected id and display values.<\/p>\n<pre lang=\"csharp\">\r\nprivate void UpdateCandidateColumns(List<SharePointHelper.ExtendedLookupColumnValues> candidate, List<SharePointHelper.ExtendedLookupColumnValues> selection)\r\n{\r\n    if (candidate.Count > 0 && selection.Count > 0)\r\n    {\r\n        foreach (SharePointHelper.ExtendedLookupColumnValues item in selection)\r\n        {\r\n            var candidateItem = (from i in candidate\r\n                                 where i.Display == item.Display && DoesEqual(i.Value, item.Value)\r\n                                 select i).FirstOrDefault();\r\n\r\n            if (candidateItem != null)\r\n            {\r\n                candidate.Remove(candidateItem);\r\n            }\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>That&#8217;s about it for the creation an initialisation of the display\\edit controls.<\/p>\n<p><strong>Saving<\/strong><\/p>\n<p>The last part of this post is to describe the saving process for the field.<\/p>\n<p>Before a save is allowed to progress, the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.sharepoint.webcontrols.basefieldcontrol.validate(v=office.12).aspx\" title=\"BaseFieldControl.Validate Method\" target=\"_blank\">Validate<\/a> method if overridden to ensure a value has been selected when the field settings dictate that it is a required field.<\/p>\n<pre lang=\"csharp\">\r\npublic override void Validate()\r\n{\r\n    if (_isRequired)\r\n    {\r\n        ErrorMessage = \"You must specify a value for this required field.\";\r\n\r\n        if (_allowMultipleItemSelection)\r\n        {\r\n            IsValid = gipLookupItems != null && gipLookupItems.SelectedIds.Count > 0;\r\n        }\r\n        else\r\n        {\r\n            IsValid = ddlLookupItems != null && !string.IsNullOrEmpty(ddlLookupItems.SelectedItem.Value);\r\n        }\r\n    }\r\n    else\r\n    {\r\n        IsValid = true;\r\n    }\r\n}\r\n<\/pre>\n<p>Now onto the save methods.  First, the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.sharepoint.webcontrols.basefieldcontrol.updatefieldvalueinitem(v=office.12).aspx\" title=\"BaseFieldControl.UpdateFieldValueInItem Method\" target=\"_blank\">UpdateFieldValueInItem<\/a> method is overridden:<\/p>\n<pre lang=\"csharp\">\r\npublic override void UpdateFieldValueInItem()\r\n{\r\n    base.UpdateFieldValueInItem();\r\n            \r\n    if (Field.Required)\r\n    {\r\n        IsValid = Value != null;\r\n    }\r\n\r\n    if (_lookupData != null)\r\n    {\r\n        string data = Serialization<ExtendedLookupData>.SerializeObject(_lookupData);\r\n\r\n        Item[_relatedFieldName] = data;\r\n    }\r\n}\r\n<\/pre>\n<p>This method is called whenever a save is initiated against the field.  Here additional validation is performed before serializing the ExtendedLookupData object to the hidden related field &#8211; see the <a href=\"http:\/\/www.stuartroberts.net\/index.php\/2012\/12\/10\/enhanced-lookup-field-part-4\/\" title=\"Enhanced Lookup Field - Part 4\">fourth post<\/a> for details on how this is implemented.<\/p>\n<p>The call to the base method ensures the Value property is hit.  This property is used to get the value for the field.<\/p>\n<pre lang=\"csharp\">\r\npublic override object Value\r\n{\r\n    get\r\n    {\r\n        EnsureChildControls();\r\n        InitialiseControls();\r\n\r\n        _lookupData = new ExtendedLookupData();\r\n        _lookupData.WebId = _webSourceId;\r\n        _lookupData.ListId = _lookupListId;\r\n        _lookupData.DisplayId = _displayColumnId;\r\n        _lookupData.ValueId = _valueColumnId;\r\n\r\n        if (_allowMultipleItemSelection)\r\n        {\r\n            SPFieldLookupValueCollection lookupValues = GetMultipleItemSelectionValues();\r\n            AddLookupValuesToLookupData(lookupValues);\r\n            return lookupValues;\r\n        }\r\n                \r\n        SPFieldLookupValue lookupValue = GetSingleItemSelectionValue();\r\n        if (lookupValue != null)\r\n        {\r\n            AddLookupValueToLookupData(lookupValue);\r\n        }\r\n        return lookupValue;\r\n    }\r\n    set\r\n    {\r\n    }\r\n}\r\n<\/pre>\n<p>During the setter, a new ExtendedLookupData class is initialised and configured with the identifiers for the lookup list.<\/p>\n<p>Where multiple items are permitted the <em>GetMultipleItemSelectionValues<\/em> method is called and the returned collection saved against the ExtendedLookupData  object.<\/p>\n<p>For single selection, the <em>GetSingleItemSelectionValue<\/em> method is called and, once again, the returned collection saved against the ExtendedLookupData  object.<\/p>\n<pre lang=\"csharp\">\r\nprivate SPFieldLookupValueCollection GetMultipleItemSelectionValues()\r\n{\r\n    SPFieldLookupValueCollection lookupValues = new SPFieldLookupValueCollection();\r\n\r\n    if (gipLookupItems.SelectedIds == null || gipLookupItems.SelectedIds.Count > 0)\r\n    {\r\n        \/\/ From all available items, filter by the selected item ids.\r\n        lookupValues.AddRange(from KeyValuePair<int, string> kvp in _itemDetails\r\n                              where (from gip in gipLookupItems.SelectedIds.Cast<string>()\r\n                                     where Convert.ToInt32(gip) == kvp.Key\r\n                                     select gip).Contains(kvp.Key.ToString())\r\n                              select new SPFieldLookupValue(kvp.Key, kvp.Value));\r\n    }\r\n    \r\n    return lookupValues;\r\n}\r\n<\/pre>\n<p>Here, the list of selected id values from the GroupItemPicker is queried against the _itemDetails variable (created during the <em>InitialiseMultipleItemSelectionControls<\/em> method) and where the id from the GroupItemPicker matches a key from _itemDetails a new LINQ object is created containing the id and the display value.  Remember, the GroupItemPicker control does not expose the display property, only id and I don&#8217;t like to use reflection in case the underlying object changes.<\/p>\n<p>The <em>GetSingleItemSelectionValue<\/em> method is a bit simpler:<\/p>\n<pre lang=\"csharp\">\r\nprivate SPFieldLookupValue GetSingleItemSelectionValue()\r\n{\r\n    SPFieldLookupValue lookupValue = null;\r\n\r\n    if (ddlLookupItems != null && !string.IsNullOrEmpty(ddlLookupItems.SelectedItem.Value))\r\n    {\r\n        lookupValue = new SPFieldLookupValue(Convert.ToInt32(ddlLookupItems.SelectedItem.Value), ddlLookupItems.SelectedItem.Text);\r\n    }\r\n\r\n    return lookupValue;\r\n}\r\n<\/pre>\n<p>This method returns the selected id and display values from the single selection drop down list.<\/p>\n<p>Part four of this post will continue with the creation of the custom hidden related field, Click <a href=\"http:\/\/www.stuartroberts.net\/index.php\/2012\/12\/10\/enhanced-lookup-field-part-4\/\" title=\"Enhanced Lookup Field - Part 4\">here<\/a> to view it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is part three of the post on 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 2 &#8211; Add field editor class Part &hellip; <a href=\"http:\/\/www.stuartroberts.net\/index.php\/2012\/11\/13\/enhanced-lookup-field-part-3\/\">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-jS","_links":{"self":[{"href":"http:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/posts\/1232"}],"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=1232"}],"version-history":[{"count":21,"href":"http:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/posts\/1232\/revisions"}],"predecessor-version":[{"id":1309,"href":"http:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/posts\/1232\/revisions\/1309"}],"wp:attachment":[{"href":"http:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/media?parent=1232"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/categories?post=1232"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/tags?post=1232"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}