为什么会覆盖.GetHashCode在WinForms中清除这些数据绑定值?

我们遇到了一个奇怪的错误,我们在调试时遇到了问题。

我们有一个使用Microsoft CAB,DevExpress组件和.Net 3.5的MDI工作区。

如果用户在工作区中打开两个窗口,每个窗口包含绑定到两个单独数据模型的UserControl ,则最小化它们,第一个最小化窗口是在第二个窗口最小化时清除它的绑定字段。

数据模型的.Equals.GetHashCode方法已被覆盖,因此两个数据模型都被认为是相等的。 如果我们改变它以使它们是唯一的,我们就不会得到这种行为。

这是一些显示问题的示例伪代码

 var a = new MyWindow(); a.DataModel = new SomeClass(123); a.ShowInMdiWorkspace(); var b = new MyWindow(); b.DataModel = new SomeClass(123); b.ShowInMdiWorksace(); a.Minimize(); // If SomeClass.GetHashCode() is overwritten to consider two objects // as equal based on the value passed in, then the data bindings for A // get cleared on this call. If SomeClass.GetHashCode is unique, then // this problem does not happen. b.Minimize(); 

这是第二个窗口最小化时的调用堆栈:

在此处输入图像描述

在上面的堆栈跟踪中的EndEditSession()调用中,它调用EndEditSession以使第二个窗口最小化,而当堆栈跟踪超过[External Code]到我设置的OnChange断点时,它将触发更改方法在第一个窗口中。

EndEditSession()是我们实现的自定义,看起来像这样

 protected void EndEditSession() { IBindingValue bv = null; if (_bindingValues == null) return; if (_data != null) { foreach (KeyValuePair kvp in _bindingValues) { bv = kvp.Value; if (bv.IsBindable) ((PropertyManager)bv.Component.BindingContext[_data]).EndCurrentEdit(); } } } 

当UserControl初始化其数据绑定时, _bindingValues将被填充。 关键字段是绑定控件的名称,值字段是存储控件本身,其名称,绑定值和默认值的自定义对象。 bv.Component返回绑定设置的控件,在我测试的情况下是自定义的DevExpress LookupEdit

_data包含UserControl的数据模型,我可以validation它是否设置为第二个窗口的实例。

我最初的想法是共享BindingContext ,因此返回了错误的PropertyManager ,但是我已经validation了两个表单和控件的.BindingContext是分开的。

GetHashCode方法被覆盖时,将两个单独的UserControl副本绑定到数据模型的两个独立实例是否可能会使其绑定混淆,从而使两个对象被认为是相等的?

我对WinForms绑定系统的内部工作方式不太熟悉,或者对CAB的MDI工作空间的管理方式非常熟悉。

我的理论是,当第一个窗口最小化时,它会卸载控件以节省内存,然后当第二个窗口最小化管理绑定的内部哈希表时,会错误地混淆并运行更新以从第一个最小化窗口获取数据(现在是空白的)并更新其数据源。 这个理论有很多漏洞,但这是我唯一能想到的。

我不知道WinForm小部件的内部工作方式,但似乎因为你遇到了覆盖 equals的问题,你最好不要再解决

如果您需要为自己的目的评估相等性:

一种方法是提供自己的方法来评估相等性,而不是更改默认行为。

如果您打算更改窗口小部件处理对象的方式:

一种方法是为您的类创建一个静态对象工厂。 工厂可以维护使用弱引用创建的所有对象的集合。 弱引用允许GC收集对象。 然后工厂可以检查先前创建的对象的集合。 如果找到匹配项,则返回现有匹配项。 如果没有,那么创建它。 这种方式,而不是有两个不同的对象,评估两个相等(覆盖等于),你有一个对象有两个相同的引用(相同的内存)。

希望其中一种方法可以解决您的问题。

BindingContext对象不与任何其他BindingContext共享其字段和属性,因为它的字段和属性不是静态的。

但是,有几个控件可以有一个BindingContext对象。

在第一种情况下,如果几个控件具有相同的父级并且没有自己的BindingContext则此控件的BindingContext属性将返回Control.Parent(.Parent...).BindingContext对象。

在第二种情况下,可能会有这样的事情:

 var bindingContext = new BindingContext(); var a = new SomeControl(); var b = new SomeControl(); a.BindingContext = bindingContext; b.BindingContext = bindingContext; 

在第三种情况下,可以用这种方式覆盖BindingContext

我不知道你的情况发生了什么,所以我只能建议在初始化数据绑定之前做这样的事情:

 var a = new SomeControl(); var b = new SomeControl(); a.BindingContext = new BindingContext(); b.BindingContext = new BindingContext(); 

如果这不能解决您的问题,那么您需要检查_bindingValues对象的_bindingValues 。 在填充此对象期间,可能会填充错误的值。