A way to specify UI elements as properties declaratively in XAML

Here is an screenshot of an icon button type UserControl I developed in Silverlight:

This shows nine instances of the same UserControl, which is composed of a TextBlock and a panel containing an icon. The content of both of these are specified via properties. The challenge was to enable the panel to be specified declaratively in XAML. The problem was that you can’t use UI elements as static resources in Silverlight. For example, the following approach results in a runtime error:

<local:IconButton IconPanel="{StaticReource RectangleIcon}" Command="AddRectangle" Caption="Rectangle"/>

However, using a TypeConverter, I devised a way to allow the control to be specified like this:

<local:IconButton IconPanel="RectangleIcon" Command="AddRectangle" Caption="Rectangle"/>

A good general introduction to using Typeconverters with XAML is in Umair Saeed’s blog. The implementation of the TypeConverter class here is adapted from the generic example on that page.

//The TypeConverter class is in the System.ComponentModel namespace. The CultureInfo class is in System.Globalization.

public class IconConverter : TypeConverter
{
   public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
   {
      if (sourceType == typeof(string))
      {
         return true;
      }
      return base.CanConvertFrom(context, sourceType);
   }

   public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
   {
      string strValue;
      if (value is string)
      {
         strValue = (string)value;
         if (strValue == "RectangleIcon")
            return Icons.Rectangle; 
         else
            return new Grid();
      }
      return base.ConvertFrom(context, culture, value);
   }

The Icons class just gives a convenient way of referring to the Icons. The code below can return a UserControl, or the root element from it. Note that in the latter case that you need to remove the root element from the created UserControl Instance, or an error will be generated: A UI element cannot be contained in multiple UI elements.

The RectangleIcon class it refers to is a UserControl just containing the Grid with the rectangle.

Lastly, here is how the implementation of the IconPanel property in the IconButton class:

private Panel _iconPanel;

[TypeConverter(typeof(IconConverter))]
public Panel IconPanel
{
   get
   {
      return _iconPanel;
   }
   set
   {
       _iconPanel = value;
       IconHolder.Content = _iconPanel;
    }
}

IconHolder is an empty UserControl which will hold the icon. Adding it directly as a child to the root element is also possible – however you need to make sure the names of the panels are different. Otherwise you may try to add a grid named LayoutRoot to another grid named LayoutRoot – which results in the error “Value does not fall within expected range” being thrown at runtime.

 

[Edited 21 July 2019 – restored image and code excerpts]