Поле со списком Wpf равно нелогичному поведению метода

#wpf #combobox #equals

#wpf #поле со списком #равно

Вопрос:

У меня здесь есть академический вопрос о Combobox контроле. Итак, у меня есть этот элемент управления и есть ComboboxItem класс, экземпляры которого являются элементами в combobox. Я переопределил Equals метод в ComboboxItem классе и пытаюсь наблюдать, что именно в нем происходит. И я вижу неожиданное (как я думаю) поведение. Посмотрите.

Ошибка с кодом:

 [DebuggerDisplay("N = {Name}")]
public class ComboboxItem
{
    public Int32 Id { get; set; }
    public String Name { get; set; }

    // It is not very correctly written but it is not a problem
    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;

        var cmb = obj as ComboboxItem;
        if (cmb == null)
            return false;

        if (Id.Equals(cmb.Id))
            return true;

        return false;
    }
}

public partial class MainWindow
{
    private ComboboxItem _selectedItem;
    private List<ComboboxItem> _source;

    public ComboboxItem SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            _selectedItem = value; 
        }
    }

    public List<ComboboxItem> Source    
    {
        get { return _source; }
        set { _source = value; }
    }

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

        _source = new List<ComboboxItem>
        {
            new ComboboxItem
            {
                Id = 1,
                Name = "Name11"
            },
            new ComboboxItem
            {
                Id = 2,
                Name = "Name22"
            },
            new ComboboxItem
            {
                Id = 3,
                Name = "Name33"
            },
        };

        // Name must be different from "Name33" for better observing
        _selectedItem = new ComboboxItem {Id = 3, Name = "AlmostName33"};
    }       
}
  

XAML:

   <ComboBox ItemsSource="{Binding Source}"
            DisplayMemberPath="Name"
            SelectedItem="{Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
  </ComboBox>
  

Довольно просто, как вы можете видеть. Это действительно работает, т.Е. Элемент с Name = «AlmostName33» будет установлен как SelectedItem in Combobox .


Проблема заключается в Equals методе и количестве его вызовов. Я думал Equals , что будет вызываться только 3 (три!) Раза для сравнения SelectedItem с каждым элементом Combobox . Но он вызывает 6 (шесть!) Раз, а параметр obj очень неожиданный для меня. Подробнее:

1) this = «Name11» obj = «Name11» ———- что это такое? Почему не «AlmostName33»?

2) this = «Name11» obj = «AlmostName33»

3) this = «Name22» obj = «AlmostName33»

4) this = «Name22» obj = «AlmostName33»

5) this = «Name33» obj = «AlmostName33»

6) this = «Name33» obj = «AlmostName33»

Может ли кто-нибудь четко объяснить мне, почему у меня шесть вызовов и почему obj = «Name11» при первом вызове?


Обновить

 _source = new List<ComboboxItem>
{
    new ComboboxItem
    {
        Id = 1,
        Name = "Name11"
    },
    new ComboboxItem
    {
        Id = 2,
        Name = "Name22"
    }

};

var item = new ComboboxItem
{
    Id = 3,
    Name = "Name33"
},

_source.Add(item);
_selectedItem = item;
  

Комментарии:

1. 1 за интересный вопрос.

Ответ №1:

После тестирования вашего кода я вообще не могу проверить ваши результаты. Мои результаты частично соответствуют ожиданиям и выглядят следующим образом:

1) this = «Name11» obj = «Name11» — это потому, что изначально было выбрано «Name11».

2) this = «Name11» obj = «AlmostName33» — это потому, что выбирается «AlmostName33».

3) this = «Name22» obj = «AlmostName33» — это потому, что выбирается «AlmostName33».

4) this = «Name33» obj = «AlmostName33» — это потому, что выбирается «AlmostName33».

5) this = «Name11» obj = «AlmostName33»

6) this = «Name22» obj = «AlmostName33»

7) this = «Name33» obj = «AlmostName33»

8) this = «Name11» obj = «AlmostName33»

9) this = «Name22» obj = «AlmostName33»

10) this = «Name33» obj = «AlmostName33»

Итак, по сути, первый вызов Equals метода был вызван тем "Name11" , что изначально было выбрано значение… вы можете проверить это, установив точку останова в методе, а затем переходя по коду, пока он не выйдет из метода и не вернется к получателю SelectedItem свойств.

Следующие три вызова являются ожидаемыми вызовами, в которых Фреймворк сравнивает потенциальное выбранное значение с каждым элементом в коллекции. Затем что-то идет не так. Я подозреваю, что это как-то связано либо с тем фактом, что вы неправильно реализовали свой Equals метод, либо с тем, что вы пытаетесь выбрать элемент, которого нет в коллекции.

Комментарии:

1. Я могу подтвердить, что Selector (и его производные, такие как ComboBox ) НЕ поддерживает наличие a SelectedItem , которое не является частью Items коллекции. Следует ожидать неожиданного поведения (без каламбура), если это так.

2. Вау! У вас есть даже 10 вызовов. Это еще более странно, чем мои результаты. Если говорить о первом вызове, я проверяю, как вы сказали SelectedItem ComboboxItem , и его значение равно "AlmostName33" , а не "Name11" . Но я думаю, что это правильно, я сам установил его значение в конструкторе. Поэтому вопрос о первом вызове с obj = "Name" остается открытым. Кроме того, я меняю Combobox инициализацию (см. Обновление) и SelectedItem теперь является частью Items коллекции. И снова у меня те же результаты 🙂