Table of Contents
- Introduction
- Building the Sample
- Description
- 1. List<string> SelectedItem
- 2. List<Class> SelectedItem
- 3. List<Class> SelectedValue
- 4. ComboBox ItemTemplate
- 5. XAML Array - Static XAML Data
- 6. XML Data - XmldataProvider
- 7. Static Class Property
- 8. Composite Collection
- 9. DataGridTemplateColumn CellTemplate
- 10. DataGridTemplateColumn CellEditingTemplate
- 11. DataGridComboBoxColumn
- 12. Using an Object Collection for Options
- Final word
Introduction
This article explains one of my most popular MSDN Samples Gallery projects and covers everything you need to know about ComboBoxes and so much more! It covers populating data from many sources and types, as well as binding and generating list items with
code-behind and MVVM style sources. This applies to any ItemsControl like ListBoxes and also DataGridComboBoxColumns.
Building the Sample
Description
This project contains 12 distinct examples of ComboBox creation.
1. List<string> SelectedItem
This ComboBox is simply populated with a string collection. As a string object is not a Class, SelectedItem and SelectedValue are the same. We bind SelectedItem to a property that is also shared with a label to show the result.
<
TextBlock
Text
=
"Example 1"
VerticalAlignment
=
"Center"
/>
<
ComboBox
ItemsSource
=
"{Binding MyStringOptions}"
Grid.Column
=
"1"
SelectedItem
=
"{Binding SelectedOption1}"
Margin
=
"5"
/>
<
TextBlock
Text
=
"{Binding SelectedOption1}"
Grid.Column
=
"2"
Margin
=
"10,5,5,0"
VerticalAlignment
=
"Center"
/>
2. List<Class> SelectedItem
This ComboBox's ItemsSource is a collection of MyClass, which has Name and Age properties. SelectedItem is again used, but as it is a class, we can now use all the properties of the SelectedItem. Notice we use DisplayMemberPath to define which property
is shown in the list.
<
TextBlock
Grid.Row
=
"1"
Text
=
"Example 2"
VerticalAlignment
=
"Center"
/>
<
ComboBox
Grid.Row
=
"1"
Grid.Column
=
"1"
ItemsSource
=
"{Binding MyClassOptions}"
SelectedItem
=
"{Binding SelectedClass}"
DisplayMemberPath
=
"Name"
Margin
=
"5"
/>
<
TextBlock
Grid.Row
=
"1"
Grid.Column
=
"2"
Margin
=
"10,5,5,0"
VerticalAlignment
=
"Center"
><
Run
Text
=
"{Binding SelectedClass.Name}"
/><
Run
Text
=
" - "
/><
Run
Text
=
"{Binding SelectedClass.Age}"
/>
</
TextBlock
>
This example is similar to the previous, but captures one SelectedValue from the Class, instead of the whole object. When you use SelectedValue on a class object, you need to specify SelectedValuePath.
<
TextBlock
Grid.Row
=
"2"
Text
=
"Example 3"
VerticalAlignment
=
"Center"
/>
<
ComboBox
Grid.Row
=
"2"
Grid.Column
=
"1"
ItemsSource
=
"{Binding MyClassOptions}"
SelectedValuePath
=
"Age"
SelectedValue
=
"{Binding SelectedAge}"
DisplayMemberPath
=
"Name"
Margin
=
"5"
/>
<
TextBlock
Grid.Row
=
"2"
Grid.Column
=
"2"
Margin
=
"10,5,5,0"
VerticalAlignment
=
"Center"
><
Run
Text
=
"Selected age: "
/><
Run
Text
=
"{Binding SelectedAge}"
/>
</
TextBlock
>
4. ComboBox ItemTemplate
What if you want to display more than one property? Instead of using DisplayMemberPath, you can define your own ItemTemplate, and build your list item however you want.
<
ComboBox
. . .
ItemTemplate
=
"{StaticResource Example4ItemTemplate}"
/>
<
DataTemplate
x:Key
=
"Example4ItemTemplate"
>
<
StackPanel
Orientation
=
"Horizontal"
>
<
TextBlock
Text
=
"{Binding Name}"
/>
<
TextBlock
Text
=
", Aged "
/>
<
TextBlock
Text
=
"{Binding Age}"
Grid.Column
=
"1"
/>
</
StackPanel
>
</
DataTemplate
>
5. XAML Array - Static XAML Data
If your ComboBox options are limited, static and not worth coding, or if you are in a hurry, then you can just dump the data into the actual XAML.
<
ComboBox
. . .
ItemsSource
=
"{StaticResource Example9_XamlArray}"
/>
<
x:Array
x:Key
=
"Example9_XamlArray"
Type
=
"sys:String"
xmlns:sys
=
"clr-namespace:System;assembly=mscorlib"
>
<
sys:String
>Bear</
sys:String
>
<
sys:String
>Bird</
sys:String
>
<
sys:String
>Cat</
sys:String
>
<
sys:String
>Cow</
sys:String
>
<
sys:String
>Dog</
sys:String
>
<
sys:String
>Elephant</
sys:String
>
<
sys:String
>Fish</
sys:String
>
<
sys:String
>Goat</
sys:String
>
<
sys:String
>Hamster</
sys:String
>
</
x:Array
>
6. XML Data - XmldataProvider
There are many reasons why your data may be in XML. ComboBoxes can consume your XMl through an XmlDataProvider.
<
ComboBox
. . .
ItemsSource
=
"{Binding Source={StaticResource WorkmenData}}"
DisplayMemberPath
=
"@Name"
SelectedValuePath
=
"@Name"
/>
<
XmlDataProvider
x:Key
=
"WorkmenData"
XPath
=
"Workmen/Man"
>
<
x:XData
>
<
Workmen
xmlns
=
""
>
<
Man
Name
=
"Bob"
/>
<
Man
Name
=
"Charles"
/>
<
Man
Name
=
"Harry"
/>
<
Man
Name
=
"Mark"
/>
<
Man
Name
=
"Nick"
/>
<
Man
Name
=
"William"
/>
</
Workmen
>
</
x:XData
>
</
XmlDataProvider
>
7. Static Class Property
ItemSource binding can even target a static class property, which can either contain preformatted data, or could be used to retrieve data from database or file sources.
<
ComboBox
Grid.Row
=
"6"
Grid.Column
=
"1"
DisplayMemberPath
=
"Model"
Margin
=
"5"
ItemsSource
=
"{Binding Source={x:Static model:StaticData.MyCarsStatic}}"
/>
8. Composite Collection
Composite Collections allow you to group together any collections or objects of different types, into one listable collection.
<
CompositeCollection
x:Key
=
"Example7_CompositeCollection"
>
<
CollectionContainer
Collection
=
"{Binding Source={StaticResource WorkmenData}}"
/>
<
CollectionContainer
Collection
=
"{Binding Source={StaticResource MyCarsCollection}}"
/>
<
ListBoxItem
Background
=
"Yellow"
>Another ListBoxItem</
ListBoxItem
>
</
CompositeCollection
>
<
ComboBox.Resources
>
<
DataTemplate
DataType
=
"Man"
>
<
TextBlock
Background
=
"LightBlue"
Text
=
"{Binding XPath=@Name}"
/>
</
DataTemplate
>
<
DataTemplate
DataType
=
"{x:Type model:MyCar}"
>
<
TextBlock
Background
=
"LightGreen"
Text
=
"{Binding Model}"
/>
</
DataTemplate
>
</
ComboBox.Resources
>
9. DataGridTemplateColumn CellTemplate
With a CellTemplate, you can add your own physical ComboBox to a DataGrid column. This SHOWS the ComboBox, and allows single-click access.
<
DataGridTemplateColumn
Header
=
"Example 9"
>
<
DataGridTemplateColumn.CellTemplate
>
<
DataTemplate
>
<
ComboBox
ItemsSource
=
"{Binding PartIds, RelativeSource={RelativeSource AncestorType=Window}}"
SelectedItem
=
"{Binding PartId, UpdateSourceTrigger=PropertyChanged}"
/>
</
DataTemplate
>
</
DataGridTemplateColumn.CellTemplate
>
</
DataGridTemplateColumn
>
Note the ItemsSource is pointing to a separate source, using RelativeSource AncestorType=Window to get back to the DataContext/ViewModel.
10. DataGridTemplateColumn CellEditingTemplate
This example has both CellTemplate, for a custom label style, and CellEditingTemplate which shows the ComboBox, similar to the default DataGridComboBoxCoilumn, but customisable.
<
DataGridTemplateColumn.CellTemplate
>
<
DataTemplate
>
<
TextBlock
Background
=
"LightBlue"
Foreground
=
"BlueViolet"
Text
=
"{Binding PartId}"
/>
</
DataTemplate
>
</
DataGridTemplateColumn.CellTemplate
>
<
DataGridTemplateColumn.CellEditingTemplate
>
<
DataTemplate
>
<
ComboBox
ItemsSource
=
"{Binding PartIds, RelativeSource={RelativeSource AncestorType=Window}}"
SelectedItem
=
"{Binding PartId, UpdateSourceTrigger=PropertyChanged}"
/>
</
DataTemplate
>
</
DataGridTemplateColumn.CellEditingTemplate
>
11. DataGridComboBoxColumn
This is the standard autogenerated ComboBox column of a DataGrid. CellTemplate defaults to a label, CellEditingTemplate is a ComboBox.
<
DataGridComboBoxColumn
Header
=
"Example 11"
SelectedItemBinding
=
"{Binding PartId, UpdateSourceTrigger=PropertyChanged}"
>
<
DataGridComboBoxColumn.ElementStyle
>
<
Style
TargetType
=
"ComboBox"
>
<
Setter
Property
=
"ItemsSource"
Value
=
"{Binding Path=PartIds, RelativeSource={RelativeSource AncestorType=Window}}"
/>
</
Style
>
</
DataGridComboBoxColumn.ElementStyle
>
<
DataGridComboBoxColumn.EditingElementStyle
>
<
Style
TargetType
=
"ComboBox"
>
<
Setter
Property
=
"ItemsSource"
Value
=
"{Binding Path=PartIds, RelativeSource={RelativeSource AncestorType=Window}}"
/>
</
Style
>
</
DataGridComboBoxColumn.EditingElementStyle
>
</
DataGridComboBoxColumn
>
The previous DataGrid examples used a List<string> for the ComboBox Items. This final example assumes the options are themselves objects with properties.
<
DataGridTemplateColumn.CellTemplate
>
<
DataTemplate
>
<
ComboBox
ItemsSource
=
"{Binding CarParts, RelativeSource={RelativeSource AncestorType=Window}}"
DisplayMemberPath
=
"PartName"
SelectedValuePath
=
"PartID"
SelectedValue
=
"{Binding PartId, UpdateSourceTrigger=PropertyChanged}"
/>
</
DataTemplate
>
</
DataGridTemplateColumn.CellTemplate
>
This final DataGrid also allows inputting new rows of data. When you add a new row of data, you will notice the Model and registration columns don't update with new values. This is because they do not implement INotifyPropertyChanged.
Further reading specifically on ComboBox in DataGrid:
WPF ComboBox Binding in DataGrid
Further reading specifically on ComboBox in DataGrid:
WPF ComboBox Binding in DataGrid
Final word
This article was copied over from MSDN Samples into TechNet wiki, as it is a suitable learning resource for new developers.
This article has been submitted to the TechNet Guru Competition for August as an example of how to contribute. You don't need to think of something new, simply copy in your own work from another source, like an old blog of yours or in my example, an MSDN Samples project.
I hope this sample helps many others. If you like this project, please browse my other MSDN contributions : http://code.msdn.microsoft.com/site/search?f%5B0%5D.Type=User&f%5B0%5D.Value=XAML%20guy