Связывание WPF ComboBox с пользовательским списком

У меня есть ComboBox, который, похоже, не обновляет SelectedItem / SelectedValue.

ComboBox ItemsSource привязан к свойству classа ViewModel, в котором перечислены записи записей телефонной книги RAS в виде CollectionView. Затем я связал (в разное время) и SelectedItem или SelectedValue с другим свойством ViewModel. Я добавил MessageBox в команду save для отладки значений, заданных привязкой данных, но привязка SelectedItem / SelectedValue не установлена.

Класс ViewModel выглядит примерно так:

 public ConnectionViewModel { private readonly CollectionView _phonebookEntries; private string _phonebookeEntry; public CollectionView PhonebookEntries { get { return _phonebookEntries; } } public string PhonebookEntry { get { return _phonebookEntry; } set { if (_phonebookEntry == value) return; _phonebookEntry = value; OnPropertyChanged("PhonebookEntry"); } } } 

Коллекция _phonebookEntries инициализируется в конструкторе из бизнес-объекта. ComboBox XAML выглядит примерно так:

  

Меня интересует только фактическое строковое значение, отображаемое в ComboBox, а не любые другие свойства объекта, так как это значение, которое мне нужно передать в RAS, когда я хочу сделать VPN-соединение, поэтому DisplayMemberPath и SelectedValuePath – это имя свойство ConnectionViewModel. ComboBox находится в DataTemplate примененном к ItemsControl в окне, у которого DataContext установлен в экземпляр ViewModel.

ComboBox отображает список элементов правильно, и я могу выбрать его в пользовательском интерфейсе без проблем. Однако, когда я отображаю окно сообщения из этой команды, свойство PhonebookEntry по-прежнему имеет начальное значение, а не выбранное значение из ComboBox. Другие экземпляры TextBox обновляются и отображаются в MessageBox.

Что мне не хватает с привязкой к ComboBox? Я много искал и не могу найти ничего, что я делаю неправильно.


Это поведение, которое я вижу, однако это не работает по какой-то причине в моем конкретном контексте.

