I have a ComboBox that binds SelectedValue. The SelectedValue on the ComboBox is lost when the ItemsSource for the ComboBox is updated. By "lost" I mean that SelectedIndex becomes -1, SelectedItem becomes null and SelectedValue becomes null.
However, the property in the business object to which SelectedValue is bound does not change.
If I use SelectedItem instead of SelectedValue (which is not an option in my actual application) I do not see the same behavior.
Here is some test code showing the problem. To view the behavior:
- Run the application
- Select a value in the combo boxes
- Click the "Rebuild Lists" button.
The second ComboBox will become blank. Here is some additional interesting information:
- If you examine the second ComboBox in Snoop the ComboBox will suddenly display the selected item correctly.
- After the error occurs, if you get the BindingExpression for SelectedValue in the second ComboBox and call UpdateTarget() on it, the ComboBox will once again have the correct selection.
Here is the sample code:
Xaml
<Windowx:Class="WPFExperiments.ComboBoxSelectedValue"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="ComboBoxSelectedValue"SizeToContent="WidthAndHeight"x:Name="root"><Grid><Grid.RowDefinitions><RowDefinitionHeight="Auto"/><RowDefinitionHeight="Auto"/><RowDefinitionHeight="Auto"/><RowDefinitionHeight="Auto"/><RowDefinitionHeight="Auto"/></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinitionWidth="Auto"/><ColumnDefinitionWidth="Auto"/><ColumnDefinitionWidth="Auto"/></Grid.ColumnDefinitions><TextBlockGrid.Row="0"Grid.Column="0">SelectedItem:</TextBlock><TextBlockGrid.Row="1"Grid.Column="0">SelectedValue (no converter):</TextBlock><ComboBoxGrid.Row="0"Grid.Column="1"Margin="10,0,0,0"Width="100"ItemsSource="{Binding ElementName=root, Path=PrimaryColorList}"SelectedItem="{Binding ElementName=root, Path=ChildSelectedItem.FavoriteColor}"/><ComboBoxx:Name="cbNoConverter"Grid.Row="1"Grid.Column="1"Margin="10,0,0,0"Width="100"ItemsSource="{Binding ElementName=root, Path=PrimaryColorListEnumValues}"DisplayMemberPath="Display"SelectedValue="{Binding ElementName=root, Path=ChildSelectedValueNoConverter.FavoriteColor}"SelectedValuePath="Color"ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/><TextBlockGrid.Row="0"Grid.Column="2"Margin="5,0,5,0"Text="{Binding ElementName=root, Path=ChildSelectedItem.FavoriteColor, StringFormat='Favorite: {0}'}"/><TextBlockGrid.Row="1"Grid.Column="2"Margin="5,0,5,0"Text="{Binding ElementName=root, Path=ChildSelectedValueNoConverter.FavoriteColor, StringFormat='Favorite: {0}'}"/><ButtonGrid.Row="3"Grid.ColumnSpan="3"x:Name="test"Content="Rebuild Lists"Click="test_Click"/><ButtonGrid.Row="4"Grid.ColumnSpan="3"x:Name="updateBindingExpressions"Content="Update Binding Expressions"Click="updateBindingExpressions_Click"/></Grid></Window>
Code-Behind
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Shapes;namespace WPFExperiments {///<summary>/// Interaction logic for ComboBoxSelectedValue.xaml///</summary>publicpartialclass ComboBoxSelectedValue : Window {public ComboBoxSelectedValue() { InitializeComponent(); BuildList(false); ChildSelectedItem = new ChildWithFavoriteColor(); ChildSelectedValueNoConverter = new ChildWithFavoriteColor(); }void BuildList(bool includePercula) { List<PrimaryColors> l1 = new List<PrimaryColors>(); l1.Add(PrimaryColors.red); l1.Add(PrimaryColors.blue); l1.Add(PrimaryColors.yellow);if (includePercula) l1.Add(PrimaryColors.percula); PrimaryColorList = l1; List<PrimaryColorEnumValue> l2 = new List<PrimaryColorEnumValue>(); l2.Add(new PrimaryColorEnumValue(PrimaryColors.red)); l2.Add(new PrimaryColorEnumValue(PrimaryColors.blue)); l2.Add(new PrimaryColorEnumValue(PrimaryColors.yellow));if (includePercula) l2.Add(new PrimaryColorEnumValue(PrimaryColors.percula)); PrimaryColorListEnumValues = l2; } privatevoid test_Click(object sender, RoutedEventArgs e) { BuildList(true); }public List<PrimaryColors> PrimaryColorList {get { return (List<PrimaryColors>)GetValue(PrimaryColorListProperty); }set { SetValue(PrimaryColorListProperty, value); } }publicstaticreadonly DependencyProperty PrimaryColorListProperty = DependencyProperty.Register("PrimaryColorList", typeof(List<PrimaryColors>), typeof(ComboBoxSelectedValue),new PropertyMetadata(null));public List<PrimaryColorEnumValue> PrimaryColorListEnumValues {get { return (List<PrimaryColorEnumValue>)GetValue(PrimaryColorListEnumValuesProperty); }set { SetValue(PrimaryColorListEnumValuesProperty, value); } }publicstaticreadonly DependencyProperty PrimaryColorListEnumValuesProperty = DependencyProperty.Register("PrimaryColorListEnumValues", typeof(List<PrimaryColorEnumValue>), typeof(ComboBoxSelectedValue),new PropertyMetadata(null));public ChildWithFavoriteColor ChildSelectedItem {get { return (ChildWithFavoriteColor)GetValue(ChildSelectedItemProperty); }set { SetValue(ChildSelectedItemProperty, value); } }publicstaticreadonly DependencyProperty ChildSelectedItemProperty = DependencyProperty.Register("ChildSelectedItem", typeof(ChildWithFavoriteColor), typeof(ComboBoxSelectedValue),new PropertyMetadata(null));public ChildWithFavoriteColor ChildSelectedValueNoConverter {get { return (ChildWithFavoriteColor)GetValue(ChildSelectedValueNoConverterProperty); }set { SetValue(ChildSelectedValueNoConverterProperty, value); } }publicstaticreadonly DependencyProperty ChildSelectedValueNoConverterProperty = DependencyProperty.Register("ChildSelectedValueNoConverter", typeof(ChildWithFavoriteColor), typeof(ComboBoxSelectedValue),new PropertyMetadata(null));privatevoid updateBindingExpressions_Click(object sender, RoutedEventArgs e) {var be1 = BindingOperations.GetBindingExpression(cbNoConverter, ComboBox.SelectedValueProperty); be1.UpdateTarget(); } } publicenum PrimaryColors { red, yellow, blue, percula }publicclass ChildWithFavoriteColor : DependencyObject {public PrimaryColors FavoriteColor {get { return (PrimaryColors)GetValue(FavoriteColorProperty); }set { SetValue(FavoriteColorProperty, value); } }publicstaticreadonly DependencyProperty FavoriteColorProperty = DependencyProperty.Register("FavoriteColor", typeof(PrimaryColors), typeof(ChildWithFavoriteColor),new PropertyMetadata(PrimaryColors.red)); }publicclass PrimaryColorEnumValue {publicstring Display { get; set; }public PrimaryColors Color { get; set; }public PrimaryColorEnumValue(PrimaryColors color) { Color = color; Display = Color.ToString(); }publicoverridestring ToString() {return Display; } } }
Thanks for any help you can offer,
David Cater