다음을 통해 공유


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.

See Also

WPF Tips
WPF Resources on the Technet Wiki