13

We have run into a problem where

  • We have two instances of the same window in an MDI workspace bound to two separate object models.
  • The object models have their .Equals and .GetHashCode methods overwritten to be considered equal.
  • Calling .EndCurrentEdit() on window 2 is triggering a binding update for Window 1
  • Both windows are setup to use separate a BindingContext

We have discovered the problem has to do with calling

((PropertyManager)ctrl.BindingContext[dataSource]).EndCurrentEdit();

If we change that to

((PropertyManager)ctrl.BindingContext[dataSource, dataMember]).EndCurrentEdit();

It works correctly. It also works correctly if we remove our .Equals and .GetHashCode overrides so the two object models are no longer considered equal.

That doesn't make sense to me because the windows are the same, so the dataMember property would be the same too.

From this link, I believe the definition of these calls is:

public BindingManagerBase this[object dataSource] {
    get {
        return this[dataSource, ""];
    }
}

public BindingManagerBase this[object dataSource, string dataMember] {
    get {
        return EnsureListManager(dataSource, dataMember);
    }

internal BindingManagerBase EnsureListManager(object dataSource, string dataMember) {
    BindingManagerBase bindingManagerBase = null;

    if (dataMember == null)
        dataMember = "";

    // Check whether data source wants to provide its own binding managers
    // (but fall through to old logic if it fails to provide us with one)
    //
    if (dataSource is ICurrencyManagerProvider) {
        bindingManagerBase = (dataSource as ICurrencyManagerProvider).GetRelatedCurrencyManager(dataMember);

        if (bindingManagerBase != null) {
            return bindingManagerBase;
        }
    }

    // Check for previously created binding manager
    //
    HashKey key = GetKey(dataSource, dataMember);
    WeakReference wRef;
    wRef = listManagers[key] as WeakReference;
    if (wRef != null)
        bindingManagerBase = (BindingManagerBase) wRef.Target;
    if (bindingManagerBase != null) {
        return bindingManagerBase;
    }

    if (dataMember.Length == 0) {
        // No data member specified, so create binding manager directly on the data source
        //
        if (dataSource is IList || dataSource is IListSource) {
            // IListSource so we can bind the dataGrid to a table and a dataSet
            bindingManagerBase = new CurrencyManager(dataSource);
        }
        else {
            // Otherwise assume simple property binding
            bindingManagerBase = new PropertyManager(dataSource);
        }
    }
    else {
        // Data member specified, so get data source's binding manager, and hook a 'related' binding manager to it
        //
        int lastDot = dataMember.LastIndexOf(".");
        string dataPath = (lastDot == -1) ? "" : dataMember.Substring(0, lastDot);
        string dataField = dataMember.Substring(lastDot + 1);

        BindingManagerBase formerManager = EnsureListManager(dataSource, dataPath);

        PropertyDescriptor prop = formerManager.GetItemProperties().Find(dataField, true);
        if (prop == null)
            throw new ArgumentException(SR.GetString(SR.RelatedListManagerChild, dataField));

        if (typeof(IList).IsAssignableFrom(prop.PropertyType))
            bindingManagerBase = new RelatedCurrencyManager(formerManager, dataField);
        else
            bindingManagerBase = new RelatedPropertyManager(formerManager, dataField);
    }

My dataSource is not an ICurrencyManagerProvider

What is the difference between these two calls, and why does accessing the PropertyManager by only the dataSource result in the bindings for another window with a separate BindingContext being updated?

Community
  • 1
  • 1
Rachel
  • 130,264
  • 66
  • 304
  • 490

2 Answers2

1

You don't state this explicitly, so in case you haven't noticed it is the collection look up that is not working as you expect, because of the equal override.

BindingContext[datasource] is a lookup against the collection using datasource as the key.

BindingContext[datasource, datamember] is a lookup against the collection using a composite key.

It is clear from the code that BindingContext is maintaining two separate collections. One a collection on a datasource key and the other a collection based on a composite key.

Obviously your override of equal will twice place similar values to datasource in the BindingContext[datasource] collection, but will result in one collection entry, because the values/keys are the same. Whereas, it will place two entries in BindingContext[datasource, datamember].

If you inspect both collections and can get the counts you'll see that the later collection has more entries.

You have to remember that you have two separate objects that evaluate to equal, not two references to the same object. This is the crux of the problem.

It would appear that when adding the entries to the second collection (BindingContext[datasource, datamember]) datamember evaluates to unique.

N-ate
  • 6,051
  • 2
  • 40
  • 48
  • 1
    To say it simply: Your equals override is returning true although they are not actually equal objects. – pashute Jan 20 '15 at 09:42
0

When you accessing BindingContext[dataSource], you actually accessing BindingContext[dataSource, ""]. So, there are no difference except HashCode, which uses both DataSource and DataMember values for calculation, that can be seen in your link.

public override int GetHashCode() {
    return dataSourceHashCode * dataMember.GetHashCode();
}

The problem in separate BindingContext objects can be that they are not filled correctly.

nempoBu4
  • 6,521
  • 8
  • 35
  • 40