Click here to Skip to main content
15,885,757 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more: , +
I've been running into the problem of databinding controls to properties that are updated from threads other than the UI thread. I'm using Winforms but am adopting somewhat of an MVVM architecture so I'm using databindings similar to:
C#
button1.DataBindings.Add("Enabled",_viewModel,"IsActive");

My ViewModel implements INotifyPropertyChanged and fires a NotifyPropertyChanged event every time one of its parameters, such as IsActive, is changed. I have several processes that are run continuously throughout the entirety of the application's lifetime which I execute on separate threads to keep the UI responsive.

Of course, the problem is that when the property is set from one of these other threads, an InvalidOperationException is thrown when the PropertyChanged event is fired.

I am trying to stick with the architecture of MVVM so I do not want to have my ViewModel have any knowledge of how the View (Form)is using its parameters. So
my (hopefully temporary) solution to the problem is creating a Control in the constructor of my ViewModel object so that I have something to Invoke on within the NotifyPropertyChanged method which looks like:
C#
public event PropertyChangedEventHandler PropertyChanged;

public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
    if (PropertyChanged != null)
    {
        Action action = new Action(() =>
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        });

        if (!_control.InvokeRequired)
            action();
        else
            _control.BeginInvoke(action);

    }
}


I have 3 questions regarding all of this:

1) Creating a control for the sole purpose of having something to invoke on so that there is no cross-threading problems seems both ridiculous and wasteful. I thought perhaps a little less wasteful (less memory involved because no control object would need to be created) way of doing this would be to capture and save in a local variable the thread that the ViewModel object is created on by putting this._uiThread = System.Threading.Thread.CurrentThread; in the objects constructor and then somehow get the PropertyChanged event to fire on this thread but I can't figure out what code would be needed to do this in the <notifypropertychanged> method. So shortly put: How can I execute a block of code on a specific thread given only a reference to the thread I want it executed on (the UI thread)?

2) Regardless of my solution to the problem I'm having, how would you resolve this issue given the constraints that the properties WILL be updated from a different thread and the ViewModel has no knowledge of the View?

3) This last question is just a generalized WHY?! How is this not such a common problem with databinding that there is not a built in solution for this issue or at the very least an extremely simple solution to implement? In my mind I don't see why, when designing databindings, there isn't another constructor parameter that is of type Thread so that the Add method looks like: Control.DataBindings.Add(string propertyName, object dataSource, string dataMember, Thread bindOnThread); and whenever a PropertyChanged event is fired, the Binding handles the invoke if it is required. This way there could be multiple forms that all have separate UI threads and the same object's property could be bound to each of them in some way and yet the ViewModel doesn't have to have any knowledge of the Views. Even in my solution to the problem that I outlined in part (1), I make the assumption that the ViewModel was created on the UI thread and that it is only bound to controls created on that thread. The solution in (3) wouldn't care about any of that and would work regardless... so again WHY isn't there something like this that is built in to handle this common problem?
Posted
Updated 4-May-15 9:13am
v3

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900