diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Binding.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Binding.cs index 0ddb07d5c38..a8e4ee8f3e8 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Binding.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Binding.cs @@ -1086,6 +1086,7 @@ internal void UpdateIsBinding() private class BindToObject { + private BindingManagerBase _bindingManager; private PropertyDescriptor _fieldInfo; private readonly Binding _owner; private string _errorText = string.Empty; @@ -1095,7 +1096,7 @@ private class BindToObject private void PropValueChanged(object sender, EventArgs e) { - _owner.BindingManagerBase?.OnCurrentChanged(EventArgs.Empty); + _bindingManager?.OnCurrentChanged(EventArgs.Empty); } private bool IsDataSourceInitialized @@ -1157,13 +1158,19 @@ private void DataSource_Initialized(object sender, EventArgs e) internal void SetBindingManagerBase(BindingManagerBase lManager) { + if (_bindingManager == lManager) + { + return; + } + // remove notification from the backEnd - if (_owner.BindingManagerBase != null && _fieldInfo != null && _owner.BindingManagerBase.IsBinding && !(_owner.BindingManagerBase is CurrencyManager)) + if (_bindingManager != null && _fieldInfo != null && _bindingManager.IsBinding && !(_bindingManager is CurrencyManager)) { - _fieldInfo.RemoveValueChanged(_owner.BindingManagerBase.Current, new EventHandler(PropValueChanged)); + _fieldInfo.RemoveValueChanged(_bindingManager.Current, new EventHandler(PropValueChanged)); _fieldInfo = null; } + _bindingManager = lManager; CheckBinding(); } @@ -1200,7 +1207,7 @@ private string GetErrorText(object value) internal object GetValue() { - object obj = _owner.BindingManagerBase.Current; + object obj = _bindingManager.Current; // Update IDataErrorInfo text: it's ok to get this now because we're going to need // this as part of the BindingCompleteEventArgs anyway. @@ -1222,7 +1229,7 @@ internal Type BindToType { // if we are bound to a list w/o any properties, then // take the type from the BindingManager - Type type = _owner.BindingManagerBase.BindType; + Type type = _bindingManager.BindType; if (typeof(Array).IsAssignableFrom(type)) { type = type.GetElementType(); @@ -1240,7 +1247,7 @@ internal void SetValue(object value) if (_fieldInfo != null) { - obj = _owner.BindingManagerBase.Current; + obj = _bindingManager.Current; if (obj is IEditableObject editableObject) { editableObject.BeginEdit(); @@ -1252,7 +1259,7 @@ internal void SetValue(object value) } else { - if (_owner.BindingManagerBase is CurrencyManager cm) + if (_bindingManager is CurrencyManager cm) { cm[cm.Position] = value; obj = value; @@ -1274,24 +1281,24 @@ internal void CheckBinding() } // Remove propertyChangedNotification when this binding is deleted - if (_owner.BindingManagerBase != null && + if (_bindingManager != null && _fieldInfo != null && - _owner.BindingManagerBase.IsBinding && - !(_owner.BindingManagerBase is CurrencyManager)) + _bindingManager.IsBinding && + !(_bindingManager is CurrencyManager)) { - _fieldInfo.RemoveValueChanged(_owner.BindingManagerBase.Current, new EventHandler(PropValueChanged)); + _fieldInfo.RemoveValueChanged(_bindingManager.Current, new EventHandler(PropValueChanged)); } - if (_owner.BindingManagerBase != null && + if (_bindingManager != null && _owner.BindableComponent != null && _owner.ComponentCreated && IsDataSourceInitialized) { string dataField = _owner.BindingMemberInfo.BindingField; - _fieldInfo = _owner.BindingManagerBase.GetItemProperties().Find(dataField, true); - if (_owner.BindingManagerBase.DataSource != null && _fieldInfo == null && dataField.Length > 0) + _fieldInfo = _bindingManager.GetItemProperties().Find(dataField, true); + if (_bindingManager.DataSource != null && _fieldInfo == null && dataField.Length > 0) { throw new ArgumentException(string.Format(SR.ListBindingBindField, dataField), "dataMember"); } @@ -1302,11 +1309,11 @@ internal void CheckBinding() // if the binding is of the form (Control, ControlProperty, DataSource, Property1.Property2.Property3) // then we want to get notification from Current.Property1.Property2 and not from DataSource // when we get the backEnd notification we push the new value into the Control's property - if (_fieldInfo != null && _owner.BindingManagerBase.IsBinding && - !(_owner.BindingManagerBase is CurrencyManager)) + if (_fieldInfo != null && _bindingManager.IsBinding && + !(_bindingManager is CurrencyManager)) { - _fieldInfo.AddValueChanged(_owner.BindingManagerBase.Current, new EventHandler(PropValueChanged)); + _fieldInfo.AddValueChanged(_bindingManager.Current, new EventHandler(PropValueChanged)); } } else diff --git a/src/System.Windows.Forms/tests/IntegrationTests/System.Windows.Forms.IntegrationTests/Mocks/MainObject.cs b/src/System.Windows.Forms/tests/IntegrationTests/System.Windows.Forms.IntegrationTests/Mocks/MainObject.cs new file mode 100644 index 00000000000..c39a361e4fc --- /dev/null +++ b/src/System.Windows.Forms/tests/IntegrationTests/System.Windows.Forms.IntegrationTests/Mocks/MainObject.cs @@ -0,0 +1,39 @@ +// 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.ComponentModel; + +namespace System.Windows.Forms.IntegrationTests.Mocks +{ + public class MainObject : INotifyPropertyChanged + { + private string text; + private PropertyChangedEventHandler _propertyChanged; + + public string Text + { + get { return text; } + set + { + if (text != value) + { + text = value; + if (_propertyChanged != null) + { + _propertyChanged(this, new PropertyChangedEventArgs(nameof(Text))); + } + } + } + } + + public event PropertyChangedEventHandler PropertyChanged + { + add => _propertyChanged += value; + remove => _propertyChanged -= value; + } + + public bool IsPropertyChangedAssigned { get { return _propertyChanged != null; } } + + } +} diff --git a/src/System.Windows.Forms/tests/IntegrationTests/System.Windows.Forms.IntegrationTests/WinformsControlsTest.cs b/src/System.Windows.Forms/tests/IntegrationTests/System.Windows.Forms.IntegrationTests/WinformsControlsTest.cs index d28eea66e95..98a74605a23 100644 --- a/src/System.Windows.Forms/tests/IntegrationTests/System.Windows.Forms.IntegrationTests/WinformsControlsTest.cs +++ b/src/System.Windows.Forms/tests/IntegrationTests/System.Windows.Forms.IntegrationTests/WinformsControlsTest.cs @@ -8,7 +8,7 @@ namespace System.Windows.Forms.IntegrationTests { - public class WinformsControlsTest + public partial class WinformsControlsTest { private const string ProjectName = "WinformsControlsTest"; private readonly string _exePath; @@ -315,5 +315,27 @@ public void WinformsControlsTest_RichTextBoxesTest() Assert.True(process.HasExited); } + + [Fact] + public void DataBindings_remove_should_unsubscribe_INotifyPropertyChanged_PropertyChanged_event() + { + var mainObject = new Mocks.MainObject(); + mainObject.Text = "Test text"; + Form form = new Form(); + TextBox textBox = new TextBox(); + Binding binding = new Binding("Text", mainObject, "Text"); + textBox.DataBindings.Add(binding); + textBox.Parent = form; + form.Show(); + + // bindings set + Assert.True( mainObject.IsPropertyChangedAssigned); + + // remove bindings + textBox.DataBindings.Clear(); + + // bindings unset + Assert.False(mainObject.IsPropertyChangedAssigned); + } } }