MVVM和View / ViewModel层次结构

我正在使用Windows 8的C#和XAML来开发我的第一款游戏。我仍然在学习核心概念和最佳实践,而MVVM一直是一个障碍。 我将尝试分两个部分来提问。

背景

我正在做的游戏是数独。 数独有一个包含9×9网格的棋盘。 我有三个模型 – GameBoardTile 。 当一个Game创build时,它会自动创build一个Board ,当Board创build时,它会创build81个(9×9)的Board

1.使用视图层次结构,如何创build相应的视图模型?

为了匹配模型的层次结构,我想要有一个层次的视图( BoardView包含一个包含81个TileViews )。 在XAML中,使用用户控件创build这个视图层次结构是相当容易的,但我不明白视图模型是如何创build的。

在我看到的例子中,用户控件的数据上下文经常被设置为视图模型(使用ViewModelLocator作为源),这创build了视图模型的新实例。 如果你有一个平面的视图,这似乎工作得很好,但当你有一个层次结构时,似乎也会变得混乱。 BoardView是否创build一个BoardViewModelBoardView子进行创buildBoardViewModel ? 如果是这样, BoardViewModel如何与BoardViewModel通信? BoardViewModel可以将层次结构BoardViewModel回给BoardViewModel吗?

2.视图模型如何获取模型数据?

在iOS中,我将首先使用服务来获取预先填充数据的Game模型。 然后我会创build一个GameViewController视图控制器(负责创build视图)并将其传递给它。 在MVVM中,我认为有一个视图负责创build自己的视图模型(理想情况下使用ViewModelLocator ),但我不明白视图模型如何获得模型。

在所有我在网上find的例子中,视图模型都使用一些服务来获取自己的数据。 但我还没有遇到任何接受从更高级别的导航传递来的构造参数或参数的例子。 这是怎么做的?

我不想为我的模型使用应用程序资源或其他types的单例存储方法,因为我不这样做,但是如果我想要一次在屏幕上显示多个拼图,该怎么办? 每个GameView应该包含它自己的Game

BoardViewModel不仅需要引用Game模型,而且以某种方式创build的BoardViewModel (请参见问题1)需要引用属于Game模型的Board模型。 所有的Tiles 。 所有这些信息如何传递下去? 我可以在XAML中完成这么多繁重的工作,还是需要在代码中进行某种绑定或其他初始化?

唷!

我感谢您可以给予的任何build议,即使这不是一个完整的答案。 我也热衷于find任何与我自己的挑战相似的MVVM项目的例子。 万分感谢!

我将开始创建一个类来开始应用程序。 通常我会调用这个类,比如ApplicationViewModel或者ShellViewModel ,虽然在技术上它可以遵循不同于我通常用于ViewModel

这个类在启动时被实例化,并且是ShellViewApplicationViewDataContext

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

这通常是我直接为UI组件设置DataContext的唯一地方。 从这一点来看, 你的ViewModel是应用程序 。 在使用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对象作为ContentControl.Content呈现在UI中。 要指定如何绘制BoardViewModel ,可以在ContentControl.ContentTemplate指定DataTemplate ,也可以使用隐式DataTemplates

隐式的DataTemplate只是一个没有与之关联的x:Key的类的DataTemplate 。 无论何时遇到UI中指定类的对象,WPF都将使用此模板。

所以使用

 <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 Prism的EventAggregator或MVVM Light的Messenger等消息系统。 它们像一种寻呼系统一样工作:任何类都可以订阅接收特定类型的消息,任何类都可以广播消息。

例如,您的ShellViewModel可能会订阅接收ExitProgram消息,并在应用程序听到该消息时关闭该应用程序,并且可以从应用程序的任何位置广播ExitProgram消息。

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

无论如何,我希望能够回答你的一些问题,并指出你正确的方向。 Goodluck与你的项目:)