Silverlight Color Picker ComboBox (DropDownList)

During a Silverlight project I finished up a while ago I needed to create a "simple" Color Picker ComboBox (or DropDownList for us ASP.NET Developers).  By "simple" I mean limited to only a handful of colors, not as complex as something like Paint.NET (read PhotoShop) would provide.  This is the easiest solution that I found.

The first thing you need to know is that the ComboBox has an ItemTemplate.  This template allows you to completely control the look and feel of each entry in the ComboBox.  From simple things like binding to a property to change the background color (hint hint, that is what we are about to do), to more complex things like changing the entire template based on the type of class being bound to this item in the ComboBox (ex: the ComboBox is bound to a list of "fruits" some are apples and some are bananas.  The apples all use one template, the bananas use a completely different template - powerful).

We'll be binding this ComboBox to a list of strings.  Each string we be the Hex representation of an eventual Color in the ComboBox.  Here is the ViewModel that holds the List (and "SelectedColor" property that holds the currently selected color)


   10 public class MyViewModel
   11 {
   12     public string SelectedColor { get; set; }
   13 
   14     public IList<string> Colors { get; set; }
   15 
   16     public MyViewModel()
   17     {
   18         Colors = new List<string>() {
   19             "000000",//black
   20             "404040",//gray
   21             "808080",//light gray
   22             "FFFFFF",//white
   23             "402000",//brown
   24             "FF0000",//red
   25             "FF8000",//orange
   26             "FFFF00",//yellow
   27             "00FF00",//green
   28             "008000",//dark green
   29             "0080FF",//light blue
   30             "0000FF",//blue
   31             "400080",//purple
   32             "8000FF",//light purple
   33             "FF00FF",//pink
   34             "FF80FF"//light pink
   35             };
   36     }
   37 }


Then we'll need to create a new IValueConverter that will assist us in binding the strings to the ComboBox (and more specifically the Background Brush in the ItemTemplate).  We can call the IValueConverter simply, StringToBrushConverter.  IValueConverter is a fairly easy interface to implement, it only has two methods, Covert and ConvertBack.  (As far as I can tell, the ColorConverter class is not in Silverlight yet, so the code is a bit messy for converting back and forth, but it'll do.)


   10 public class StringToBrushConverter : IValueConverter
   11 {
   12     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
   13     {
   14         string colorString = value as string;
   15         if (String.IsNullOrEmpty(colorString))
   16             return new SolidColorBrush(Color.FromArgb(255, 0, 0, 0));
   17 
   18         try
   19         {
   20             Color c = new Color();
   21             c.A = 255;
   22             c.R = byte.Parse(colorString.Substring(0, 2), NumberStyles.HexNumber);
   23             c.G = byte.Parse(colorString.Substring(2, 2), NumberStyles.HexNumber);
   24             c.B = byte.Parse(colorString.Substring(4, 2), NumberStyles.HexNumber);
   25 
   26             return new SolidColorBrush(c);
   27         }
   28         catch
   29         {
   30             return new SolidColorBrush(Color.FromArgb(255, 0, 0, 0));
   31         }
   32     }
   33 
   34     public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
   35     {
   36         //NOTE "value as Color" - doesn't work
   37 
   38         if (value is Color)
   39         {
   40             Color c = (Color)value;
   41             return c.ToString();
   42         }
   43 
   44         return "000000";
   45     }
   46 }


Okay, so we've got our data, and the converter, so now we just need to wire it up.  First we add the StringToBrushConverter as a Resource for the UserControl.  Then we define the ComboBox's ItemTemplate.  Now at first I thought I could use a simple Canvas in the ItemTemplate and set its Background to a SolidColorBrush that was the same color as the string, but for some reason in my project that was just not working.  The Canvas was not aligning properly in the ComboBox.  When I pulled the code out and put it into its own solution, it worked fine.  I ended up Placing a Grid in the ItemTemplate and setting its background color, then placing a single TextBlock inside the grid and setting its Height and Width to size the grid....  try the Canvas first, but if that doesn't work for you I've included the "alternate solution".

Note the use of "Binding ." in the Binding statement of the Background property.  That means Bind to the "entire" current DataContext itself and not a property of the current DataContext.  (the ComboBox binds to the "Colors" collection, each Item binds to a individual string in the collection, so saying "Binding ." is saying bind to that string.  A more common scenario might be to bind to a List of Employees, and bind each Item to the "Name" property.  If you said "Binding ." in that case, you would be binding to a single Employee instance in the Employees collection)


   10 public partial class MainPage : UserControl
   11 {
   12     public MainPage()
   13     {
   14         InitializeComponent();
   15 
   16         this.Loaded += (s, e) =>
   17         {
   18             this.DataContext = new MyViewModel();
   19         };
   20     }
   21 }


   10 <UserControl x:Class="Adh.Silverlight.MainPage"
   11    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   12    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   13    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   14    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   15    xmlns:adh="clr-namespace:Adh.Silverlight"
   16    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
   17     <UserControl.Resources>
   18         <adh:StringToBrushConverter x:Key="StringToBrushConverter">adh:StringToBrushConverter>
   19     UserControl.Resources>
   20     <Grid x:Name="LayoutRoot">
   21         <ComboBox ItemsSource="{Binding Colors}" SelectedItem="{Binding Color, Mode=TwoWay}" Width="140" Height="24">
   22             <ComboBox.ItemTemplate>
   23                 <DataTemplate>
   24                     <Canvas Background="{Binding ., Converter={StaticResource StringToBrushConverter}}" Width="100" Height="18">Canvas>
   25                    
   28                 DataTemplate>
   29             ComboBox.ItemTemplate>
   30         ComboBox>
   31     Grid>
   32 UserControl>


And there it is, that's all there is to it.  Hope you can find a use for this somewhere.

-

Comments

This comment has been removed by the author.
No matter what I do the background color remain white, it changes only if I put a Text box in the Grid within DataTemplate. However, I lose functionality of clicking and selecting the item.
Aaron Hoffman said…
Make sure you are putting a TextBlock in the grid, not a TextBox.

Popular posts from this blog

Search iPhone Text Messages with SQLite SQL Query

Configure SonarAnalyzer.CSharp with .editorconfig, no need for SonarCloud or SonarQube

Edit Default Visual Studio 2012 Item and Project Templates