-2

I have a simple MVVM application. Some ViewModels perform asynchronous processing, which require notifying the View about status updates and when the processing finished.

Since direct calls to View are restricted in MVVM, I chose Commands. Unfortunately, the commands do not get executed when called from continuation tasks even though the tasks are synchronized to the main (UI) thread:

public void DoAction()
{
    //NOTE: command gets executed correctly
    AppCommands.SomeCommand.Execute(null, null);

    // run some task asynchronously
    Task task = new Task( ... );

    task.ContinueWith(
        taskAntecedent =>
        {
            //NOTE: command does not get executed at all!
            AppCommands.SomeCommand.Execute(null, null);
        },
        TaskScheduler.FromCurrentSynchronizationContext());

    task.Start();
}

The coomand gets always executed if the Execute method contains proper target element (so that WPF can walk up the visual tree and find the command binding). But this cannot be done in ViewModel as it should not see the View.

Some other solutions I tried use:

Dependency injection - injecting View to ViewModel via constructor as some interface. Doable, but this creates a room for errors: 1. the ViewModel cannot be used in XAML as ViewModel no longer has an empty contructor. 2. even if I define an empty contructor, the develper may forget to use proper one and inject proper View in it (some Views have several possible ViewModels that can be replaced by the user).

Routed Events - the ViewModel should be replaceable and this means the View has to unhook events from old model and rehook to the new model which requires code-behing in the View (not MVVM-like)

Services - some Views are UserControls and unlike Windows, they don't have Loaded, Unloaded events - so there is no good place to register and unregister the service. Also multiple controls can be created, thus registering the same service multiple times, then the binding between Views and ViewModels would no loger be 1:1.

The problem is simple:

How to e.g. close window from UserControl's ViewModel without lots of extra coding on the View side or by the user/developer?

I want to do this in vanilla WPF, not use a 3rd party MVVM framework as the application should be lightweight/easy-to-understand (with as little black magic as possible).

Libor
  • 3,285
  • 1
  • 33
  • 41

1 Answers1

0

Try to use the correct overload of the ContinueWith method that actually acceps a TaskScheduler that performs the action, i.e. calls the command, back on the UI thread:

task.ContinueWith(
    taskAntecedent =>
    {
        AppCommands.SomeCommand.Execute(null, null);
    },
    System.Threading.CancellationToken.None,
    TaskContinuationOptions.None,
    TaskScheduler.FromCurrentSynchronizationContext());

If this doesn't work for some reason, I suggest that you provide a Minimal, Complete, and Verifiable example of your issue.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • I have finally fixed the problem using CLR events and hooking/unhooking the ViewModel's events in the UserControl's Loaded, Unloaded and DataContextChanged handlers. Building the example would take like two hours which unfortunately I don't have. I hope to get back to this some time later. – Libor Jun 12 '17 at 18:24