views:

166

answers:

2

I've got my DataContext set to a Book object. Book has properties: Title, Category.

I've got a CollectionViewSource "categoryList" that holds a list of Categories.

Question: How do I select the book's Category in this combobox?

<TextBox Text="{Binding Path=Title}"/>

<ComboBox SelectedValuePath="Id" 
          SelectedValue="{Binding Path=Category.Id}" 
          SelectedItem="{Binding Path=Category}"
          ItemsSource="{Binding Source = {StaticResource categoryList}}" 
          DisplayMemberPath="Name" />

The code above displays the Book's title correctly and then it displays the list of category names in the combobox. But it does not select the Book's Category. It instead just selects the first item in the list.

+3  A: 

You're binding too much; you only need to set SelectedValue and SelectedValuePath, or SelectedItem. In this case, it looks like you're actually trying to bind to a specific object. If you're trying to have the ComboBox set the Category property on your Book and the current Book object actually has a reference to a Category instance that's in categoryList, then you should use the SelectedItem binding and remove the bindings for SelectedValue and SelectedValuePath.

Edit

To expand a little on how this is done, SelectedValue is designed to be used when you have a common piece of information to link your bound item with a property on the list source. For instance, let's say I have a Book class with a CategoryID property.

public class Book
{
    public string CategoryID { get; set; }
    public string Title { get; set; }
}

public class CategoryID
{
    public string ID { get; set; }
    public string Name { get; set; }
}

In this case, you would do:

<ComboBox SelectedValue = "{Binding CategoryID}"
          SelectedValuePath = "ID"
          DisplayMemberPath = "Name" />

SelectedItem, on the other hand, is for when the bound instance has an actual reference to (or, more precisely, an object that is equivalent to) an item in the bound list. So, let's assume the Book class actually looks like this:

public class Book
{
    public Category Category { get; set; }
    public string Title { get; set; }
}

In this case, you would do:

<ComboBox SelectedItem = "{Binding Category}"
          DisplayMemberPath = "Name" />

But it's very important to note that this will not work unless the Book class has a reference to the same instance of Category as you have in the list. If the references are different (even if the values on the classes are equal) then this won't work, as the ComboBox won't be able to find the Category referenced in the Book in the list.

The real problem with the way you were binding it up top (by binding to Category.ID) is that you're mixing the schemes. You have a reference, but you're trying to bind on the key instead. All this will do is try to set the value on your reference, it will not try to change the reference on your class.

Adam Robinson
Sorry yes, I just kind of threw it all out there. :) SelectedValue + SelectedValuePath does not work. Nor does SelectedItem on it's own. Or even the SelectedItem + SelectedValuePath as Doug shows below. Ergh. I think the categoryList's categories are somehow interfering with my ability to get the Book's Category (if I just bind directly to Category.Name in a textfield below it I get the wrong value. But if I comment out the binding to categoryList the value is right).Going to try his debug suggestion. Thanks.
Abby Fichtner
This was really helpful, thank you. Although, bizarrely i had to also do isSynchronizedWithCurrentItem=True to get it to work (I'm using SelectedItem as you show above). Not understanding that part.
Abby Fichtner
@Abby: Are you binding this list of `Category` objects to any other data source? If so, then that would make sense.
Adam Robinson
+1  A: 

To describe in code what Adam's talking about:

<ComboBox 
     SelectedValuePath="Id"
     SelectedItem="{Binding Path=Category}"
     ItemsSource="{Binding Source={StaticResource categoryList}}"
     DisplayMemberPath="Name" />

is probably more appropriate. Generally it's better to treat SelectedValue as read-only, and use SelectedItem to choose which item you want to select. When you bind the SelectedItem to the book's Category, it automatically sets the SelectedValue property for you anyway.

If that still doesn't work, you might want to check that your binding to Category is working properly. In particular, adding a DebugConverter works well to ensure the value you expect is being bound. You can see the use of DebugConverter is the answer to this question.

-Doug

Doug
This is true (though in this case `SelectedValuePath` isn't required) only if the bound object shares the same reference with the element in the list. If they're different references but share a common key, then this is incorrect.
Adam Robinson
WOW, thank you for the debug idea - that's awesome. I used it and found that it is somehow setting my Category value (on the ConvertBack) to the 1st item from the ComboBox list. Now I'm really confused. But with a better tool at least, thanks.
Abby Fichtner
and this (just to follow up) was fixed by adding IsSynchronizedWithCurrentItem=True, although I'm not clear why.
Abby Fichtner
No problem Abby - the DebugConverter has saved my life on more than one occasion. :)
Doug