MVVM和查看/视图模型层次(MVVM and View/ViewModel hierarchy)

2019-06-27 23:47发布

我正在使用C#和XAML的Windows 8.我还在学习的核心理念和最佳实践,使我的第一场比赛,和MVVM一直是一个障碍。 我会尝试问的问题两个部分。

背景

我在做游戏是数独。 数独具有包含瓷砖的9x9的网格板。 我有三种模式- GameBoardTile 。 当一个Game被创建,它会自动创建一个Board ,并在Board创建,它创建81(9×9) Tiles

1.视图的层次,如何对应的视图模型产生的?

为了匹配模型的层次,我想有视图的层次( GameView包含BoardView其中包含81个TileViews )。 在XAML中,它很容易地创建与用户控件的意见这种层次结构,但我不明白的视图模型是如何被创建的。

在我见过的示例中,用户控件的数据上下文通常被设置到视图模型(使用ViewModelLocator它创建视图模型的新鲜实例作为源)。 这似乎是,如果你有一个平面视图工作得很好,而且看起来它就会变得混乱,当你有一个层次。 是否GameView创建GameViewModel并把它留给其BoardView孩子创造一个BoardViewModel ? 如果是的话,请问GameViewModel与沟通BoardViewModel ? 在可BoardViewModel沟通备份层次的GameViewModel

2.如何做一个视图模型得到的模型数据?

在iOS中,我会用服务来获取一个启动Game模式,预填充数据。 然后我将创建一个GameViewController视图控制器(它是在电荷创建的视图的)和通过Game给它。 在MVVM,我看到价值在具有视图负责(最好使用创建自己的视图模型的ViewModelLocator ),但我不明白,视图模型如何得到的模型。

在所有的,我在网上找到的例子中,视图模型使用了一些服务来获取自己的数据。 但是,我还没有碰到接受来自导航更高层次的传递构造params中,PARAMS任何例子。 如何做到这一点?

我不希望使用的应用程序资源或一些其他类型的单存储法在我的模型,因为,不是我做的,但如果我想在屏幕上同时显示多个谜题? 每个GameView应该包含它自己的Game

不仅在GameViewModel需要将参考Game模式,但BoardViewModel这在某种程度上创建(见问题1)需要的参考Board是属于典型的Game模式。 这同样适用于所有的Tiles 。 所有这些信息是如何流传下来的链条? 我能做到这一点很多繁重完全在XAML,还是我将不得不做一些代码绑定或其他初始化的?

唷!

我很欣赏你可以给任何意见,即使它不是一个完整的答案。 我也敏锐地发现,分享我自己的类似挑战MVVM项目的任何实例。 万分感谢!

Answer 1:

我将通过创建一个类来开始与应用程序启动。 通常我调用该类像ApplicationViewModelShellViewModel ,即使在技术上它可以比我通常会用一个由不同遵守规则ViewModel

该类被实例化的启动,是DataContextShellViewApplicationView

// App.xaml.cs
private void OnStartup(object sender, StartupEventArgs e)
{
    var shellVM = new ShellViewModel(); 
    var shellView = new ShellView();    
    shellView.DataContext = shellVM;  
    shellView.Show(); 
}

这通常是我设置的唯一的地方DataContext直接UI组件。 从这一点上, 你的ViewModels是应用程序 。 与MVVM工作时,它的重要的是要考虑到这一点。 大家的意见只是一个友好的用户界面,使用户能够用的ViewModels互动。 它们实际上没有考虑应用程序代码的一部分。

例如,您的ShellViewModel可能包含:

  • BoardViewModel CurrentBoard
  • UserViewModel CurrentUser
  • ICommand NewGameCommand
  • ICommand ExitCommand

和你的ShellView可能包含是这样的:

<DockPanel>
    <Button Command="{Binding NewGameCommand}" 
            Content="New Game" DockPanel.Dock="Top" />
    <ContentControl Content="{Binding CurrentBoard}" />
</DockPanel>

这实际上将导致您的BoardViewModel对象到UI作为ContentControl.Content 。 要指定如何绘制你的BoardViewModel ,你可以指定一个DataTemplateContentControl.ContentTemplate ,或使用隐式DataTemplates

隐式的DataTemplate仅仅是一个DataTemplate的一类是不具有x:Key与它相关联。 WPF将使用这个模板随时遇到的UI指定的类的对象。

因此,使用

<Window.Resources>
    <DataTemplate DataType="{x:Type local:BoardViewModel}">
        <local:BoardView />
    </DataTemplate>
</Window.Resources>

将意味着代替绘图

<ContentControl>
    BoardViewModel
</ContentControl>

它会画

<ContentControl>
    <local:BoardView />
</ContentControl>

现在BoardView可能包含类似

<ItemsControl ItemsSource="{Binding Squares}">
    <ItemsControl.ItemTemplate>
        <ItemsPanelTemplate>
            <UniformGrid Rows="3" Columns="3" />
        </ItemsPanelTemplate>
    <ItemsControl.ItemTemplate>
</ItemsControl>

并且这将绘制使用3×3的一板UniformGrid ,用含有您的内容的每个单元Squares阵列。 如果您BoardViewModel.Squares财产正好是数组TileModel对象,那么每个网格单元将包含一个TileModel ,你可以再次使用隐式DataTemplate告诉WPF如何绘制每个TileModel

现在为您的如何ViewModel获得其实际的数据对象,这是给你的。 我更喜欢抽象类的后面所有的数据访问,如Repository ,并有我的ViewModel简单地调用像SodokuRepository.GetSavedGame(gameId); 。 它使应用程序易于测试和维护。

然而,你得到你的数据时,请记住, ViewModelModels是应用程序,所以他们应该负责获取数据。 不这样做,在View 。 个人而言,我喜欢让我的Model层用于保存数据而已,所以永远只能从我的ViewModels进行数据访问操作简单的对象。

对于之间的通信ViewModels ,其实我有一个在我的博客文章有关。 总之,使用邮件系统,如Microsoft棱镜的EventAggregator或MVVM光的Messenger 。 他们的工作就像是一种寻呼系统:任何类可以订阅一个特定类型的消息,任何类都可以广播消息。

例如,您的ShellViewModel可能订阅接收ExitProgram消息,并关闭该应用程序时,听到一个,你可以播放一个ExitProgram在应用程序的任何地方的消息。

我想的另一种方法是只从一个类附加处理程序到另一个,例如调用CurrentBoardViewModel.ExitCommand += Exit;ShellViewModel ,但我发现,凌乱和喜欢使用的邮件系统。

不管怎么说,我希望这能回答大家的一些问题,并会指出你在正确的方向。 好运与您的项目:)



文章来源: MVVM and View/ViewModel hierarchy