Search code examples
c#mvvmdialogmahapps.metrocontinuations

How to get the user-slection of a dialog from MahApps using task continuation


I am using MahApps and I am trying to implement an abort-dialog if a user cancels a print-process. Since I am still using .Net 4.0 I cannot use await, but need to use continuations as stated here:

However for the life of me I cannot figure out how to do this. I have created a class DialogService that I want to use to present dialogs, when needed. I added the method AbortPrintingDialog(ViewModelBase parentViewModel), where parentViewModel is the ViewModel that wants to show the dialog. Originally I had the following for AbortPrintingDialog, which is an adaption of code from the MahApps sample program and works as expected giving the correct output on the debug-console:

public class DialogService
{
    private IDialogCoordinator dialogCoordinator;

    public DialogService(IDialogCoordinator dialogCoordinator)
    {
        this.dialogCoordinator = dialogCoordinator;
    }

    public void AbortPrintingDialog(ViewModelBase parentViewModel)
    {
        dialogCoordinator.ShowMessageAsync(parentViewModel,
                        "Abort Printing",
                        "Printing is in progress. Are you sure you want to abort the printing process?",
                        MessageDialogStyle.AffirmativeAndNegative).ContinueWith(t => { Debug.WriteLine("t.Result: " + t.Result); });
    }
}

I now tried to change this using continuations so that I can get the user-selected value in order to return it later on from my function AbortPrintingDialog. So I modified AbortPrintingDialog like this, which I thought would work after reading the code on this MSDN page:

public MessageDialogResult AbortPrintingDialog(ViewModelBase parentViewModel)
{
    Task<MessageDialogResult> WaitUserInputTask = dialogCoordinator.ShowMessageAsync(parentViewModel,
                        "Abort Printing",
                        "Printing is in progress. Are you sure you want to abort the printing process?",
                        MessageDialogStyle.AffirmativeAndNegative);
    Task.WaitAll(WaitUserInputTask);
    Task<MessageDialogResult> continuation = WaitUserInputTask.ContinueWith((antecedent) =>
     {
         return antecedent.Result;
     });

    return continuation.Result;
}

However, now when I hit the abort-button in my GUI to call AbortPrintingDialog, the GUI locks up and I don't get any error-message. So what am I doing wrong? I have been trying to figure this out and fiddling around for quite for some time now...


Solution

  • I'll try to explain a bit why you cannot do it exactly as you want to. First, why your code deadlocks. When user made a choice and some internal code in dialogCoordinator.ShowMessageAsync needs to continue - it does that on UI (user interface) thread. But you already blocked UI thread - you are waiting on it when dialogCoordinator.ShowMessageAsync will be completed. So you just cannot wait on UI thread for ShowMessageAsync to complete, because of how that ShowMessageAsync is internally implemented. Of course the developers of this library should have provided you a way to call that method synchronously, but they didn't so you have to live with this.

    What are your options then?

    1. Use task continuations. For that you will need to rewrite your code which uses AbortPrintingDialog, but you already know that.
    2. Upgrade to newer framework version and use async\await features.
    3. Install this package and use async\await features in .NET 4.