diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/BindingContext.cs b/src/System.Windows.Forms/src/System/Windows/Forms/BindingContext.cs
index 48541d1375a..e4eb75dbfd8 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/BindingContext.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/BindingContext.cs
@@ -2,438 +2,428 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-namespace System.Windows.Forms {
+using System.Collections;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
- using System;
- using Microsoft.Win32;
- using System.ComponentModel;
- using System.Collections;
- using System.Globalization;
- using System.Diagnostics.CodeAnalysis;
-
- ///
+namespace System.Windows.Forms
+{
///
- /// Manages the collection of System.Windows.Forms.BindingManagerBase
- /// objects for a Win Form.
+ /// Manages the collection of System.Windows.Forms.BindingManagerBase
+ /// objects for a Win Form.
///
[DefaultEvent(nameof(CollectionChanged))]
- public class BindingContext : ICollection {
+ public class BindingContext : ICollection
+ {
+ private Hashtable _listManagers;
- private Hashtable listManagers;
+ ///
+ /// Initializes a new instance of the System.Windows.Forms.BindingContext class.
+ ///
+ public BindingContext()
+ {
+ _listManagers = new Hashtable();
+ }
- ///
- ///
///
- ///
- /// Gets the total number of System.Windows.Forms.BindingManagerBases
- /// objects.
- ///
+ /// Gets the total number of System.Windows.Forms.BindingManagerBases objects.
///
- int ICollection.Count {
- get {
+ int ICollection.Count
+ {
+ get
+ {
ScrubWeakRefs();
- return listManagers.Count;
+ return _listManagers.Count;
}
}
- ///
- ///
///
- ///
/// Copies the elements of the collection into a specified array, starting
/// at the collection index.
- ///
///
void ICollection.CopyTo(Array ar, int index)
{
ScrubWeakRefs();
- listManagers.CopyTo(ar, index);
+ _listManagers.CopyTo(ar, index);
}
- ///
- ///
///
- ///
/// Gets an enumerator for the collection.
- ///
///
IEnumerator IEnumerable.GetEnumerator()
{
ScrubWeakRefs();
- return listManagers.GetEnumerator();
+ return _listManagers.GetEnumerator();
}
- ///
- ///
///
- ///
- /// Gets a value indicating whether the collection is read-only.
- ///
+ /// Gets a value indicating whether the collection is read-only.
///
- public bool IsReadOnly {
- get {
- return false;
- }
- }
+ public bool IsReadOnly => false;
- ///
- ///
///
- ///
/// Gets a value indicating whether the collection is synchronized.
- ///
///
- bool ICollection.IsSynchronized {
- get {
- // so the user will know that it has to lock this object
- return false;
- }
- }
-
- ///
- ///
- ///
- /// Gets an object to use for synchronization (thread safety).
- ///
- object ICollection.SyncRoot {
- get {
- return null;
- }
- }
+ bool ICollection.IsSynchronized => false;
-
- ///
///
- /// Initializes a new instance of the System.Windows.Forms.BindingContext class.
+ /// Gets an object to use for synchronization (thread safety).
///
- public BindingContext() {
- listManagers = new Hashtable();
- }
+ object ICollection.SyncRoot => null;
- ///
///
- ///
- /// Gets the System.Windows.Forms.BindingManagerBase
- /// associated with the specified data source.
- ///
+ /// Gets the System.Windows.Forms.BindingManagerBase associated with the specified
+ /// data source.
///
- public BindingManagerBase this[object dataSource] {
- get {
- return this[dataSource, ""];
- }
- }
+ public BindingManagerBase this[object dataSource] => this[dataSource, string.Empty];
- ///
///
- /// Gets the System.Windows.Forms.BindingManagerBase associated with the specified data source and
- /// data member.
+ /// Gets the System.Windows.Forms.BindingManagerBase associated with the specified
+ /// data source and data member.
///
- public BindingManagerBase this[object dataSource, string dataMember] {
- get {
- return EnsureListManager(dataSource, dataMember);
- }
+ public BindingManagerBase this[object dataSource, string dataMember]
+ {
+ get => EnsureListManager(dataSource, dataMember);
}
- ///
///
- /// Adds the listManager to the collection. An ArgumentNullException is thrown if this listManager
- /// is null. An exception is thrown if a listManager to the same target and Property as an existing listManager or
- /// if the listManager's column isn't a valid column given this DataSource.Table's schema.
+ /// Adds the listManager to the collection. An ArgumentNullException is thrown if this
+ /// listManager is null. An exception is thrown if a listManager to the same target
+ /// and Property as an existing listManager or if the listManager's column isn't a
+ /// valid column given this DataSource.Table's schema.
/// Fires the CollectionChangedEvent.
///
- internal protected void Add(object dataSource, BindingManagerBase listManager) {
- /* !!THIS METHOD IS OBSOLETE AND UNUSED!! */
+ ///
+ /// This method is obsolete and unused.
+ ///
+ internal protected void Add(object dataSource, BindingManagerBase listManager)
+ {
AddCore(dataSource, listManager);
OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, dataSource));
}
- ///
- ///
- ///
- protected virtual void AddCore(object dataSource, BindingManagerBase listManager) {
- /* !!THIS METHOD IS OBSOLETE AND UNUSED!! */
+ ///
+ /// This method is obsolete and unused.
+ ///
+ protected virtual void AddCore(object dataSource, BindingManagerBase listManager)
+ {
if (dataSource == null)
+ {
throw new ArgumentNullException(nameof(dataSource));
+ }
if (listManager == null)
+ {
throw new ArgumentNullException(nameof(listManager));
+ }
- // listManagers[dataSource] = listManager;
- listManagers[GetKey(dataSource, "")] = new WeakReference(listManager, false);
+ _listManagers[GetKey(dataSource, string.Empty)] = new WeakReference(listManager, false);
}
-
- ///
///
- ///
- /// Occurs when the collection has changed.
- ///
+ /// Occurs when the collection has changed.
///
+ ///
+ /// This method is obsolete and unused.
+ ///
[SRDescription(nameof(SR.collectionChangedEventDescr)), EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
- public event CollectionChangeEventHandler CollectionChanged {
- /* !!THIS EVENT IS OBSOLETE AND UNUSED!! */
+ public event CollectionChangeEventHandler CollectionChanged
+ {
[SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")]
- add {
+ add
+ {
throw new NotImplementedException();
}
[SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")]
- remove {
+ remove
+ {
}
}
- ///
///
/// Clears the collection of any bindings.
/// Fires the CollectionChangedEvent.
///
- internal protected void Clear() {
- /* !!THIS METHOD IS OBSOLETE AND UNUSED!! */
+ ///
+ /// This method is obsolete and unused.
+ ///
+ internal protected void Clear()
+ {
ClearCore();
OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null));
}
- ///
///
- ///
- /// Clears the collection.
- ///
+ /// Clears the collection.
///
- protected virtual void ClearCore() {
- /* !!THIS METHOD IS OBSOLETE AND UNUSED!! */
- listManagers.Clear();
- }
+ ///
+ /// This method is obsolete and unused.
+ ///
+ protected virtual void ClearCore() => _listManagers.Clear();
- ///
///
- /// Gets a value indicating whether the System.Windows.Forms.BindingContext
- /// contains the specified
- /// data source.
+ /// Gets a value indicating whether the System.Windows.Forms.BindingContext contains
+ /// the specified data source.
///
- public bool Contains(object dataSource) {
- return Contains(dataSource, "");
- }
+ public bool Contains(object dataSource) => Contains(dataSource, string.Empty);
- ///
///
- /// Gets a value indicating whether the System.Windows.Forms.BindingContext
- /// contains the specified data source and data member.
+ /// Gets a value indicating whether the System.Windows.Forms.BindingContext
+ /// contains the specified data source and data member.
///
- public bool Contains(object dataSource, string dataMember) {
- return listManagers.ContainsKey(GetKey(dataSource, dataMember));
+ public bool Contains(object dataSource, string dataMember)
+ {
+ return _listManagers.ContainsKey(GetKey(dataSource, dataMember));
}
- internal HashKey GetKey(object dataSource, string dataMember) {
+ internal HashKey GetKey(object dataSource, string dataMember)
+ {
return new HashKey(dataSource, dataMember);
}
- ///
- ///
- ///
- ///
- //
- internal class HashKey {
- WeakReference wRef;
- int dataSourceHashCode;
- string dataMember;
+ internal class HashKey
+ {
+ private WeakReference _wRef;
+ private int _dataSourceHashCode;
+ private string _dataMember;
- internal HashKey(object dataSource, string dataMember) {
+ internal HashKey(object dataSource, string dataMember)
+ {
if (dataSource == null)
+ {
throw new ArgumentNullException(nameof(dataSource));
+ }
if (dataMember == null)
- dataMember = "";
- // The dataMember should be case insensitive.
- // so convert the dataMember to lower case
- //
- this.wRef = new WeakReference(dataSource, false);
- this.dataSourceHashCode = dataSource.GetHashCode();
- this.dataMember = dataMember.ToLower(CultureInfo.InvariantCulture);
- }
+ {
+ dataMember = string.Empty;
+ }
- ///
- ///
- ///
- ///
- public override int GetHashCode() {
- return dataSourceHashCode * dataMember.GetHashCode();
+ // The dataMember should be case insensitive, so convert the
+ // dataMember to lower case
+ _wRef = new WeakReference(dataSource, false);
+ _dataSourceHashCode = dataSource.GetHashCode();
+ _dataMember = dataMember.ToLower(CultureInfo.InvariantCulture);
}
- ///
- ///
- ///
- ///
- public override bool Equals(object target) {
- if (target is HashKey) {
- HashKey keyTarget = (HashKey)target;
- return wRef.Target == keyTarget.wRef.Target && dataMember == keyTarget.dataMember;
+ public override int GetHashCode() => _dataSourceHashCode * _dataMember.GetHashCode();
+
+ public override bool Equals(object target)
+ {
+ if (!(target is HashKey keyTarget))
+ {
+ return false;
}
- return false;
+
+ return _wRef.Target == keyTarget._wRef.Target && _dataMember == keyTarget._dataMember;
}
}
- ///
- ///
///
- /// This method is called whenever the collection changes. Overriders
- /// of this method should call the base implementation of this method.
- /// NOTE: This shipped in Everett, so we need to keep it, but we don't do
- /// anything here.
+ /// This method is called whenever the collection changes. Overriders of this method
+ /// should call the base implementation of this method.
///
- protected virtual void OnCollectionChanged(CollectionChangeEventArgs ccevent) {
+ protected virtual void OnCollectionChanged(CollectionChangeEventArgs ccevent)
+ {
}
- ///
///
/// Removes the given listManager from the collection.
- /// An ArgumentNullException is thrown if this listManager is null. An ArgumentException is thrown
- /// if this listManager doesn't belong to this collection.
+ /// An ArgumentNullException is thrown if this listManager is null. An ArgumentException
+ /// is thrown if this listManager doesn't belong to this collection.
/// The CollectionChanged event is fired if it succeeds.
///
- internal protected void Remove(object dataSource) {
- /* !!THIS METHOD IS OBSOLETE AND UNUSED!! */
+ ///
+ /// This method is obsolete and unused.
+ ///
+ internal protected void Remove(object dataSource)
+ {
RemoveCore(dataSource);
OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, dataSource));
}
- ///
- ///
- ///
- ///
- protected virtual void RemoveCore(object dataSource) {
- /* !!THIS METHOD IS OBSOLETE AND UNUSED!! */
- listManagers.Remove(GetKey(dataSource, ""));
+ ///
+ /// This method is obsolete and unused.
+ ///
+ protected virtual void RemoveCore(object dataSource)
+ {
+ _listManagers.Remove(GetKey(dataSource, string.Empty));
}
- ///
///
- /// Create a suitable binding manager for the specified dataSource/dataMember combination.
- /// - If one has already been created and cached by this BindingContext, return that instead.
- /// - If the data source is an ICurrencyManagerProvider, just delegate to the data source.
+ /// Create a suitable binding manager for the specified dataSource/dataMember combination.
+ /// - If one has already been created and cached by this BindingContext, return that
+ /// instead.
+ /// - If the data source is an ICurrencyManagerProvider, just delegate to the data
+ /// source.
///
- internal BindingManagerBase EnsureListManager(object dataSource, string dataMember) {
+ internal BindingManagerBase EnsureListManager(object dataSource, string dataMember)
+ {
BindingManagerBase bindingManagerBase = null;
if (dataMember == null)
- dataMember = "";
+ {
+ dataMember = string.Empty;
+ }
// 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) {
+ if (dataSource is ICurrencyManagerProvider currencyManagerProvider)
+ {
+ bindingManagerBase = currencyManagerProvider.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;
+ WeakReference wRef = _listManagers[key] as WeakReference;
if (wRef != null)
- bindingManagerBase = (BindingManagerBase) wRef.Target;
- if (bindingManagerBase != null) {
+ {
+ bindingManagerBase = (BindingManagerBase)wRef.Target;
+ }
+ if (bindingManagerBase != null)
+ {
return bindingManagerBase;
}
- if (dataMember.Length == 0) {
+ if (dataMember.Length == 0)
+ {
// No data member specified, so create binding manager directly on the data source
- //
- if (dataSource is IList || dataSource is IListSource) {
+ 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 {
+ else
+ {
// Otherwise assume simple property binding
bindingManagerBase = new PropertyManager(dataSource);
}
}
- else {
+ 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);
+ int lastDot = dataMember.LastIndexOf(".");
+ string dataPath = (lastDot == -1) ? string.Empty : 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(string.Format(SR.RelatedListManagerChild, dataField));
+ }
if (typeof(IList).IsAssignableFrom(prop.PropertyType))
+ {
bindingManagerBase = new RelatedCurrencyManager(formerManager, dataField);
+ }
else
+ {
bindingManagerBase = new RelatedPropertyManager(formerManager, dataField);
+ }
}
// if wRef == null, then it is the first time we want this bindingManagerBase: so add it
// if wRef != null, then the bindingManagerBase was GC'ed at some point: keep the old wRef and change its target
if (wRef == null)
- listManagers.Add(key, new WeakReference(bindingManagerBase, false));
+ {
+ _listManagers.Add(key, new WeakReference(bindingManagerBase, false));
+ }
else
+ {
wRef.Target = bindingManagerBase;
+ }
ScrubWeakRefs();
// Return the final binding manager
return bindingManagerBase;
}
- // may throw
- private static void CheckPropertyBindingCycles(BindingContext newBindingContext, Binding propBinding) {
+ private static void CheckPropertyBindingCycles(BindingContext newBindingContext, Binding propBinding)
+ {
if (newBindingContext == null || propBinding == null)
+ {
return;
- if (newBindingContext.Contains(propBinding.BindableComponent, "")) {
+ }
+
+ if (newBindingContext.Contains(propBinding.BindableComponent, string.Empty))
+ {
// this way we do not add a bindingManagerBase to the
// bindingContext if there isn't one already
- BindingManagerBase bindingManagerBase = newBindingContext.EnsureListManager(propBinding.BindableComponent, "");
- for (int i = 0; i < bindingManagerBase.Bindings.Count; i++) {
+ BindingManagerBase bindingManagerBase = newBindingContext.EnsureListManager(propBinding.BindableComponent, string.Empty);
+ for (int i = 0; i < bindingManagerBase.Bindings.Count; i++)
+ {
Binding binding = bindingManagerBase.Bindings[i];
- if (binding.DataSource == propBinding.BindableComponent) {
+ if (binding.DataSource == propBinding.BindableComponent)
+ {
if (propBinding.BindToObject.BindingMemberInfo.BindingMember.Equals(binding.PropertyName))
- throw new ArgumentException(string.Format(SR.DataBindingCycle, binding.PropertyName), "propBinding");
- } else if (propBinding.BindToObject.BindingManagerBase is PropertyManager)
+ {
+ throw new ArgumentException(string.Format(SR.DataBindingCycle, binding.PropertyName), nameof(propBinding));
+ }
+ }
+ else if (propBinding.BindToObject.BindingManagerBase is PropertyManager)
+ {
CheckPropertyBindingCycles(newBindingContext, binding);
+ }
}
}
}
- private void ScrubWeakRefs() {
+ private void ScrubWeakRefs()
+ {
ArrayList cleanupList = null;
- foreach (DictionaryEntry de in listManagers) {
- WeakReference wRef = (WeakReference) de.Value;
- if (wRef.Target == null) {
- if (cleanupList == null) {
+ foreach (DictionaryEntry de in _listManagers)
+ {
+ WeakReference wRef = (WeakReference)de.Value;
+ if (wRef.Target == null)
+ {
+ if (cleanupList == null)
+ {
cleanupList = new ArrayList();
}
cleanupList.Add(de.Key);
}
}
- if (cleanupList != null) {
- foreach (object o in cleanupList) {
- listManagers.Remove(o);
+ if (cleanupList != null)
+ {
+ foreach (object o in cleanupList)
+ {
+ _listManagers.Remove(o);
}
}
}
- ///
///
- /// Associates a Binding with a different BindingContext. Intended for use by components that support
- /// IBindableComponent, to update their Bindings when the value of IBindableComponent.BindingContext
- /// is changed.
+ /// Associates a Binding with a different BindingContext. Intended for use by components
+ /// that support IBindableComponent, to update their Bindings when the value of
+ /// IBindableComponent.BindingContext is changed.
///
- public static void UpdateBinding(BindingContext newBindingContext, Binding binding) {
+ public static void UpdateBinding(BindingContext newBindingContext, Binding binding)
+ {
+ if (binding == null)
+ {
+ throw new ArgumentNullException(nameof(binding));
+ }
+
BindingManagerBase oldManager = binding.BindingManagerBase;
- if (oldManager != null) {
+ if (oldManager != null)
+ {
oldManager.Bindings.Remove(binding);
}
- if (newBindingContext != null) {
+ if (newBindingContext != null)
+ {
// we need to first check for cycles before adding this binding to the collection
// of bindings.
if (binding.BindToObject.BindingManagerBase is PropertyManager)
+ {
CheckPropertyBindingCycles(newBindingContext, binding);
+ }
BindToObject bindTo = binding.BindToObject;
BindingManagerBase newManager = newBindingContext.EnsureListManager(bindTo.DataSource, bindTo.BindingMemberInfo.BindingPath);
diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/BindingContextTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/BindingContextTests.cs
new file mode 100644
index 00000000000..2d9273201bc
--- /dev/null
+++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/BindingContextTests.cs
@@ -0,0 +1,1025 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using Moq;
+using Xunit;
+
+namespace System.Windows.Forms.Tests
+{
+ public class BindingContextTests
+ {
+ [Fact]
+ public void BindingContext_Ctor_Default()
+ {
+ var context = new BindingContext();
+ Assert.False(context.IsReadOnly);
+ Assert.Empty(context);
+ }
+
+ [Fact]
+ public void BindingContext_ICollection_GetProperties_ReturnsExpected()
+ {
+ ICollection context = new BindingContext();
+ Assert.False(context.IsSynchronized);
+ Assert.Null(context.SyncRoot);
+ Assert.Empty(context);
+ }
+
+ [Fact]
+ public void BindingContext_Count_GetWithNullWeakReferenceTarget_ScrubsWeakRefs()
+ {
+ var context = new BindingContext();
+ var dataSource1 = new DataSource { Property = 1 };
+ var dataSource2 = new DataSource { Property = 2 };
+ PropertyManager manager1 = Assert.IsAssignableFrom(context[dataSource1, "Property"]);
+ PropertyManager manager2 = Assert.IsAssignableFrom(context[dataSource2, "Property"]);
+ var array = new DictionaryEntry[4];
+ ((ICollection)context).CopyTo(array, 0);
+
+ WeakReference reference1 = Assert.IsType(array.Single(p => ((WeakReference)p.Value).Target == manager1).Value);
+ WeakReference reference2 = Assert.IsType(array.Single(p => ((WeakReference)p.Value).Target == manager2).Value);
+ Assert.Same(manager1, reference1.Target);
+ Assert.Same(manager2, reference2.Target);
+
+ // Simulate a GC by assigning the weak reference to null.
+ reference1.Target = null;
+ reference2.Target = null;
+
+ // Verify these weak references have been scrubbed.
+ Assert.Equal(2, ((ICollection)context).Count);
+ }
+
+ [Fact]
+ public void BindingContext_Add_NullDataSource_ThrowsArgumentNullException()
+ {
+ var context = new BindingContext();
+ var source = new BindingSource();
+ Assert.Throws("dataSource", () => context.Add(null, source.CurrencyManager));
+ }
+
+ [Fact]
+ public void BindingContext_Add_Invoke_GetReturnsExpected()
+ {
+ var context = new BindingContext();
+ var source1 = new BindingSource();
+ var source2 = new BindingSource();
+ var dataSource = new DataSource();
+ context.Add(dataSource, source1.CurrencyManager);
+ Assert.Single(context);
+
+ Assert.Same(source1.CurrencyManager, context[dataSource]);
+ Assert.Same(source1.CurrencyManager, context[dataSource, null]);
+ Assert.Same(source1.CurrencyManager, context[dataSource, string.Empty]);
+
+ // Set new value.
+ context.Add(dataSource, source2.CurrencyManager);
+ Assert.Single(context);
+ Assert.Same(source2.CurrencyManager, context[dataSource]);
+ Assert.Same(source2.CurrencyManager, context[dataSource, null]);
+ Assert.Same(source2.CurrencyManager, context[dataSource, string.Empty]);
+ }
+
+ [Fact]
+ public void BindingContext_Add_InvokeMultiple_Success()
+ {
+ var context = new BindingContext();
+ var source1 = new BindingSource();
+ var source2 = new BindingSource();
+ var dataSource1 = new DataSource();
+ var dataSource2 = new DataSource();
+ context.Add(dataSource1, source1.CurrencyManager);
+ context.Add(dataSource2, source2.CurrencyManager);
+ Assert.Equal(2, ((ICollection)context).Count);
+
+ Assert.Same(source1.CurrencyManager, context[dataSource1]);
+ Assert.Same(source2.CurrencyManager, context[dataSource2]);
+ }
+
+ [Fact]
+ public void BindingContext_Add_NullListManager_ThrowsArgumentNullException()
+ {
+ var context = new BindingContext();
+ Assert.Throws("listManager", () => context.Add(1, null));
+ }
+
+ [Fact]
+ public void BindingContext_AddCore_Invoke_GetReturnsExpected()
+ {
+ var context = new SubBindingContext();
+ var source1 = new BindingSource();
+ var source2 = new BindingSource();
+ var dataSource = new DataSource();
+ context.AddCore(dataSource, source1.CurrencyManager);
+ Assert.Single(context);
+
+ Assert.Same(source1.CurrencyManager, context[dataSource]);
+ Assert.Same(source1.CurrencyManager, context[dataSource, null]);
+ Assert.Same(source1.CurrencyManager, context[dataSource, string.Empty]);
+
+ // Set new value.
+ context.AddCore(dataSource, source2.CurrencyManager);
+ Assert.Single(context);
+ Assert.Same(source2.CurrencyManager, context[dataSource]);
+ Assert.Same(source2.CurrencyManager, context[dataSource, null]);
+ Assert.Same(source2.CurrencyManager, context[dataSource, string.Empty]);
+ }
+
+ [Fact]
+ public void BindingContext_AddCore_InvokeMultiple_Success()
+ {
+ var context = new SubBindingContext();
+ var source1 = new BindingSource();
+ var source2 = new BindingSource();
+ var dataSource1 = new DataSource();
+ var dataSource2 = new DataSource();
+ context.AddCore(dataSource1, source1.CurrencyManager);
+ context.AddCore(dataSource2, source2.CurrencyManager);
+ Assert.Equal(2, ((ICollection)context).Count);
+
+ Assert.Same(source1.CurrencyManager, context[dataSource1]);
+ Assert.Same(source2.CurrencyManager, context[dataSource2]);
+ }
+
+ [Fact]
+ public void BindingContext_AddCore_NullDataSource_ThrowsArgumentNullException()
+ {
+ var context = new SubBindingContext();
+ var source = new BindingSource();
+ Assert.Throws("dataSource", () => context.AddCore(null, source.CurrencyManager));
+ }
+
+ [Fact]
+ public void BindingContext_AddCore_NullListManager_ThrowsArgumentNullException()
+ {
+ var context = new SubBindingContext();
+ Assert.Throws("listManager", () => context.AddCore(1, null));
+ }
+
+ [Fact]
+ public void BindingContext_CopyTo_Invoke_Success()
+ {
+ var context = new BindingContext();
+ var source = new BindingSource();
+ var dataSource = new DataSource();
+ context.Add(dataSource, source.CurrencyManager);
+
+ var array = new object[] { 1, 2, 3 };
+ ((ICollection)context).CopyTo(array, 1);
+ Assert.Equal(1, array[0]);
+ Assert.NotNull(Assert.IsType(array[1]).Key);
+ Assert.Equal(source.CurrencyManager, Assert.IsType(Assert.IsType(array[1]).Value).Target);
+ Assert.Equal(3, array[2]);
+ }
+
+ [Fact]
+ public void BindingContext_CopyTo_WithNullWeakReferenceTarget_ScrubsWeakRefs()
+ {
+ var context = new BindingContext();
+ var dataSource1 = new DataSource { Property = 1 };
+ var dataSource2 = new DataSource { Property = 2 };
+ PropertyManager manager1 = Assert.IsAssignableFrom(context[dataSource1, "Property"]);
+ PropertyManager manager2 = Assert.IsAssignableFrom(context[dataSource2, "Property"]);
+ var array = new DictionaryEntry[4];
+ ((ICollection)context).CopyTo(array, 0);
+
+ WeakReference reference1 = Assert.IsType(array.Single(p => ((WeakReference)p.Value).Target == manager1).Value);
+ WeakReference reference2 = Assert.IsType(array.Single(p => ((WeakReference)p.Value).Target == manager2).Value);
+ Assert.Same(manager1, reference1.Target);
+ Assert.Same(manager2, reference2.Target);
+
+ // Simulate a GC by assigning the weak reference to null.
+ reference1.Target = null;
+ reference2.Target = null;
+
+ // Verify these weak references have been scrubbed.
+ var destArray = new object[] { 1, 2, 3, 4 };
+ ((ICollection)context).CopyTo(destArray, 1);
+ Assert.Equal(1, destArray[0]);
+ Assert.IsType(destArray[1]);
+ Assert.IsType(destArray[2]);
+ Assert.Equal(4, destArray[3]);
+ }
+
+ [Fact]
+ public void BindingContext_GetEnumerator_WithNullWeakReferenceTarget_ScrubsWeakRefs()
+ {
+ var context = new BindingContext();
+ var dataSource1 = new DataSource { Property = 1 };
+ var dataSource2 = new DataSource { Property = 2 };
+ PropertyManager manager1 = Assert.IsAssignableFrom(context[dataSource1, "Property"]);
+ PropertyManager manager2 = Assert.IsAssignableFrom(context[dataSource2, "Property"]);
+ var array = new DictionaryEntry[4];
+ ((ICollection)context).CopyTo(array, 0);
+
+ WeakReference reference1 = Assert.IsType(array.Single(p => ((WeakReference)p.Value).Target == manager1).Value);
+ WeakReference reference2 = Assert.IsType(array.Single(p => ((WeakReference)p.Value).Target == manager2).Value);
+ Assert.Same(manager1, reference1.Target);
+ Assert.Same(manager2, reference2.Target);
+
+ // Simulate a GC by assigning the weak reference to null.
+ reference1.Target = null;
+ reference2.Target = null;
+
+ // Verify these weak references have been scrubbed.
+ IEnumerator enumerator = ((ICollection)context).GetEnumerator();
+ Assert.True(enumerator.MoveNext());
+ Assert.IsType(enumerator.Current);
+ Assert.True(enumerator.MoveNext());
+ Assert.IsType(enumerator.Current);
+ Assert.False(enumerator.MoveNext());
+ }
+
+ [Fact]
+ public void BindingContext_Remove_Invoke_Success()
+ {
+ var context = new BindingContext();
+ var source1 = new BindingSource();
+ var dataSource1 = new DataSource();
+ var source2 = new BindingSource();
+ var dataSource2 = new DataSource();
+ context.Add(dataSource1, source1.CurrencyManager);
+ context.Add(dataSource2, source2.CurrencyManager);
+
+ context.Remove(dataSource1);
+ Assert.Single(context);
+
+ // Remove again.
+ context.Remove(dataSource1);
+ Assert.Single(context);
+
+ context.Remove(dataSource2);
+ Assert.Empty(context);
+
+ // Remove again.
+ context.Remove(dataSource2);
+ Assert.Empty(context);
+ }
+
+ [Fact]
+ public void BindingContext_Remove_NullDataSource_ThrowsArgumentNullException()
+ {
+ var context = new SubBindingContext();
+ Assert.Throws("dataSource", () => context.Remove(null));
+ }
+
+ [Fact]
+ public void BindingContext_RemoveCore_Invoke_Success()
+ {
+ var context = new SubBindingContext();
+ var source1 = new BindingSource();
+ var dataSource1 = new DataSource();
+ var source2 = new BindingSource();
+ var dataSource2 = new DataSource();
+ context.Add(dataSource1, source1.CurrencyManager);
+ context.Add(dataSource2, source2.CurrencyManager);
+
+ context.RemoveCore(dataSource1);
+ Assert.Single(context);
+
+ // Remove again.
+ context.RemoveCore(dataSource1);
+ Assert.Single(context);
+
+ context.RemoveCore(dataSource2);
+ Assert.Empty(context);
+
+ // Remove again.
+ context.RemoveCore(dataSource2);
+ Assert.Empty(context);
+ }
+
+ [Fact]
+ public void BindingContext_RemoveCore_NullDataSource_ThrowsArgumentNullException()
+ {
+ var context = new SubBindingContext();
+ Assert.Throws("dataSource", () => context.RemoveCore(null));
+ }
+
+ [Fact]
+ public void BindingContext_Clear_Empty_Success()
+ {
+ var context = new BindingContext();
+ context.Clear();
+ Assert.Empty(context);
+
+ // Clear again.
+ context.Clear();
+ Assert.Empty(context);
+ }
+
+ [Fact]
+ public void BindingContext_Clear_NotEmpty_Success()
+ {
+ var context = new BindingContext();
+ var source = new BindingSource();
+ context.Add(new DataSource(), source.CurrencyManager);
+
+ // Clear again.
+ context.Clear();
+ Assert.Empty(context);
+
+ context.Clear();
+ Assert.Empty(context);
+ }
+
+ [Fact]
+ public void BindingContext_ClearCore_Empty_Success()
+ {
+ var context = new SubBindingContext();
+ context.ClearCore();
+ Assert.Empty(context);
+
+ // Clear again.
+ context.ClearCore();
+ Assert.Empty(context);
+ }
+
+ [Fact]
+ public void BindingContext_ClearCore_NotEmpty_Success()
+ {
+ var context = new SubBindingContext();
+ var source = new BindingSource();
+ context.Add(new DataSource(), source.CurrencyManager);
+
+ // Clear again.
+ context.ClearCore();
+ Assert.Empty(context);
+
+ context.ClearCore();
+ Assert.Empty(context);
+ }
+
+ public static IEnumerable