У меня есть MainWindowViewModel, который имеет CollectionView для ConnectionViewModels. В исходном коде MainWindowView.xaml я установил DataContext в MainWindowViewModel. MainWindowView.xaml имеет ItemsControl связанный с коллекцией ConnectionViewModels. У меня есть DataTemplate, который содержит ComboBox, а также некоторые другие TextBoxes. Text="{Binding Path=ConnectionName}" поля привязаны непосредственно к свойствам ConnectionViewModel, используя Text="{Binding Path=ConnectionName}" .

 public class ConnectionViewModel : ViewModelBase { public string Name { get; set; } public string Password { get; set; } } public class MainWindowViewModel : ViewModelBase { // List... public CollectionView Connections { get; set; } } 

Код кода XAML:

 public partial class Window1 { public Window1() { InitializeComponent(); DataContext = new MainWindowViewModel(); } } 

Тогда XAML:

        

TextBoxes все связывают правильно, и данные перемещаются между ними и ViewModel без проблем. Это только ComboBox, который не работает.

Вы правы в своем предположении относительно classа PhonebookEntry.

Предположение, которое я делаю, заключается в том, что DataContext, используемый моим DataTemplate, автоматически устанавливается через иерархию привязки, поэтому мне не нужно явно устанавливать его для каждого элемента в ItemsControl . Это показалось мне немного глупым.


Вот тестовая реализация, которая демонстрирует проблему, основанную на примере выше.

XAML:

              

Код :

 namespace WpfApplication7 { ///  /// Interaction logic for Window1.xaml ///  public partial class Window1 : Window { public Window1() { InitializeComponent(); DataContext = new MainWindowViewModel(); } } public class PhoneBookEntry { public string Name { get; set; } public PhoneBookEntry(string name) { Name = name; } } public class ConnectionViewModel : INotifyPropertyChanged { private string _name; public ConnectionViewModel(string name) { _name = name; IList list = new List { new PhoneBookEntry("test"), new PhoneBookEntry("test2") }; _phonebookEntries = new CollectionView(list); } private readonly CollectionView _phonebookEntries; private string _phonebookEntry; public CollectionView PhonebookEntries { get { return _phonebookEntries; } } public string PhonebookEntry { get { return _phonebookEntry; } set { if (_phonebookEntry == value) return; _phonebookEntry = value; OnPropertyChanged("PhonebookEntry"); } } public string Name { get { return _name; } set { if (_name == value) return; _name = value; OnPropertyChanged("Name"); } } private void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler PropertyChanged; } public class MainWindowViewModel { private readonly CollectionView _connections; public MainWindowViewModel() { IList connections = new List { new ConnectionViewModel("First"), new ConnectionViewModel("Second"), new ConnectionViewModel("Third") }; _connections = new CollectionView(connections); } public CollectionView Connections { get { return _connections; } } } } 

Если вы запустите этот пример, вы получите поведение, о котором я говорю. TextBox обновляет привязку при редактировании, но ComboBox этого не делает. Очень запутанное видение, поскольку действительно единственное, что я сделал, представляет родительский ViewModel.

В настоящее время я испытываю впечатление, что элемент, связанный с дочерним элементом DataContext, имеет этот ребенок как свой DataContext. Я не могу найти документацию, которая очищает это так или иначе.

То есть,

Окно -> DataContext = MainWindowViewModel
..Items -> Связано с DataContext.PhonebookEntries
…. Item -> DataContext = PhonebookEntry (неявно связанный)

Я не знаю, объясняет ли это мое предположение лучше (?).


Чтобы подтвердить мое предположение, измените привязку TextBox к

  

И это покажет, что корень привязки TextBox (который я сравниваю с DataContext) является экземпляром ConnectionViewModel.

Вы устанавливаете DisplayMemberPath и SelectedValuePath в «Name», поэтому я предполагаю, что у вас есть class PhoneBookEntry с публичным именем свойства.

Установили ли вы DataContext объект ConnectionViewModel?

Я скопировал ваш код и внес некоторые незначительные изменения, и, похоже, он работает нормально. Я могу установить свойства viewModels PhoneBookEnty и выбранный элемент в изменениях combobox, и я могу изменить выбранный элемент в свойствах combobox и view, свойство PhoneBookEntry установлено правильно.

Вот мой контент XAML:

         

И вот мой код:

 namespace WpfApplication6 { ///  /// Interaction logic for Window1.xaml ///  public partial class Window1 : Window { public Window1() { InitializeComponent(); ConnectionViewModel vm = new ConnectionViewModel(); DataContext = vm; } private void Button_Click(object sender, RoutedEventArgs e) { ((ConnectionViewModel)DataContext).PhonebookEntry = "test"; } } public class PhoneBookEntry { public string Name { get; set; } public PhoneBookEntry(string name) { Name = name; } public override string ToString() { return Name; } } public class ConnectionViewModel : INotifyPropertyChanged { public ConnectionViewModel() { IList list = new List(); list.Add(new PhoneBookEntry("test")); list.Add(new PhoneBookEntry("test2")); _phonebookEntries = new CollectionView(list); } private readonly CollectionView _phonebookEntries; private string _phonebookEntry; public CollectionView PhonebookEntries { get { return _phonebookEntries; } } public string PhonebookEntry { get { return _phonebookEntry; } set { if (_phonebookEntry == value) return; _phonebookEntry = value; OnPropertyChanged("PhonebookEntry"); } } private void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler PropertyChanged; } } 

Изменить: второй пример Geoffs, похоже, не работает, что кажется мне немного странным. Если я изменил свойство PhonebookEntries на ConnectionViewModel на тип ReadOnlyCollection , привязка TwoWay свойства SelectedValue в combobox отлично работает.

Может быть, проблема с CollectionView? Я заметил предупреждение в выходной консоли:

System.Windows.Data Warning: 50: Использование CollectionView напрямую не поддерживается полностью. Основные функции работают, хотя с некоторыми неэффективными, но расширенные функции могут сталкиваться с известными ошибками. Подумайте об использовании производного classа, чтобы избежать этих проблем.

Edit2 (.NET 4.5) . Содержимое DropDownList может быть основано на ToString (), а не на DisplayMemberPath, а DisplayMemberPath указывает член только для выбранного и отображаемого элемента.

Чтобы связать данные с ComboBox

 List ListData = new List(); ListData.Add(new ComboData { Id = "1", Value = "One" }); ListData.Add(new ComboData { Id = "2", Value = "Two" }); ListData.Add(new ComboData { Id = "3", Value = "Three" }); ListData.Add(new ComboData { Id = "4", Value = "Four" }); ListData.Add(new ComboData { Id = "5", Value = "Five" }); cbotest.ItemsSource = ListData; cbotest.DisplayMemberPath = "Value"; cbotest.SelectedValuePath = "Id"; cbotest.SelectedValue = "2"; 

ComboData выглядит так:

 public class ComboData { public int Id { get; set; } public string Value { get; set; } } 

У меня было то, что сначала казалось одинаковой проблемой, но оказалось, что это связано с проблемой совместимости с NHibernate / WPF. Проблема была вызвана тем, как WPF проверяет равенство объектов. Мне удалось заставить мои вещи работать, используя свойство ID объекта в свойствах SelectedValue и SelectedValuePath.

  

Для получения дополнительной информации см. Сообщение в блоге от Chester, WPF ComboBox – SelectedItem, SelectedValue и SelectedValuePath с NHibernate .

У меня была аналогичная проблема, когда SelectedItem никогда не обновлялся.

Моя проблема заключалась в том, что выбранный элемент не был тем же экземпляром, что и элемент, содержащийся в списке. Поэтому мне просто пришлось переопределить метод Equals () в MyCustomObject и сравнить идентификаторы этих двух экземпляров, чтобы сообщить ComboBox, что это тот же объект.

 public override bool Equals(object obj) { return this.Id == (obj as MyCustomObject).Id; } 
  • WPF привязка к локальной переменной
  • Обход проблемы из-за отсутствия оператора 'nameof' в C # для безопасного хранения данных по типу?
  • WPF привязка данных к интерфейсу, а не к фактическому объекту - возможность литья?
  • Когда следует использовать # и = в элементах управления ASP.NET?
  • Обнаружение ошибок проверки WPF
  • Как обнаружить сломанные привязки данных WPF?
  • WPF перед записью
  • Связывающие свойства в коде
  • ItemsControl с несколькими DataTemplates для viewmodel
  • Похоже, что привязки данных не обновляются
  • Image UriSource и привязка данных
  • Давайте будем гением компьютера.