April 16, 2012

How to create a custom Multilist field not related to the items

Sometimes you need to create any fields who list and store some values but you don’t have any sitecore items related to those values. For example, in one of our projects we have a list of categories who come from a webservice.

To do that easily, I have created a base class that you can download here
(the code of this file is also at the bottom of this post)

After that you need to impelemnt your field by creating a new class who will inherit from my base class. Here is the skeleton of this class.
public class Test : MultilistExBase
{
 protected override void InitRendering()
 {
  //You can Initialise some variables here.
 }

 protected override IEnumerable<KeyValuePair<string, string>> GetNonSelectedItems()
 {
  //Return here your unselected items. First value is the ID you will store into your field, the second one is the display text.
 }

 protected override IEnumerable<KeyValuePair<string, string>> GetSelectedItems()
 {
  //Return here your selected items. First value is the ID you will store into your field, the second one is the display text.
 }
}
You need to implement only 3 methods :
  • InitRendering: Allow you to initialize your variables, webservice, ...
  • GetNonSelectedItems: You need to return the list of unselected items
  • GetSelectedItems: You need to return the list of selected items
As you can see the items are only a KeyValuePair where the key is the id stored into the sitecore field and the value is the display text. With this method you can really have the control of what is stored and display into the field.

  • To create your customfield into sitecore you need: - Register your namespace in the tag of your .config file
    <controlSources>
          <source mode="on" namespace="MyNamespace" assembly="MyAssembly" prefix="myControls"/>
    </controlSources>
  • Create a new item based on the /sitecore/templates/System/Templates/Template field type in the core database below /sitecore/system/Field types/List Types


After that you will have a field like:


But you can see that you can handle the raw value as I want:
If you want to be complete, you can also create the field who will be used into your code to retrieve the content of your field (as you use the fields from Sitecore.Data.Fields). The most important method here it the GetAvailableFields() method where you need to return the list of objects corresponding to the raw value.
public class TestField : DelimitedField
{
 // Methods
 public TestField(Field innerField)
  : base(innerField, '|')
 {
 }

 public List<MyObject> GetAvailableFields()
 {
  //Return the list of object corresponding to the raw value
  List<MyObject> lst = new List<MyObject>(); 
  foreach (var item in this.Items)
  {
   lst.Add(new MyObject(item));
  }
  return lst;
 }

 public static implicit operator TestField(Field field)
 {
  if (field != null)
  {
   return new TestField(field);
  }
  return null;
 }
}


Here is the source code of my base class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sitecore.Shell.Applications.ContentEditor;
using Sitecore.Diagnostics;
using Sitecore.Data.Items;
using System.Collections;
using Sitecore;
using Sitecore.Globalization;
using Sitecore.Resources;
using System.Web.UI;
using Sitecore.Text;
using Sitecore.Data;

namespace BaseFields
{
    public abstract class MultilistExBase : Sitecore.Shell.Applications.ContentEditor.MultilistEx
    {
        /// <summary>
        /// Return here your unselected items. First value is the ID you will store into your field, the second one is the display text.
        /// </summary>
        /// <returns>The unselected items</returns>
        protected abstract IEnumerable<KeyValuePair<string, string>> GetNonSelectedItems();
        /// <summary>
        /// Return here your selected items. First value is the ID you will store into your field, the second one is the display text.
        /// </summary>
        /// <returns>The selected items</returns>
        protected abstract IEnumerable<KeyValuePair<string, string>> GetSelectedItems();

        /// <summary>
        /// By overideing this method, you can initialise some variables here.
        /// </summary>
        protected virtual void InitRendering()
        {
        }

