Introduction
This article will teach you how to use data binding with ComboBox
es. It walks you through the following examples:
- Displays a
string
value and binds to a string
value, all bindings in XAML. The ComboBox
items collection is defined as a StaticResource
in the application resources in the App.xaml.
- Displays a
string
value and binds to an enum
value, all bindings in XAML. The ComboBox
items collection is defined as a list in the code behind file. In addition, this technique provides an easy way to get friendly translatable names for enum
s.
These two examples will cover the basics on how to get data binding to work with ComboBox
es.
Background
There are many articles on the web that cover the topic of data binding. So why write another one? Good question. ComboBox
es have complications that don’t exist when you are binding to a control like a TextBox
. With all of the articles out there, when I was trying to use a ComboBox
for the first time, I didn't find a single article that clearly explained how to bind a ComboBox
. In addition, I found many questions that didn't have complete answers. After finally figuring out how to get things to work, I decided to share my findings. The main difference with data binding and ComboBox
es are that there are actually 4 bindings that have to be set. This article walks you through, step by step, how to get each of the 4 data bindings to work with ComboBox
es.
Before jumping into working with ComboBox
es, I recommend you first read and understand the basics of data binding. Josh Smith has published several excellent articles on this subject here on CodeProject. This one, A Guided Tour of WPF – Part 3 (Data binding), does an excellent job of explaining the basics of data binding. This one, Moving Toward WPF Data Binding One Step at a Time, walks you through getting data binding working with a TextBox
. It is important to understand the basics covered in these articles before you try and tackle data binding with ComboBox
es.
The Examples in the Demo Application
This article covers the important points of data binding with ComboBox
's in two separate examples. The second example builds off the first, so make sure you check out both examples to get a full understanding of this topic.
Databinding ComboBox Example #1
This example:
- Displays a
string
value and binds to a string
value.
- All bindings are done in XAML.
- The
ComboBox
items collection is defined as an application resource in the App.xaml file.
- Note: The
TextBlock
beneath the ComboBox
is bound to the same property as the ComboBox
to prove the data binding is working. When you select a new value in the ComboBox
the data object will be updated, it will send a property changed notification, the TextBlock
will receive the property changed notification and it will be updated also.
Before looking at the bindings of the ComboBox
, let's look at what we are going to be filling it with. First we create an array of three objects, of type ComboBoxItemString
(which is a class defined in this project).The objects ValueString
properties are set to: Red
, Green
, and Blue
respectively.This collection is defined as a StaticResource
in the application resources in the App.xaml file like this:
<Application.Resources>
<x:Array x:Key="ColorListString" Type="local:ComboBoxItemString"/>
<local:ComboBoxItemString ValueString = "Red" />
<local:ComboBoxItemString ValueString = "Green"/>
<local:ComboBoxItemString ValueString = "Blue"/>
</x:Array>
</Application.Resources>
Now let's look at the ComboItemString
class we are using above. It is defined in this project like this:
namespace ComboBoxDataBindingExamples
{
public class ComboBoxItemString
{
public string ValueString { get; set; }
}
}
You can't get a much simpler class than this. All it has is one public
property called ValueString
of type string
. So why is it needed?
This is where things get interesting, read this carefully. In order for data binding to work correctly, you need a 'property' to bind to. If we had an array of string
s what property would we bind to? There aren't any. The only property on the string
class is 'Length
' and we don't want to bind to that. This is one of the issues that confused me when I first started trying to bind ComboBox
es. To make matters more confusing, you can use an array of string
s to populate the ComboBox
list if you don't use data binding. The reason that works is the ToString
method is called to get the display value of the object. That's okay until you try to bind to it, that's when it doesn't work.
Let me repeat this because it is important, in order for data binding to work, you MUST bind to a PROPERTY on an object. The ComboBoxItemString
class provides us with a property 'ValueString' to bind to.
Now let's look at the ComboBox
definition in the XAML file. Here it is:
<ComboBox ItemsSource="{StaticResource ColorListString}"
DisplayMemberPath="ValueString"
SelectedValuePath="ValueString"
SelectedValue="{Binding ColorString}" />
The ComboBox
has four properties that are set. Let's look at them one at a time.
ItemsSource
- is bound to the static
resource array 'ColorListString
' that we defined above as an application resource in the App.xaml file. This list of items is used to populate the Items
collection of the ComboBox
. This collection is used to generate the items that show up in the dropdown list.
DisplayMemberPath
- is bound to the ValueString
property of the ComboBoxItemString
object, in the ItemsSource
list. When an item is selected from the dropdown list, this is the value that is displayed to the user.
SelectedValuePath
- is bound to ValueString
property of the ComboBoxItemString
object, in the ItemsSource
list. When an item is selected from the dropdown list, this is the property used to get the value to set the SelectedItem
value to.
SelectedValue
- is bound using a property binding of "{Binding ColorString}
". When an item is selected in the list, this is the property on the data object that is set to the value returned from the SelectedValuePath
property.
Looking at the binding above, you might think that the DisplayMemberPath
and the SelectedValuePath
are redundant since they bind to the same property, but they aren't. In the next example, you will see that we make use of the fact that these properties can be bound to two different values. Stay tuned.
But there are still two missing pieces. Do you know what they are? One of them is we haven't defined the data object we will be binding to and the other is that we haven't set the DataContext
yet. All data binding are based on binding to a property on an object. The DataContext
must be set to the object that contains the property you want to bind.
So what object are we bound to? Let's take a look. The rest is done in the code behind file. Here it is:
using System.Windows;
namespace ComboBoxDataBindingExamples
{
public partial class Example1Window : Window
{
private ViewModelString viewModelString = new ViewModelString();
public Example1Window()
{
DataContext = viewModelString;
InitializeComponent();
}
}
}
Could it be that this is the entire code behind file? It is except for some comments. This file only does a couple of things. Let's look at them one at a time.
- It has a
private
member viewModelString
object which it assigns to a new instance of the ViewModelString
class.
- The
DataContext
is set to be the viewModelString
member that is part of this class. This means that the ComboBox
's SelectedValue
property binding is going to bind to a property called 'ColorString
' on the ViewModelString
class.
Ok, but what does the ViewModelString
object look like. This is the final piece, hang in there. We know it has to have a public
property called 'ColorString
' or our binding won't work. Here is the entire ViewModelString
class.
using System.ComponentModel;
namespace ComboBoxDataBindingExamples
{
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
}
private string _colorString = "";
public string ColorString
{
get { return _colorString; }
set
{
if (_colorString != value)
{
_colorString = value;
NotifyPropertyChanged("ColorString");
}
}
}
#region INotifyPropertyChanged Members
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
This class contains the following items:
- A
void
constructor so the class can be used as an object element in the XAML.
- A
string
property, named 'ColorString
', that is used to do the data binding to the ComboBox
.
- A
NotifyPropertyChanged
property which is required in order to get your bindings to work.
That's it. That wasn't that bad, was it.
Now we will build on this example by adding a few additional twists.
Databinding ComboBox Example #2
This example:
- Displays a
string
value and binds to an enum
value.
- All binding are done in XAML.
- The
ComboBox
items collection is defined as a list in the code behind file.
- This technique also provides an easy way to get friendly translatable names for
enum
s.
- Note: The
TextBlock
beneath the ComboBox
is bound to the same property as the ComboBox
to prove the data binding is working. When you select a new value in the ComboBox
the data object will be updated, it will send a property changed notification, the TextBlock
will receive the property changed notification and it will be updated as well.
Just like in the first example, we need to create an array of objects to display in ComboBox
. This time, we will create the list in the code behind file since it introduces a different twist when dealing with the bindings. We'll look at this in more detail a little later.This is what the code looks like:
ColorListEnum = new List<ComboBoxItemColor>()
{
new ComboBoxItemColor(){ ValueColorEnum = ViewModelEnum.Colors.Red,
ValueColorString = Properties.Resources.RedText },
new ComboBoxItemColor(){ ValueColorEnum = ViewModelEnum.Colors.Green,
ValueColorString = Properties.Resources.GreenText },
new ComboBoxItemColor(){ ValueColorEnum = ViewModelEnum.Colors.Blue,
ValueColorString = Properties.Resources.BlueText },
};
As you can see ,we are using a different object in this list than what we used in the first example. Let's take a look at the ComboBoxItemColor
class we are using above. It is defined in this project like this:
namespace ComboBoxDataBindingExamples
{
public class ComboBoxItemColor
{
public ViewModelEnum.Colors ValueColorEnum { get; set; }
public string ValueColorString { get; set; }
}
}
Just like in the previous example, this is a very simple class. This class has two public
properties called ValueColorString
(type string
) and ValueColorEnum
(type ViewModelEnum.Colors
). Since we want to display a string
value and bind to an enum
value, we need these two properties.
Now let's look at the ComboBox
definition in the XAML file. Here it is:
<ComboBox ItemsSource="{Binding ColorListEnum}"
DisplayMemberPath="ValueColorString"
SelectedValuePath="ValueColorEnum"
SelectedValue="{Binding ViewModelEnum.ColorEnum}" />
Do you notice anything different about these bindings compared to the first example? There are some important differences. Let's look at them one at a time.
ItemsSource
- is bound using a property binding of "{Binding ColorListEnum}
". In the previous example, we had bound this to a static
resource.
DisplayMemberPath
- is bound to the ValueColorString
property of the ComboBoxItemEnum
object, in the ItemsSource
list. Similar to the last example.
SelectedValuePath
- is bound to the ValueColorEnum
property of the ComboBoxItemString
object, in the ItemsSource
list. This is different than it was in the last example. We are binding to a different property than the DisplayMemberPath
. This binding sets the SelectedItem
to be the enum
value while the display value is the string
value. This explains why there are two properties.
SelectedValue
- is bound using a property binding of "{Binding ViewModelEnum.ColorEnum}
". At first glance, this might look similar to the first example, but there is an important difference. Do you see it? In this example, we are binding to a property (ColorEnum
) of a property (ViewModelEnum
), but why? The answer has to do with the change to our ItemsSource
binding and a change to our DataContext
, keep reading.
Just like in the first example, the rest is done in the code behind file. Here it is:
using System.Windows;
using System.Collections.Generic;
namespace ComboBoxDataBindingExamples
{
public partial class Example2Window : Window
{
public List<ComboBoxItemColor> ColorListEnum { get; set; }
public ViewModelEnum ViewModelEnum { get; set; }
private ViewModelEnum viewModelEnum = new ViewModelEnum();
public Example2Window()
{
ViewModelEnum = viewModelEnum;
ColorListEnum = new List<ComboBoxItemColor>()
{
new ComboBoxItemColor(){ ValueColorEnum = ViewModelEnum.Colors.Red,
ValueColorString = Properties.Resources.RedText },
new ComboBoxItemColor(){ ValueColorEnum = ViewModelEnum.Colors.Green,
ValueColorString = Properties.Resources.GreenText },
new ComboBoxItemColor(){ ValueColorEnum = ViewModelEnum.Colors.Blue,
ValueColorString = Properties.Resources.BlueText },
};
DataContext = this;
InitializeComponent();
}
}
}
This class is a lot different than in the first example. Let's look at things one at a time.
ColorListEnum
- This property is the property the ItemsSource
binds to. This replaces the StaticResource
. It is sometimes useful to be able to bind to a collection that is defined in code behind. This is how you do it. Notice that this is a property and not a data member, this enables the binding.
ViewModelEnum
- The SelectedValue
property binds to the ColorEnum
property through this property. Notice the SelectedValue
binding is set to "{Binding ViewModelEnum.ColorEnum}
". In this case, we are binding to a property of a property.
viewModelEnum
- This is the data member the above property is set to. Remember you cannot bind directly to a data member.
ColorListEnum
- is initialized to a List
containing three ComboBoxItemColor
objects. This is equivalent to the static
resource in the first example.
DataContext
- is set to this window.
Wait a minute. In the first example, the DataContext
was set to the view model class. Couldn't we have done that here. No we couldn't. Can you figure out why? It's because we are NOT binding the ItemsSource
to a static
resource. Confused yet? It was the first time I tried this.
This is one of the most confusing parts of data binding and ComboBox
's. Let's walk through it slowly. You only have one DataContext
for a ComboBox
, or any other control for that matter. This means (if your ItemsSource
is being bound to a code behind property) that the DataContext
of your ComboBox
must contain two properties for your ComboBox
to bind to. The first property is for your ItemsSource
collection (in this case ColorListEnum
) and a second property for your SelectedValue
property (in this the ColorEnum
property on the ViewModelEnum
property). This is an important difference when binding a ComboBox
versus a simple control like a TextBox
.
Now we will take a look at the ViewModelEnum
class. Here it is in its entirety.
using System.ComponentModel;
namespace ComboBoxDataBindingExamples
{
public class ViewModelEnum : INotifyPropertyChanged
{
public enum Colors
{
Red = 1,
Green = 2,
Blue = 3,
}
public ViewModelEnum()
{
}
private ViewModelEnum.Colors _colorEnum = ViewModelEnum.Colors.Red;
public ViewModelEnum.Colors ColorEnum
{
get { return _colorEnum; }
set
{
if (_colorEnum != value)
{
_colorEnum = value;
NotifyPropertyChanged("ColorEnum");
}
}
}
#region INotifyPropertyChanged Members
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
The big difference here from the first example is that this class contains the enum
we want to store in the view model. This same technique could be used if the enum
were defined in a separate class.
That's it. You now know everything you need to know when it comes to data binding and ComboBox
es.
Using the Code
I have included the Visual Studio 2010 project that contains all of the source code you need to run these examples. I have also included the application if you just want to run it.
History