在 WPF DataGrid 中选择行时,System.ArgumentException

本文提供了解决在 WPF DataGrid 控件中选择行时发生的解决方法 System.ArgumentException

原始产品版本: Microsoft .NET Framework 4.5
原始 KB 数: 2909048

症状

你开发了一个使用 Windows Presentation Foundation (WPF) DataGrid 控件的 Microsoft .NET Framework 4.x 应用程序。 DataGrid 控件的 ItemsSource 属性绑定到自定义对象的集合。 修改 DataGrid 控件中某行的值并选择其他行后,会收到以下异常和调用堆栈。 只有在安装了 Microsoft .NET Framework 4.5 或更高版本的计算机上运行应用程序时,才会发生此行为。

System.ArgumentException 未处理
HResult=-2147024809
Message=已添加具有相同键的项。
Source=mscorlib
堆栈跟踪:
在 System.ThrowHelper.ThrowArgumentException(ExceptionResource 资源)
at System.Collections.Generic.Dictionary 2.Insert(TKey key, TValue value, Boolean add)
at System.Collections.Generic.Dictionary 2..ctor(IDictionary 2 dictionary, IEqualityComparer 1 比较器)
at System.Windows.Controls.Primitives.Selector.InternalSelectedItemsStorage..ctor(InternalSelectedItemsStorage 集合,IEqualityComparer 1 equalityComparer)
at System.Windows.Controls.Primitives.Selector.SelectionChanger.ApplyCanSelectMultiple()
at System.Windows.Controls.Primitives.Selector.SelectionChanger.End()
at System.Windows.Controls.SelectedItemCollection.EndUpdateSelectedItems()
at System.Windows.Controls.Primitives.MultiSelector.EndUpdateSelectedItems()
at System.Windows.Controls.DataGrid.MakeFullRowSelection(ItemInfo info, Boolean allowsExtendSelect, Boolean allowsMinimalSelect)

原因

DataGrid 的 ItemsSource 绑定到自定义对象的集合,其类型定义已重写 Object.GetHashCode 方法。 重写 GetHashCode 的方法具有不正确的实现,该实现基于类中的可变属性计算哈希。 这是Microsoft .NET Framework 4.5 及更高版本中公开的应用程序 bug。 在 Microsoft .NET Framework 4.5 中,WPF Selector 类的实现已更改为更广泛地使用 HashTable;以提高性能优势。

决议

修改自定义对象的类型定义,以遵守重写 GetHashCode准则。

详细信息

如果类型通过依赖可变属性计算其哈希,并且一个或多个可变属性的值会更改;然后,从其重写 GetHashCode 的方法返回的计算哈希也会更改。 如果在对象存储在 WPF 的内部 HashTable 中时发生这种情况,则不再可根据原始哈希进行检索。 WPF 检测到对象在 HashTable 中不可用,并基于新哈希再次添加该对象。 然后,它会尝试使用键将相关 Selector 数据插入内部字典中。 但是,发生异常的原因是以前已将具有该键的项添加到字典中。

GetHashCode仅当:

  • 可以从不可可变的字段计算哈希代码;或
  • 可以确保在对象包含在依赖于其哈希代码的集合中时可变对象的哈希代码不会更改。