2016-02-29 24 views
0

考虑下面的对象,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)替代列表,但都无效。

我怎样才能避免点击事件呢?

+1

你可以在MyViewModel中为所有的MyObjects绑定'PropertyChangedEvent',当Model上的IsSelected'属性被改变时它会通知ViewModel,然后你可以为'CanDoSomething提出另一个属性改变的事件表明这可能已经改变了。 –

+1

这基本上是如何在ViewModels之间进行通信的问题。一种方法是传递给每个父项ViewModel,所以你可以在'IsSelected'设置器中调用父ViewModel的方法。其他可能的方法(应该更多)是使用静态事件,它由IsSelected设置器触发,在您的父ViewModel中订阅此事件并在处理器NotifyPropertyChanged(nameof(CanDoSomething))中调用。 – Sinatr

+1

好点,Sinatr。在这篇文章中,Matt Hamilton将父ViewModel注入为一个宿主对象。在没有父级的情况下,子ViewModel确实不能存在的情况下,这是可以接受的(例如在没有父级时间轴的情况下为Tweet)。 http://matthamilton.net/nested-viewmodels – Christoph

回答

1

我觉得如果我对自己的最终目标有了更好的了解,我可能会推荐更好的方法。有一些东西正在发生,只是感觉有点外出。就像'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"); 
      } 
     } 
    } 
} 
1

首先创建一个类定义此附加属性:

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/

让我知道它是否有效

相关问题