2017-03-27 149 views
0

我试图创建一个内存游戏,同时严格遵循MVVM模式来学习如何使用它。现在我在运行时创建视图时遇到问题。C#MVVM如何动态创建视图

我创建了以下项目结构:

  • 示范项目
  • -MemoryCardModel30
  • - 卡片
  • 视图模型项目
  • -MainWindowViewModel
  • -CardViewModel
  • 查看项目
  • -CardView
  • StartApplication项目
  • -MainWindowView

的依赖关系如下:StartApplication项目 - >查看项目 - >视图模型项目 - >模型项目

单击MainWindowView上的按钮后,该按钮的ICommand函数在MainWindowViewModel内将从Model项目加载一个MemoryCardModel30实例。对于MemoryCardModel30实例中的每个卡,将创建一个CardViewModel。

现在我要面对的问题:如何创建CardView实例,如何将其DataContexts链接到CardViewModels以及如何在MainWindowView上安排/分配CardViews? ViewModel项目不能创建视图,因为它对View项目没有依赖性(会创建一个循环依赖关系并打破该模式)。如何解决这个问题,同时遵循MVVM模式?

P.S .:卡片视图需要准确定位x和y pos。这将需要一些复杂的计算,应该去相应的CardViewModel。所以我认为一些基本的布局如grid不够用。

+0

您是否在使用框架,并且您的方法是“模型优先”还是“先查看”?此外,你可能能够得到一个WrapPanel来做你想做的事...... https://msdn.microsoft.com/en-us/library/system.windows.controls.wrappanel(v=vs.110).aspx –

+0

这种事情就是为什么我们在我的工作中使用MV ** P ** VM:将模型链接到视图,然后显示这些视图不是一个很适合MVVM的工作,但是是一个Presenter工作 –

+0

@BerinLoritsch不,我目前没有使用任何框架。我想从一开始就学习这种模式。我只是在EntityFramework的上下文中“模型第一”而不熟悉MVVM。然而,在这种情况下,我开始使用ViewModel,然后是模型并保存视图。 – user2653422

回答

4

将它们显示在ItemsControl中。我假设MainWindowViewModel.CardsObservableCollection<CardViewModel>

<ItemsControl 
    ItemsSource="{Binding Cards}" 
    > 
    <!-- 
    This creates UI for each item. There are other ways, if you've got a collection 
    of heterogeneous item types. 
    --> 
    <ItemsControl.ItemTemplate> 
     <DataTemplate DataType="local:CardViewModel"> 
      <views:CardView /> 
     </DataTemplate> 
    </ItemsControl.ItemTemplate> 

    <!-- 
    Make it use a Canvas to be the actual container for the items, so we can control 
    their position arbitrarily, instead of the default StackPanel that just stacks 
    them up vertically. 
    --> 
    <ItemsControl.ItemsPanel> 
     <ItemsPanelTemplate> 
      <Canvas /> 
     </ItemsPanelTemplate> 
    </ItemsControl.ItemsPanel> 

    <!-- 
    The ItemsControl will put the instantiated item templates in ContentPresenters 
    that it creates. The positioning attributes have to go on the ContentPresenters, 
    because those are the direct children of the Canvas. The ContentPresenters are 
    the "item containers". You can customize them via the ItemContainerStyle property 
    of the ItemsControl. 
    --> 
    <ItemsControl.ItemContainerStyle> 
     <Style TargetType="ContentPresenter"> 
      <!-- 
      The datacontext will be CardViewModel. 

      Bind Canvas.Left and Canvas.Top to appropriate properties 
      of CardViewModel. I'll assume it's got Point Position { get; } 

      A much better, more "pure MVVM" way to do this is for the items to 
      provide some kind of abstraction, maybe row/column or something else, 
      and either place them in a Grid or UniformGrid or some other kind of 
      dynamic layout control, or else convert that abstraction into Canvas 
      coordinates with a value converter on the Binding. 

      Then you can display the same item objects in different ways at the same 
      time without locking them into one layout. 

      Don't drive yourself crazy striving for ideological purity at the expense 
      of getting code out the door, but do consider redesigning that part. 
      --> 

      <Setter Property="Canvas.Left" Value="{Binding Position.X}" /> 
      <Setter Property="Canvas.Top" Value="{Binding Position.Y}" /> 
     </Style> 
    </ItemsControl.ItemContainerStyle> 

这是在WPF/MVVM中执行它的规范方法。使用DataTemplate s创建相应类型的视图实例。视图模型负责将对象呈现给用户;观点负责他们如何显示。您不需要或不需要任何MVVM框架。 WPF的内置DataTemplate功能非常强大。不要相信任何人认为你需要在这个规模的两个数量级之内的任何项目。

+1

我不会把坐标放到虚拟层,但它们的抽象,这将在视图层通过转换器解决。 – Rekshino

+0

@Rekshino这是正确的(或至少“正确的”)方法,我会适当地更新我的答案。 –

0

我想我误解了你的问题。我原本以为你在问如何显示特定视图模型的新窗口。虽然这个答案不会特别适用于你,但我会留下它,因为它与切线相关。它可能会帮助其他人对要搜索的内容感到困惑。


我有一个ViewManager类将视图类型链接到viewmodel类型。其中一个就可以了方法是ShowViewFor处理这一任务,它需要一个视图模型实例和:

  • 查找该视图模型类型的视图。
  • 创建该视图的一个实例。
  • 将该视图实例的DataContext设置为传入的视图模型。
  • 显示视图。

它也确实喜欢开跟踪的意见,显示消息框和对话框等

一堆其他任务的ViewManager可虽然IoC容器通过一个接口,因此它可以被嘲笑弥补单元测试。

我确定有很多现有的框架可以做到这一点,但是和你一样,我想从“根源”学习MVVM。