anthoLB29
anthoLB29

Reputation: 105

Combobox binding itemsource to custom list and selecteditem to an instance of that list doesn't work

I tried really different ways to make my combobox working but I'm still stuck :(

Here is a very simplified version of my app : (just edited, sorry for mistakes)

<ListView ItemsSource="{Binding People}" SelectedItem="{Binding SelectedPerson}"/>
<ComboBox ItemsSource="{Binding Grades}" SelectedItem="{Binding SelectedPerson.MyGrade}" 
     DisplayMemberPath="Name"/>

And the code behind :

public class Person
{
    private string name;
    public string Name
    {
        get { return name; }
        set
        {
            if (name != value)
            {
                name = value;
                NotifyPropertyChanged("Name");
            }
        }
    }

    private Grade myGrade;
    public Grade MyGrade
    {
        get { return myGrade; }
        set
        {
            if (myGrade != value)
            {
                myGrade = value;
                NotifyPropertyChanged("MyGrade");
            }
        }

    }

    //-- INotifyPropertyChanged implementation
}
public class Grade
{
    private string name;
    public string Name
    {
        get { return name; }
        set
        {
            if (name != value)
            {
                name = value;
                NotifyPropertyChanged("Name");
            }
        }
    }

    private int prop;
    public int Prop
    {
        get { return prop; }
        set
        {
            if (prop != value)
            {
                prop = value;
                NotifyPropertyChanged("Prop");
            }
        }

    }

    //-- INotifyPropertyChanged implementation
}
public partial class MainWindow : Window
{
    public ObservableCollection<Person> People { get; set; }
    public ObservableCollection<Grade> Grades { get; set; }

    private Person selectedPerson;
    public Person SelectedPerson
    {
        get { return selectedPerson; }
        set
        {
            if (selectedPerson != value)
            {
                selectedPerson = value;
                NotifyPropertyChanged("SelectedPerson");
            }
        }
    }

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;

        People = new ObservableCollection<Person>();
        Grades = new ObservableCollection<Grade>();

        Grades.Add(new Grade() { Name = "Grade 1", Prop = 1 });
        Grades.Add(new Grade() { Name = "Grade 2", Prop = 2 });

        People.Add(new Person() { Name = "guy 1", MyGrade = Grades[0] });
        People.Add(new Person() { Name = "guy 2", MyGrade = Grades[0] });
        People.Add(new Person() { Name = "guy 3", MyGrade = Grades[1] });
    }

    //-- INotifyPropertyChanged implementation
}

The problem is that the combobox is still empty when I select an item in the listview. The itemsource is OK (If I click on the combobox, I can see "grade 1" and "grade 2"). I think there is something missing to tell "the Person.Grade is part of the Grades list" but I cannot find what.

Hope you can help me ;)

Upvotes: 5

Views: 4086

Answers (3)

Rachel
Rachel

Reputation: 132548

Is the SelectedItem the exact same reference in memory as the item in the ItemsSource?

By default, WPF will compare the SelectedItem to the items in the ItemsSource by reference, and if they're not the same reference in memory, it will return no matching item.

If you are unable to do this in your code, the most common workarounds are to either:

  • Bind the SelectedValue to a value type instead of a reference type, and set the SelectedValuePath

    <ComboBox ItemsSource="{Binding Grades}" 
              SelectedValue="{Binding SelectedPerson.MyGrade.GradeId}" 
              SelectedValuePath="GradeId"
              DisplayMemberPath="Name"/>
    
  • Or override the .Equals() to ensure the two objects are considered equal when specific properties match, as opposed to being considered equal when the reference in memory matches.

    public override bool Equals(object obj) 
    { 
        if (obj == null || !(obj is Grade)) 
            return false; 
    
        return ((Grade)obj).GradeId == this.GradeId); 
    }
    

Upvotes: 8

Richard Vella
Richard Vella

Reputation: 186

Please make sure you have no binding errors in the first place. You can verify this by opening up the output window in Visual studio and check you have no errors messages relating to binding expressions.

You should be able to easily spot these because the error message will contain the following text: BindingExpression path error

As an alternative approach why don't you try and bind the ComboBox selected item directly with the selected item from the ListView.

Below is a sample snippet of how one would achieve this:

<ListView
    x:Name="listView"
    ItemsSource="{Binding People}"
    DisplayMemberPath="Name" />
<ComboBox 
    ItemsSource="{Binding Grades}" 
    SelectedItem="{Binding SelectedItem.MyGrade, ElementName=listView}" 
    DisplayMemberPath="Name"/>

Upvotes: 0

Kevin DiTraglia
Kevin DiTraglia

Reputation: 26058

A couple things, first you are binding to Grade and your property is called MyGrade. Second you are instantiating different objects for grade in the Grades list and using different objects to assign to each person, you want to use the same objects if you want them to map correctly, something like this:

Grades.Add(new Grade() { Name = "Grade 1", Prop = 1 });
Grades.Add(new Grade() { Name = "Grade 2", Prop = 2 });

People.Add(new Person() { Name = "guy 1", MyGrade = Grades[0] });
People.Add(new Person() { Name = "guy 2", MyGrade = Grades[0] });
People.Add(new Person() { Name = "guy 3", MyGrade = Grades[1] });

Lastly you might want to make the Grades collection an ObservableCollection as well if you are going to bind it to the UI. Also it seems a bit odd that your RaisePropertyChanged doesn't take in the name of the property.

There may be other mistakes as well but that is what jumped out to me right away.

Upvotes: 2

Related Questions