3

I have a ViewModel which instantiates an event in a synchronous method. The event signals to the UI that we need a "Yes" or "No" answer from the user before continuing.

I am trying to display a MessageDialog and wait until the user provides an answer - Yes or No. I am having a difficult time doing this. I currently get an UnauthorizedAccessException when trying to do this.

Here's a look at the code in the UI:

async Task<bool> Instance_SwitchConfirmation(string question)
{
    MessageDialog md = new MessageDialog(question);
    md.Commands.Add(new UICommand("Yes", CommandInvokedHandler));
    md.Commands.Add(new UICommand("No", CommandInvokedHandler));
    md.ShowAsync();

    return this.canSwitch;
}

async void CommandInvokedHandler(IUICommand command)
{
    this.canSwitch = command.Label == "Yes" ? true : false;
}

I have tried:

var uiContext = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() => {
    MessageDialog md = new MessageDialog(question);
    md.Commands.Add(new UICommand("Yes", CommandInvokedHandler));
    md.Commands.Add(new UICommand("No", CommandInvokedHandler));
    md.ShowAsync();
}, 
new System.Threading.CancellationToken(), 
TaskCreationOptions.PreferFairness, uiContext);

but this fails with the same exception.

Lastly, if I simply await the MessageDialog like so, the dialog is not displayed and the UI thread locks up.

MessageDialog md = new MessageDialog(question);
md.Commands.Add(new UICommand("Yes", CommandInvokedHandler));
md.Commands.Add(new UICommand("No", CommandInvokedHandler));
await md.ShowAsync();

If MessageDialog had a synchronous version of Show() I would be fine but the asynchrnous behvaior of MessageDialog coupled with my synchrounous routine, coupled with cross threading is confusing me. I'm wondering if someone can outline what I need to do to wait for user input on a MessageDialog before continuing a synchronous method in my backend ViewModel.

Thanks in advance for the help.

Kalyan
  • 1,395
  • 2
  • 13
  • 26
user2334154
  • 429
  • 1
  • 6
  • 12

1 Answers1

8

See Win8 C# Metro dispatcher and RPC_E_WRONG_THREAD and CoreDispatcher.RunAsync.

Since your method isn't executing on the Dispatcher you're going to need to invoke the code on it manually. In order to avoid refactoring into a callback pattern you can use a TaskCompletionSource(T) to set the result and the background thread will continue after the result is set.

var tcs = new TaskCompletionSource<bool>();
var dialogTask = tcs.Task;

MessageDialog md = new MessageDialog(question);
md.Commands.Add(new UICommand("Yes"));
md.Commands.Add(new UICommand("No"));

Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => {
    var result = await md.ShowAsync();
    var canSwitch = result.Label == "Yes";
    tcs.SetResult(canSwitch);
});

var result = await dialogTask;
return result;
Community
  • 1
  • 1
Dustin Kingen
  • 20,677
  • 7
  • 52
  • 92
  • Thanks for the response, but I need to await the user input before I can continue code execution. If I await the Dispather.RunAsync call, the MessageDialog is not displayed and the UI thread locks up. Any suggestions? Thanks in advance. – user2334154 Jun 24 '13 at 12:36
  • Thanks again for the quick response. Unfortunately, this still isn't awaiting, I have a return canSwitch; statement after this call and its being called prior to showing the MessageDialog, if I await the Dispatcher.RunAsync call I again have the UI thread locked. Any other thoughts? – user2334154 Jun 24 '13 at 13:12
  • What you can probably do is use a `TaskCompletionSource(T)` to let the `Dispatcher` run. See my edit. – Dustin Kingen Jun 24 '13 at 14:27
  • Thanks again for the quick response, but this also does not display my MessageDialog and locks the UI thread... – user2334154 Jun 24 '13 at 14:39
  • Something else interesting, if I call your code above from the initialization of the UI class, this behaves as expected. But when called in the UI event handler that's run after I raise an event in my backend ViewModel (in a different assembly) - I get the locked behavior. Any ideas? Thanks again. – user2334154 Jun 24 '13 at 14:45
  • I think you might be calling `Wait` somewhere which is blocking the UI. Make sure all of your code is using the async pattern. – Dustin Kingen Jun 24 '13 at 15:18
  • So how was this solved? This answered is marked correct, but the solution doesn't work for me with the same problems as @user2334154 is mentioning. – Jasper Jun 16 '16 at 08:40