我有一个ComboBox,它似乎没有更新SelectedItem / SelectedValue。
ComboBox ItemsSource绑定到ViewModel类上的一个属性,该类将一堆RAS电话簿条目作为CollectionView列出。然后,我(在不同的时间)将ViewModel 的SelectedItem或绑定SelectedValue到了另一个属性。我已经在保存命令中添加了一个MessageBox来调试由数据绑定设置的值,但是未设置SelectedItem/ SelectedValue绑定。
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}" />
我只是在实际字符串值兴趣显示在组合框,而不是对象,因为这任何其他属性是我需要跨传递给RAS时,我想使VPN连接,因此价值DisplayMemberPath和SelectedValuePath是双方的名称属性ConnectionViewModel。ComboBox DataTemplate应用于ItemsControl一个窗口,该窗口的DataContext已设置为ViewModel实例。
DisplayMemberPath
SelectedValuePath
DataTemplate
ItemsControl
ComboBox可以正确显示项目列表,并且我可以在UI中选择一个项目而没有问题。但是,当我从命令显示消息框时,PhonebookEntry属性中仍具有初始值,而不是ComboBox中的选定值。其他TextBox实例可以正常更新并显示在MessageBox中。
数据绑定ComboBox我缺少什么?我已经做了很多搜索,但似乎找不到我做错的任何事情。
这是我所看到的行为,但是由于某种原因,在我的特定情况下它无法正常工作。
我有一个MainWindowViewModel,其中有一个CollectionViewConnectionViewModels。在后面的MainWindowView.xaml文件中,我将DataContext设置为MainWindowViewModel。MainWindowView.xaml ItemsControl绑定到ConnectionViewModels的集合。我有一个包含ComboBox以及其他一些TextBoxes的DataTemplate。使用可以将TextBox直接绑定到ConnectionViewModel的属性Text="{Binding Path=ConnectionName}"。
CollectionView
Text="{Binding Path=ConnectionName}"
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}" />
所有TextBox都正确绑定,并且数据在它们和ViewModel之间移动没有问题。只是ComboBox不起作用。
您对PhonebookEntry类的假设是正确的。
我所做的假设是,我的DataTemplate使用的DataContext是通过绑定层次结构自动设置的,因此我不必为中的每个项目都显式设置它ItemsControl。对我来说,这似乎有点愚蠢。
这是一个基于上述示例的演示该问题的测试实现。
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则不会。真正使我感到困惑的是,我引入了父视图模型。
我目前的工作是给绑定到DataContext的子项的项以该子项作为DataContext的印象。我找不到任何一种可以清除这种情况的文档。
即
窗口-> DataContext = MainWindowViewModel ..Items->绑定到DataContext.PhonebookEntries .... Item- > DataContext = PhonebookEntry(隐式关联)
我不知道这是否可以更好地解释我的假设(?)。
为了确认我的假设,将TextBox的绑定更改为
<TextBox Text="{Binding Mode=OneWay}" Width="50" />
这将显示TextBox绑定根(我正在与DataContext进行比较)是ConnectionViewModel实例。
您将DisplayMemberPath和SelectedValuePath设置为“ Name”,因此我假设您具有带有公用属性Name的PhoneBookEntry类。
您是否已将DataContext设置为ConnectionViewModel对象?
我复制了您的代码,并做了一些小的修改,看来工作正常。我可以设置viewmodels PhoneBookEnty属性,并且更改组合框中的所选项目,并且可以更改组合框中的所选项目,并且正确设置了视图模型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属性更改为ReadOnlyCollection类型 ,则组合框上的SelectedValue属性的TwoWay绑定可以正常工作。
也许CollectionView有问题?我在输出控制台中注意到一条警告:
System.Windows.Data警告:50:不完全支持直接使用CollectionView。基本功能可以运行,尽管效率低下,但是高级功能可能会遇到已知的错误。考虑使用派生类来避免这些问题。
Edit2(.NET 4.5): DropDownList的内容可以基于ToString()而不基于DisplayMemberPath,而DisplayMemberPath仅为所选和显示的项目指定成员。