如何在Prism框架中用DelegateCommand跨视图删除DataGrid绑定的ObservableCollection条目
嘿,在Prism+Unity的WPF MVVM场景里处理跨ViewModel的交互,用**EventAggregator(事件聚合器)**绝对是最优解——完全符合MVVM的解耦原则,不用让两个ViewModel硬关联。我给你一步步拆解怎么实现:
首先创建一个继承自Prism PubSubEvent的事件类,用来传递删除触发信号(如果需要直接传递待删除的项,也可以用泛型指定类型)。假设你的数据实体是DataItem,代码如下:
using Prism.Events; // 泛型参数指定要传递的类型,这里是你的数据项类型 public class DeleteSelectedItemEvent : PubSubEvent<DataItem> { // 空类即可,PubSubEvent已经封装了所有发布订阅的核心逻辑 }
如果你的DataGrid ViewModel自己维护选中项,也可以用无泛型的PubSubEvent,只传递触发信号就行。
假设DataGrid对应的ViewModel是MainContentViewModel,它持有绑定DataGrid的ObservableCollection<DataItem>,还有选中项的绑定属性。我们需要在这个ViewModel里订阅删除事件,并且实现实际的删除逻辑:
using Prism.Events; using Prism.Mvvm; using System.Collections.ObjectModel; public class MainContentViewModel : BindableBase { private readonly IEventAggregator _eventAggregator; private DataItem _selectedItem; // 绑定DataGrid的ItemsSource public ObservableCollection<DataItem> DataItems { get; } = new ObservableCollection<DataItem>(); // 绑定DataGrid的SelectedItem public DataItem SelectedItem { get => _selectedItem; set => SetProperty(ref _selectedItem, value); } // 通过构造函数注入EventAggregator,Unity容器会自动处理依赖 public MainContentViewModel(IEventAggregator eventAggregator) { _eventAggregator = eventAggregator; // 订阅删除事件,指定触发时执行的方法 // 可选:加上ThreadOption.UIThread确保在UI线程执行更新(避免跨线程操作UI) _eventAggregator.GetEvent<DeleteSelectedItemEvent>() .Subscribe(DeleteSelectedItem, ThreadOption.UIThread); } private void DeleteSelectedItem(DataItem itemToDelete) { // 如果事件传递了待删除项就用传递的,否则用当前选中项 var targetItem = itemToDelete ?? SelectedItem; if (targetItem != null && DataItems.Contains(targetItem)) { DataItems.Remove(targetItem); } } // 可选:如果ViewModel会被销毁,记得取消订阅避免内存泄漏 public void Dispose() { _eventAggregator.GetEvent<DeleteSelectedItemEvent>().Unsubscribe(DeleteSelectedItem); } }
如果你的DataGrid允许多选,可以把SelectedItem换成IList<DataItem> SelectedItems,删除时遍历移除所有选中项即可。
RibbonTab对应的ViewModel(比如RibbonViewModel)同样注入IEventAggregator,给删除按钮绑定一个DelegateCommand,在命令执行时发布删除事件:
using Prism.Commands; using Prism.Events; using Prism.Mvvm; public class RibbonViewModel : BindableBase { private readonly IEventAggregator _eventAggregator; // 绑定Ribbon按钮的Command public ICommand DeleteCommand { get; } public RibbonViewModel(IEventAggregator eventAggregator) { _eventAggregator = eventAggregator; DeleteCommand = new DelegateCommand(ExecuteDelete); } private void ExecuteDelete() { // 两种发布方式选一种: // 1. 如果RibbonViewModel能拿到选中项(比如通过共享服务),直接传递过去 // _eventAggregator.GetEvent<DeleteSelectedItemEvent>().Publish(targetItem); // 2. 直接发布空信号,让DataGrid的ViewModel自己处理选中项(更解耦) _eventAggregator.GetEvent<DeleteSelectedItemEvent>().Publish(null); } }
第二种方式更推荐,因为Ribbon的ViewModel不需要关心数据细节,只负责触发操作,符合单一职责原则。
因为你用的是Unity容器,只要在注册环节(比如Module的RegisterTypes方法或者App.xaml.cs的初始化代码)把ViewModel和View关联好,Prism会自动把IEventAggregator注入到ViewModel的构造函数中:
// 示例:在Module的RegisterTypes方法中注册View和ViewModel public void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterForNavigation<MainContentView, MainContentViewModel>(); containerRegistry.RegisterForNavigation<RibbonTabView, RibbonViewModel>(); }
如果是直接给View的DataContext赋值(比如在XAML里用ViewModelLocator.AutoWireViewModel="True"),Prism的ViewModelLocator也会自动从容器中获取带依赖的ViewModel实例。
内容的提问来源于stack exchange,提问作者Peasant King




