为啥覆盖 .GetHashCode 会清除 WinForms 中的这些数据绑定值?
Posted
技术标签:
【中文标题】为啥覆盖 .GetHashCode 会清除 WinForms 中的这些数据绑定值?【英文标题】:Why would overwriting .GetHashCode clear these databound values in WinForms?为什么覆盖 .GetHashCode 会清除 WinForms 中的这些数据绑定值? 【发布时间】:2014-08-01 22:50:58 【问题描述】:我们遇到了一个奇怪的错误,我们在调试时遇到了问题。
我们有一个使用 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我设置的断点,它正在触发 first 窗口中的 change 方法。
EndEditSession()
是我们自定义的东西,看起来像这样
protected void EndEditSession()
IBindingValue bv = null;
if (_bindingValues == null)
return;
if (_data != null)
foreach (KeyValuePair<string, IBindingValue> kvp in _bindingValues)
bv = kvp.Value;
if (bv.IsBindable)
((PropertyManager)bv.Component.BindingContext[_data]).EndCurrentEdit();
_bindingValues
在 UserControl 初始化其数据绑定时填充。键字段是绑定控件的名称,值字段是自定义对象,其中存储控件本身、控件名称、绑定值和默认值。 bv.Component
返回设置绑定的控件,在我的测试中是自定义的 DevExpress LookupEdit
_data
包含UserControl
的数据模型,我可以验证它是否设置为第二个窗口的实例。
我最初的想法是BindingContext
是共享的,因此返回了错误的PropertyManager
,但是我已经验证了两个表单和控件的.BindingContext
是分开的。
当GetHashCode
方法被覆盖以使这两个对象被视为相等时,是否有可能将UserControl
的两个单独副本绑定到数据模型的两个单独实例时会混淆其绑定?
我不太熟悉 WinForms 绑定系统的内部工作原理,也不了解 CAB 的 MDI 工作区是如何管理的。
我的理论是,当第一个窗口最小化时,它正在卸载控件以节省内存,然后当第二个窗口最小化管理绑定的内部哈希表时,会错误地混淆并运行更新以从第一个最小化窗口(现在为空白)并更新其数据源。这个理论有很多漏洞,但这是我唯一能想到的。
【问题讨论】:
【参考方案1】:我不知道 WinForm 小部件的内部工作原理,但似乎既然您遇到了 overriding equals 的问题,那么您最好解决强>。
如果您需要为自己的目的评估平等:
一种方法是提供您自己的方法来评估相等性,而不是更改默认行为。
如果您的意图是更改小部件处理对象的方式:
一种方法是为您的类创建一个静态对象工厂。工厂可以维护使用弱引用创建的所有对象的集合。弱引用允许 GC 收集对象。然后工厂可以检查以前创建的对象的集合。如果找到匹配项,则返回现有匹配项。如果没有,则创建它。这样,您将拥有一个具有两个相等引用(相同内存)的单个对象,而不是让两个不同的对象评估两个相等(覆盖等于)。
希望这些其他方法之一可以解决您的问题。
【讨论】:
【参考方案2】: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
对象的填充情况。可能在填充此对象期间填充了错误的值。
【讨论】:
这也是我的第一个想法,但是在分配每个控件时仍然会出现同样的问题BindingContext
@Rachel 你能提供一个示例项目吗?似乎没有足够的信息来说明您的问题。我认为问题不在于BindingContext
。问题可能出在您的 _bindingValues
对象中。
我实际上无法在示例项目中重现这一点,我认为这是因为我没有在示例中使用智能部件创建完整的 MDI 工作区。我使用的示例代码只是一个带有 TabControl、一个自定义对象和一个带有自定义绑定代码的自定义 UserControl 的表单。我今天再试一次,看看能否在更大的示例项目中重现该问题。以上是关于为啥覆盖 .GetHashCode 会清除 WinForms 中的这些数据绑定值?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我需要覆盖 C# 中的 .Equals 和 GetHashCode [重复]
如果在覆盖 Equals() 时未能覆盖 GetHashCode(),会出现啥问题? [复制]