【问题标题】:C# await on GUI thread and catch exceptions WPFC# 在 GUI 线程上等待并捕获异常 WPF
【发布时间】:2017-08-08 22:28:03
【问题描述】:

我正在使用 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 中定义

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

    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 线程,则未阻塞,显示消息框,并且一切正常。直到我得到一个例外。异常没有被代码处理。错误是“在 mscorlib.dll 中发生了‘System.ServiceModel.FaultException`1’类型的第一次机会异常”并且应用程序崩溃。正如我一直在阅读的,这与 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 很多。

【问题讨论】:

  • 我忘了说我使用的是.net4.5框架。

标签: c# wpf async-await task ui-thread


【解决方案1】:

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

要解决此问题,请创建一个采用 async equivalent of Action, which is Func&lt;Task&gt; 的辅助方法的重载:

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;
}

【讨论】:

  • 您好,感谢您的解决方案。有一个小问题。我已经检查过这个解决方案。问题是在更改方法签名后,我需要更改大约 200 种其他方法。
  • @Gintaras:不要改变你的方法;只需添加这个。
猜你喜欢
  • 2015-07-17
  • 2015-09-27
  • 1970-01-01
  • 2020-07-08
  • 2014-12-01
  • 2013-06-07
  • 2015-06-19
  • 2011-12-15
  • 2018-07-24
相关资源
最近更新 更多