Caliburn.Micro和DataGrid:检测个体细胞变化的可靠方法?

时间:2012-10-26 12:16:35

标签: c# wpf mvvm wpfdatagrid caliburn.micro

我有一个基于MVVM的WPF应用程序,它依赖于 Caliburn.Micro

在一个视图中,我显示的是DataGridButtonDataGrid显示项目集合,其中项目类派生自PropertyChangedBase 应根据可编辑DataGrid单元格中的内容启用或禁用该按钮。使用 Caliburn.Micro 实现此目标的最可靠方法是什么?

示意图,这就是我的代码现在看起来的样子:

public class ItemViewModel : PropertyChangedBase { }

...

public class ItemsViewModel : PropertyChangedBase
{
    private IObservableCollection<ItemViewModel> _items;

    // This is the DataGrid in ItemsView
    public IObservableCollection<ItemViewModel> Items
    {
        get { return _items; }
        set
        {
            _items = value;
            NotifyOfPropertyChange(() => Items);
        }
    }

    // This is the button in ItemsView
    public void DoWork() { }

    // This is the button enable "switch" in ItemsView
    public bool CanDoWork
    {
        get { return Items.All(item => item.NotifiableProperty == some_state); }
    }
}

正如代码所代表的那样,当ItemsViewModel.CanDoWork更改一个NotifiableProperty时,{em>没有通知ItemsView,例如当用户编辑DataGrid中的一个单元格时是DoWork。因此,Items按钮启用状态永远不会改变。

一种可能的解决方法是为foreach (var item in Items) item.PropertyChanged += (sender, args) => NotifyOfPropertyChange(() => CanDoWork); 集合中的每个项目添加(匿名)事件处理程序:

Items

但是我还需要跟踪Items集合中添加或删除项目的时间(if),或者{{1}}集合是否完全重新初始化。

这个问题有更优雅可靠的解决方案吗?我确信有,但到目前为止我还没有找到它。

2 个答案:

答案 0 :(得分:2)

我认为这是INPC运作良好的情况;要简化注册/取消注册添加和删除,只需将CollectionChanged处理程序添加到Items集合中:

Items.CollectionChanged += OnItemsCollectionChanged;

private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
    if (e.NewItems != null && e.NewItems.Count != 0) {
        foreach (ItemViewModel  vm in e.NewItems)
            vm.PropertyChanged += OnDetailVmChanged;
    }
    if (e.OldItems != null && e.OldItems.Count != 0) {
        foreach (ItemViewModel  vm in e.OldItems) {
            vm.PropertyChanged -= OnDetailVmChanged;
        }
    }
}

Josh Smith编写了一个PropertyObserver类here,我发现它比猎枪INPC跟踪更优雅,但在像你这样的主细节场景中,你仍然需要跟踪添加和删除。

编辑Anders Gustafsson
请注意,要使上述代码在一般情况下工作,需要在附加事件处理程序之前使用默认构造函数初始化Items。为确保正确添加和删除OnDetailVmChanged事件处理程序,需要将Items属性setter扩展为以下内容:

public IObservableCollection<ItemViewModel> Items
{
    get { return _items; }
    set
    {
        // If required, initialize empty _items collection and attach
        // event handler
        if (_items == null) {
            _items = new BindableCollection<ItemViewModel>();
            _items.CollectionChanged += OnItemsCollectionChanged;
        }
        // Clear old contents in _items
        _items.Clear();
        // Add value item:s one by one to _items
        if (value != null) foreach (var item in value) _items.Add(item);

        NotifyOfPropertyChange(() => Items);
    }
}

(当然,使用上面的Items setter,最顶层的Items.CollectionChanged事件处理程序附件不应包含在代码中。)

理想情况下,我会使用if (value != null) _items.AddRange(value);,但当AddRange方法触发OnItemsCollectionChanged事件处理程序时,e.NewItems似乎为空(或null )。在调用e.OldItems方法时,我没有明确验证Clear()是非空的;否则Clear()也需要在_items中逐项删除项目:s替换{/ 1}。

答案 1 :(得分:-1)

每当属性更改为按钮cummand命令触发RaiseCanExecuteChanged时?

例如:

public DelegateCommand<object> MyDeleteCommand { get; set; }

string _mySelectedItem;
    public string MySelectedItem
    {
        get { return _mySelectedItem; }
        set
        {
            _mySelectedItem = value;
            OnPropertyChanged("MySelectedItem");
            MyDeleteCommand.RaiseCanExecuteChanged();
        }
    }
相关问题