1

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:

  1. Select a favorite food e.g. pizza
  2. Use the tab key to move to the second combo
  3. Press the first letter of a known option e.g. press 'P' for "Pepperoni"
  4. Press another letter for which there are no options starting with it i.e. press 'e', as if you were still typing "Pepperoni"
  5. Now choose a different favorite food e.g. Ice Cream. The value "Pepperoni" should disappear from the second box.
  6. 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.

Dan McCoy
  • 313
  • 2
  • 15
  • As a first ideav(i have not tried your code),what if you check in your `FavoriteFood` setter if value is null? `set { if (value!=null) {_favoriteFood = value;.....}` – Pikoh Dec 07 '16 at 10:41
  • It already does. The switch statement has a default. – Dan McCoy Dec 07 '16 at 10:44
  • Hmm..i downloaded your solution and i can't reproduce the issue... – Pikoh Dec 07 '16 at 10:50
  • It's a strange one. Sometimes it doesn't happen. Try going through the steps 2 or three times. Use the tab key to get to the second box. Once you've selected something, make sure you press another key such as 'z'. – Dan McCoy Dec 07 '16 at 10:53
  • Did you manage to reproduce? – Dan McCoy Dec 07 '16 at 11:14
  • No,not really, I'm sorry. But i suspect it has something to do with the `Keyboard.FocusedElement` property being null somehow. Try to check that you that can reproduce the issue. – Pikoh Dec 07 '16 at 11:20
  • I can reproduce the issue. Every time. It always happens at least the second time around. – Dan McCoy Dec 07 '16 at 11:21
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/130006/discussion-between-dan-mccoy-and-pikoh). – Dan McCoy Dec 07 '16 at 11:23
  • Well, i've tried to reproduce it and it has been impossible. Maybe windows version (mine is windws 10 64 bit? – Pikoh Dec 07 '16 at 11:23
  • What's the Keyboard.FocusedElement a property of? – Dan McCoy Dec 07 '16 at 11:29
  • It's inherent to the wpf Window. Try to add this to the `FavoriteFood` setter after `PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Flavours"));` : `if (Keyboard.FocusedElement==null) { Keyboard.Focus(this.FavoriteFlavourCombo); }` – Pikoh Dec 07 '16 at 11:38
  • No, still crashes – Dan McCoy Dec 07 '16 at 11:43
  • Are you sure you uploaded the right code? I couldn't reproduce the behavior. And also, where is the `ToggleButton`? The exception says something about a `ToggleButton` but there is none. – Yusuf Tarık Günaydın Dec 07 '16 at 11:45
  • Well,as i'm unable to reproduce it,i can't help you farther. I hope someone else could help you in this,i'm sorry. – Pikoh Dec 07 '16 at 11:46
  • Yes, it's the right code. – Dan McCoy Dec 07 '16 at 11:46
  • When you try to reproduce, are you following the steps exactly as in the video? Have you noted the keypresses in the bottom left of the video? – Dan McCoy Dec 07 '16 at 11:47
  • Yes I tried a lot but haven't seen any exception. Do you have any styles or control templates somewhere else? – Yusuf Tarık Günaydın Dec 07 '16 at 11:49
  • No. No styles or templates. Just what's in that project. – Dan McCoy Dec 07 '16 at 11:51
  • Isn't the ToggleButton internal to the ComboBox? – Dan McCoy Dec 07 '16 at 11:52
  • I've seen something similar in the past - I suspect you may be able to work around it by explicitly setting SelectedIndex = -1 and IsDropDownOpen = false on the flavour dropdown when the FavouriteFood is set before raising the Flavours changed event. – Ben Jackson Dec 07 '16 at 13:24
  • I was able to reproduce the issue. Looks like an internal .Net framework issue. – Jamleck Dec 07 '16 at 14:04
  • Have a look at http://stackoverflow.com/questions/29306589/crash-in-combobox-coerce-not-my-code – Jamleck Dec 07 '16 at 14:17

0 Answers0