Search code examples
mvvmuwpuwp-xamltemplate10

Set SelectedItem for UWP ComboBox


A UWP ComboBox ItemsSource displays correctly but the SelectedItem is not selected in the list. Why is that so?

The XAML:

<ComboBox Name="FooComboBox" 
    ItemsSource="{x:Bind ViewModel.Foos}" 
    SelectedItem="{x:Bind ViewModel.Foo,Mode=TwoWay,Converter={StaticResource ChangeTypeConverter}}"/>

ChangeTypeConverter comes from Template10

Foos and the value of the property Foo are set in the ViewModel

public class ViewModel : ViewModelBase
{    
    Foo _Foo = default(Foo);
    public Foo Foo { get { return _Foo; } set { Set(ref _Foo, value); } }
    public ObservableCollection<Foo> Foos = new ObservableCollection<Foo>(FooService.GetFoos());
    public ViewModel()
    {
        Foo = FooService.GetDefaultFoo();

A Foo looks like this

public class Foo
{
    public Guid FooId { get; set; } = Guid.NewGuid();
    public string FooCode { get; set; }

    public override string ToString()
    {
        return FooCode;
    }
}

Yet although FooComboBox correctly renders the list of Foos, the SelectedItem is not set to the current value of the property Foo. Why is that so?


Solution

  • To turn the comments into an answer,

    The SelectedItem should be an actual item inside the ItemsSource list, as determined by the Equals() method. In your case it was a separate instance and although it had the same Id it wasn't considered Equal.

    There are several ways to solve this, perhaps the one that will solve similar issues down the road is to override Equals:

    public class Foo
    {
        ...
        // untested
        public override bool Equals(object obj)
        {
            Foo other = obj as Foo;
            return FooId.Equals(other?.FooId);
        }
    }
    

    but this has implications for the rest of your app. I would only consider using this to solve a SelectedItem issue when Foo is a ViewModel.


    The other solution is to go find the actual item in the source list:

    public ObservableCollection<Foo> Foos = ...;
    public ViewModel()
    {
       var d = FooService.GetDefaultFoo();
       Foo = Foos.FirstOrDefault(f => f.FooId == d.FooId);    
    }