是否可以等待 Device.BeginInvokeOnMainThread 代码完成(使用 UI 调用的结果继续后台工作)

R. *_*ats 3 c# asynchronous async-await xamarin.forms

在我的代码中,我有一个名为“ShowMessageBoxAsync”的任务。我想使用此代码向用户显示(并等待)DisplayAlert 并返回结果。像这样: var messageBoxResult = await View.ShowMessageBoxAsync("这是一个错误");

ShowMessageBoxAsync 的代码是:

public async System.Threading.Tasks.Task<bool> ShowMessageBoxAsync(string message)
{
    var result = false;
    Device.BeginInvokeOnMainThread(async () =>
    {
        result = await DisplayAlert("Error", message, "OK", "Cancel");
    });
    return result;
}
Run Code Online (Sandbox Code Playgroud)

在我添加 Device.BeginInvokeOnMainThread 之前,该任务给了我一个异常,表明它没有在主/UI 线程上运行。所以添加了BeginInvokeOnMainThread后,就开始正常工作了。然而,问题在于代码直接转到结果,而不是等待“await DisplayAlert”的结果。

只有在 Device.BeginInvokeOnMainThread 代码完成后才可能返回“result”的值吗?

我对此做了一些研究,有人建议使用 TaskCompletionSource,但这会阻止 UI 线程并且 DisplayAlert 根本不显示。

Ste*_*ary 12

我做了一些研究,有人建议使用 TaskCompletionSource

那是正确的解决方案。TaskCompletionSource充当Task. 在这种情况下,这Task是代表用户交互,因此您希望 UI 线程上的代码完成Task,而后台线程上的代码 (a) 等待 `Task.

所以,像这样:

public Task<bool> ShowMessageBoxAsync(string message)
{
  var tcs = new TaskCompletionSource<bool>();
  Device.BeginInvokeOnMainThread(async () =>
  {
    try
    {
      var result = await DisplayAlert("Error", message, "OK", "Cancel");
      tcs.TrySetResult(result);
    }
    catch (Exception ex)
    {
      tcs.TrySetException(ex);
    }
  });
  return tcs.Task;
}
Run Code Online (Sandbox Code Playgroud)

这应该可以让你暂时解锁。但是,更好的长期解决方案是让您的后台线程逻辑采用某种“与 UI 交互”的界面,如下所示:

public interface IAskUser
{
  Task<bool> AskUserAsync(string message);
}
Run Code Online (Sandbox Code Playgroud)

具有与上述类似的 Xamarin-Forms 特定实现。

这样,您的后台线程逻辑就不会绑定到特定的 UI,并且可以更轻松地进行单元测试和重用。


Ada*_*ent 8

这个问题现在值得更新 - 在最新版本的 Xamarin Forms 中,现在有一个名为 awaitable 的方法Device.InvokeOnMainThreadAsync,在我看来,它比其他答案更易于使用且不那么冗长。

        var result = false;
        await Device.InvokeOnMainThreadAsync(async () =>
        {
            result = await DisplayAlert("Error", message, "OK", "Cancel");
        });
        return result;
Run Code Online (Sandbox Code Playgroud)

或者,如果您不是在 Xamarin 表单中运行,您也可以从 Xamarin.iOS、Xamarin.Android 或 Xamarin.UWP 引用 Xamarin.Essentials 并像这样调用 MainThread 扩展

 var result = false;
       await  MainThread.InvokeOnMainThreadAsync(async () =>
        {
            result = await DisplayAlert("Error", message, "OK", "Cancel");
        })
       return result;
Run Code Online (Sandbox Code Playgroud)