Why does Windows Forms databinding want to set my nested boolean databound property when I raise a notification event on the parent object?
Asked Answered
C

3

11

OK, so here is some context for my problem, written as pseudo-C# code (feel free to point out any mistake): (You can jump directly to the stacktrace and read the context later.)

public class SomeForm {
    private _model = new ViewModelClass
    public void new() {
        // Normal Winforms init omitted
        ViewModelClassBindingSource.DataSource = _model;
        SomeControl1.SetModel(_model);
    }
}
public class SomeControl {
    private _model = new ViewModelClass

    internal void SetModel(ViewModelClass model) {
        _model = model;
        ViewModelClassBindingSource.DataSource = model;
        ViewModelClassBindingSource.ResetBindings(true);
    }
}

public class ComplexObject : IPropertyChanging, IPropertyChanged {
    public property bool BoolProp {get; set;}
}

public class ViewModelClass : IPropertyChanged {
    property IList<ComplexObject> ComplexObjects {get;}

    property ComplexObject SelectedComplexObject {get; set;}

    property Object SomethingNotNecessarilyRelated {get; set;}

    private void NotifyPropertyChanged(string propName) {
        PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
}

All of the mentioned properties in these classes are databound in the Visual Studio 2008 Windows Forms designer, in the SomeForm or in the SomeControl classes. (ComplexObject.BoolProp is databound in both). Don't hesitate to ask more questions about the context.

The problem: When I make some (bunch of) notifications in the ViewModelClass class, there is some kind of knee-jerk reaction that sets ComplexObject.BoolProp to false, using that kind of stack trace:

System.dll!System.ComponentModel.ReflectPropertyDescriptor.SetValue(object component = "Object Exposed in 'SelectedComplexObject'", object value = false) + 0x124 bytes 
System.Windows.Forms.dll!System.Windows.Forms.BindToObject.SetValue(object value) + 0x5d bytes  
System.Windows.Forms.dll!System.Windows.Forms.Binding.PullData(bool reformat, bool force) + 0x15a bytes 
System.Windows.Forms.dll!System.Windows.Forms.BindingManagerBase.PullData(out bool success = true) + 0x6e bytes 
System.Windows.Forms.dll!System.Windows.Forms.BindingSource.ParentCurrencyManager_CurrentItemChanged(object sender = {System.Windows.Forms.CurrencyManager}, System.EventArgs e) + 0x54 bytes   
[Native to Managed Transition]  
[Managed to Native Transition]  
System.Windows.Forms.dll!System.Windows.Forms.CurrencyManager.OnCurrentItemChanged(System.EventArgs e) + 0x17 bytes 
System.Windows.Forms.dll!System.Windows.Forms.CurrencyManager.List_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e) + 0x3bc bytes   
System.Windows.Forms.dll!System.Windows.Forms.BindingSource.OnListChanged(System.ComponentModel.ListChangedEventArgs e) + 0x7e bytes    
System.Windows.Forms.dll!System.Windows.Forms.BindingSource.InnerList_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e) + 0x2e bytes 
System.dll!System.ComponentModel.BindingList<System.__Canon>.OnListChanged(System.ComponentModel.ListChangedEventArgs e) + 0x17 bytes   
System.dll!System.ComponentModel.BindingList<MyCompany.ViewModelClass>.Child_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + 0x176 bytes 
[Native to Managed Transition]  
[Managed to Native Transition]  
MyCompany.Gui.exe!MyCompany.ViewModelClass.NotifyPropertyChanged(String propertyName = "SomethingNotNecessarilyRelated") Line 437 + 0x3c bytes  Basic

Why does the program want to set SomeBool to false? And how I can prevent that?

Contrived answered 23/11, 2010 at 16:20 Comment(2)
How are the notifications in the ViewModelClass class made? For example, from code or as a result of actions in the user interface?Veda
you write "set SomeBool to false". Should it be "set BoolProp to false"?Veda
C
0

I have refactored the single ViewModelClass into SomeFormViewModel and SomeControlViewModel, and binding them to their respective classes.

Simply doing that has made the issue disappear.

I would still like a better understanding of the stacktrace that I put, though - I conjecture that the gist of the problem is that each BindingSource maintains its own information of the changes made to the object - and as both of them made changes to the same object, they did not know what was happening anymore.

Contrived answered 30/11, 2010 at 16:50 Comment(0)
V
4

My very first question on Stack Overflow was about a field in a Windows Forms application containing an unexpected value, like yours. The solution was to wait until the form load event was fired to set up the GUI elements of the form.

I would postpone setting up _model (including constructing it with new) and other GUI elements until in the handler for the form load event.

HOWTO:

Add the form load handler in Visual Studio:

  1. Open the form in the graphical view (for example, double-click SomeForm.cs in Solution Explorer)

  2. Double click in the form, outside any controls or other GUI elements (for example, in the title bar). This will add skeleton code for a function named SomeForm_Load and the line this.Load += new System.EventHandler(this.SomeForm_Load); will be added the SomeForm.Designer.cs.

Move the setup code to SomeForm_Load:

private void SomeForm_Load(object aSender, EventArgs anEvent)
{
    _model = new ViewModelClass;

    ViewModelClassBindingSource.DataSource = _model;
    SomeControl1.SetModel(_model);
}

Remove "= new ViewModelClass" from the declaration of _model.

Veda answered 28/11, 2010 at 0:43 Comment(4)
Thank you for replying. I'll try that when I can (which will only be in a little more than 2 days, so please be patient.)Contrived
sigh does not fix my problem. Thing is, the problem was not at the initialization, but at some time after it - the Windows Forms Databinding goes out of its way to set one of my object properties. Again, thank you for giving an answer.Contrived
@jhominal: 1) did you instrument the code to catch the first setting of BoolProp to false? (for the stack dump in the question) 2) do you think I should be able to reproduce the problem?Veda
1) I had placed a breakpoint on the setter to that property. 2) I believe you should, but please do not bother - I have refactored the big ViewModelClass into SomeFormModel and SomeControlModel - and the issue has disappeared. I believe the problem was (indirectly) due to the fact that a single instance of ViewModelClass was used in two different BindingSources. Maybe, if I have the time, I'll extract a minimal, working example of the bug.Contrived
C
0

I have refactored the single ViewModelClass into SomeFormViewModel and SomeControlViewModel, and binding them to their respective classes.

Simply doing that has made the issue disappear.

I would still like a better understanding of the stacktrace that I put, though - I conjecture that the gist of the problem is that each BindingSource maintains its own information of the changes made to the object - and as both of them made changes to the same object, they did not know what was happening anymore.

Contrived answered 30/11, 2010 at 16:50 Comment(0)
X
0

Had the same issue, where changing tab in a tabcontrol would reset all my databound boolean values to "false". All non-boolean values were fine. Stack trace showed exactly the same as OP.

Tried moving around the setting of the viewmodel to Form_Load like Peter suggested, but no luck. Eventually gave up and moved all the databinding from code to setting it in the UI, by creating a data source and setting the databinding on all the control properties.

Xavler answered 28/12, 2017 at 8:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.