0

I would like my objects bound to ComboBox to be identified by Id instead of a reference. Is there anyway to do this in XAML without overriding Equals and GetHashCode?

Minimal example:

XAML:

<ComboBox
    DisplayMemberPath="Name"
    ItemsSource="{Binding People}"
    SelectedValue="{Binding SelectedPerson}"
    SelectedValuePath="Id" />
<Button
    Height="32"
    Click="ButtonBase_OnClick"
    Content="Test assign" />

Code-behind:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private Person _selectedPerson;

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

    public Person SelectedPerson
    {
        get => _selectedPerson;
        set
        {
            _selectedPerson = value;
            OnPropertyChanged(nameof(SelectedPerson));
        }
    }

    public ObservableCollection<Person> People { get; set; } = new ObservableCollection<Person>
    {
        new Person { Id = 1, Name = "Angelina Jolie"},
        new Person { Id = 2, Name = "Brad Pitt"}
    };

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        SelectedPerson = new Person {Id = 1, Name = "Angelina Jolie"};
        // SelectedPerson = People[0]; will work but i don't want that 
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

As you can see I'm assigning SelectedPerson to a new object but with the same Name and Id but it doesn't work. ComboBox still compares by reference.

In Vue and other frameworks, it's possible to specify a "key" used to identify objects while binding. v-bind:key https://v2.vuejs.org/v2/guide/list.html

It works if you override Equals: How do you bind a ComboBox's SelectedItem to an object that is a copy of an item from ItemsSource? but I wonder if there's any way to avoid that.

I was hoping SelectedValuePath will do what I want but I was wrong, it just affects the return value of SelectedValue

tony19
  • 125,647
  • 18
  • 229
  • 307
Konrad
  • 6,385
  • 12
  • 53
  • 96
  • Why are you opposed to using the working solution yo already have that solves the problem perfectly? – Servy Jun 18 '19 at 13:28
  • @Servy which working solution? Do you mean overriding `Equals`? – Konrad Jun 18 '19 at 13:32
  • Do you have another working solution that you're unwilling to use besides that? – Servy Jun 18 '19 at 13:32
  • @Servy I was hoping there's a simpler solution other than overriding `Equals` – Konrad Jun 18 '19 at 13:33
  • 1
    Why is overriding a method not simple enough? It's a single line of code that's only a handful of characters long...I guess two lines of code if you provide a `GetHashCode` implementation as well, which you probably should. – Servy Jun 18 '19 at 13:34
  • It's simple but doing it in XAML would be simpler. – Konrad Jun 18 '19 at 13:36
  • 1
    Can you tell me why u cant use it `SelectedPerson = People[0];` – Avinash Reddy Jun 18 '19 at 13:38
  • 1
    @AvinashReddy because data can be fetched multiple times during application lifetime and addresses will be different. – Konrad Jun 18 '19 at 13:39
  • @Konrad Why do you think it's simpler to define equality for a type every single time you use it instead of one in that type's definition? – Servy Jun 18 '19 at 13:40
  • In Vue and possibly other js frameworks this is possible to specify with an attribute. – Konrad Jun 18 '19 at 13:40
  • You can replace the new object based on id in `People` Collection – Avinash Reddy Jun 18 '19 at 13:42
  • @Servy it seems cleaner to me, but you're right. I was just wondering if there's a different solution – Konrad Jun 18 '19 at 13:43
  • @AvinashReddy that sounds over-complicated. – Konrad Jun 18 '19 at 13:45
  • 1
    @Konrad `SelectedPerson = People[0]` is definitly the right thing to do. You are asking for trouble if you have multiple copies of the same object with different states in this viewmodel. – Firo Jul 14 '22 at 06:28

2 Answers2

1

Is there anyway to do this in XAML without overriding Equals and GetHashCode?

No, there isn't.

You could bind the SelectedValue property of the ComboBox to a int source property, i.e. change the type of SelectedPerson, but if you want to bind the SelectedItem property to a Person property, you'll have to override Equals.

mm8
  • 163,881
  • 10
  • 57
  • 88
1

When you set

SelectedValuePath="Id"

the property that SelectedValue is bound to should be of the same type as Id (in contrast to SelectedItem, which should bind to a property of the item type).

E.g.

SelectedValue="{Binding SelectedPersonId}"

with

public int SelectedPersonId
{
    get => _selectedPersonId;
    set
    {
        _selectedPersonId = value;
        OnPropertyChanged(nameof(SelectedPersonId));
    }
}

You do not need to override Equals and GetHashCode.

Clemens
  • 123,504
  • 12
  • 155
  • 268