To create a Dynamic Object which you can use for two-way data-binding to Windows Forms control, you should derive from DynamicObject
and also implement ICustomTypeDescriptor
and INotifyPropertyChanged
interface.
- DynamicObject: Deriving from
DynamicObject
provides a solution for specifying dynamic behavior at run time. - ICustomTypeDescriptor:
ICustomTypeDescriptor
is responsible to return metadata about the object including a list of properties. SinceDynamicObject
doesn’t have real properties, you should return a list of customPropertyDescriptor
objects which allow the consumer to get/set value of the property. - INotifyPropertyChanged:
INotifyPropertyChanged
is responsible for raisingPropertyChanged
event. It’s a key point in two-way data-binding.
When setting up data binding to a property, framework invokes AddValueChanged
method of the PropertyDescriptor
of that property. To provide two-way data binding, your property descriptor should override that method and subscribe for PropertyChanged
event of the component and call OnValueChanged
method of the property descriptor:
void PropertyChanged(object sender, EventArgs e)
{
OnValueChanged(sender, e);
}
public override void AddValueChanged(object component, EventHandler handler)
{
base.AddValueChanged(component, handler);
((INotifyPropertyChanged)component).PropertyChanged += PropertyChanged;
}
public override void RemoveValueChanged(object component, EventHandler handler)
{
base.RemoveValueChanged(component, handler);
((INotifyPropertyChanged)component).PropertyChanged -= PropertyChanged;
}
How to update controls using data-binding when the dynamic object updates in a non-UI thread?
To update the controls using data-binding when the dynamic object updates in a non-UI thread , you can use either of the following options:
- Option 1 You can change
MyCustomObject
implementation a bit toInvoke
the event in UI thread when required by passing an instance ofISynchronizeInvoke
. - Option 2 You can use the instance of
ISynchronizeInvoke
toInvoke
the code in UI thread, in the other thread which you are going to update the object.
Option 1
To implement the first solution, you can add the following constructor to MyCustomObject
class to accept and instance of ISynchronizeInvoke
:
ISynchronizeInvoke syncronzeInvoke;
public MyCustomObject(ISynchronizeInvoke value = null)
{
syncronzeInvoke = value;
}
Then in OnPropertyChanged
, use ISyncronzeInvoke.InvokeRequired
to check if invoke is required and then if invoke is required, use ISyncronzeInvoke.Invoke
to invoke the event in the UI thread:
private void OnPropertyChanged(string name)
{
var handler = PropertyChanged;
if (handler != null)
{
if (syncronzeInvoke != null && syncronzeInvoke.InvokeRequired)
syncronzeInvoke.Invoke(handler, new object[]
{ this, new PropertyChangedEventArgs(name) });
else
handler(this, new PropertyChangedEventArgs(name));
}
}
Then when creating the object, pass this
as an instance of ISynchronizeInvoke
to MyCustomObject
:
dynamic data;
private void Form1_Load(object sender, EventArgs e)
{
data = new MyCustomObject(this);
//...
}
Then when changing data, simply change data in the other thread:
private void button2_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
data.StringProperty = "Another Text";
data.BooleanProperty = true;
data.DateTimeProperty = DateTime.Now.AddYears(30);
data.IntegerProperty = 300;
});
}
Using this solution, the thread doesn’t need to know anything about the ISynchronizeInvoke
. It just updates MyCustomObject
.
Option 2
To use the second solution, you can simply use Invoke
in the other thread to change object properties:
private void button2_Click(object sender, EventArgs e)
{
Task.Run(()=> {
this.Invoke(new Action(() =>
{
data.StringProperty = "Another Text";
data.BooleanProperty = true;
data.DateTimeProperty = DateTime.Now.AddYears(30);
data.IntegerProperty = 300;
}));
});
}
In this solution, the thread should care about UI thread and should consider changing the dynamic object using ISyncronzeInvoke.Invoke
.
Example
In the example, I’ve created an implementation of the DynamicObject
which you can simply add properties to it and use for two-way data-binding in Windows Forms. You can find a working implementation in the following repository or download it. The implementation contains option 1 to support multi thread environments.
Notes
- The post is written to extend and support the answer which I posted for this question in stackoverflow: Dynamic object two way data binding and its follow up questions which I received.