4
votes

I have a ComboBox that has an ItemSource bound to a an ObservableCollection and its SelectedItem property is bound to a property of T. Both properties live on my view model. Everything works great except for one issue that is causing me to tear my hair out. When I set a property on the object bound to the SelectedItem, the ComboBox binding stops working. VS 2012 output window does not report any binding errors and no exceptions are thrown.

XAML

<ComboBox ItemSource="{Binding Path=Connections, Mode=OneWay}"
          SelectedItem="{Binding Path=SelectedConnection, Mode=TwoWay}"
          DisplayMemberPath="Name"/>
<TextBlock Text="{Binding Path=ConnectionName, Mode=TwoWay}" />
<TextBlock Text="{Binding Path=ConnectionGroup, Mode=TwoWay}" />

View Model

private void SaveChanges() 
{
    this.SelectedConnection.Name = this.ConnectionName;
    this.SelectedConnection.Group = this.ConnectionGroup;

    // -- Save the changes
}

Now all of that works just fine. But as soon as SaveChanges() completes the ComboBox starts acting funny. It looks like everything is fine, but if you attempt to change the selection nothing happens; the same item is selected and the setter for "SelectedConnection" does not fire. I tried systematically commenting out code in SaveChanges() and if I leave only a single line that is setting any property on SelectedConnection I see this failure happen.

I also added an event handler in the code behind of the control to handle the SelectionChanged event on the ComboBox. After the break happens, any time I try to change the value via the UI or in Code the event handler fires twice in immediate succession. The first has the event args' AddedItems set with a single value which is the value I just selected and would expect. However the event fires again immediately with only a single item in the RemovedItems collection of the event args. That collection also contains the item I just selected.

I have tried setting the SelectedConnection property to null and then after the method finishes, setting back to the object in question. No luck.

I also saw an old bug post about moving the ItemSource property after the SelectedItem property on my XAML mark up but that had no effect either.

I'm trying to avoid creating a shadow list and trying to keep everything in sync as that was going to be my next try but that is just getting ugly to do something so simple. Any ideas would be greatly appreciated.

If it matters, this is Visual Studio 2012 building to the 4.5 framework.

EDIT

I've now also tried just completely removing the modified object from the ObservableCollection after setting its properties. It still shows as if it was the selected value (its name is immediately visible in the ComboBox) but it no longer shows up in the drop down list. It also will still not allow itself to be "unselected" even though it doesn't exist in ItemSource any more.

** UPDATE **

I changed the method in question to completely null out the ObservableCollection that is bound to the ItemSource and then restore it once the process is over with like so:

private void SaveChanges()
{
    var selected = this.SelectedConnection;

    var name = this.ConnectionName; // -- bound to fields on the view
    var group = this.ConnectionGroup;

    this.Connections = null;
    this.RaisePropertyChanged("Connections"); // -- SelectionChanged fires as expected

    selected.Name = name;
    selected.Group = group;

    this._repository.SaveChanges();

    this.Connections = new ObservableCollection<Connection>(this._repository.AllConnections);
    this.RaisePropertyChanged("Connections"); // -- SelectionChanged event doesn't fire
    this.SelectedConnection = selected;  // -- SelectionChanged event doesn't fire
}

After that is done the ComboBox once again allows me to change the value as you would expect. However the selected item does not actually appear as selected (even though it is correctly set in the View Model). So yeah, it's a really ugly workaround and it creates its own issue as well.

2
Since you're using SelectedItem with a complex type you should make sure you override Equals() so that objects that are the same are selected.James Sampica
Thanks, Jim. I had overridden equals before but no change.Chris
Weird, I couldn't replicate the problem with a simple mockup, is anything happening to the collection itself when you're saving?Chris
No, nothing. The only thing that I thought may be sort of odd was that when I nulled the property that is bound to SelectedItem, I saw that the SelectedItem was null but SelectionBoxItem was still the same old object (even if I had deleted that object from the collection). The Only situation in which any of these problems manifested was when I changed the value of any property on the bound instance directly.Chris
Hmm, is the bound property (the one bound to SelectedItem notifying the UI of the change (provided you've implemented INotifyPropertyChanged). So in my case I was using a standard implementation and calling OnPropertyChanged(); in the setter.Chris

2 Answers

4
votes

Have just had this same issue, and I think I tracked down what was doing on.

It only appeared after I made some recent changes (suggested by the code analysis) to add equals/hashcode/operator overloads for a class that I had as IComparable, and was used in an ObservableCollection for a combobox. I made the hashcode based on a descriptive field of the object. However that field could be user-modified, and after the user modified the value, the combobox broke, being unable to unselect that item anymore.

Given that there are no unchanging fields in this object, there's no way to create a fixed hash code based on the object's properties. However if I remove the hashcode override entirely (or set it to something fixed, like type.GetHashCode), everything works again.

So, check to see if your hashcode is based on whatever value you were changing.

0
votes

I gave up on doing it the way I had originally envisioned. Instead of directly modifying the object in the ItemSource collection, I made the ItemSource collection to be a list of Key/Value pairs and extracted the object I needed from the source list based on the key of the selected pair. I was able to edit that object without issue (as expected). It works now, I just added a layer of code in there that I was hoping to avoid. The UI also allowed me to update the Value side of the pair to reflect changes to the underlying model's "Name" field without giving me the same issues it was previously. I'll just count it as a small win and move on.