Sitefinity CMS

Using Non-string Properties with Controls Send comments on this topic.
Developing with Sitefinity > Controls > Adding New Controls to Sitefinity > Using Non-string Properties with Controls

Glossary Item Box

In order for Sitefinity to be able to save the property automatically the value of your property needs to be in the string format. Sitefinity will automatically convert primitive types for you, such as integer or boolean, but what if you need to persist property that is an array or a list. Our sample Links List control needs to persist the list of links (link titles and link urls), so we will need to find a way to make Sitefinity persist a property of type Dictionary<string, string>.

 

We will start by declaring the public ListLinks property of type Dictionary<string, string> where the key of the dictionary is a link title, while the value is link url. We will categorize it under "Links settings" category, and finally we will add one more attribute: TypeConverter. TypeConverter editor will define the class that will provide logic for converting the Dictionary<string,string> type to string, and vice versa. Basically, the idea is to serialize the object.

Our property declaration will look like this:

C# Copy Code
/// <summary>
/// Gets or sets the list of items to be displayed by the bulleted list control
/// </summary>
[Category("List settings")]
[TypeConverter(
"Telerik.Samples.DictionaryConverter, App_Code")]
public Dictionary<string, string> ListLinks
{
   get
   {
       
// if listLinks dictionary is null, we'll create an empty dictionary
       
// to avoid dealing with null reference in code
       
if(listLinks == null)
           listLinks =
new Dictionary<string, string>();
       
return listLinks;
   }
   set
   {
       listLinks = value;
   }
}

 

The next step we have to do is to implement the DictionaryConverter class that we have declared as the type of TypeConverter. To do so we will implement a new class which will inherit from .NET framework’s TypeConverter and place that class into the App_Code folder. The code for that class looks like this:

DictionaryConverter.cs Copy Code
using System;
using System.Globalization;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Text;
namespace Telerik.Samples
{
   
/// <summary>
   
/// This class implements methods that convert string representation of Dictionary
   
/// to Dictionary object and vice versa - converter will convert only dictionaries
   
/// where both key and value are of type string
   
/// </summary>
   
public class DictionaryConverter : TypeConverter
   {
       
/// <summary>
       
/// Returns whether this converter can convert an object of the given type to
       
/// the type of this converter, using the specified context.
       
/// </summary>
       
/// <param name="context">A System.ComponentModel.ITypeDescriptorContext that provides a format context.</param>
       
/// <param name="sourceType">A System.Type that represents the type you want to convert from.</param>
       
/// <returns>true if this converter can perform the conversion; otherwise, false.</returns>
       
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
       {
           
return ((sourceType == typeof(string)) || base.CanConvertFrom(context, sourceType));
       }
       
/// <summary>
       
/// Returns whether this converter can convert the object to the specified type,
       
/// using the specified context.
       
/// </summary>
       
/// <param name="context">A System.ComponentModel.ITypeDescriptorContext that provides a format context.</param>
       
/// <param name="destinationType">A System.Type that represents the type you want to convert to.</param>
       
/// <returns>true if this converter can perform the conversion; otherwise, false.</returns>
       
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
       {
           
return ((destinationType == typeof(InstanceDescriptor)) || base.CanConvertTo(context, destinationType));
       }
       
/// <summary>
       
/// Converts the given object to the type of this converter, using the specified
       
/// context and culture information.
       
/// </summary>
       
/// <param name="context">A System.ComponentModel.ITypeDescriptorContext that provides a format context.</param>
       
/// <param name="culture">The System.Globalization.CultureInfo to use as the current culture.</param>
       
/// <param name="value">The System.Object to convert.</param>
       
/// <returns>An System.Object that represents the converted value.</returns>
       
/// <exception cref="System.NotSupportedException">The conversion cannot be performed.</exception>
       
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
       {
           
// the logic in this method will convert string into a Dictionary. The string has following
           
// format:
           
// key1=value1;key2=value2
           
// where semi-color (;) separates dictionary items, while equal sign (=) separates
           
// item key from item value
           
if (value is string)
           {
               Dictionary<
string, string> dictionary = new Dictionary<string, string>();
               
               
// split string by semi-colon (;) to get array of dictionary items
               
string[] dictionaryItems = ((string) value).Split(';');
               
foreach(string dictionaryItem in dictionaryItems)
               {
                   
// split item by equal sign (=) to get array of key and value
                   
string[] keyValuePair = dictionaryItem.Split('=');
                   
// add new item to the dictionary
                   
if(keyValuePair.Length == 2)
                       dictionary.Add(keyValuePair[0].Trim(), keyValuePair[1].Trim());
               }
               
return dictionary;                
           }
           
return base.ConvertFrom(context, culture, value);
       }
       
/// <summary>
       
/// Converts the given value object to the specified type, using the specified
       
///     context and culture information.
       
/// </summary>
       
/// <param name="context">A System.ComponentModel.ITypeDescriptorContext that provides a format context.</param>
       
/// <param name="culture">A System.Globalization.CultureInfo. If null is passed, the current culture is assumed.</param>
       
/// <param name="value">The System.Object to convert.</param>
       
/// <param name="destinationType">The System.Type to convert the value parameter to.</param>
       
/// <returns>An System.Object that represents the converted value.</returns>
       
/// <exception cref="System.NotSupportedException">The conversion cannot be performed.</exception>
       
/// <exception cref="System.ArgumentNullException">The destinationType parameter is null.</exception>
       
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
       {
           
// the logic in this method will convert Dictionary into a string (serialize the dictionary).
           
//The string has following format:
           
// key1=value1;key2=value2
           
// where semi-color (;) separates dictionary items, while equal sign (=) separates
           
// item key from item value
           
if (destinationType == null)
               
throw new ArgumentNullException("destinationType");
           
if ((destinationType == typeof(string)) && (value is Dictionary<string, string>))
           {
               StringBuilder sb =
new StringBuilder();
               
foreach(KeyValuePair<string, string> dictionaryItem in (Dictionary<string,string>)value)
               {
                   sb.AppendFormat(
"{0}={1};", dictionaryItem.Key, dictionaryItem.Value);
               }
               
return sb.ToString();
           }
           
return base.ConvertTo(context, culture, value, destinationType);
       }
   }
}

 

