Two-way data-binding to Dynamic Object

Dynamic Object Two Way Data Binding

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. Since DynamicObject doesn’t have real properties, you should return a list of custom PropertyDescriptor objects which allow the consumer to get/set value of the property.
  • INotifyPropertyChanged: INotifyPropertyChanged is responsible for raising PropertyChanged 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 to Invoke the event in UI thread when required by passing an instance of ISynchronizeInvoke.
  • Option 2 You can use the instance of ISynchronizeInvoke to Invoke 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.

Two way data-binding to Dynamic Object

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.

You May Also Like

About the Author: Reza Aghaei

I’ve been a .NET developer since 2004. During these years, as a developer, technical lead and architect, I’ve helped organizations and development teams in design and development of different kind of applications including LOB applications, Web and Windows application frameworks and RAD tools. As a teacher and mentor, I’ve trained tens of developers in C#, ASP.NET MVC and Windows Forms. As an interviewer I’ve helped organizations to assess and hire tens of qualified developers. I really enjoy learning new things, problem solving, knowledge sharing and helping other developers. I'm usually active in .NET related tags in stackoverflow to answer community questions. I also share technical blog posts in my blog as well as sharing sample codes in GitHub.

Leave a Reply

Your email address will not be published. Required fields are marked *