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

时间:2017-03-17 11:23:18

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

我正在使用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线程未被阻止,则显示消息框,并且一切正常。直到我得到一个例外。代码无需例外。错误是&#34;类型&#39; System.ServiceModel.FaultException`1&#39;的第一次机会异常。发生在mscorlib.dll&#34;和应用程序崩溃。正如我一直在阅读这与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很多。

1 个答案:

答案 0 :(得分:1)

您的问题归因于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;
}