通知视图模型已选择集合中的对象

本文关键字:对象 集合 选择 视图 模型 通知 | 更新日期: 2023-09-27 18:36:55

请考虑以下对象,它是 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 中没有地方可以放置它,因为当选择列表中的对象时,没有任何反应。

我尝试将列表替换为可观察集合和自定义"TrulyObservableCollection"(请参阅在项目更改时通知可观察集合),但两者都不起作用。

如何在不诉诸点击事件的情况下解决这个问题?

通知视图模型已选择集合中的对象

我觉得

如果我对你的最终目标有更好的了解,我可能会推荐一个更好的方法。 有些事情只是感觉有点不合适。 就像也许"CanDoSomething"应该是命令对象的一部分。 我想知道是否一次选择多个MyObject? 如果没有,那么我会以完全不同的方式解决这个问题。

因此,无论如何,您希望随时更新CanDoSomething MyObjects中某个项的 IsSelected 属性更改。 听起来您曾经使用过ObservableCollection然后放弃了它。 这是一个错误。 每当发生两个事件之一时,您需要随时更新CanDoSomething;第一个是向MyObjects添加项或从中删除项时,第二个是MyObjects中任何对象的 IsSelected 属性发生更改时。 对于第一个事件,你需要实现INotifyCollectionChanged的东西,即ObservableCollection。 您已经覆盖了第二个事件,因为这些对象实现了INotifyPropertyChanged 。 所以你只需要把这两件事结合起来。

在下面的示例中,我采用了您的代码并进行了一些更改。 首先,我将MyObjects改回ObservableCollection<MyObject>。 它没有二传手,因为我发现通常没有充分的理由更改可观察的集合;只需根据需要添加和删除对象即可。 然后在视图模型的构造函数中,我注册了 MyObjectsCollectionChanged事件。 在该处理程序中,我正在抓取添加到集合中的项并将其PropertyChanged事件挂接到OnIsSelectedChanged事件处理程序,并且我正在从从集合中删除的任何对象的OnIsSelectedChanged中取消PropertyChanged事件。 由于已添加或删除项目,我们不知道MyObjects中对象的IsSelected状态可能是什么,因此这是更新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/

让我知道它是否有效