        protected override void DoRender(System.Web.UI.HtmlTextWriter output)
        {
            Assert.ArgumentNotNull(output, "output");

            base.ServerProperties["ID"] = this.ID;

            InitRendering();

            string text = string.Empty;
            if (this.ReadOnly)
            {
                text = " disabled=\"disabled\"";
            }
            output.Write(string.Concat(new string[]
                   {
                    "<input id=\"", 
                    this.ID, 
                    "_Value\" type=\"hidden\" value=\"", 
                    StringUtil.EscapeQuote(this.Value), 
                    "\" />"
                   }));
            output.Write("<table" + this.GetControlAttributes() + ">");
            output.Write("<tr>");
            output.Write("<td class=\"scContentControlMultilistCaption\" width=\"50%\">" + Translate.Text("All") + "</td>");
            output.Write("<td width=\"20\">" + Images.GetSpacer(20, 1) + "</td>");
            output.Write("<td class=\"scContentControlMultilistCaption\" width=\"50%\">" + Translate.Text("Selected") + "</td>");
            output.Write("<td width=\"20\">" + Images.GetSpacer(20, 1) + "</td>");
            output.Write("</tr>");
            output.Write("<tr>");
            output.Write("<td valign=\"top\" height=\"100%\">");
            output.Write(string.Concat(new string[]
                   {
                    "<select id=\"", 
                    this.ID, 
                    "_unselected\" class=\"scContentControlMultilistBox\" multiple=\"multiple\" size=\"10\"", 
                    text, 
                    " ondblclick=\"javascript:scContent.multilistMoveRight('", 
                    this.ID, 
                    "')\" onchange=\"javascript:document.getElementById('", 
                    this.ID, 
                    "_all_help').innerHTML=this.selectedIndex>=0?this.options[this.selectedIndex].innerHTML:''\" >"
                   }));

            //Bind the selected values?
            foreach (KeyValuePair<string, string> field in GetNonSelectedItems())
            {
                //Item item2 = dictionaryEntry.Value as Item;
                //if (item2 != null)
                //{
                output.Write(string.Concat(new string[]
    {
     "<option value=\"", 
     field.Key, 
     "\">", 
     field.Value, 
     "</option>"
    }));
                //}
            }

            output.Write("</select>");
            output.Write("</td>");
            output.Write("<td valign=\"top\">");
            this.RenderButton(output, "Core/16x16/arrow_blue_right.png", "javascript:scContent.multilistMoveRight('" + this.ID + "')");
            output.Write("<br />");
            this.RenderButton(output, "Core/16x16/arrow_blue_left.png", "javascript:scContent.multilistMoveLeft('" + this.ID + "')");
            output.Write("</td>");
            output.Write("<td valign=\"top\" height=\"100%\">");
            output.Write(string.Concat(new string[]
               {
                "<select id=\"", 
                this.ID, 
                "_selected\" class=\"scContentControlMultilistBox\" multiple=\"multiple\" size=\"10\"", 
                text, 
                " ondblclick=\"javascript:scContent.multilistMoveLeft('", 
                this.ID, 
                "')\" onchange=\"javascript:document.getElementById('", 
                this.ID, 
                "_selected_help').innerHTML=this.selectedIndex>=0?this.options[this.selectedIndex].innerHTML:''\">"
               }));

            //Bind the available items list
            foreach (KeyValuePair<string, string> field in GetSelectedItems())
            {
                output.Write(string.Concat(new string[]
    {
                    "<option value=\"", 
     field.Key, 
     "\">", 
     field.Value, 
     "</option>"
    }));

            }
            output.Write("</select>");
            output.Write("</td>");
            output.Write("<td valign=\"top\">");
            this.RenderButton(output, "Core/16x16/arrow_blue_up.png", "javascript:scContent.multilistMoveUp('" + this.ID + "')");
            output.Write("<br />");
            this.RenderButton(output, "Core/16x16/arrow_blue_down.png", "javascript:scContent.multilistMoveDown('" + this.ID + "')");
            output.Write("</td>");
            output.Write("</tr>");
            output.Write("<tr>");
            output.Write("<td valign=\"top\">");
            output.Write("<div style=\"border:1px solid #999999;font:8pt tahoma;padding:2px;margin:4px 0px 4px 0px;height:14px\" id=\"" + this.ID + "_all_help\"></div>");
            output.Write("</td>");
            output.Write("<td></td>");
            output.Write("<td valign=\"top\">");
            output.Write("<div style=\"border:1px solid #999999;font:8pt tahoma;padding:2px;margin:4px 0px 4px 0px;height:14px\" id=\"" + this.ID + "_selected_help\"></div>");
            output.Write("</td>");
            output.Write("<td></td>");
            output.Write("</tr>");
            output.Write("</table>");
            
        }        

        private void RenderButton(HtmlTextWriter output, string icon, string click)
        {
            Assert.ArgumentNotNull(output, "output");
            Assert.ArgumentNotNull(icon, "icon");
            Assert.ArgumentNotNull(click, "click");
            ImageBuilder imageBuilder = new ImageBuilder();
            imageBuilder.Src = icon;
            imageBuilder.Width = 16;
            imageBuilder.Height = 16;
            imageBuilder.Margin = "2px";
            if (!this.ReadOnly)
            {
                imageBuilder.OnClick = click;
            }
            output.Write(imageBuilder.ToString());
        }
    }
}quot;_selected_help').innerHTML=this.selectedIndex

1 comment:

  1. who can we implement this for Multilist with Search FieldType?

    ReplyDelete