You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
            }
        }
    }
}

错误原因分析

  1. setter逻辑完全错误:TransactionSelected的setter直接覆盖传入的value为列表ViewModel的选中值,违背了属性setter的设计逻辑——setter应接收外部传入值,而非强制覆盖。这种写法只会在TransactionSelected被主动赋值时触发一次,无法响应SelectedTransaction的动态变化。
  2. 缺少属性变更监听:未订阅ChkbkTransactionListViewModel.SelectedTransaction的PropertyChanged事件,DataGrid切换选中行时,EditTransactionViewModel.TransactionSelected不会自动更新。
  3. 字段未初始化:_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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 22:22:41