我有一个似乎没有更新 SelectedItem/SelectedValue 的组合框。
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
都是的 Name 属性连接视图模型。ComboBoxDataTemplate
应用于ItemsControl
其 DataContext 已设置为 ViewModel 实例的 Window 上。
ComboBox 正确显示项目列表,我可以在 UI 中毫无问题地选择一个。但是,当我从命令中显示消息框时,PhonebookEntry 属性仍然具有初始值,而不是 ComboBox 中的选定值。其他 TextBox 实例正在更新并显示在 MessageBox 中。
对 ComboBox 进行数据绑定时我缺少什么?我做了很多搜索,似乎找不到任何我做错的事情。
这是我看到的行为,但是在我的特定上下文中由于某种原因它不起作用。
我有一个 MainWindowViewModel,它有一个CollectionView
ConnectionViewModels。在 MainWindowView.xaml 文件代码隐藏中,我将 DataContext 设置为 MainWindowViewModel。MainWindowView.xamlItemsControl
绑定到 ConnectionViewModels 的集合。我有一个包含 ComboBox 以及其他一些 TextBoxes 的 DataTemplate。文本框直接绑定到 ConnectionViewModel 的属性,使用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}" />
文本框都正确绑定,数据在它们和 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。我找不到任何可以以一种或另一种方式解决此问题的文档。
IE,
Window -> DataContext = MainWindowViewModel
..Items -> 绑定到 DataContext.PhonebookEntries
....Item -> DataContext = PhonebookEntry(隐式关联)
我不知道这是否更好地解释了我的假设(?)。
为了确认我的假设,将 TextBox 的绑定更改为
<TextBox Text="{Binding Mode=OneWay}" Width="50" />
这将显示 TextBox 绑定根(我将其与 DataContext 进行比较)是 ConnectionViewModel 实例。