Question

I have two object: UserDto and RoleDto. User has a property which is the RoleDto. In my viewmodel I have the following:

public UserDto User
    {
        get { return _user; }
        set
        {
            if (_user == value) return;

            _user = value;
            User.PropertyChanged += UserPropertyChanged;
            OnPropertyChanged("User");
        }
    }
    private UserDto _user;

public IEnumerable<RoleDto> Roles { get; set; } //I load all available roles in here

In the view, I want to select the role that the user belongs. This is how I define the combobox in the view:

<ComboBox Grid.Row="3" Grid.Column="1" Margin="5" ItemsSource="{Binding Roles}" SelectedItem="{Binding User.Role, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Description" />

If I try to create a new user and select a role from the combobox, it is correctly binded to the user. The problem is that when I load a user that already exists, the role is not displayed in the combobox (even the user has a role defined).

Any help please?

Thanks in advance

Was it helpful?

Solution

This is because the reference of RoleDTO that your UserDTO has, does not match any of the RoleDTOs in Roles collection which you set as ItemsSource of ComboBox.

Better define a property on your ViewModel like

    public RoleDTO SelectedRole
    {
        get { return Roles.FirstOrDefault(role => role.Role == User.RoleDto.Role); }
        set { User.RoleDto = value; OnPropertyChanged("SelectedRole"); }
    }

and set it as SelectedItem of you combobox

ItemsSource="{Binding Roles}" SelectedItem="{Binding SelectedRole, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Description" />

OTHER TIPS

In my opinion the second option on this page is the easiest way.

https://rachel53461.wordpress.com/2011/08/20/comboboxs-selecteditem-not-displaying/

You can override the equals property on your object so that it returns true if the items have the same data. Then when the combo box box goes to check to make sure your item is in the selection it will find a match.

The other way to solve this problem is using Converter on Binding. when you use binding to bind SelectedItem, WPF will check the reference of SelectedItem against all objects inside ItemsSource property and of course if there was no match, SelectedItem will be empty. using Converter you can tell WPF that how it should match SelectedItem.
In this case you just need find SelectedItem among ItemsSource and return it to Binding. so follow these steps:
1- Create a class and implement IValueConverter. It has two methods: Convert and ConvertBack
2- for Convert method do something like this:

public class MySelecteItemBindingConverter : IValueConverter
{
    public object Convert(object value, Type targetType, 
        object parameter, CultureInfo culture)
    {
        var mySelectedItem = value as MySelectedItemType;
        var myItemsSource = parameter as List<MySelectedItemType>;
        var matchedItem = myItemsSource.FirstOrDefault(i=>i.Id == mySelectedItem.Id);
        return matchedItem;
    }

    public object ConvertBack(object value, Type targetType, 
        object parameter, CultureInfo culture)
    {
        // Do just like Convert method
    }
}

3- Use this Converter on your Binding like this:

var myBinding = new Binding("YourBindingPath");
myBinding.Converter = new MySelectedItemBindingConverter();
myBinding.ConverterParameter = myItemsSource; //this is List<MySelectedItemType> in this example
myCombo.SetBinding(ComboBox.SelectedItemProperty, myBinding);

Note: if you want to do binding from XAML you can not pass ConverterParameter like this, instead you should create a static list and use that as ItemsSource or use MultiBinding to pass your ConverterParameter using a trick. here there is a good and simple explanation about it: Binding ConverterParameter

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top