我有一个基于MVVM的WPF应用程序,它依赖于 Caliburn.Micro 。
在一个视图中,我显示的是DataGrid
和Button
。 DataGrid
显示项目集合,其中项目类派生自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}}集合是否完全重新初始化。
这个问题有更优雅可靠的解决方案吗?我确信有,但到目前为止我还没有找到它。
答案 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();
}
}