I've hit a problem with WPF combo boxes and changing the data source after the user has pressed characters that don't appear in the data.
<Label Name="FavoriteFoodLbl" Grid.Column="0" Grid.Row="13">Favorite Food</Label>
<ComboBox Name="FavoriteFoodCombo" Grid.Column="1" Grid.Row="13" ItemsSource="{Binding Foods}" SelectedItem="{Binding FavoriteFood, UpdateSourceTrigger=PropertyChanged}" />
<Label Name="FavoriteFlavourLbl" Grid.Column="2" Grid.Row="13">Favorite Flavour</Label>
<ComboBox Name="FavoriteFlavourCombo" Grid.Column="3" Grid.Row="13" ItemsSource="{Binding Flavours}" />
Code....
public ObservableCollection<string> Foods { get; set; } = new ObservableCollection<string>() { "Pizza", "Ice Cream", "Soup" };
private string _favoriteFood;
public string FavoriteFood
{
get { return _favoriteFood; }
set
{
_favoriteFood = value;
switch (_favoriteFood)
{
case "Pizza":
_flavours = new ObservableCollection<string>(PizzaToppings);
break;
case "Ice Cream":
_flavours = new ObservableCollection<string>(IceCreamFlavours);
break;
case "Soup":
_flavours = new ObservableCollection<string>(SoupFlavours);
break;
default:
_flavours = new ObservableCollection<string>(new List<string>() { "None" });
break;
}
this.FavoriteFlavour = null;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("FavoriteFood"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Flavours"));
}
}
public List<string> PizzaToppings { get; set; } = new List<string>() { "Margarita", "Pepperoni", "Meat Feast" };
public List<string> IceCreamFlavours { get; set; } = new List<string>() { "Vanilla", "Strawberry", "Chocolate" };
public List<string> SoupFlavours { get; set; } = new List<string>() { "Tomato", "Leek and Potato", "Chicken" };
private ObservableCollection<string> _flavours = null;
public ObservableCollection<string> Flavours
{
get
{
return _flavours;
}
set
{
_flavours = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Flavours"));
}
}
string _favoriteFlavour;
public string FavoriteFlavour
{
get
{
return _favoriteFlavour;
}
set
{
_favoriteFlavour = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("FavoriteFlavour"));
}
}
The problem only occurs when the user selects the value in the second combo box by typing (rather than via a mouse click). Steps to reproduce:
- Select a favorite food e.g. pizza
- Use the tab key to move to the second combo
- Press the first letter of a known option e.g. press 'P' for "Pepperoni"
- Press another letter for which there are no options starting with it i.e. press 'e', as if you were still typing "Pepperoni"
- Now choose a different favorite food e.g. Ice Cream. The value "Pepperoni" should disappear from the second box.
- Now with the mouse, try to choose a favorite flavour of ice cream.
At this point I get an exception being thrown...
Activated Event Time Duration Thread
Cannot save value from target back to source. BindingExpression:Path=IsDropDownOpen; DataItem='ComboBox' (Name='FavoriteFlavourCombo'); target element is 'ToggleButton' (Name='toggleButton'); target property is 'IsChecked' (type 'Nullable`1') NullReferenceException:'System.NullReferenceException: Object reference not set to an instance of an object.
at System.Windows.Controls.ComboBox.CoerceIsSelectionBoxHighlighted(Object o, Object value)
at System.Windows.DependencyObject.ProcessCoerceValue(DependencyProperty dp, PropertyMetadata metadata, EntryIndex& entryIndex, Int32& targetIndex, EffectiveValueEntry& newEntry, EffectiveValueEntry& oldEntry, Object& oldValue, Object baseValue, Object controlValue, CoerceValueCallback coerceValueCallback, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, Boolean skipBaseValueChecks)
at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
at System.Windows.DependencyObject.CoerceValue(DependencyProperty dp)
at System.Windows.Controls.ComboBox.OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
at MS.Internal.Data.PropertyPathWorker.SetValue(Object item, Object value)
at MS.Internal.Data.ClrBindingWorker.UpdateValue(Object value)
at System.Windows.Data.BindingExpression.UpdateSource(Object value)' 28.20s
I have uploaded a sample to github: https://github.com/DanMcCoy/ComboBoxBugDemo This sample uses framework 4.6.1. I am using Visual Studio Enterprise 2015 (14.0.25425.01 Update 3).
There's a video in the GitHub repo demonstrating the problem.