+3  A: 

I think that your ViewModel really shouldn't know anything about the View, including whether or not it's a WPF UI, or whether or not that UI even has the concept of a Dispatcher thread, so the red flag should fly as soon as you start writing code in your ViewModel that attempts to CheckAccess() or InvokeRequired in order to marshal some code to the UI thread. Instead I'd have the model raise an event that the View can listen for and update itself accordingly, or have the ViewModel expose a property (eg. bool FileIsLoading) that the View simply binds to in order to detect and display what the model is doing asynchronously, and it's the ViewModel's responsibility to ensure that the value of that property is accurate.

For example:

public partial class MainWindow : Window {
    private ViewModel _model = new ViewModel();

    public MainWindow() {
        InitializeComponent();
        DataContext = _model;
    }

    private void Button_Click(object sender, RoutedEventArgs e) {
        _model.Run();
    }
}


<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Click="Button_Click"
                Content="Run"
                IsEnabled="{Binding IsIdle}" />
    </Grid>
</Window>



public class ViewModel : INotifyPropertyChanged {

    private bool _isIdle = true;

    public bool IsIdle {
        get { return _isIdle; }
        set {
            _isIdle = value;
            OnPropertyChanged("IsIdle");
        }
    }

    public void Run() {
        ThreadPool.QueueUserWorkItem((state) => {
            IsIdle = false;
            Thread.Sleep(10000);
            IsIdle = true;
        });
    }

    #region INotifyPropertyChanged Implementation

    protected void OnPropertyChanged(string propertyName) {
        PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
        if (propertyChanged != null) {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}
Wayne
That's absolutely true, the lower level stuff shouldn't know anything about the higher level stuff. I try to keep that in mind when I working on my app. In this particular case, I totally didn't even notice that I was modifying things in the ViewModel, and it could be because this particular component I am working on has the Model and ViewModel in one class (to save time with the alpha). I think the event is the way to go, because your suggestion about having the VM expose a property is exactly what I'm doing, but ultimately I can't update that property from the thread without an event.
Dave
Well, technically even exposing/changing a property is raising an event -- you'd need to make sure that your ViewModel implements INotifyPropertyChanged to inform the WPF databinding plumbing that your property had changed.
Wayne
Databinding is fine, I've had that working for a while. So maybe I should just confirm this -- your suggestion for an event-based solution will work when my *worker thread* raises the event, and my *ViewModel* has registered the event handler?
Dave
The worker thread is initiated from the ViewModel? In that case the View is not involved, right? Your problem is "how do I tell the View that the background work finished?" For that I don't see why a simple (data-bound) property in the ViewModel wouldn't suffice. Am I missing something?
Wayne
The worker thread is indeed initiated from the ViewModel. Here's how it goes -> user clicks button, ICommand is executed and calls into the VM, which then calls into the Model. The model spawns a worker thread and does its thing. The model eventually fails and needs to recover. The act of recovery then requires that the GUI get refreshed. I want the model to somehow be able to asynchronously signal the GUI to update, and this is currently attempted by having the ViewModel update an ObservableCollection that is databound to the View. BTW, the event isn't syncing with the main GUI thread.
Dave
>>The act of recovery then requires that the GUI get refreshed.In WPF, you don't (shouldn't want to) control this. The databinding plumbing will take care of it as long as the bound items (ViewModel, objects) are INotifyPropertyChanged/INotifyCollectionChanged). I'll post a sample above.
Wayne
Note that any event will still be raised on the background thread, so you still need to figure out how to switch to the UI thread to do the work there. Whether that switch should be in the model or the viewmodel is a good design discussion, but it's orthogonal to your question.
Franci Penov
@Wayne: your approach seems to work for properties, but it does not work for ObservableCollections. I modified your example (thanks for posting it!!!) to add an ObservableCollection, and modifying your Run() method accordingly results in the same crash I'm trying to work around. This is pretty interesting (to me at least) :)
Dave
Right - I'd forgotten that although WPF supports INotifyPropertyChanged dispatch, it doesn't yet support INotifyCollectionChanged dispatch. I looked back at some old code to see how I was doing this in my apps, and I'm using something akin to this:http://www.julmar.com/blog/mark/2009/04/01/AddingToAnObservableCollectionFromABackgroundThread.aspx
Wayne
@Wayne: cool, thanks, I'll check it out!
Dave
+2  A: 

If you want to schedule some work from a background thread to the UI thread in WPF, use the DispatcherObject. Here's a nice article on how to Build More Responsive Apps with the Dispatcher.

Update: Note that if you use an event to send notifications from the Model to the ViewModel, you still need to switch to the UI thread somewhere. Whether that switch should be in the Model or the ViewModel is a good design discussion, but it's orthogonal to your question.

The event will be raised on the corresponding Dispatcher thread. Since you need to get to the UI thread, you need to use a Dispatcher that is created on the UI thread. The easiest way to get one is to use the DispatcherObject.Dispatcher property on one of the UI elements. The alternative is to create one in your Model or ViewModel. If you are a design purist, I would suggest you create the Dispatcher in your Model and dispatch the call back to the UI thread before you raise the event to which the ViewModel is listening. This way all the thread switching and management is contained in your Model and the ViewModel works as a single-threaded on the UI thread only.

Franci Penov
how funny, I was just reading that article and was thinking about the DispatcherObject. However, Wayne brings up a good point, and I should look into using an event to deal with this.
Dave
I think I'm going to have to give the DispatcherObject a whirl. Your point is valid that regardless of the GUI architecture, I still need a way to synchronize with the main thread. Can you explain why Wayne's example above works for properties? I stepped through just to prove to myself that it really does everything from a different thread, and it does, yet it still works as one would want it to.
Dave
I should have refreshed my browser before commenting. Anyhow, thanks for the advice. It's an interesting idea, if I understand you correctly. Instead of taking the Dispatcher property from the View and passing it down (which seems to violate MVVM anyway), you recommend creating a reference to a Dispatcher in my Model, and then essentially, via a callback interface (which gets passed to the Model's constructor), the Model can request the View's Dispatcher? I assume that then the View should also pass a reference to the ViewModel when it is created?
Dave
Problem solved. I just have the controller get a reference to the View's dispatcher (which is passed down from View -> VM -> Model), and then everything work. Thanks, Franci, and thanks Wayne for trying to help me out with this.
Dave
A: 

I've got another approach that seems to work, and I just wanted to throw it out there to get some comments (if anyone is even reading this question anymore!).

I started to use the MVVM Light Toolkit's Messenger class, and it seems to work really well for me. For example, let's take the ProgressBar as an example. I registered two messages with my ViewModel for setting the progress value and progress maximum. Then in my model, as it sets up the tasks and overall process, it sends these messages. When the VM receives the messages, it just updates databound values, and my GUI updates automatically! It's super duper easy, but I was wondering what you all thought about this approach. Is anyone else doing this without incident?

Dave