[API Proposal]: Allow `Control.Invoke` on `Binding`
See original GitHub issueBackground and motivation
Binding
s allow for Form Control
s and models to be updated when their values are changed by the system in any way.
This would allow the form to be reactive, with processes running in the background performing all the necessary actions and having them reflected by the bindings automatically.
This already works with MAUI, specially if you use the CommunityToolkit.Mvvm
and its generators.
However, while Winforms support bindings as well, it does not support asynchronous updates, because doing so would raise a InvalidOperationException
.
This happens because the Binding
class updates the value of the property directly, without checking if the control is in the UI Thread or not.
I then propose a new constructor to the Binding
class in which you can set a flag to prefer using Control.Invoke
when updating the bindings. Control.Invoke
forces the Action
to run at the UI Thread, solving the problem.
I have already opened an issue about this as well as a PR.
API Proposal
namespace System.Windows.Forms;
public class Binding
{
public Binding(string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString, IFormatProvider formatInfo, bool invokeControl);
}
namespace System.Windows.Forms;
public class ControlBindingsCollection : BindingsCollection
{
public Binding Add(string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode updateMode, object nullValue, string formatString, IFormatProvider formatInfo, bool invokeControl);
}
API Usage
// Create a new form and a text box to bind values to
Form form = new Form();
TextBox textBox = new TextBox();
textBox.Parent = form;
// Create a binding to handle the invoke method
Binding binding = new Binding("Text", mainObject, "Text", false, 0, null, string.Empty, null, true);
textBox.DataBindings.Add(binding);
form.Show();
// Perform the binding update in a separate thread to escape the UI Thread on purpose
var thread = new Thread(() =>
{
textBox.Text = "Updated test text";
Assert.Equal("Updated test text", textBox.Text);
Assert.Equal("Updated test text", mainObject.Text);
});
thread.Start();
Alternative Designs
I have added a new constructor to the System.Windows.Forms.Binding
class and a new public method to System.Windows.Forms.ControlBindingsCollection
without changing previous APIs.
Risks
In my limited study of the APIs, I didn’t see any breaking risk associated with this proposal.
There may be performance repercussions in the Control.Invoke
mechanic on heavy load but I was unable to test it.
The PR I mentioned has some tests which could be useful in analyzing those scenarios.
Issue Analytics
- State:
- Created 7 months ago
- Comments:14 (9 by maintainers)
Top GitHub Comments
Just a quick update: We’re still very much considering this but need to address a couple of higher priority issues, first.
I am in the process of building a better example as I only have my code to show for, which wasn’t crafted for this case specifically.
Yes, we are talking about
INotifyPropertyChanged
derived objects, specifically when you have a asynchronous operation on said object which triggers the event.I have a previous example here from my project, which illustrates how I was creating the binding.
However, because of my message is asynchronous, some of the message handlers changed their properties and the binding raised the
System.InvalidOperationException
.Hope this clarifies the issue better.