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

如何检测DataGrid绑定的List<Materials>中被修改的行及具体变更属性?

如何检测DataGrid绑定的List中被修改的行及具体变更属性?

你遇到的核心问题其实是引用类型的绑定特性——List<Materials>里存的是对象引用,DataGrid只是直接关联这些对象,不管你绑原列表、数组还是直接赋值的新列表(这些都是浅拷贝,只复制了引用),修改DataGrid里的值都会直接同步到原对象上,自然没法用原列表对比出变化。下面给你几个实用的解决方案,按需选就行:

方案一:创建原始数据的深拷贝作为对比基准

先给原始数据做一个深拷贝(不是浅拷贝),这样修改DataGrid里的对象时,原始拷贝不会被影响,之后就能逐个对比差异了。

实现方式:

给Materials类写一个克隆方法,手动复制所有属性(如果有引用类型属性,要递归克隆):

public class Materials
{
    public int ID { get; set; }
    public string Name { get; set; }
    // 其他属性...

    // 实现深拷贝的Clone方法
    public Materials Clone()
    {
        return new Materials
        {
            ID = this.ID,
            Name = this.Name,
            // 把所有属性都手动复制一遍
        };
    }
}

初始化的时候生成基准拷贝,再绑定原列表到DataGrid:

// 生成原始数据的深拷贝,作为对比基准
List<Materials> originalMaterials = Materials.allofmaterials.Select(m => m.Clone()).ToList();
// 绑定原列表到DataGrid,修改操作会直接作用在原对象上
dataGridView1.DataSource = Materials.allofmaterials;

之后要检查修改时,遍历DataGrid的行,和基准列表同ID的对象对比:

foreach (DataGridViewRow row in dataGridView1.Rows)
{
    if (row.IsNewRow) continue; // 跳过新添加的空行
    Materials currentMat = row.DataBoundItem as Materials;
    Materials originalMat = originalMaterials.FirstOrDefault(m => m.ID == currentMat.ID);
    if (originalMat == null) continue;

    // 逐个对比属性,找出变化的内容
    if (currentMat.Name != originalMat.Name)
    {
        Console.WriteLine($"ID为{currentMat.ID}的物料,Name属性从「{originalMat.Name}」变为「{currentMat.Name}」");
    }
    // 其他属性按同样方式对比...
}

方案二:给Materials类加修改跟踪机制

在Materials类内部添加标记,记录哪些属性被修改过,以及对应的原始值,这样可以随时检查对象是否被修改,还能直接拿到变更详情。

实现方式:

public class Materials : INotifyPropertyChanged
{
    private int _id;
    private string _name;
    // 存储属性原始值的字典
    private Dictionary<string, object> _originalValues = new Dictionary<string, object>();

    public int ID
    {
        get => _id;
        set
        {
            if (_id != value)
            {
                // 只在第一次修改时记录原始值
                if (!_originalValues.ContainsKey(nameof(ID)))
                    _originalValues[nameof(ID)] = _id;
                _id = value;
                OnPropertyChanged(nameof(ID));
            }
        }
    }

    public string Name
    {
        get => _name;
        set
        {
            if (_name != value)
            {
                if (!_originalValues.ContainsKey(nameof(Name)))
                    _originalValues[nameof(Name)] = _name;
                _name = value;
                OnPropertyChanged(nameof(Name));
            }
        }
    }

    // 判断当前对象是否被修改过
    public bool IsModified()
    {
        foreach (var kvp in _originalValues)
        {
            var property = GetType().GetProperty(kvp.Key);
            if (property == null) continue;
            var currentValue = property.GetValue(this);
            // 对比原始值和当前值
            if (!Equals(currentValue, kvp.Value))
                return true;
        }
        return false;
    }

    // 获取所有修改的属性及变化值(原始值+当前值)
    public Dictionary<string, (object Original, object Current)> GetChanges()
    {
        var changes = new Dictionary<string, (object, object)>();
        foreach (var kvp in _originalValues)
        {
            var property = GetType().GetProperty(kvp.Key);
            if (property == null) continue;
            var currentValue = property.GetValue(this);
            if (!Equals(currentValue, kvp.Value))
            {
                changes.Add(kvp.Key, (kvp.Value, currentValue));
            }
        }
        return changes;
    }

    // 实现INotifyPropertyChanged,让DataGrid双向绑定更流畅(可选)
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

使用方式:

// 遍历DataGrid的行,检查修改
foreach (DataGridViewRow row in dataGridView1.Rows)
{
    if (row.IsNewRow) continue;
    Materials mat = row.DataBoundItem as Materials;
    if (mat.IsModified())
    {
        var changes = mat.GetChanges();
        foreach (var change in changes)
        {
            Console.WriteLine($"ID{mat.ID}的{change.Key}属性:{change.Value.Original} → {change.Value.Current}");
        }
    }
}

方案三:结合CellValueChanged事件+状态记录(处理改回原值的情况)

如果你想用CellValueChanged事件,可以维护一个字典记录每个单元格的原始值,触发事件时对比当前值和原始值,就算用户改了又改回原值,也能正确判断。

实现方式:

// 存储单元格原始值的字典,键格式:「ID_列名」
Dictionary<string, object> originalCellValues = new Dictionary<string, object>();

// DataGrid绑定完成后,初始化原始值
private void dataGridView1_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
    originalCellValues.Clear();
    foreach (DataGridViewRow row in dataGridView1.Rows)
    {
        if (row.IsNewRow) continue;
        Materials mat = row.DataBoundItem as Materials;
        foreach (DataGridViewColumn col in dataGridView1.Columns)
        {
            string key = $"{mat.ID}_{col.Name}";
            originalCellValues[key] = row.Cells[col.Index].Value;
        }
    }
}

// CellValueChanged事件处理
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    if (e.RowIndex < 0 || e.ColumnIndex < 0) return;
    DataGridViewRow row = dataGridView1.Rows[e.RowIndex];
    if (row.IsNewRow) return;

    Materials mat = row.DataBoundItem as Materials;
    DataGridViewColumn col = dataGridView1.Columns[e.ColumnIndex];
    string key = $"{mat.ID}_{col.Name}";

    object originalValue = originalCellValues[key];
    object currentValue = row.Cells[e.ColumnIndex].Value;

    if (Equals(originalValue, currentValue))
    {
        // 改回了原始值,标记为未修改(可以自己维护一个修改记录字典来更新状态)
        Console.WriteLine($"ID{mat.ID}的{col.Name}属性改回原值");
    }
    else
    {
        // 记录修改
        Console.WriteLine($"ID{mat.ID}的{col.Name}属性:{originalValue} → {currentValue}");
    }
}

总结

  • 如果只是需要一次性检查所有修改,方案一的深拷贝最直接简单;
  • 如果需要实时跟踪对象的修改状态,方案二更灵活,能随时获取变更详情;
  • 方案三适合需要实时响应单元格修改的场景,但要注意处理新行、列顺序变化等边界情况。

备注:内容来源于stack exchange,提问作者Daria

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 14:24:29