C# 捕获对话框viewmodel异步加载中的异常

C# 捕获对话框viewmodel异步加载中的异常,c#,wpf,mvvm,dialog,async-await,C#,Wpf,Mvvm,Dialog,Async Await,我有一个带有async Task LoadData()方法的DialogViewModel类。此方法异步加载数据并显示此对话框,通知用户加载。代码如下: try { var dialog = new DialogViewModel(); var loadTask = dialog.LoadData(); WindowManager.ShowDialog(dialog); await loadTask; } catch (Exception ex) { Lo

我有一个带有
async Task LoadData()
方法的
DialogViewModel
类。此方法异步加载数据并显示此对话框,通知用户加载。代码如下:

try
{
    var dialog = new DialogViewModel();
    var loadTask = dialog.LoadData();
    WindowManager.ShowDialog(dialog);
    await loadTask;
}
catch (Exception ex)
{
    Logger.Error("Error in DialogViewModel", ex);
    // Notify user about the error
}
LoadData
抛出异常时,直到用户退出对话框,才会处理该异常。之所以发生这种情况,是因为调用
wait
时处理了异常,并且直到
WindowManager.ShowDialog(dialog)
完成后才会发生异常

显示异步加载对话框的正确方式是什么?我试过这样做:

  • OnShow()
    、构造函数或类似程序中调用
    LoadData()
    。但是,如果我需要在没有任何数据的情况下显示此对话框,那么这将不起作用
  • 在显示对话框之前,调用
    等待LoadData()
    。这样,用户在实际看到窗口之前必须等待数据加载,但我希望窗口立即显示一个加载指示器

  • 为什么有一个显式的public
    LoadData
    方法

    如果必须这样做,则在构造函数内部使用
    Task
    ContinueWith
    异步执行,以处理通过检查返回任务的
    IsFaulted
    属性生成的任何异常

    这将解决您强调的两个问题

    下面显示了一个非常简单的示例,很明显,您的实现将更加复杂

    public class DialogViewModel
    {
        private Task _task;
    
        public DialogViewModel()
        {
            var context = TaskScheduler.FromCurrentSynchronizationContext();
    
            _task = Task.Factory.StartNew(() =>
                {
                    var data = GetDataCollection();
                    return data;
                })
                .ContinueWith(t =>
                {
                    if (t.IsFaulted)
                    {
                        HasErrored = true;
                        ErrorMessage = "It's borked!";
                    }
                    else
                    {
                        Data = t.Result;
                    }
                }, context);
        }
    
        public IEnumerable<string> Data { get; private set; }
    
        public bool HasErrored { get; private set; }
    
        public string ErrorMessage { get; private set; }
    
        private static IEnumerable<string> GetDataCollection()
        {
            return new List<string>()
            {
                "John",
                "Jack",
                "Steve"
            };
        }
    }
    
    公共类对话框视图模型
    {
    私人任务(u Task),;
    公共对话框ViewModel()
    {
    var context=TaskScheduler.FromCurrentSynchronizationContext();
    _task=task.Factory.StartNew(()=>
    {
    var data=GetDataCollection();
    返回数据;
    })
    .ContinueWith(t=>
    {
    如果(t.IsFaulted)
    {
    HasErrored=true;
    ErrorMessage=“它是borked!”;
    }
    其他的
    {
    数据=t.结果;
    }
    },上下文);
    }
    公共IEnumerable数据{get;private set;}
    public bool hasrerred{get;private set;}
    公共字符串错误消息{get;private set;}
    私有静态IEnumerable GetDataCollection()
    {
    返回新列表()
    {
    “约翰”,
    “杰克”,
    “史蒂夫”
    };
    }
    }
    
    或者,如果您不想显式地使用
    Task
    并想使用async\await功能,您可以使用稍微不同的方法,因为您不能将async\await与类构造函数一起使用:

    public class DialogViewModel
    {
        public IEnumerable<string> Data { get; private set; }
    
        public bool HasErrored { get; private set; }
    
        public string ErrorMessage { get; private set; }
    
        async public static Task<DialogViewModel> BuildViewModelAsync()
        {
            try
            {
                var data = await GetDataCollection();
                return new DialogViewModel(data);
            }
            catch (Exception)
            {
                return new DialogViewModel("Failed!");
            }
        }
    
        private DialogViewModel(IEnumerable<string> data)
        {
            Data = data;
        }
    
        private DialogViewModel(string errorMessage)
        {
            HasErrored = true;
            ErrorMessage = errorMessage;
        }
    
        private async static Task<IEnumerable<string>> GetDataCollection()
        {
            // do something async...
            return await Task.Factory.StartNew(() => new List<string>()
            {
                "John",
                "Jack",
                "Steve"
            });
        }
    }
    
    公共类对话框视图模型
    {
    公共IEnumerable数据{get;private set;}
    public bool hasrerred{get;private set;}
    公共字符串错误消息{get;private set;}
    异步公共静态任务BuildViewModelAsync()
    {
    尝试
    {
    var data=await GetDataCollection();
    返回新的DialogViewModel(数据);
    }
    捕获(例外)
    {
    返回新的DialogViewModel(“失败!”);
    }
    }
    私有对话框ViewModel(IEnumerable数据)
    {
    数据=数据;
    }
    专用对话框ViewModel(字符串错误消息)
    {
    HasErrored=true;
    ErrorMessage=ErrorMessage;
    }
    私有异步静态任务GetDataCollection()
    {
    //做一些异步的事情。。。
    返回wait Task.Factory.StartNew(()=>newlist())
    {
    “约翰”,
    “杰克”,
    “史蒂夫”
    });
    }
    }
    
    不幸的是,这个问题的答案不适合我。我已经写过
    Show()
    。我想需要从外部调用“LoadData()”,因为只有调用者决定是否加载数据。至于“BackgroundWorker”-我希望有一种方法可以通过async await做到这一点,
    WindowManager.ShowDialog()
    来自哪里?是Caliburn Micro吗?是的,但这并不重要:它可以是任何
    ShowDialog()
    实现,例如视图注入。但是
    LoadData()
    将始终被调用,但我可能希望在不加载任何数据的情况下显示此对话框(如我在第一种情况中所述)。好吧,这就是我认为您有问题的地方,要么永远叫它,要么永远不叫它。因为否则它在什么时候应该被称为。。。