Why would overwriting .GetHashCode clear these databound values in WinForms?
Asked Answered
E

2

14

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:

enter image description here

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.

Embree answered 11/6, 2014 at 18:50 Comment(0)
N
1

I don't know the internal workings the WinForm widget, but it seems that since you've encountered an issue with overriding equals that you'd be better off working around.

If you need to evaluate equality for your own purposes:

An approach is to provide your own method to evaluate equality, rather than changing the default behavior.

If your intention is to change how the widget treats the objects:

An approach is to make a static object factory for your class. The factory could maintain a collection of all of the objects created using weak references. Weak references allow the GC to collect the objects. The factory can then check the collection of previously created objects. If a match is found then return the existing one. If not then create it. This way rather than having two different objects that evaluate two equal (override equals) you'd have a single object with two references that is equal (same memory).

Hopefully one of these other approaches will solve your problem.

Nearby answered 13/10, 2014 at 15:1 Comment(0)
A
0

BindingContext object are not sharing its fields and properties with any other BindingContext because its fields and properties are not static.

But, it is possible to have one BindingContext object for several controls.

In the first case if several controls have the same parent and have not their own BindingContext then BindingContext property of this controls will return Control.Parent(.Parent...).BindingContext object.

In the second case there are can be something like this:

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

In the third case BindingContext can be overwritten in such a way.

I don't know what is going on in your case, so I can only recommend to do something like this before initializing data bindings:

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


If this does not solve your problem then you need to check the populating of your _bindingValues object. It is possible that during the populating of this object it is populated with wrong values.

Abaddon answered 17/6, 2014 at 4:35 Comment(3)
That was my first thought too, however the same problem still occurs when assigning each control it's own BindingContextEmbree
@Embree Can you provide a sample project? It seems that there not enough information about you problem. I think that problem is not with BindingContext. The problem may be in your _bindingValues object.Abaddon
I've actually been unable to reproduce this in a sample project, which I think is because I haven't been creating a full MDI workspace with smartparts in my sample. The sample code I was working with is just a form with a TabControl, a custom object, and a custom UserControl with the customized binding code. I'll try again today and see if I can reproduce the problem in a larger sample project.Embree

© 2022 - 2024 — McMap. All rights reserved.