将WPFcombobox绑定到自定义列表

我有一个ComboBox似乎并没有更新SelectedItem / SelectedValue。

ComboBox ItemsSource被绑定到ViewModel类的一个属性上,它将一大堆RAS电话簿条目列为一个CollectionView,然后我将SelectedItemSelectedValue绑定到ViewModel的另一个属性(在不同的时间)。 我在Save命令中添加了一个MessageBox来debugging由数据绑定设置的值,但是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 ItemsSource="{Binding Path=PhonebookEntries}" DisplayMemberPath="Name" SelectedValuePath="Name" SelectedValue="{Binding Path=PhonebookEntry}" /> 

我只对ComboBox中显示的实际string值感兴趣,而不是对象的任何其他属性,因为这是我想要build立VPN连接时需要传递给RAS的值,因此DisplayMemberPathSelectedValuePath都是Name ConnectionViewModel的属性。 ComboBox在一个DataTemplate应用于一个窗口上的ItemsControl ,它的DataContext已经被设置为一个ViewModel实例。

ComboBox显示正确的项目列表,我可以select一个没有问题的用户界面。 但是,当从命令显示消息框时,PhonebookEntry属性仍然具有初始值,而不是从ComboBox中select的值。 其他TextBox实例正在更新并显示在MessageBox中。

我用数据绑定ComboBox丢失了什么? 我做了很多search,似乎无法find任何我做错了的事情。


这是我所看到的行为,但是由于某种原因,在我的特定情况下它不起作用。

我有一个MainViewViewModel有一个ConnectionViewModels的CollectionView 。 在MainWindowView.xaml文件代码隐藏中,我将DataContext设置为MainWindowViewModel。 MainWindowView.xaml具有绑定到ConnectionViewModel集合的ItemsControl 。 我有一个DataTemplate持有ComboBox以及一些其他文本框。 TextBoxes使用Text="{Binding Path=ConnectionName}"直接绑定到ConnectionViewModel的属性。

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

XAML代码隐藏:

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

然后XAML:

 <DataTemplate x:Key="listTemplate"> <Grid> <ComboBox ItemsSource="{Binding Path=PhonebookEntries}" DisplayMemberPath="Name" SelectedValuePath="Name" SelectedValue="{Binding Path=PhonebookEntry}" /> <TextBox Text="{Binding Path=Password}" /> </Grid> </DataTemplate> <ItemsControl ItemsSource="{Binding Path=Connections}" ItemTemplate="{StaticResource listTemplate}" /> 

文本框全部绑定正确,数据在它们和ViewModel之间移动,没有任何问题。 这只是combobox不工作。

你对PhonebookEntry类的假设是正确的。

我所做的假设是我的DataTemplate使用的DataContext是通过绑定层次自动设置的,所以我不必为ItemsControl每个项目明确地设置它。 这对我来说似乎有点愚蠢。


基于上面的例子,这是一个testing实现,它演示了这个问题。

XAML:

 <Window x:Class="WpfApplication7.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Window.Resources> <DataTemplate x:Key="itemTemplate"> <StackPanel Orientation="Horizontal"> <TextBox Text="{Binding Path=Name}" Width="50" /> <ComboBox ItemsSource="{Binding Path=PhonebookEntries}" DisplayMemberPath="Name" SelectedValuePath="Name" SelectedValue="{Binding Path=PhonebookEntry}" Width="200"/> </StackPanel> </DataTemplate> </Window.Resources> <Grid> <ItemsControl ItemsSource="{Binding Path=Connections}" ItemTemplate="{StaticResource itemTemplate}" /> </Grid> </Window> 

代码隐藏

 namespace WpfApplication7 { /// <summary> /// Interaction logic for Window1.xaml /// </summary> 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<PhoneBookEntry> list = new List<PhoneBookEntry> { 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<ConnectionViewModel> connections = new List<ConnectionViewModel> { 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
.. Item – >绑定到DataContext.PhonebookEntries
…. Item – > DataContext = PhonebookEntry(隐式关联)

我不知道这是否解释了我的假设更好?


为了确认我的假设,将TextBox的绑定更改为

 <TextBox Text="{Binding Mode=OneWay}" Width="50" /> 

这将显示TextBox绑定根(我正在比较DataContext)是ConnectionViewModel实例。

您将DisplayMemberPath和SelectedValuePath设置为“名称”,所以我假设您有一个具有公共属性名称的PhoneBookEntry类。

你有没有设置DataContext到你的ConnectionViewModel对象?

我复制你的代码,并做了一些小的修改,似乎工作正常。 我可以设置ViewModels PhoneBookEnty属性和combobox中选定的项目更改,我可以更改combobox中选定的项目和视图模型PhoneBookEntry属性设置正确。

这是我的xaml:

 <Window x:Class="WpfApplication6.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <StackPanel> <Button Click="Button_Click">asdf</Button> <ComboBox ItemsSource="{Binding Path=PhonebookEntries}" DisplayMemberPath="Name" SelectedValuePath="Name" SelectedValue="{Binding Path=PhonebookEntry}" /> </StackPanel> </Grid> </Window> 

这里是我的代码背后:

 namespace WpfApplication6 { /// <summary> /// Interaction logic for Window1.xaml /// </summary> 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<PhoneBookEntry> list = new List<PhoneBookEntry>(); 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; } } 

编辑:杰夫第二个例子似乎不工作,这似乎有点奇怪的我。 如果将ConnectionViewModel上的PhonebookEntries属性更改为ReadOnlyCollectiontypes ,则ComboBox的SelectedValue属性的TwoWay绑定可以正常工作。

也许有一个与CollectionView的问题? 我注意到输出控制台的一个警告:“System.Windows.Data警告:50:直接使用CollectionView并不完全支持。基本function虽然效率低下,但高级function可能会遇到已知错误,请考虑使用派生类避免这些问题。“

Edit2(.Net 4.5): DropDownList的内容可以基于ToString()而不是DisplayMemberPath,而DisplayMemberPath仅指定所选和显示的项目的成员。

将数据绑定到ComboBox

 List<ComboData> ListData = new List<ComboData>(); 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; } } 

我起初似乎是一个相同的问题,但事实certificate是由于NHibernate / WPF兼容性问题。 这个问题是由WPF检查对象相等的方式引起的。 我能够通过使用SelectedValue和SelectedValuePath属性中的对象ID属性来使我的东西工作。

 <ComboBox Name="CategoryList" DisplayMemberPath="CategoryName" SelectedItem="{Binding Path=CategoryParent}" SelectedValue="{Binding Path=CategoryParent.ID}" SelectedValuePath="ID"> 

有关详细信息,请参阅以下来自Chester的链接: 使用NHibernate的WPFcombobox – SelectedItem,SelectedValue和SelectedValuePath

我有一个类似的问题,SelectedItem从未得到更新。

我的问题是,所选项目不是列表中包含的项目相同的实例。 所以我只需要重写我的MyCustomObject中的Equals()方法,并比较这两个实例的ID来告诉ComboBox它是同一个对象。

  public override bool Equals(object obj) { return this.Id == (obj as MyCustomObject).Id; }