WPF: Tips - Combobox Matching Complex Type
https://msdnshared.blob.core.windows.net/media/2016/05/0640_NinjaAwardTinyGold.pngGold Award Winner
This tip is about how the ComboBox matches which item is selected.
Introduction
If you set the SelectedItem on a combobox then this will make that item appear in it. What can trip you up is exactly how it does that matching.
Let's put a quick sample together to experiment with this.
Experiment: Simple Type
Start a new solution up and paste this into MainWindow:
<StackPanel>
<ComboBox Name="cb"/>
<Button Content="Select One"
Click="Button_Click"
/>
</StackPanel>
Change the code behind:
public MainWindow()
{
InitializeComponent();
cb.ItemsSource = new List<string> { "a", "b", "c" };
}
private void Button_Click(object sender, RoutedEventArgs e)
{
cb.SelectedItem = "c";
}
Spin that up with an f5.
Initially, there will of course be nothing selected on the ComboBox.
Click the button.
C will appear in the ComboBox.
Experiment 2: Complex Type
Add a class "Thing":
public class Thing
{
public string Name { get; set; }
}
OK, that's not a terribly complicated class but it will suffice for our purposes.
Change your code behind to:
public partial class MainWindow : Window
{
private List<Thing> Things = new List<Thing>
{
new Thing{Name="a"},
new Thing{Name="b"},
new Thing{Name="c"}
};
public MainWindow()
{
InitializeComponent();
cb.ItemsSource = Things;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
cb.SelectedItem = new Thing { Name = "c" };
}
}
Now we have a complex type, we also have to tell the ComboBox what to do about displaying it, so change the combo to:
<ComboBox Name="cb"
DisplayMemberPath="Name"
/>
Spin that up and click the button.
Nothing happens.
Experiment 3: Same Object
Change that button click to:
private void Button_Click(object sender, RoutedEventArgs e)
{
cb.SelectedItem = Things[1];
}
And try that.
It will come up with "b".
This is because of how it's matching what you give it as the Selecteditem property to what it has in it's Items. If you use the same object as is in the collection then it can match it.
Experiment 4: OverRide Equals
What if it was inconvenient to go find an entry out that collection?
Change that handler back to:
private void Button_Click(object sender, RoutedEventArgs e)
{
cb.SelectedItem = new Thing { Name = "c" };
}
And of course, this is the code you tried before which won't match an entry.
Thing is an Object ( like pretty much everything in .Net ) so it has the methods that Object has. One of those methods is Equals. Under the covers the default behaviour is to see whether the object is exactly the same instance as the one being matched.
We can change that by over-riding the default Equals method and providing logic of our own.
Change Thing to:
public class Thing
{
public string Name { get; set; }
public override bool Equals(Object obj)
{
Thing other = obj as Thing;
if (other == null)
return false;
else
return Name.Equals(other.Name);
}
}
This is now checking to see whether the Name matches.
If you now spin that up and give it a go, you will see "c" appears in the combo when you click the button.
Conclusions
With simple value types then you don't have any complications.
With complex types then you can either make sure you're working with one of those objects the combobox has in it's items or you can override equals.
You could of course use Linq to find a specific item in a collection and use that.
Overriding Equals can be useful when the collection and what you're going to match as selected come from two different sources and is also a reminder inside your object of how equality is supposed to work for it. That can improve readability of your code - which is always a good thing.