【问题标题】:MVVM How to set datacontext when viewmodel uses asyncMVVM如何在viewmodel使用异步时设置datacontext
【发布时间】:2015-08-21 07:02:43
【问题描述】:

经过数小时的搜索,我仍然没有回答这个问题。我已经阅读了这篇关于异步 MVVM 的精彩 writing 并让我的视图模型使用工厂方法。

public class MainViewModel
{
    // sic - public, contrary to the pattern in the article I cite
    // so I can create it in the Xaml as below
    public MainViewModel() 
    {
    }

    private async Task InitializeAsync()
    {
        await DoSomethingAsync();
    }

    public static async Task<MainViewModel> CreateAsync()
    {
        var ret = new MainViewModel();
        await ret.InitializeAsync();
        return ret;
    }
}

这对我来说很清楚,但我不明白如何制作 MainViewModel 的实例并将其设置为 MainPage 中的 datacontext。我不能简单地写

<Page.DataContext>
    <viewModel:MainViewModel/>
</Page.DataContext>

因为我应该使用 MainViewModel.CreateAsync() 方法。而且我不能在代码隐藏上做到这一点,我什至想这样做,因为代码隐藏 - 构造函数是普通方法,而不是异步方法。那么哪种方法才是正确的继续呢?

【问题讨论】:

  • WP8.1 != WPF。更可惜的是。期待我们能够让它们同步的那一天。

标签: c# asynchronous mvvm windows-phone-8.1


【解决方案1】:

首先,您应该将默认构造函数设为私有以避免滥用您的类(您引用的文章就是这样做的 - 构造函数是 private)。

您用来设置DataContext 的方法不适用于MVVM 模式(View 不应自己创建ViewModel)。 您应该在更高级别的层中创建 ViewViewModel 并让该层绑定它们。表示如果Page 是您的主要View,您应该通过覆盖OnStartupApp.xaml.cs 中创建它们,如下所示:

var page = new Page();
var dataService = new YourDataService(); // iff Create or the ctor require arguments
var viewModel = await MainViewModel.CreateAsync(dataService);
page.DataContext = viewModel;
page.Show();

【讨论】:

    【解决方案2】:

    您必须在窗口打开之前初始化视图模型。转到您的 App.xaml 文件并删除部分:StartupUri="MainWindow.xaml"。然后你去App.xaml.cs添加这个:

    protected async override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        var mainWindow = new MainWindow { DataContext = await CreateAsync() };
        mainWindow.Show();
    }
    

    【讨论】:

    • 谢谢,看起来不错。但我的应用程序适用于 wp 8.1,因此它在我的 App.xaml 上没有 StartupUri-property。我应该修改 App.xaml.cs 的哪一部分以及如何修改?
    【解决方案3】:

    我会重新考虑。使MainViewModel 构造/实例化轻量级。然后在您的 VM 上创建 LoadInitialize 方法。从代码隐藏创建一个实例,将其设置为DataContext,然后调用 init 方法并让它运行。

    例如

    /// <summary>Interaction logic for MainWindow.xaml</summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
    
            var dc = new MainViewModel();
            dc.Initialize("Hello", " ", "world");
            this.DataContext = dc;
        }
    }
    
    public class MainViewModel
    {
        /// <summary>Simple constructor</summary>
        public MainViewModel() { }
    
        public void Initialize(params object[] arguments)
        {
            //use the task to properly start a new thread as per:
            //http://stackoverflow.com/a/14904107/1144090 and
            //https://msdn.microsoft.com/en-us/library/hh965065.aspx
            //(what would happen if we simply invoke init async here?)
            this.InitializeAsync(arguments)
                .ContinueWith(result =>
                {
                    if (!result.IsFaulted)
                        return;
    
                    MessageBox.Show("Unexpected error: " + Environment.NewLine + result.Exception.ToString());
                });
        }
    
        private async Task InitializeAsync(params object[] arguments)
        {
            await Task.Delay(2333);
    
            MessageBox.Show(String.Concat(arguments));
        }
    }
    

    请注意,这是一种快速而简单的解决方案,其他两个答案(与依赖注入框架配对)将为您的解决方案提供适当的高级结构。

    【讨论】:

    • Windows 不应初始化 ViewModel。您最终会得到非常脆弱的代码,并且很难干净地管理初始化失败时应该发生的事情(即,在一般情况下显示这样的消息框是不现实的)。我强烈建议首先考虑其他答案中的方法。
    • @RubenBartelink 实际上应该是管理应用程序启动的 IOC 容器。是的,像其他示例中那样覆盖应用程序启动更“正确”。放置 IOC 容器的更合适的位置。
    【解决方案4】:

    让我的视图模型使用工厂方法

    我通常是这种方法的粉丝 - 这是我最喜欢的解决“无异步构造函数”限制的方法。但是,它在 MVVM 模式中效果不佳。

    这是因为从逻辑上讲,VM 就是您的 UI。当用户导航到应用中的屏幕时,应用需要立即(同步)响应。它不一定要显示任何有用的东西,但它确实需要显示 something。因此,VM 的构建必须是同步的。

    因此,与其尝试异步构建您的虚拟机,不如先确定您希望“正在加载”或“不完整”的 UI 的外观。您的(同步)VM 构造函数应该初始化为该状态,并且它可以启动一些异步工作,当它完成时更新VM。

    这并不难手工完成,或者您可以使用我在MSDN article on async MVVM data binding 中描述的NotifyTaskCompletion 方法来使用数据绑定来驱动状态转换。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-23
      • 2010-11-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多