将WPF组合框绑定到自定义列表
我有一个ComboBox似乎没有更新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 ConnectionViewModel的属性。 ComboBox位于DataTemplate
中,该DataTemplate
应用于窗口上的ItemsControl
,该窗口的DataContext已设置为ViewModel实例。
ComboBox显示正确的项目列表,我可以在UI中选择一个没有问题的项目。 但是,当我从命令显示消息框时,PhonebookEntry属性仍然具有初始值,而不是ComboBox中的选定值。 其他TextBox实例正在更新并显示在MessageBox中。
数据绑定ComboBox时我错过了什么? 我做了很多搜索,似乎无法找到任何我做错的事情。
这是我看到的行为,但是由于某种原因,在我的特定环境下它不起作用。
我有一个MainWindowViewModel,它具有ConnectionViewModels的CollectionView
。 在MainWindowView.xaml代码隐藏文件中,我将DataContext设置为MainWindowViewModel。 MainWindowView.xaml具有绑定到ConnectionViewModels集合的ItemsControl
。 我有一个DataTemplate包含组合框以及其他一些文本框。 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
每个项目明确地设置它。 这对我来说似乎有点愚蠢。
根据上面的示例,这是一个用于演示问题的测试实现。
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实例。
您将DisplayMemberPath和SelectedValuePath设置为“Name”,因此我假设您有一个具有公共属性Name的PhoneBookEntry类。
你是否将DataContext设置为ConnectionViewModel对象?
我复制你的代码并做了一些小的修改,并且它似乎工作正常。 我可以设置视图模型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;
}
}
编辑: Geoffs第二个例子似乎不起作用,这对我来说似乎有点奇怪。 如果我将ConnectionViewModel上的PhonebookEntries属性更改为ReadOnlyCollection类型 ,则ComboBox的SelectedValue属性的TwoWay绑定可以正常工作。
CollectionView可能存在问题? 我在输出控制台中发现了一条警告:
System.Windows.Data警告:50:不完全支持直接使用CollectionView。 基本功能虽然效率低下,但高级功能可能会遇到已知的错误。 考虑使用派生类来避免这些问题。
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; }
}
我有什么起初似乎是一个相同的问题,但它原来是由于NHibernate / WPF兼容性问题。 这个问题是由WPF检查对象相等的方式引起的。 我能够通过使用SelectedValue和SelectedValuePath属性中的对象ID属性来使我的东西工作。
<ComboBox Name="CategoryList"
DisplayMemberPath="CategoryName"
SelectedItem="{Binding Path=CategoryParent}"
SelectedValue="{Binding Path=CategoryParent.ID}"
SelectedValuePath="ID">
有关详细信息,请参阅Chester的WPF组合框 - SelectedItem,SelectedValue和SelectedValuePath的博客文章。
链接地址: http://www.djcxy.com/p/34571.html上一篇: Binding a WPF ComboBox to a custom list
下一篇: Flex: Custom Item Renderer For Combobox controls truncates text