Everything you ever needed to know about ComboBoxes, but were afraid to ask...


 

Introduction

A grand title indeed! But I hope you agree, this article covers covers everything you will 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.
 
  

Download the code!

 
This article relates to a Visual Studio WPF project you can download and study.
 
Download: http://code.msdn.microsoft.com/Best-ComboBox-Tutorial-5cc27f82

 

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.
 
<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.
 
<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>
 

3. List<Class> SelectedValue

 
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.
 
<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>
 
How does it know how to display each type of data? You define DataTemplates for each DataType.
 
<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 customizable.
 
<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>

Note we have to help wire back in the ItemsSource, because DataGrid.Columns property doesn't have DataContext.
 
 

12. Using an Object Collection for Options

 
The previous DataGrid examples used a List<string> for the ComboBox Items. This final example goes back to example 3, with the options themselves being 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>

The main purpose of this final DataGrid is to allow 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.

 

Return to Top


Summary

I sincerely hope you found this tutorial useful.

Once you have understood these concepts you will be able to do just about everything you will need to with ComboBoxes, ListBoxes, etc.

Item Controls like these are one of the most powerful and appealing concepts of WPF that make life easy for the developer, and often saves a whole heap of code.

If you liked this tutorial, please also rate the sample project.

Download: http://code.msdn.microsoft.com/Best-ComboBox-Tutorial-5cc27f82

 

 

Return to Top


See Also

 

Return to Top


Community Resources

 

Return to Top