We have run into a strange bug that we're having problems debugging.
We have a MDI workspace that uses Microsoft CAB, DevExpress components, and .Net 3.5.
If users open two windows in the workspace that each contain a UserControl
bound to two separate data models, then minimize both of them, the first window to minimize is getting it's bound fields cleared when the second one minimizes.
The .Equals
and .GetHashCode
methods of the data model have been overridden so that both data models are considered equal. If we change that so they are unique, we do not get this behavior.
Here's some example pseudocode showing the problem
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();
Here's the Call Stack when the second window gets minimized:
At the EndEditSession()
call in the stack trace above, it is calling EndEditSession
for the second window minimized, while by the time the Stack Trace gets past the [External Code]
to the OnChange breakpoint I have set, it is firing the change method in the first window.
EndEditSession()
is something custom we have implemented which looks something like this
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
gets populated when the UserControl initializes its data bindings. The key fields are the name of the bound control, and the value fields are a custom object which stores the control itself, its name, its bound value, and default value. bv.Component
returns the control that the binding is set on, which in the case of my testing is a customized DevExpress LookupEdit
_data
contains the data model for the UserControl
, and I can verify that it is set to the instance for the second window.
My original thought was that the BindingContext
was shared so the wrong PropertyManager
was being returned, however I have verified that the .BindingContext
for the two forms and controls are separate.
Is it possible that having two separate copies of a UserControl
bound to two separate instances of a data model would get its bindings mixed up when the GetHashCode
method has been overridden so that the two objects are considered equal?
I am not very familiar with the inner workings of the WinForms binding system, or with exactly how CAB's MDI workspace gets managed.
My theory is that when the first window minimizes, it is unloading the controls to save on memory, then when the second window minimizes the internal hash table that manages the bindings is incorrectly getting confused and running an update to take data from the first minimized window (which is now blank) and updating its datasource. There are plenty of holes in this theory, however its the only thing I can think of.
BindingContext
– Embree