3

I know this question has been asked tons of times, but I couldn't find an answer which works for me.

I'm writing a Xamarin Forms app. In the Android project I need to override this method:

public override bool OnJsConfirm(WebView view, string url, string message, JsResult result)

So unfortunately I cannot make the method async.

Within that method I want to call this other one:

public static Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
{
    var tcs = new TaskCompletionSource<bool>();

    Device.BeginInvokeOnMainThread(async () =>
    {
        var result = await MainPage.DisplayAlert(title, message, accept, cancel);
        tcs.SetResult(result);
    });

    return tcs.Task;
}

The MainPage.DisplayAlert() needs to be called on the UI thread which my method ensures.

I already tried to call my DisplayAlert from the synchronous OnJsConfirm method with these variants:

//bool success = UI.DisplayAlert(title, message, ok, cancel).Result;                                // hangs !!!
//bool success = UI.DisplayAlert(title, message, ok, cancel).WaitAndUnwrapException();              // hangs !!! WaitAndUnwrapException is in Nito.AsyncEx.Synchronous
//bool success = Nito.AsyncEx.AsyncContext.Run(() => UI.DisplayAlert(title, message, ok, cancel));  // hangs !!!
//bool success = TaskEx.Run(() => UI.DisplayAlert(title, message, ok, cancel)).Result;              // hangs !!!
//bool success = Task.Run(() => UI.DisplayAlert(title, message, ok, cancel)).Result;                // hangs !!!
//bool success = Task.Run(async () => await UI.DisplayAlert(title, message, ok, cancel)).Result;    // hangs !!!
bool success = Task.Run(async () => await UI.DisplayAlert(title, message, ok, cancel).ConfigureAwait(false)).Result;    // hangs !!!

I took the suggestions using Nito.AsyncEx from this answer by Stephen Cleary to a similar question.

I also already tried to add .ConfigureAwait(false) to the await MainPage.DisplayAlert(...), but that also didn't work.

When I set a breakpoint at the await MainPage.DisplayAlert line, then it was hit in all but the last two variants and when I then pressed F10, VS2015 stopped with this call stack: enter image description here

There was no exception though. The app just hang.

Does anybody know, how I can call my async method?

Community
  • 1
  • 1
Michael Rumpler
  • 305
  • 2
  • 17

1 Answers1

3

The return value of OnJsConfirm only indicates, if you will handle the confirm dialog. So you need to return true and do the rest asynchronously. And if your DisplayAlert returns, you can call Confim() or Cancel() on the JsResult depending on the task's result.

public override bool OnJsConfirm(WebView view, string url, string message, JsResult result)
{
    DisplayAlert("Really?", message, "OK", "Cancel").ContinueWith(t => {
        if(t.Result) {
            result.Confirm();
        }
        else {
            result.Cancel();
        }
    });
    return true;
}


public static Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
{
    var tcs = new TaskCompletionSource<bool>();

    Device.BeginInvokeOnMainThread(async () =>
    {
        var result = await MainPage.DisplayAlert(title, message, accept, cancel);
        tcs.SetResult(result);
    });

    return tcs.Task;
}
Sven-Michael Stübe
  • 14,560
  • 4
  • 52
  • 103