Single Select Tree in Sitecore

Consider the situation of a field in sitecore meant to allow selecting a state, and the data items in sitecore are organized by country.

2015-03-15_225216

In this situation, it is most convenient for the content author to be able to navigate through the items in a static tree structure (yes static – the drop tree makes it a little easy to just escape out and lose where you were at navigating!).

Which is why, we came up with the need for a treelist – which allows single item selection.

This can be achieved in 2 ways:

Add validation on the existing TreeList control:
This can be done using the validation field on the sitecore field in question. The following regular expression should help achieve this:

^$|^{[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\}$

2015-03-15_225547

2015-03-15_225304

Here’s how the validation message shows up – to note here – this validation message comes up only on item save, as opposed to while adding the multiple items in the field.

2015-03-15_225324

Create a new control and add validation / transform view:

If you did have additional requirements – say sorting the items in the treelist a certain custom way in addition to restricting the number of selections to 1, you could go all out and create a new control, inheriting from the existing treelist control.
I went in and grabbed the existing code of the treelist using a reflector, and updated the code as was needed.
Below are the updates that were made (the remaining code of Sitecore.Shell.Applications.ContentEditor.TreeList were left in as is)

namespace MySite.SitecoreFields
{
    public class SingleSelectTree : TreeList
    {
        ...
        ...

        [Category("Data")]
        [Description("If set to Yes, allows only 1 item to be selected in the control")]
        public bool RestrictSingleSelection
        {
            get
            {
                return this.GetViewStateBool("RestrictSingleSelection");
            }
            set
            {
                this.SetViewStateBool("RestrictSingleSelection", value);
            }
        }

        ...
        ...

        protected new void Add()
        {
            if (Disabled)
                return;
            string viewStateString = GetViewStateString("ID");
            TreeviewEx treeviewEx = FindControl(viewStateString + "_all") as TreeviewEx;
            Assert.IsNotNull(treeviewEx, typeof(DataTreeview));
            Listbox listbox = FindControl(viewStateString + "_selected") as Listbox;
            Assert.IsNotNull(listbox, typeof(Listbox));
            if (treeviewEx != null)
            {
                Item selectionItem = treeviewEx.GetSelectionItem(Language.Parse(this.ItemLanguage), Sitecore.Data.Version.Latest);
                if (selectionItem == null)
                {
                    SheerResponse.Alert("Select an item in the Content Tree.", new string[0]);
                }
                else
                {
                    if (HasExcludeTemplateForSelection(selectionItem))
                        return;
                    if (ItemSelected(selectionItem, listbox))
                    {
                        SheerResponse.Alert("You cannot select more than one item.", new string[0]);
                    }
                    else if (IsDeniedMultipleSelection(selectionItem, listbox))
                    {
                        SheerResponse.Alert("You cannot select the same item twice.", new string[0]);
                    }
                    else
                    {
                        if (!this.HasIncludeTemplateForSelection(selectionItem))
                            return;
                        SheerResponse.Eval("scForm.browser.getControl('" + viewStateString + "_selected').selectedIndex=-1");
                        ListItem listItem = new ListItem { ID = GetUniqueID("L") };
                        Sitecore.Context.ClientPage.AddControl(listbox, listItem);
                        listItem.Header = GetHeaderValue(selectionItem);
                        listItem.Value = listItem.ID + (object)"|" + selectionItem.ID;
                        SheerResponse.Refresh(listbox);
                        TreeList.SetModified();
                    }
                }
            }
        }

        private bool ItemSelected(Item item, Listbox listbox)
        {
            Assert.ArgumentNotNull(listbox, "listbox");
            if (item == null)
                return true;
            if (!RestrictSingleSelection)
                return false;
            return (from Control control in listbox.Controls
                    select control.Value.Split('|')).Count() > 1;
        }

        ...
        ...

        private void SetProperties()
        {
            Guid tempGuid;
            string @string = StringUtil.GetString(new string[1]
              {
                this.Source
              });
            if (@string.StartsWith("query:"))
            {
                if (Sitecore.Context.ContentDatabase == null || this.ItemID == null)
                    return;
                Item current = Sitecore.Context.ContentDatabase.GetItem(this.ItemID);
                if (current == null)
                    return;
                Item obj = null;
                try
                {
                    obj = LookupSources.GetItems(current, @string).FirstOrDefault();
                }
                catch (Exception ex)
                {
                    Log.Error("Treelist field failed to execute query.", ex, (object)this);
                }
                if (obj == null)
                    return;
                this.DataSource = obj.Paths.FullPath;
            }
            else if (Guid.TryParse(@string, out tempGuid))
                this.DataSource = this.Source;
            else if (this.Source != null && !@string.Trim().StartsWith("/", StringComparison.OrdinalIgnoreCase))
            {
                this.ExcludeTemplatesForSelection = StringUtil.ExtractParameter("ExcludeTemplatesForSelection", this.Source).Trim();
                this.IncludeTemplatesForSelection = StringUtil.ExtractParameter("IncludeTemplatesForSelection", this.Source).Trim();
                this.IncludeTemplatesForDisplay = StringUtil.ExtractParameter("IncludeTemplatesForDisplay", this.Source).Trim();
                this.ExcludeTemplatesForDisplay = StringUtil.ExtractParameter("ExcludeTemplatesForDisplay", this.Source).Trim();
                this.ExcludeItemsForDisplay = StringUtil.ExtractParameter("ExcludeItemsForDisplay", this.Source).Trim();
                this.IncludeItemsForDisplay = StringUtil.ExtractParameter("IncludeItemsForDisplay", this.Source).Trim();
                this.AllowMultipleSelection = string.Compare(StringUtil.ExtractParameter("AllowMultipleSelection", this.Source).Trim().ToLowerInvariant(), "yes", StringComparison.InvariantCultureIgnoreCase) == 0;
                this.RestrictSingleSelection = string.Compare(StringUtil.ExtractParameter("RestrictSingleSelection", this.Source).Trim().ToLowerInvariant(), "yes", StringComparison.InvariantCultureIgnoreCase) == 0;
                this.DataSource = StringUtil.ExtractParameter("DataSource", this.Source).Trim().ToLowerInvariant();
                this.DatabaseName = StringUtil.ExtractParameter("databasename", this.Source).Trim().ToLowerInvariant();
            }
            else
                this.DataSource = this.Source;
        }
    }
}

Additionally you would need the corresponding sitecore updates in the Core database to add the new control:

2015-03-16_011545

You’d need the corresponding config updates here:

<sitecore>
  <!-- New control added - Single Select Tree -->
  <controlSources>
    <source mode="on" namespace="MySite.SitecoreFields" assembly="MySite" prefix="contentExtension" />
  </controlSources>
</sitecore>

And the control should now be available:

2015-03-16_011844

One thought on “Single Select Tree in Sitecore

Leave a comment