【问题标题】:Update ViewModel with async value, after/during call to ShowDialog在调用 ShowDialog 之后/期间使用异步值更新 ViewModel
【发布时间】:2020-09-05 21:30:13
【问题描述】:

我正在创建一个模态窗口,将 DataContext 分配给我的视图模型,然后调用 ShowDialog

var vm = new MyViewModel();  // implements INotifyPropertyChanged

// populate vm properties here

var window = new MyWindow(); // inherits from System.Windows.Window
window.DataContext = vm;
window.ShowDialog();

但是,我想从 async 方法的结果中填充视图模型的属性。

vm.Result = await provider.GetResultAsync();

我希望这些更改对窗口中的数据绑定属性可见。

在调用ShowDialog 之前我不能这样做,因为那样ShowDialog 只有在结果可用后才会被调用。

在调用ShowDialog 后我不能这样做,因为ShowDialog 会阻塞直到窗口关闭。

我该怎么做?

【问题讨论】:

    标签: c# .net wpf async-await


    【解决方案1】:

    将此代码移到 MyWindow 类中

    然后在window_loaded()中

    var vm = new MyViewModel();  // implements INotifyPropertyChanged
    
    // populate vm properties here
    
    this.DataContext = vm;
    

    【讨论】:

    • 1) 这可以使用await-ed 结果来完成吗? 2) 我可以附加到MyWindow 之外的Loaded 事件。这很重要,因为在我的情况下,MyWindow 是在不同的 DLL 中定义的,我无法真正更改它。
    • 我认为 await / async 取决于提供者。
    • 就问题而言,您可以放心地假设provider 有一个名为GetResultAsyncasync 方法,它返回一个Task-ed 值。
    • 不,它没有。
    • 对不起,我帮不上忙!
    【解决方案2】:

    您可以通过多种方式做到这一点。我推荐@WowoOt 的答案,因为视图模型必须是您尝试使用的窗口的一部分。

    另外一种实现方式,就是在provider.GetResultAsync()的调用中使用Task.ContinueWith方法,然后在回调中写上赋值vm.Result = task.Response。但是,这仅在您的视图模型实现 INotifyPropertyChanged 时才有效。

    下面是代码:

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e) {
        // Supposed to open the window after clicking a button.
        var vm = new MyViewModel();
        var window = new MyWindow {
            DataContext = vm
        };
        //vm.Result = await GetResultAsync();
        _ = GetResultAsync().ContinueWith(x => vm.Result = x.Result);
    
        window.ShowDialog();
    
        static async Task<string> GetResultAsync() {
            await Task.Delay(1000); // just to simulate a long process to recover the data.
            return $"generated string: {new Random().Next(10)}";
        }
    }
    

    我的示例视图模型实现了INotifyPropertyChanged 接口,以通知 XAML 框架有关其属性的任何更改。

    public class MyViewModel : INotifyPropertyChanged {
        private string result;
        public string Result {
            get => this.result;
            set {
                this.result = value;
                this.OnPropertyChanged();
            }
        }
    
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        } 
        #endregion
    }
    

    【讨论】:

    • 1) 视图模型实现INotifyPropertyChanged,如问题中所述。 2) 为什么我需要引入一个新的本地函数? _ = provider.GetResultAsync().ContinueWith(x =&gt; vm.Result = x.Result); 应该也能正常工作。 3) 视图模型必须是您尝试使用它的窗口的一部分 不是。同一个窗口可以使用不同的视图模型类型,并且相同的视图模型类型可以应用于多个视图。 4)为什么我需要分配给一个丢弃(_)?
    • 代码中的@ZevSpitz,本地函数是给一个完整的解决方案,你需要用你的provider的方法替换它。使用视图模型的目的是对 UIElement 中的元素(Window、UserControl 等)进行整形,因此您无需为视图编写多个视图模型。
    【解决方案3】:

    感谢@Wowo Ot's answer@Marlonchonsky's answer,我正在做以下事情:

    var vm = new MyViewModel();
    var window = new MyWindow();
    window.DataContext = vm;
    window.Loaded += async (s, e) => vm.Result = await provider.GetResultAsync();
    window.ShowDialog();
    

    它有效。

    【讨论】:

      【解决方案4】:

      你可以这样做

      vm.GetResultsTask = provider.GetResultsAsync();
      

      然后,当您需要 MyWindow 中的结果时,您将执行 await DataContext.GetResultsTask 获取它们。

      【讨论】:

      • 我没有明确获取结果的代码;我依靠数据绑定。我应该什么时候写await DataContext.GetResultsTask;
      • 我建议将此信息纳入您的答案。假设这可行,我更喜欢它,因为它可以将结果检索封装在视图模型中。
      猜你喜欢
      • 2010-09-14
      • 1970-01-01
      • 2016-06-15
      • 2019-12-24
      • 2012-01-31
      • 1970-01-01
      • 2013-06-19
      • 1970-01-01
      • 2011-05-30
      相关资源
      最近更新 更多