我有一个似乎没有更新SelectedItem / SelectedValue的ComboBox .
ComboBox ItemsSource绑定到ViewModel类的属性,该类将一堆RAS电话簿条目列为CollectionView . 然后我(在不同的时间)将 SelectedItem
或 SelectedValue
绑定到ViewModel的另一个属性 . 我在save命令中添加了一个MessageBox来调试数据绑定设置的值,但是没有设置 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中显示的实际字符串值感兴趣,而不是对象的任何其他属性,因为这是我想要 Build VPN连接时需要传递给RAS的值,因此 DisplayMemberPath
和 SelectedValuePath
都是名称ConnectionViewModel的属性 . ComboBox在 DataTemplate
中应用于Window上的 ItemsControl
,其中DataContext已设置为ViewModel实例 .
ComboBox正确显示项目列表,我可以在UI中选择一个没有问题 . 但是,当我从命令中显示消息框时,PhonebookEntry属性仍然具有初始值,而不是ComboBox中的选定值 . 其他TextBox实例正在更新并在MessageBox中显示 .
使用数据绑定ComboBox我错过了什么?我做了很多搜索,似乎找不到任何我做错的事情 .
这是我所看到的行为,但是在我的特定情况下,它不能用于某些原因 .
我有一个MainWindowViewModel,它有一个 CollectionView
的ConnectionViewModel . 在MainWindowView.xaml文件代码隐藏中,我将DataContext设置为MainWindowViewModel . MainWindowView.xaml具有绑定到ConnectionViewModel集合的 ItemsControl
. 我有一个DataTemplate,它包含ComboBox以及其他一些TextBox . 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();
}
}
Then 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>
code-behind :
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 . 我找不到任何能够以这种方式清除它的文档 .
即,
Window - > DataContext = MainWindowViewModel
..Items - >绑定到DataContext.PhonebookEntries
.... Item - > DataContext = PhonebookEntry(隐式关联)
我不知道这是否能更好地解释我的假设(?) .
要确认我的假设,请更改TextBox的绑定
<TextBox Text="{Binding Mode=OneWay}" Width="50" />
这将显示TextBox绑定根(我正在与DataContext进行比较)是ConnectionViewModel实例 .
4 回答
我有一个类似的问题,其中SelectedItem从未更新 .
我的问题是所选项目与列表中包含的项目不是同一个实例 . 所以我只需要覆盖MyCustomObject中的Equals()方法,并比较这两个实例的ID,告诉ComboBox它是同一个对象 .
您将DisplayMemberPath和SelectedValuePath设置为“Name”,因此我假设您有一个具有公共属性Name的PhoneBookEntry类 .
您是否已将DataContext设置为ConnectionViewModel对象?
我复制了你的代码并做了一些小修改,似乎工作正常 . 我可以设置viewmodels PhoneBookEnty属性和组合框中的选定项目更改,我可以更改组合框中的选定项目,并正确设置视图模型PhoneBookEntry属性 .
这是我的XAML内容:
和这是我的代码隐藏:
Edit: 杰夫斯的第二个例子似乎不起作用,这对我来说似乎有点奇怪 . 如果我 change the PhonebookEntries property on the ConnectionViewModel to be of type ReadOnlyCollection ,组合框上的SelectedValue属性的TwoWay绑定工作正常 .
也许CollectionView存在问题?我注意到输出控制台中有一个警告:
Edit2 (.NET 4.5): DropDownList的内容可以基于ToString()而不是DisplayMemberPath,而DisplayMemberPath仅指定所选项目和显示项目的成员 .
将数据绑定到ComboBox
ComboData看起来像:
我最初看起来似乎是一个相同的问题,但结果是由于NHibernate / WPF兼容性问题 . 问题是由WPF检查对象相等性的方式引起的 . 通过使用SelectedValue和SelectedValuePath属性中的对象ID属性,我能够使我的东西工作 .
有关详细信息,请参阅Chester的博客文章,The WPF ComboBox - SelectedItem, SelectedValue, and SelectedValuePath with NHibernate .