I’m trying to implement my first application using the MVVM pattern. I’ve manged to get most things working, but now I’m facing a problem with the following (IMHO pretty common) scenario:
Pressing a Button (View) shall invoke a Method (Model). Using a ICommand (ViewModel) this is pretty easy. But what to do if a time consuming operation has to be executed?
My current solution required me to implement a WorkQueue class containing WorkQueueItems. The WorkQueue has a Thread associated with it which executes the WorkQueueItems. Each WorkQueueItem has a Name, a Status and a Progress which is updated during execution.
Each Window has its own WorkQueue – visualized as StatusBar.
My problem: How can a ViewModel find the appropriate WorkQueue? Do I have to pass the WorkQueue to each ViewModel I create (this would be really be annoying)? Or are there other mechanism I could use?
I’m not really familiar with RoutedCommands – tough the basic concept seems to go into this direction. What’d love to see is a solution where I can bind a WorkQueueItem to a Command/Event which then bubbles up to the containing Window where it is added to the Window‘s WorkQueue.
I also considered making WorkQueue a Singleton – but this only works if I only have one Window at a time.
With the later .Net Frameworks (4.0+) and WPF you can utilize the
System.Threading.Taskslibrary to provide a lot of this work under the hood.If say your Command on your needs to update a property on your View Model, but it has to wait for the information, you simply start a task to perform the IO:
Breaking this down into manageable parts, we’re starting a new task which calls some method
IEnumerable<Foo> FindData(string). This is the plain old boring synchronous code you’ve always written. Likely it already exists on your view model!Next we tell the framework to start a new task when that one finishes using
ContinueWith, but to do it on the WPF Dispatcher instead. This allows you to avoid the hassles of cross-thread problems with UI elements.You can extend this for monitoring with a helper class:
Using this class in XAML would be along the lines of adding its instance to your Window’s
ViewModel:You would then add a method to your TaskManager to handle the adding of tasks to and from the Running collection:
Now we simply change our
FindDataCommandimplementation:The
WorkItemclass could expose the properties on theTaskclass to the UI, or it could be extended to encapsulate aCancellationTokento support cancellation in the future.