Why isn't the binding value saved in the data source on first change when using EndCurrentEdit?
I have CheckBox
one that has a property Checked
associated with a bool value. During an event CheckedChanged
, several logical loops are executed that use the bool property in the data source.
My problem is the first time the user checks CheckBox
out the associated data source is not updated. Subsequent updates work fine though.
Here is some sample code to test the problem. Just create an empty shape and add to it CheckBox
.
public partial class Form1 : Form
{
private bool _testBool;
public bool TestBool
{
get { return _testBool; }
set { _testBool = value; }
}
public Form1()
{
InitializeComponent();
checkBox1.DataBindings.Add(new Binding("Checked", this, "TestBool"));
checkBox1.CheckedChanged += new EventHandler(checkBox1_CheckedChanged);
}
void checkBox1_CheckedChanged(object sender, EventArgs e)
{
checkBox1.BindingContext[this].EndCurrentEdit();
Debug.WriteLine(TestBool.ToString());
}
}
The first time I check the checkbox, the property TestBool
stays at false even if it is checkBox1.Checked
set to true
. Subsequent changes update the property correctly TestBool
to match checkBox1.Checked
.
If I add a breakpoint to the event CheckedChanged
and swipe checkBox1.BindingContext[this].Bindings[0]
out of the immediate window, I can see that modified = false
on first run, which is probably why the EndCurrentEdit()
datasource is not updating correctly.
The same thing happens when using the event TextBox
and TextChanged
, therefore, it is not limited to only CheckBox.Checked
.
Why is this? And is there a general general way of solving the problem?
Edit: I know of a few workarounds so far, although none are perfect as they are not universal and need to be remembered every time we want to use an event Changed
.
- setting a property in the data source directly from the CheckedChanged event
- binding search and invocation
WriteValue()
- connecting bindings after loading the control
I'm more worried about why this is happening, although if anyone knows of a standard generic solution to prevent it from relying on any special encoding in the event Changed
, I'd be happy with that too.
source to share
Controls usually want to pass validation before writing to the data source, so writing a value usually won't happen until you try to leave the control.
You can force the value to be written:
void checkBox1_CheckedChanged(object sender, EventArgs e) {
Binding b = checkBox1.DataBindings["Checked"];
if (b != null) {
b.WriteValue();
}
Debug.WriteLine(TestBool.ToString());
}
source to share
The event appears to be CheckedChanged
too early in the process.
But you can use BindingComplete
:
public partial class Form1 : Form
{
private Boolean _testBool;
public Boolean TestBool
{
get { return _testBool; }
set { _testBool = value; }
}
public Form1()
{
InitializeComponent();
checkBox1.DataBindings.Add(new Binding("Checked", this, "TestBool", true, DataSourceUpdateMode.OnPropertyChanged));
checkBox1.DataBindings[0].BindingComplete += Form1_BindingComplete;
}
private void Form1_BindingComplete(Object sender, BindingCompleteEventArgs e)
{
Debug.WriteLine("BindingComplete: " + TestBool.ToString());
}
}
Note that the event is triggered on startup when the original binding occurs. You have to deal with the potential unintended consequences, but otherwise it works the first time you click and every click.
Also note that true
(format) is required in the constructor Binding
to make the event fire.
source to share
The closest I can find to explain this behavior is this third party explanation
Basically, this is a problem of timing. The way the binding works in DotNet is actually very simple. There is no magic in the DotNet framework that tells the BindingManager when something changes. What does it do when you bind to a property (like CheckedValue). The BindingManager looks for an event on a control called propertynameChanged (for example, "CheckedValueChanged"). This is the same event when your code connects to your sample form.
When a control fires an event, the order in which listeners receive the event is arbitrary. There is no reliable way to tell whether the BindingManager will receive the event first or by the Form.
The My event CheckBox1_CheckChanged
runs before it BindingManager
handles the changed event, so the data source was not updated at this time.
My best guess is why this is only happening the first time that the control is not showing yet, so some code doesn't run to fix the order events in which it handles. I have seen other posts about not being able to bind to invisible elements due to the handle not being created yet, and one answer states
Until the control is first visible, some initialization in the background will never happen, and part of that initialization is allowing data binding.
So I suspect it has something to do with it.
I can verify that if I attach a Changed handler later, for example during an event Load
, it works as I expected.
public partial class Form1 : Form
{
private bool _testBool;
public bool TestBool
{
get { return _testBool; }
set { _testBool = value; }
}
public Form1()
{
InitializeComponent();
checkBox1.DataBindings.Add(new Binding("Checked", this, "TestBool"));
Load += new EventHandler(Form1_Load);
}
void Form1_Load(object sender, EventArgs e)
{
checkBox1.CheckedChanged += new EventHandler(checkBox1_CheckedChanged);
}
void checkBox1_CheckedChanged(object sender, EventArgs e)
{
// Not needed anymore
//checkBox1.BindingContext[this].EndCurrentEdit();
Debug.WriteLine(TestBool.ToString());
}
}
source to share