通知视图模型,一个集合中的对象已被选定
考虑下面的对象,WPF MVVM应用程序的一部分:通知视图模型,一个集合中的对象已被选定
public class MyObject : INotifyPropertyChanged
{
// INotifyPropertyChanged gubbins
private bool _isSelected;
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
}
及其以下视图模型使用:
public class MyViewModel : INotifyPropertyChanged
{
// INotifyPropertyChanged gubbins
private List<MyObject> _myObjects;
public List<MyObject> MyObjects
{
get
{
return _myObjects;
}
set
{
_myObjects = value;
OnPropertyChanged("MyObjects");
}
}
public bool CanDoSomething
{
get
{
return MyObjects.Where(d => d.IsSelected).Count() > 0;
}
}
}
在在这种情况下,我可以跟踪哪些对象已被选中,并选择它们将触发OnPropertyChanged,因此可以通知父视图。
但是,CanDoSomething将始终是错误的,因为我无法触发OnPropertyChanged来创建通知。如果我把它放在MyObject中,它不知道有关该属性的任何内容,因此什么也不知道。没有地方可以将它放入ViewModel中,因为当列表中的对象被选中时没有任何反应。
我试着用ObservableCollection和自定义的“TrulyObservableCollection”(请参阅Notify ObservableCollection when Item changes)替代列表,但都无效。
我怎样才能避免点击事件呢?
我觉得如果我对自己的最终目标有了更好的了解,我可能会推荐更好的方法。有一些东西正在发生,只是感觉有点外出。就像'CanDoSomething'应该是命令对象的一部分。我想知道是否一次选择了多个MyObject
?如果不是的话,我会以完全不同的方式来解决这个问题。
所以无论如何,你想更新CanDoSomething
任何时候属性MyObjects
中的其中一个项目的变化。这听起来像你在某个时候使用ObservableCollection
然后放弃了它。那是个错误。任何时候发生两个事件中的任何一个都需要更新CanDoSomething
;第一种是当项目被添加到MyObjects
或从MyObjects
中删除时,第二种是当MyObjects
中的任何对象的IsSelected
属性发生更改时。对于第一个事件,您需要执行INotifyCollectionChanged
的某些操作,即ObservableCollection
。您已经有了第二个事件,因为对象实现了INotifyPropertyChanged
。所以你只需要结合这两件事情。
在下面的例子中,我把你的代码做了一些修改。首先,我将MyObjects
更改为ObservableCollection<MyObject>
。它没有安装者,因为我发现通常没有很好的理由来改变可观察的集合;只需添加和删除对象。然后在视图模型的构造函数中,我注册了CollectionChanged
事件MyObjects
。在该处理程序中,我抓取添加到集合中的项目,并将其PropertyChanged
事件连接到OnIsSelectedChanged
事件处理程序,并且我从OnIsSelectedChanged
中解除了从该集合中移除的任何对象的PropertyChanged
事件。由于项目已被添加或删除,我们不知道可能是MyObjects
中的对象的状态,因此这是更新CanDoSomething
的好机会,而且我在事件处理程序的底部执行操作。最后,OnIsSelectedChanged
是另一半魔法发生的地方。 MyObjects
中的每个对象都将其PropertyChanged
事件挂接到此事件处理程序。每当任何这些对象的IsSelected
属性更改时,事件处理程序将更新CanDoSomething
。
public class MyViewModel : INotifyPropertyChanged
{
// INotifyPropertyChanged gubbins
public MyViewModel()
{
this._myObjects.CollectionChanged += (o, e) =>
{
if (e.NewItems != null)
{
foreach (var obj in e.NewItems.OfType<MyObject>())
{
obj.PropertyChanged += this.OnIsSelectedChanged;
}
}
if (e.OldItems != null)
{
foreach (var obj in e.OldItems.OfType<MyObject>())
{
obj.PropertyChanged -= this.OnIsSelectedChanged;
}
}
if (e.PropertyName == "IsSelected")
{
this.CanDoSomething = this.MyObjects.Any(x => x.IsSelected);
}
};
}
private readonly ObservableCollection<MyObject> _myObjects =
new ObservableCollection<MyObject>();
public ObservableCollection<MyObject> MyObjects
{
get
{
return _myObjects;
}
}
private void OnIsSelectedChanged(object o, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsSelected")
{
this.CanDoSomething = this.MyObjects.Any(x => x.IsSelected);
}
}
private bool _canDoSomething;
public bool CanDoSomething
{
get { return this._canDoSomething; }
private set
{
if (_canDoSomething != value)
{
_canDoSomething = value;
OnPropertyChanged("CanDoSomething");
}
}
}
}
首先创建一个类定义此附加属性:
public static class ItemClickCommand
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command", typeof(ICommand),
typeof(ItemClickCommand), new PropertyMetadata(null, OnCommandPropertyChanged));
public static void SetCommand(DependencyObject d, ICommand value)
{
d.SetValue(CommandProperty, value);
}
public static ICommand GetCommand(DependencyObject d)
{
return (ICommand)d.GetValue(CommandProperty);
}
private static void OnCommandPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var control = d as ListViewBase;
if (control != null)
control.ItemClick += OnItemClick;
}
private static void OnItemClick(object sender, ItemClickEventArgs e)
{
var control = sender as ListViewBase;
var command = GetCommand(control);
if (command != null && command.CanExecute(e.ClickedItem))
command.Execute(e.ClickedItem);
}
}
然后,只需在您的视图模型此附加属性的委托指令绑定:helper:ItemClickCommand.Command="{Binding MyItemClickCommand}"
你可以找到更详细此博客文章:https://marcominerva.wordpress.com/2013/03/07/how-to-bind-the-itemclick-event-to-a-command-and-pass-the-clicked-item-to-it/
让我知道它是否有效
你可以在MyViewModel中为所有的MyObjects绑定'PropertyChangedEvent',当Model上的IsSelected'属性被改变时它会通知ViewModel,然后你可以为'CanDoSomething提出另一个属性改变的事件表明这可能已经改变了。 –
这基本上是如何在ViewModels之间进行通信的问题。一种方法是传递给每个父项ViewModel,所以你可以在'IsSelected'设置器中调用父ViewModel的方法。其他可能的方法(应该更多)是使用静态事件,它由IsSelected设置器触发,在您的父ViewModel中订阅此事件并在处理器NotifyPropertyChanged(nameof(CanDoSomething))中调用。 – Sinatr
好点,Sinatr。在这篇文章中,Matt Hamilton将父ViewModel注入为一个宿主对象。在没有父级的情况下,子ViewModel确实不能存在的情况下,这是可以接受的(例如在没有父级时间轴的情况下为Tweet)。 http://matthamilton.net/nested-viewmodels – Christoph