Background workers and FireChangesOnDependentCommands

Dec 5, 2011 at 1:37 PM

I'm using the view model support and I have a command like:

[DependsUpon("IsDownloading")]
[DependsUpon("IsValidFileSelected")]
public bool CanDownloadFirmware()

It works great except there is one case where IsDownloading gets updated after an event from a background worker.  This ends up causing an exception in FireChangesOnDependentCommands which ends up calling Extensions.Raise

System.InvalidOperationException occurred
  Message=The calling thread cannot access this object because a different thread owns it.
  Source=WindowsBase
  StackTrace:
       at System.Windows.Threading.Dispatcher.VerifyAccess()
       at System.Windows.DependencyObject.GetValue(DependencyProperty dp)
       at System.Windows.Controls.Primitives.ButtonBase.get_Command()
       at System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute()
       at System.Windows.Controls.Primitives.ButtonBase.OnCanExecuteChanged(Object sender, EventArgs e)
       at ViewModelSupport.Extensions.Raise(EventHandler eventHandler, Object source) in C:\Dev Projects\Application\Common\ViewModelSupport\Extensions.cs:line 33  InnerException: 

To repro the problem just create a project with 1 property and 1 command that DependsUpon that property.  Update the property from the completed event of the background worker.

Any idea how to fix this?

Thanks!

Coordinator
Dec 5, 2011 at 2:26 PM

This is a common MVVM problem and not one specific to this library.

Basically, you want to call BeginInvoke on the current dispatcher with your action:

 

public void InvokeOnUI(Action action)
{
#if SILVERLIGHT
    var dispatcher = Dispatcher.Current.Dispatcher;
#else
    var dispatcher = Dispatcher.CurrentDispatcher;
#endif

    if(dispatcher.CheckAccess())
        action();
    else
        dispatcher.BeginInvoke(action);
}
  
Then, when you are setting a value from the other thread, just call it like this:
InvokeOnUI( () => IsDownloading = true );

Good luck!
Brian
  

 

 

 

Dec 5, 2011 at 2:35 PM

Thanks for getting back to me so quick Brian, your code has been a great help.

Is there not a way we could move this into the viewmodelsupport classes?  It'd be nice to not have to repeat this code in various places.  It seems that WPF doesn't make you do this for simple property changes but I guess the DependsUpon has introduced something that WPF can't automatically handle.

Coordinator
Dec 5, 2011 at 2:43 PM

I don't know that it makes sense to add it directly to the base class.  When I think about it, this is a problem with WPF and SIlverlight in that when you twiddle the UI from a different thread, you get the exception.  Microsoft could have dispatched view actions to a different thread but they chose not to.

Since the ViewModel is really just an extension of the view, I feel like it should use the same policy: if you are twiddling the ViewModel from a different thread, you need to dispatch your action.  The alternative is for every action in the system to do this check and dispatch.  I feel like letting the calling thread be responsible for this is appropriate since that is what WPF requires.

If you disagree, and feel like there is an unobtrusive modification that you can make to the base class, I'd be happy to see the pull request (complete with unit tests).  I would certainly consider it.