【发布时间】:2010-10-08 08:25:54
【问题描述】:
我有一个似乎没有更新 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 中显示的实际字符串值感兴趣,而不是对象的任何其他属性,因为这是我想要建立 VPN 连接时需要传递给 RAS 的值,因此 DisplayMemberPath 和SelectedValuePath 都是 ConnectionViewModel 的 Name 属性。 ComboBox 位于 DataTemplate 中,应用于已将 DataContext 设置为 ViewModel 实例的 Window 上的 ItemsControl。
ComboBox 正确显示项目列表,我可以在 UI 中毫无问题地选择一项。但是,当我从命令中显示消息框时,PhonebookEntry 属性仍然具有初始值,而不是 ComboBox 中的选定值。其他 TextBox 实例正在正常更新并显示在 MessageBox 中。
我在对 ComboBox 进行数据绑定时缺少什么?我做了很多搜索,似乎找不到任何我做错的地方。
这是我看到的行为,但是在我的特定上下文中由于某种原因它不起作用。
我有一个 MainWindowViewModel,它有一个 CollectionView 的 ConnectionViewModel。在 MainWindowView.xaml 文件代码隐藏中,我将 DataContext 设置为 MainWindowViewModel。 MainWindowView.xaml 有一个ItemsControl 绑定到 ConnectionViewModels 的集合。我有一个包含 ComboBox 以及其他一些 TextBoxes 的 DataTemplate。文本框使用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}" />
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 不会。看到我真正做的唯一事情就是引入一个父 ViewModel,这非常令人困惑。
我目前的印象是绑定到 DataContext 的子项的项具有该子项作为其 DataContext。我找不到任何可以以一种或另一种方式解决此问题的文档。
即,
窗口 -> DataContext = MainWindowViewModel
..Items -> 绑定到 DataContext.PhonebookEntries
....Item -> DataContext = PhonebookEntry(隐式关联)
我不知道这是否能更好地解释我的假设(?)。
为了确认我的假设,将 TextBox 的绑定更改为
<TextBox Text="{Binding Mode=OneWay}" Width="50" />
这将显示 TextBox 绑定根(我将其与 DataContext 进行比较)是 ConnectionViewModel 实例。
【问题讨论】:
标签: c# wpf data-binding mvvm combobox