WPF MVVM中DataGrid选中行在ViewModel间传递失败问题排查
WPF MVVM跨ViewModel属性传递失败排查
问题场景
在WPF MVVM架构下,尝试将DataGrid选中行从ChkbkTransactionListViewModel的SelectedTransaction属性传递到EditTransactionViewModel的TransactionSelected属性用于编辑,已能获取选中行,但跨ViewModel属性传递失败。
选中行所在ViewModel代码
using Checkbook8.Data; using System.Collections.ObjectModel; namespace Checkbook8.Views { internal class ChkbkTransactionListViewModel : BindableBase { private ObservableCollection<ChkbkTransaction>? _ChkbkRecordCol; public ChkbkTransactionListViewModel() { GetData(); } private void GetData() { using (var context = new ChkbkTransactionDbContext()) { _ChkbkRecordCol = new ObservableCollection<ChkbkTransaction>(context.ChkbkTransactions.ToList()); } } public ObservableCollection<ChkbkTransaction>? ChkbkRecordCol { get => _ChkbkRecordCol; set => SetProperty(ref _ChkbkRecordCol, value); } private static ChkbkTransaction? _SelectedTransaction; public ChkbkTransaction? SelectedTransaction { get => _SelectedTransaction; set => SetProperty(ref _SelectedTransaction, value); } private static ChkbkTransactionListViewModel _Instance = new(); public static ChkbkTransactionListViewModel Instance { get { return _Instance; } } } }
目标ViewModel代码
using Checkbook8.Data; namespace Checkbook8.Views { internal class EditTransactionViewModel : BindableBase { private ChkbkTransaction _TransactionSelected; public ChkbkTransaction TransactionSelected { get => _TransactionSelected; set { value = ChkbkTransactionListViewModel.Instance.SelectedTransaction; SetProperty(ref _TransactionSelected, value); } } } }
错误原因分析
- setter逻辑完全错误:
TransactionSelected的setter直接覆盖传入的value为列表ViewModel的选中值,违背了属性setter的设计逻辑——setter应接收外部传入值,而非强制覆盖。这种写法只会在TransactionSelected被主动赋值时触发一次,无法响应SelectedTransaction的动态变化。 - 缺少属性变更监听:未订阅
ChkbkTransactionListViewModel.SelectedTransaction的PropertyChanged事件,DataGrid切换选中行时,EditTransactionViewModel.TransactionSelected不会自动更新。 - 字段未初始化:
_TransactionSelected未初始化,若SelectedTransaction初始为null,访问TransactionSelected会触发空引用异常。
修复方案
方案1:通过事件监听实现自动同步
修改EditTransactionViewModel,订阅列表ViewModel的属性变更事件,实现选中值自动同步:
using Checkbook8.Data; using System.ComponentModel; namespace Checkbook8.Views { internal class EditTransactionViewModel : BindableBase { private ChkbkTransaction? _transactionSelected; public ChkbkTransaction? TransactionSelected { get => _transactionSelected; set => SetProperty(ref _transactionSelected, value); } public EditTransactionViewModel() { // 初始化同步当前选中值 TransactionSelected = ChkbkTransactionListViewModel.Instance.SelectedTransaction; // 订阅选中值变更事件 ChkbkTransactionListViewModel.Instance.PropertyChanged += OnListViewModelPropertyChanged; } private void OnListViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(ChkbkTransactionListViewModel.SelectedTransaction)) { TransactionSelected = ChkbkTransactionListViewModel.Instance.SelectedTransaction; } } // 可选:ViewModel销毁时取消订阅,避免内存泄漏 ~EditTransactionViewModel() { ChkbkTransactionListViewModel.Instance.PropertyChanged -= OnListViewModelPropertyChanged; } } }
方案2:XAML直接绑定(无需ViewModel同步代码)
若编辑视图的DataContext是EditTransactionViewModel,可直接在XAML中绑定到列表ViewModel的单例属性,跳过ViewModel间的代码同步:
<!-- 示例:编辑界面的文本框绑定选中交易的金额 --> <TextBox Text="{Binding Source={x:Static views:ChkbkTransactionListViewModel.Instance}, Path=SelectedTransaction.Amount, Mode=TwoWay}" />
额外优化点
- 给
ChkbkTransactionListViewModel的静态字段初始化:private static ChkbkTransaction? _SelectedTransaction = null; - 避免在属性setter中做逻辑覆盖,保持setter仅负责更新字段和触发通知。
内容的提问来源于stack exchange,提问作者user8692580
相关产品推荐
相关产品推荐

