C# 在 GUI 线程上等待并捕获异常 WPF

C# await on GUI thread and catch exceptions WPF

我正在使用 MVVM Light,并且有一个用于显示对话框的接口 IDialogService。此接口已在 App.xaml.cs

中实现

一个具体的方法很有意思:

Task<bool> ShowMessage(string message, string title, string buttonConfirmText, string buttonCancelText, Action<bool> afterHideCallback);

方法实现为:

        public Task<bool> ShowMessage(string message, string title, string buttonConfirmText,
        string buttonCancelText,
        Action<bool> afterHideCallback)
    {
        return Task.Factory.StartNew(() =>
        {
            var style = new Style(typeof(MessageBox));
            style.Setters.Add(new Setter(MessageBox.OkButtonContentProperty, buttonConfirmText));
            style.Setters.Add(new Setter(MessageBox.CancelButtonContentProperty, buttonCancelText));
            var result = MessageBox.Show(_GetActiveWindow(), message, title,
                MessageBoxButton.OKCancel,
                MessageBoxImage.Question, style) == MessageBoxResult.OK;
            if (afterHideCallback != null) afterHideCallback(result);
            return result;

其中 _currentTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 定义在 OnStartup

所以通常我们应该在前面调用这个方法来获取布尔值:

    var result = await  DialogService.ShowMessage(
                        Resources.Areyousure,Resources.Warning, 
                        Resources.Yes, Resources.No, null);

到目前为止一切顺利。现在我有一个包装器方法来执行代码并捕获异常,然后显示错误消息框。

    public bool TryCatchExecution(Action action, string successMessage = null)
    {
        try
        {
            action();

            if (!string.IsNullOrEmpty(successMessage))
                DialogService.ShowMessage(successMessage, Resources.Success);
            return true;
        }
        catch (LogException ex)
        {
            DialogService.ShowError(ex.Error.LogMessage, Resources.Error, Resources.OK, null);
        }
        catch (Exception ex)
        {
            DialogService.ShowError(ex.Message, Resources.Error, Resources.OK, null);
        }
        return false;
    }

现在我遇到了一个问题。如果我像示例 A 那样使用,GUI 线程将在 var result = DialogService.ShowMessage 行被阻塞。但是,如果我像示例 B 中那样使用,GUI 线程不会被阻塞,会显示消息框,并且一切正常。直到我得到一个例外。异常未被代码处理。错误是 "A first chance exception of type 'System.ServiceModel.FaultException`1' occurred in mscorlib.dll" 并且应用程序崩溃。正如我一直在阅读的那样,这与 SynchronizationContext 有关。

    //Sample A
    private void ExecuteDeleteCommand()
    {
         TryCatchExecution(() =>
            {
                var result =  DialogService.ShowMessage(
                    Resources.Areyousure,
                    Resources.Warning,
                    Resources.Yes,
                    Resources.No, null).Result;
                if (!result) return;

                _datalayer.DeleteField(FieldSelected);
                Refresh();
                FieldEdit = new MsgSqlFieldMapping();
                RaisePropertyChanged("SqlRepository");
                DialogService.ShowMessage(Resources.OperationSucceeded, Resources.Success);
            });
    }

    //Sample B
    private void ExecuteDeleteCommand()
    {
        TryCatchExecution(async () =>
        {
            var result =await DialogService.ShowMessage(
                Resources.Areyousure,
                Resources.Warning,
                Resources.Yes,
                Resources.No, null);
            if (!result) return;

            _datalayer.DeleteField(FieldSelected);
            Refresh();
            FieldEdit = new MsgSqlFieldMapping();
            RaisePropertyChanged("SqlRepository");
            await DialogService.ShowMessage(Resources.OperationSucceeded, Resources.Success);
        });
    }

请帮助我了解这里发生了什么以及如何处理它。

THNX 很多。

您的问题是由于 async void - 具体来说,通过将 async lambda 作为类型 Action 的参数传递,您正在创建一个 async void 方法。 problems with async void methods 之一是您无法捕获异常(至少,不是正常方式)。

要解决此问题,请创建一个采用 async equivalent of Action, which is Func<Task>:

的辅助方法的重载
public async Task<bool> TryCatchExecution(Func<Task> action, string successMessage = null)
{
    try
    {
        await action();

        if (!string.IsNullOrEmpty(successMessage))
            DialogService.ShowMessage(successMessage, Resources.Success);
        return true;
    }
    catch (LogException ex)
    {
        DialogService.ShowError(ex.Error.LogMessage, Resources.Error, Resources.OK, null);
    }
    catch (Exception ex)
    {
        DialogService.ShowError(ex.Message, Resources.Error, Resources.OK, null);
    }
    return false;
}