[Mono-bugs] [Bug 664829] New: Data binding events not unhooked correctly, causing a memory leak in some situations

bugzilla_noreply at novell.com bugzilla_noreply at novell.com
Sun Jan 16 16:56:36 EST 2011


https://bugzilla.novell.com/show_bug.cgi?id=664829

https://bugzilla.novell.com/show_bug.cgi?id=664829#c0


           Summary: Data binding events not unhooked correctly, causing a
                    memory leak in some situations
    Classification: Mono
           Product: Mono: Class Libraries
           Version: 2.6.x
          Platform: x86
        OS/Version: Windows 7
            Status: NEW
          Severity: Normal
          Priority: P5 - None
         Component: Windows.Forms
        AssignedTo: mono-bugs at lists.ximian.com
        ReportedBy: cvolzke at live.com.au
         QAContact: mono-bugs at lists.ximian.com
          Found By: ---
           Blocker: ---


User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US)
AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.1.249.1036 Safari/532.5

Control and data source events are not unhooked correctly, causing a memory
leak in some circumstances.


Reproducible: Always

Steps to Reproduce:
[Test]
public void Test_DataBindingEventsUnhookedCorrectly()
{
    TestDataSource dataSource = new TestDataSource();
    Control.DataBindings.Add("ControlValue", dataSource, "IntProperty");
    Form.Controls.Add(Control);
    Form.Show();
    Application.DoEvents();

    AssertEquals(true, Control.ControlValueChanged_IsHooked);
    AssertEquals(true, dataSource.IntPropertyChanged_IsHooked);

    Form.Dispose();
    AssertEquals(false, Control.ControlValueChanged_IsHooked);
    AssertEquals(false, dataSource.IntPropertyChanged_IsHooked);
}

Form form;
Form Form
{
    get { return form ?? (form = new Form()); }
}

TestControl control;
TestControl Control
{
    get { return control ?? (control = new TestControl()); }
}

class TestControl : Control
{
    public bool ControlValueChanged_IsHooked
    {
        get { return ControlValueChanged != null; }
    }

    public event EventHandler ControlValueChanged;
    public int ControlValue
    {
        get
        {
            int result;
            int.TryParse(Text, out result);
            return result;
        }
        set
        {
            Text = value.ToString();
            if (ControlValueChanged != null)
            {
                ControlValueChanged(this, EventArgs.Empty);
            }
        }
    }
}

class TestDataSource
{
    public bool IntPropertyChanged_IsHooked
    {
        get { return IntPropertyChanged != null; }
    }

    public event EventHandler IntPropertyChanged;

    public const int IntPropertyDefaultValue = 5;
    int intProperty = IntPropertyDefaultValue;
    public int IntProperty
    {
        get { return intProperty; }
        set
        {
            intProperty = value;
            if (IntPropertyChanged != null)
            {
                IntPropertyChanged(this, EventArgs.Empty);
            }
        }
    }
}

Actual Results:  
Unit test fails

Expected Results:  
Unit test should pass, as it does with ms.net.

Code added to Binding.SetControl:
#if NET_2_0
    internal void SetControl (IBindableComponent control)
#else
    internal void SetControl (Control control)
#endif
    {
// ----- code added here ----- //
        if (control == null) // when removing the binding
        {
            // unhook control events
            if (this.control != null)
            {
                this.control.Validating -= new
CancelEventHandler(ControlValidatingHandler);
                this.control.HandleCreated -= new
EventHandler(ControlCreatedHandler);
            }

            // unhook the control ValueChanged event
            PropertyDescriptor prop_changed_event2 =
GetPropertyChangedEvent(this.control, property_name);
            if (prop_changed_event2 != null)
                prop_changed_event2.RemoveValueChanged(this.control, new
EventHandler(ControlPropertyChangedHandler));

            // unhook the data source events
            if (manager is PropertyManager)
            {
                PropertyDescriptor prop_changed_event3 =
GetPropertyChangedEvent(manager.Current, binding_member_info.BindingField);
                if (prop_changed_event3 != null)
                    prop_changed_event3.RemoveValueChanged(manager.Current, new
EventHandler(SourcePropertyChangedHandler));
            }
            return;
        }
// ----- code added here ----- //
        if (control == this.control)
            return;

Code added to ControlBindingsCollection constructor:
    public ControlBindingsCollection (IBindableComponent control)
    {
        bindable_component = control;
// ----- code added here ----- //
        // clean up the bindings once the control is disposed
        EventDescriptor disposed =
TypeDescriptor.GetEvents(control)["Disposed"];
        disposed.AddEventHandler(control, new EventHandler(delegate
        {
            foreach (Binding binding in this)
            {
                binding.SetControl(null);
            }
        }));
// ----- code added here ----- //
        this.control = control as Control;
        default_datasource_update_mode = DataSourceUpdateMode.OnValidation;
    }

Code added to ControlBindingsCollection.RemoveCore:
    protected override void RemoveCore (Binding dataBinding) {
        if (dataBinding == null)
            throw new ArgumentNullException ("dataBinding");

        dataBinding.SetControl(null); // line added
        base.RemoveCore (dataBinding);
    }

-- 
Configure bugmail: https://bugzilla.novell.com/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the QA contact for the bug.
You are the assignee for the bug.


More information about the mono-bugs mailing list