2017-02-16 35 views
0

当虚拟机(以及视图)需要接受其对象被异步初始化的构造函数参数时,如何在Views/ViewModels之间切换。或者,还有更好的方法可以为视图/虚拟机提供这些对象吗?如何切换所提供的提前异步初始化的对象的Views/ViewModels?

这是我以前的实现当View/VM没有使用构造函数参数。

MainWindow.xaml

用途的DataTemplates到景观当ContentControl中的变化之间切换。

<Window.DataContext> 
    <viewModels:MainWindowViewModel /> 
</Window.DataContext> 
<Window.Resources> 
    <DataTemplate DataType="{x:Type viewModels:InitializerViewModel}"> 
     <userControls:InitializerUserControl /> 
    </DataTemplate> 
    <DataTemplate DataType="{x:Type viewModels:GameListingViewModel}"> 
     <views:GameListingView /> 
    </DataTemplate> 
    <DataTemplate DataType="{x:Type viewModels:StreamListingViewModel}"> 
     <views:StreamListingView /> 
    </DataTemplate> 
</Window.Resources> 
<ContentControl x:Name="ContentSection" Content="{Binding CurrentViewModel, Mode=OneWay}"> 
    <interactivity:Interaction.Triggers> 
     <interactivity:EventTrigger EventName="Loaded"> 
      <interactivity:InvokeCommandAction Command="{Binding StartInitializationCommand}"/> 
     </interactivity:EventTrigger> 
    </interactivity:Interaction.Triggers> 
</ContentControl> 

MainWindowViewModel.cs

创建新的ViewModels,然后设置CurrentViewModel到相关的视图模型。当此变化时,DataTemplate基于上述数据模板切换视图。

public class MainWindowViewModel : BaseViewModel 
{ 
    private InitializerViewModel _initializerViewModel; 
    public ICommand StartInitializationCommand { get; set; }  

    public MainWindowViewModel() 
    { 
     StartInitializationCommand = new AwaitableDelegateCommand(InitializeAsync); 
    } 

    private async Task InitializeAsync() 
    { 
     _initializerViewModel = new InitializerViewModel(); 
     CurrentViewModel = _initializerViewModel; 
     var initializationResult = await _initializerViewModel.InitializeAsync(); 
     if (initializationResult) 
     { 
      GameListingViewModel = new GameListingViewModel(_initializerViewModel.MyObject1, _initializerViewModel.MyObject2); 
      CurrentViewModel = GameListingViewModel; 
     } 
    } 
} 

GameListingView.xaml(这是虚拟机我进出开关中的一个):

以前在其XAML经由定义的DataContext:

<UserControl.DataContext> 
    <viewModels:GameListingViewModel /> 
</UserControl.DataContext>--> 

所有上述作品的如预期。


现在,由于新的功能,这些需要的ViewModels才能正常接收1个或多个对象,所以我想他们传递到构造函数。因此,我不能再在xaml中定义DataContext,但我可以使用代码隐藏来创建它们。

public GameListingView(ConfigSettings configSettings, GameBoxHelper gameBoxHelper) 
{ 
    InitializeComponent(); 
    DataContext = new GameListingViewModel(configSettings, gameBoxHelper); 
} 

的问题是这需要查看的构造也采取了相关的对象(否则,我会得到这些对象的?),这使得DataTemplate中回MainWindow.xaml不再起作用。

Xaml错误消息:“类型'GameListingView'不能用作对象元素,因为它不是公共的,或者没有定义一个公共的无参数构造函数或类型转换器,没有找到默认构造函数。

重要的是,将被传递到这些的ViewModels的对象都没有准备好立即; MainWindowViewModel会事先对它们进行初始化,这是以异步方式完成的。根据上面的错误信息,我认为可以使用TypeConverter(将待传递的对象绑定到参数并将它们作为TypeConverter的参数指定给xaml),但我不明白这是如何工作的,因为它们不是立即准备就绪(异步初始化)。

仅供参考:目的是当应用程序加载完成时,所有初始化都会在前面完成(从文件系统读取配置设置,从Web APIs中检索一些数据等),以便在需要时立即做好准备应用程序的其他部分响应更快。我不希望将对象的异步初始化移动到使用它们的虚拟机。

我没有使用任何MVVM框架。

考虑到这一切,我的问题是如何继续切换视图/虚拟机,同时提供它们提前异步初始化的对象?


+0

我认为你可以在视图中使用DependancyProperty和更新每当更改视图模型。 – Ron

+0

你能提供一个例子说明如何工作吗? – JBC

+0

考虑以视图模型为中心的设计,其中您的主视图模型具有子视图模型(或子视图模型集合)作为属性,并且子视图模型可具有其自己的子VM属性。使用'DataTemplate'来隐式地将视图分配给视图模型,而不是视图创建自己的视图模型。 ''ContentControl Content =“{Binding BlahVM}”/>' - 无论'BlahVM'是什么类型,如果有一个隐含的'DataTemplate',你会在UI中得到它。 'BlahVM'可以声明为对象,其父可以整天切换到不同的子虚拟机。 –

回答

1

不能创建在App.xaml.cs或引导程序类的依赖时,应用程序启动,然后通过他们的MainViewModel构造函数的参数?

在App.xaml中取下Application根元素的属性StartupUri并覆盖在App.xaml.cs的OnStartup方法:

public partial class App : Application 
{ 
    protected async override void OnStartup(StartupEventArgs e) 
    { 
     base.OnStartup(e); 

     var yourDependency = await YourClass.CreateAsync(); 

     MainWindowViewModel vm = new MainWindowViewModel(yourDependency); 
     MainWindow mainWindow = new WpfApplication4.MainWindow() { DataContext = vm }; 
     mainWindow.Show(); 
    } 
} 

MainViewModel类将然后负责注入其他视图模型,它创建它与App类注入的依赖关系。

或者,如果您希望初始化MainViewModel类中的依赖关系,则可以在App类中初始化该类,即MainViewModel

请参考斯蒂芬·克利里的博客文章关于异步工厂方法和异步建设的更多信息:http://blog.stephencleary.com/2013/01/async-oop-2-constructors.html

+0

我觉得这并不能真正解决我的问题。我之前没有明确说明的一件事是,当所有事情都在初始化时,我正在用每个片段的状态更新UI(使用在第一个代码片段中看到的'InitializerViewModel'和控件)。如果我将init移到MainVM之外并进入App,那么我将失去这种可视化,如果将它从MainVM中分离出来,我仍然需要将视图/ VM从任何处理init的视图/ VM切换回MainVM,同时传递参数。看起来这种方法在初始化过程中只会有一个空白屏幕。 – JBC

+0

此外,通过这种方法,我将如何获取现在初始化的参数并将它们传递给子虚拟机(当ContentControl虚拟机发生变化时,我现在使用的'DataTemplates'在视图之间进行交换?我可以通过它进入MainVM,现在MainVM拥有它,但MainVM如何将其交给自己的子虚拟机,以便于交换? – JBC

+0

MainVM创建子视图模型,对吗?然后,只需向它们注入任何依赖关系需要当你创建它们时,交换只是将CurrentViewModel属性设置为已初始化子视图模型的实例,如果视图模型要异步初始化,当然你需要以某种方式对它初始化。这样做的方法是使用异步工厂方法 – mm8