可观察集合通过 MVVM 绑定不会更新视图
我有以下简化的 ViewModel
public class UserViewModel : IUserViewModel
{
public DelegateCommand<object> NewUser { get; private set; }
public ObservableCollection<UserInfo> UserList { get; set; }
public UserViewModel( ) {
NewUser = new DelegateCommand<object>( OnNewUser );
this.UserList = new ObservableCollection<UserInfo>( );
UserList.Add( new UserInfo( "Peter" );
UserList.Add( new UserInfo( "Paul" );
UserList.Add( new UserInfo( "Mary" );
}
private void OnNewUser( object parameter ) {
UserInfo user = new UserInfo( "User"+DateTime.Now.ToLongTimeString(), 0, true, false, false );
UserList.Add( user );
}
}
此视图模型是(简化的)视图的数据上下文:
public partial class UserView : ViewBase
{
public UserView( UserViewModel viewModel ) {
InitializeComponent( );
this.DataContext = viewModel;
}
}
视图本身包含一个绑定到 UserViewModel 的 UserList 的 ListBox:
<ListBox ItemsSource="{Binding UserList}" DisplayMemberPath="UserID" />
初始化视图模型时,它将用户添加到列表中。该用户显示在列表框中。之后调用 NewCommand 时,新用户将添加到列表中。但列表框不会随着新用户而更新。
我向视图模型和视图添加了一个 CollectionChanged 处理程序。视图处理程序添加到构造函数内,如下所示:
viewModel.UserList.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler( UserList_CollectionChanged );
[编辑]
将新用户添加到集合的命令是由不同视图中的按钮触发的:
public class UserButtonBarViewModel : IUserButtonBarViewModel
{
public UserButtonBarViewModel( IUserButtonBarView view, IUserViewModel mainModel ) {
this.View = view;
NewUser = mainModel.NewUser;
this.View.Model = this;
}
public ICommand NewUser { get; private set; }
public IUserButtonBarView View {get; private set; }
}
UserButtonBarView 如下所示:
<UserControl ...>
<Button Command="{Binding NewUser}" Content="New user"/>
</UserControl>
视图的代码如下:
public partial class UserButtonBarView : IUserButtonBarView
{
public UserButtonBarView( ) {
InitializeComponent( );
}
public IUserButtonBarViewModel Model {
get {
return DataContext as IUserButtonBarViewModel;
}
set {
DataContext = value;
}
}
}
[/编辑]
有趣的一点是,当新用户添加到集合中时,视图模型中的处理程序被执行,但视图的处理程序没有被调用。
为什么视图的处理程序也没有被调用?当视图模型中的集合发生更改时,为什么列表框不会自行更新?
I have the following simplified ViewModel
public class UserViewModel : IUserViewModel
{
public DelegateCommand<object> NewUser { get; private set; }
public ObservableCollection<UserInfo> UserList { get; set; }
public UserViewModel( ) {
NewUser = new DelegateCommand<object>( OnNewUser );
this.UserList = new ObservableCollection<UserInfo>( );
UserList.Add( new UserInfo( "Peter" );
UserList.Add( new UserInfo( "Paul" );
UserList.Add( new UserInfo( "Mary" );
}
private void OnNewUser( object parameter ) {
UserInfo user = new UserInfo( "User"+DateTime.Now.ToLongTimeString(), 0, true, false, false );
UserList.Add( user );
}
}
This view model is the data context for a (simplified) view:
public partial class UserView : ViewBase
{
public UserView( UserViewModel viewModel ) {
InitializeComponent( );
this.DataContext = viewModel;
}
}
The view itself contains a ListBox that is bound to the UserList of the UserViewModel:
<ListBox ItemsSource="{Binding UserList}" DisplayMemberPath="UserID" />
When the view model is initialized it adds a user to the list. This user shows up in the list box. When afterwards the NewCommand is invoked, new users get added to the list. But the list box does not update with the new users.
I added a CollectionChanged handler to the view model as well as to the view. The views handler was added inside the constructor like this:
viewModel.UserList.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler( UserList_CollectionChanged );
[EDIT]
The command to add a new user to the collection is triggered by a button from a different view:
public class UserButtonBarViewModel : IUserButtonBarViewModel
{
public UserButtonBarViewModel( IUserButtonBarView view, IUserViewModel mainModel ) {
this.View = view;
NewUser = mainModel.NewUser;
this.View.Model = this;
}
public ICommand NewUser { get; private set; }
public IUserButtonBarView View {get; private set; }
}
The UserButtonBarView looks like this:
<UserControl ...>
<Button Command="{Binding NewUser}" Content="New user"/>
</UserControl>
And the view's code behind:
public partial class UserButtonBarView : IUserButtonBarView
{
public UserButtonBarView( ) {
InitializeComponent( );
}
public IUserButtonBarViewModel Model {
get {
return DataContext as IUserButtonBarViewModel;
}
set {
DataContext = value;
}
}
}
[/EDIT]
The interesting point is, that the handler in the view model was executed when a new user was added to the collection, but the view's handler was not invoked.
Why gets the view's handler not invoked as well? And why is the list box not updating itself when the collection in the view model is changed?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
听起来您正在某处(可能在 ButtonViewModel 中)创建 ViewModel 的第二个副本,并且您的 NewUser 按钮将用户添加到一个副本,而 ListBox 仍绑定到另一个副本。
你能发布你的NewUser命令代码吗?
It sounds like you are creating a second copy of the ViewModel somewhere (probably in your ButtonViewModel) and your NewUser button is adding a user to one copy while the ListBox remains bound to another copy.
Can you post your NewUser command code?
您可以发布实际添加新用户的代码吗?看起来它可能发生在与 UI 不同的线程上?尝试使用 Dispatcher.Invoke...Can you post the code that actually adds the new user?It seems like it might happen on a different thread than the UI? Try using Dispatcher.Invoke...我认为你需要使用 TwoWay 绑定。
I think you need to use TwoWay binding.
我看到你的 UserList 属性有一个 setter - 当你对用户列表进行更改时,你是清除并添加到当前的 ObservableCollection,还是创建一个新的?
I saw that your UserList property has a setter - when you make changes to the user list are you clearing and adding to the current ObservableCollection, or creating a new one?