In WPF, we bind collections of objects to ListBox
es and the like all the time – it's part and parcel of the WPF development cycle but this is something that stung me recently.
I bound a collection of objects to the ItemsSource
property as usual but the selection was odd. Every time I selected an item, the ListBox
assumed that the first item was also selected and subsequent selections marked all previous selections as selected, also the SelectedIndex
was always 0
. It took some time but I tracked it down to the way that ListBox
es use equality.
Something I didn’t know was that the objects that I was using had an overridden Equals
method which was simply comparing a single string
on the objects to determine if they were equal. Because I only wanted a small subset of the object, I only populated those properties I needed along with the id of the object and not the string
being compared.
The below code shows an example of what I was doing.
public class MainViewModel : INotifyPropertyChanged
{
private ObservableCollection foos;
public ObservableCollection Foos
{
get
{
return foos;
}
set
{
foos = value;
}
}
public MainViewModel()
{
this.Foos = new ObservableCollection();
this.Foos.Add(new Foo { id = 1, Display = "First" });
this.Foos.Add(new Foo { id = 2, Display = "Second" });
this.Foos.Add(new Foo { id = 3, Display = "Third" });
this.Foos.Add(new Foo { id = 4, Display = "Fourth" });
this.Foos.Add(new Foo { id = 5, Display = "Fifth" });
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class Foo
{
public int id { get; set; }
public string Display { get; set; }
public string FooCode { get; set; }
public override bool Equals(object obj)
{
return FooCode == (obj as Foo).FooCode;
}
}
As we can see, I have no interest in the FooCode
property and as a result don’t use it in my ViewModel
.
The problem with this is that now the ListBox
has no way of knowing if the objects are different as we have an overriding Equals
method that implements value equality rather than reference equality and because the value is always going to be the same (as I don’t set it), then the ListBox
will assume that every object is essentially the same object.
A simple change to include the FooCode
property in each object in the collection solves this problem and gives us different values for equality testing.
A sample project with 2 lists displaying this different behaviour can be found here.