Pages

Monday 2 January 2012

WPF - Change the color of combo item dynamically

Hi everyone!

Today we are going to see how we can change the color of particular Combo box item.

We will need to controls for this sample:
  • Combo box
  • Color picker control
We will use WPF toolkit for color picker control. WPF toolkit can be downloaded from here. Once downloaded, unzip the file - it will extract 'WPFToolkit.Extended.dll'. Add reference to this DLL in our application.

Now, add both the controls to application. To use color picker control, we need to refer the downloaded DLL. Here is code snippet for adding these controls (with addition text block for usability perspective):
<Window x:Class="ComboboxWithColoredItems.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:toolkit="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit.Extended"
        Title="Colored rows in ComboBox" Height="150" Width="400">
    <Grid>
  <Grid.RowDefinitions>
   <RowDefinition Height="45"></RowDefinition>
   <RowDefinition Height="auto"></RowDefinition>
  </Grid.RowDefinitions>
  <Grid.ColumnDefinitions>
   <ColumnDefinition Width="*"></ColumnDefinition>
   <ColumnDefinition Width="60"></ColumnDefinition>
  </Grid.ColumnDefinitions>
  <TextBlock Text="Please select the item in combo box, and select the color for this item from colorpicker."
       Grid.ColumnSpan="2" TextWrapping="Wrap"></TextBlock>
  <ComboBox Grid.Row="1" HorizontalAlignment="Left" Width="320" 
      Grid.Column="0" x:Name="SampleCombo">
  </ComboBox>
  <toolkit:ColorPicker ShowAdvancedButton="False" Grid.Row="1" Grid.Column="1"
        Width="50" HorizontalAlignment="Left" Margin="5,0,0,0"></toolkit:ColorPicker>
 </Grid>
</Window>
Now, we need to bind some data with this combo box. Let's create one data class - which will have two fields:
  • Name - text to be displayed as combo item
  • MyBackColor - Solid color brush - the color for this combo item
Here is the MyData class:
 public class MyData : INotifyPropertyChanged
 {
  public MyData()
  {
   // Make the item transparent by default.
   MyBackColor = new SolidColorBrush(Colors.Transparent);
  }
  public string Name { get; set; }
  private SolidColorBrush _backColor;
  public SolidColorBrush MyBackColor
  {
   get
   {
    return _backColor;
   }
   set
   {
    _backColor = value;
    OnPropertyChanged("MyBackColor");
   }
  }
  /// 
  /// Called when [property changed].
  /// 
  /// 
Name of the property.  void OnPropertyChanged(string propertyName)
  {
   if (!string.IsNullOrEmpty(propertyName) && PropertyChanged != null)
   {
    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
   }
  }
  public event PropertyChangedEventHandler PropertyChanged;
 }
Observe that we have implemented INotifyPropertyChanged interface, which we will be using to notify that some value in referenced object is changed.

Now, we will be adding one List of MyData objects to xaml.cs file, which we will use as items source of the combo box. Also, we will initialize this list in constructor of xaml:
public partial class MainWindow : Window, INotifyPropertyChanged
 {
  public MainWindow()
  {
   InitializeComponent();
   this.DataContext = this;
   InitializeData();
  }

  private void InitializeData()
  {
   _dataList = new List();
   _dataList.Add(new MyData { Name = "Mr. Abc" });
   _dataList.Add(new MyData { Name = "Mr. Def" });
   _dataList.Add(new MyData { Name = "Mr. Ghi" });
   _dataList.Add(new MyData { Name = "Mr. Jkl", MyBackColor = new SolidColorBrush(Colors.SeaShell) });
   _dataList.Add(new MyData { Name = "Mr. Mno" });
   _dataList.Add(new MyData { Name = "Mr. Pqr" });
  }

  private List _dataList;

  public List DataList
  {
   get
   {
    return _dataList;
   }
   set
   {
    _dataList = value;
    OnPropertyChanged("DataList");
   }
  }

  private MyData _selectedData = new MyData();
  public MyData SelectedData 
  {
   get
   {
    return _selectedData;
   }
   set
   {
    _selectedData = value;
    OnPropertyChanged("SelectedData");
   }
  }

  void OnPropertyChanged(string propertyName)
  {
   if (!string.IsNullOrEmpty(propertyName) && PropertyChanged != null)
   {
    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
   }
  }
  public event PropertyChangedEventHandler PropertyChanged;
 }
We are now ready with our sample data and required controls. Now, we need to specify that the selected combo item's background color should be inline with the associated MyData object's MyBackColor property. For this, we will define the data template for combo box item:
 <Window.Resources>
  <DataTemplate x:Key="ColoredTemplate">
   <Grid Background="{Binding Path=MyBackColor}" Width="310" >
    <TextBlock Text="{Binding Name}" Margin="10,0,0,0"/>
   </Grid>
  </DataTemplate>
 </Window.Resources>
And change the combo box to refer this style, and the data list as items source:
<ComboBox Grid.Row="1" HorizontalAlignment="Left" Width="320" Grid.Column="0" x:Name="SampleCombo"
   ItemTemplate="{StaticResource ColoredTemplate}"
   SelectedItem="{Binding Path=SelectedData, Mode=TwoWay}"
   ItemsSource="{Binding Path=DataList}">
</ComboBox> 

Now, what we need to do is - when user changes the color in color picket, we will change the MyBackColor  property of selected item's associated MyData object using this new color. For this, let's use SelectedColorChanged event of color picker in xaml.cs file:
private void ColorPicker_SelectedColorChanged(object sender, RoutedPropertyChangedEventArgs e)
  {
   // Prepare new brush and assign it to the currently selected items's back color property.
   ((SampleCombo.SelectedItem) as MyData).MyBackColor = new SolidColorBrush(e.NewValue);
  }

And specify this event in Xaml also:
  <toolkit:ColorPicker SelectedColorChanged="ColorPicker_SelectedColorChanged" 
        ShowAdvancedButton="False" Grid.Row="1" Grid.Column="1"
        Width="50" HorizontalAlignment="Left" Margin="5,0,0,0"></toolkit:ColorPicker>

Yup, we are done now. Whenever we change the color in color picker, the background color of selected combo item will also be changed:

And, all the rows can have their own color:

Enjoy!