如何检测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
相关产品推荐
相关产品推荐