If you look at the code you will notice that we need to implement four methods. First, we need to implement methods which will determine can this converter convert Dictionary<string, string> to string, and second one - determining can the converter convert string to Dictionary<string, string>. Once we have implemented those two methods we are left with implementing methods which actually convert Dictionary<string,string> to a string and one that converts string to a Dictionary<string, string> object.

 

The way of implementing the conversion logic is completely left to the developer. In this sample you will notice that string representation of the Dictionary<string, string> object follows this logic:

Key1=Value1;Key2=Value2…

where the equal sign ("=") separates key from value, and the semi-colon sign (";") separates dictionary entries.

There is nothing stopping you from changing this logic.

After we have implemented this new property, let’s take a look at how our property grid looks like now:

Contrlols - Using Non-string Properties - TypeConverter

Figure 1: using TypeConverter to expose complex non-string properties as a property of Links List control

 

Finally, let us examine the code that will populate our BulletedList with the items declared in the ListLinks property. Once again, we will be placing the code in the Page_Load event handler:

C# Copy Code
protected void Page_Load(object sender, EventArgs e)
{
   
// set the text of title label to the value of ControlTitle property
   
controlTitleLabel.Text = ControlTitle;
   
// clear the bulleted list of any items and populate it
   
// with the values of ListLinks dictionary
   
linksBulletedList.Items.Clear();
   
foreach(KeyValuePair<string, string> listLink in ListLinks)
   {
       ListItem item =
new ListItem(listLink.Key, listLink.Value);
       linksBulletedList.Items.Add(item);
   }
}

 

The logic is pretty straightforward. We first clear the BulletedList items, so that the hard-coded items don’t appear. Then we loop through all KeyValuePair objects of our ListLinks dictionary and for each pair we add a new ListItem. For the text property of ListItem we use the key of our KeyValuePair, while for the url we use value of the KeyValuePair.


You can download the control from here: LinksList5.


While we have achieved our goal of exposing a complex, non-string property through our control, this is not a very user friendly way of exposing such properties. First of all, our users would have to know what kind of logic we are applying to represent a dictionary as a string and second, even if they knew the logic, TextBox is not the best GUI element to represent the value of this property. Therefore, in our next sample in topic Implementing TypeEditors for Complex Properties, we are going to use TypeEditor class. TypeEditor class will allow us to provide our users with a user friendly way of editing complex non-string properties.