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

Wpf DataGrid排序或CRUD操作致复选框状态重置的解决方法

解决WPF DataGrid复选框选中状态在排序/CRUD后重置的问题

核心原因

每次执行DgTest.ItemsSource = dc.TestTable.ToList()时,都是从数据库获取全新的实体对象集合替换原有数据源。原来的复选框选中状态要么存储在UI控件中,要么绑定到临时属性,新生成的实体对象不包含这些状态,导致选中状态丢失。


解决方案1:用ViewModel包装实体(推荐)

将选中状态从业务实体剥离,放到独立的ViewModel中,同时维护一个字典记录每个实体的选中状态,刷新数据时通过实体唯一标识恢复状态。

步骤1:创建ViewModel类

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class TestItemViewModel : INotifyPropertyChanged
{
    private readonly TestTable _entity;
    private bool _isSelected;
    // 全局字典,跨刷新保存选中状态
    private static Dictionary<int, bool> _selectionTracker = new Dictionary<int, bool>();

    // 实体唯一标识(比如主键Id)
    public int Id => _entity.Id;

    // 转发实体的业务属性(用于计算平均值的字段)
    public decimal TargetValue => _entity.TargetValue;

    public bool IsSelected
    {
        get => _isSelected;
        set
        {
            if (_isSelected != value)
            {
                _isSelected = value;
                OnPropertyChanged();
                // 更新全局状态字典
                _selectionTracker[Id] = value;
                // 触发平均值计算逻辑
                CalculateSelectedAverage();
            }
        }
    }

    public TestItemViewModel(TestTable entity)
    {
        _entity = entity;
        // 初始化时读取之前的选中状态,默认未选中
        _selectionTracker.TryGetValue(entity.Id, out _isSelected);
    }

    // 平均值计算示例
    private void CalculateSelectedAverage()
    {
        var window = Application.Current.MainWindow as YourWindow;
        if (window == null) return;

        var selectedValues = window.DgTest.Items.Cast<TestItemViewModel>()
                                   .Where(vm => vm.IsSelected)
                                   .Select(vm => vm.TargetValue);

        window.TxtAverage.Text = selectedValues.Any() 
            ? selectedValues.Average().ToString("F2") 
            : "0.00";
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

步骤2:修改DataGrid刷新逻辑

private void RefreshDataGrid()
{
    using var dc = new DataContextTest();
    var newEntities = dc.TestTable.ToList();
    
    // 将新实体包装成ViewModel,自动恢复选中状态
    var viewModelList = newEntities.Select(e => new TestItemViewModel(e)).ToList();
    
    DgTest.ItemsSource = viewModelList;
}

步骤3:修改DataGrid列绑定

在XAML中,复选框列绑定到ViewModel的IsSelected属性:

<DataGridCheckBoxColumn Header="选中" Binding="{Binding IsSelected}" />

解决方案2:给实体添加非映射的临时属性

如果不想引入ViewModel,可以在实体类中添加一个不映射到数据库的IsSelected属性,刷新数据时只更新实体的业务属性,不替换整个集合。

步骤1:修改实体类

using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel;
using System.Runtime.CompilerServices;

public class TestTable : INotifyPropertyChanged
{
    private decimal _targetValue;

    public int Id { get; set; }

    public decimal TargetValue
    {
        get => _targetValue;
        set
        {
            if (_targetValue != value)
            {
                _targetValue = value;
                OnPropertyChanged();
            }
        }
    }

    // 标记为非映射,不会存入数据库
    [NotMapped]
    public bool IsSelected { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

步骤2:用ObservableCollection维护ItemsSource

// 页面级字段,初始化时绑定到DataGrid
private ObservableCollection<TestTable> _testItems = new ObservableCollection<TestTable>();

public YourWindow()
{
    InitializeComponent();
    DgTest.ItemsSource = _testItems;
    RefreshDataGrid();
}

private void RefreshDataGrid()
{
    using var dc = new DataContextTest();
    var newEntities = dc.TestTable.ToList();
    var existingIds = _testItems.Select(item => item.Id).ToHashSet();

    // 移除新数据中不存在的项
    var itemsToRemove = _testItems.Where(item => !newEntities.Any(e => e.Id == item.Id)).ToList();
    foreach (var item in itemsToRemove)
    {
        _testItems.Remove(item);
    }

    // 新增或更新现有项(保留IsSelected状态)
    foreach (var newEntity in newEntities)
    {
        var existingItem = _testItems.FirstOrDefault(item => item.Id == newEntity.Id);
        if (existingItem == null)
        {
            _testItems.Add(newEntity);
        }
        else
        {
            // 只更新业务属性,保留选中状态
            existingItem.TargetValue = newEntity.TargetValue;
            // 其他业务属性同理更新
        }
    }
}

方案优势

  • 两种方案都无需将选中状态存入数据库,避免了大量数据的更新操作,性能远优于原方案。
  • ViewModel方案分离了UI状态与业务实体,代码更清晰、扩展性更强;临时属性方案更简洁,适合简单业务场景。

内容的提问来源于stack exchange,提问作者user8245153

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 04:45:32