如果在调试器中查看堆栈跟踪,当调用BeginEdit
时,第一次调用它的是集合视图,第二次则是BindingGroup
。
问题似乎是有两个东西都认为它们控制了IEditableObject
的状态。当WPF提供默认的集合视图时,它会查找集合中的对象上是否有IEditableObject
,并且会响应于相应的IEditableCollectionView
方法的调用来调用BeginEdit
和要么EndEdit
或CancelEdit
。但是,BindingGroup
也将响应于对BeginEdit
和CommitEdit
或CancelEdit
的调用而调用IEditableObject
方法。
DataGrid
同时使用了这两个功能:当你开始编辑并完成行编辑时,它会通知IEditableCollectionView
和 BindingGroup
,而这两件事情都认为接下来他们的职责是通知潜在源对象上的IEditableObject
实现。
因此,这看起来很像一个DataGrid
中的错误——它导致两个不同的对象调用BeginEdit
(和相关的方法)。这是因为它利用可编辑的集合视图和绑定组——从外观上看,那些设计并不是为了在同一对象上同时使用而设计的,就像DataGrid
使用它们一样。
你在工具包中看不到这个问题的原因是它似乎是一个稍旧版本的 - 对比一下工具包中的代码和Reflector显示的.NET 4.0,你会发现.NET 4.0 DataGrid
有一些额外的代码(一个新的方法EnsureItemBindingGroup
,以及在MeasureOverride
和OnRowValidationRulesChanged
中的相关代码),可以确保绑定组始终存在,无论你是否请求。因此,如果WPF Toolkit更新了,它可能会增加类似的功能,除非这个问题得到修复。(我猜想,如果你使用当前版本-即我编写本文时的2023年3月 - 的WPF Toolkit,并且使用ItemBindingGroup
属性显式地请求绑定组,那么你可能会看到完全相同的问题)。
这并没有解释你如何获得随机对象上的BeginEdit
调用,就像你所描述的那样。我无法复制这个问题。但它确实解释了选定对象上的双重调用。最好的做法似乎是编写源对象,使其能够容忍双重调用。
private bool _isDirty = false;
#region IEditableObject Members
public void BeginEdit()
{
// Bug Fix: Windows Controls call EndEdit twice; Once
// from IEditableCollectionView, and once from BindingGroup.
// This makes sure it only happens once after a BeginEdit.
_isDirty = true;
}
public void CancelEdit() { }
public void EndEdit()
{
if (ItemEndEdit != null && _isDirty)
{
_isDirty = false;
ItemEndEdit(this);
}
}
#endregion
我使用.NET Framework 4数据网格时遇到了同样的问题。
将引用添加到最新版本的WPFToolkit。
添加
xmlns:dg="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
将<DataGrid>
替换为<dg:DataGrid>
+1 @IanGriffiths 诊断了问题。至于解决方案(或变通方法),您可以计算“待处理”编辑的数量。这意味着类似于:
void BeginEdit()
{
_numEdits++;
}
void CancelEdit()
{
if(--_numEdits < 0)
throw new Exception("WTF?");
}
void EndEdit()
{
CancelEdit();
if(_numEdits == 0)
commitEdit();
}