Using INotifyPropertyChanged in background threads

Posted by digitaldias on Geeks with Blogs See other posts from Geeks with Blogs or by digitaldias
Published on Fri, 30 Apr 2010 05:17:08 GMT Indexed on 2010/04/30 6:37 UTC
Read the original article Hit count: 323

Filed under:

Following up on a previous blog post where I exemplify databinding to objects, a reader was having some trouble with getting the UI to update.

Here’s the rough UI:

image

The idea is, when pressing Start, a background worker process starts ticking at the specified interval, then proceeds to increment the databound Elapsed value.

The problem is that event propagation is limeted to current thread, meaning, you fire an event in one thread, then other threads of the same application will not catch it.

The Code behind

So, somewhere in my ViewModel, I have a corresponding bethod Start that initiates a background worker, for example:

public void Start( )
{
    BackgroundWorker backgroundWorker = new BackgroundWorker( );
    backgroundWorker.DoWork += IncrementTimerValue;
    backgroundWorker.RunWorkerAsync( );
}

protected void IncrementTimerValue( object sender, DoWorkEventArgs e )
{
    do
    {
        if( this.ElapsedMs == 100 )
            this.ElapsedMs = 0;
        else
            this.ElapsedMs++;

    }while( true );
}
Assuming that there is a property: 
public int ElapsedMs
{
    get { return _elapsedMs; }
    set
    {
        if( _elapsedMs == value )
            return;

        _elapsedMs = value;
        NotifyThatPropertyChanged( "ElapsedMs" );
    }
}

The above code will not work. If you step into this code in debug, you will find that INotifyPropertyChanged is called, but it does so in a different thread, and thus the UI never catches it, and does not update.

One solution

Knowing that the background thread updates the ElapsedMs member gives me a chance to activate BackgroundWorker class’ progress reporting mechanism to simply alert the main thread that something has happened, and that it is probably a good idea to refresh the ElapsedMs binding.

public void Start( )
{
    BackgroundWorker backgroundWorker = new BackgroundWorker( );
    backgroundWorker.DoWork += IncrementTimerValue;

    // Listen for progress report events
    backgroundWorker.WorkerReportsProgress = true;

    // Tell the UI that ElapsedMs needs to update
    backgroundWorker.RunWorkerCompleted +=
        ( sender, e ) =>
            {
                NotifyThatPropertyChanged( "ElapsedMs" )
            };
    backgroundWorker.RunWorkerAsync( );
}

protected void IncrementTimerValue( object sender, DoWorkEventArgs e )
{
    do
    {
        if( this.ElapsedMs == 100 )
            this.ElapsedMs = 0;
        else
            this.ElapsedMs++;

        // report any progress
        ( sender as BackgroundWorker ).ReportProgress( 0 );

    }while( true );
}

What happens above now is that I’ve used the BackgroundWorker cross thread mechanism to alert me of when it is ok for the UI to update it’s ElapsedMs field.

Because the property itself is being updated in a different thread, I’m removing the NotifyThatPropertyChanged call from it’s Set method, and moving that responsability to the anonymous method that I created in the Start method.

This is one way of solving the issue of having a background thread update your UI. I would be happy to hear of other cross-threading mechanisms for working in a MCP/MVC/MVVM pattern.

© Geeks with Blogs or respective owner