C#MVVM如何动态创建视图

时间:2017-03-27 17:55:00

标签: c# wpf design-patterns mvvm

我正在尝试创建一个记忆游戏,同时严格遵循MVVM模式来学习如何使用它。现在我遇到了在运行时创建视图的问题。

我创建了以下项目结构:

  • 示范项目
  • -MemoryCardModel30
  • - 卡片
  • ViewModel项目
  • -MainWindowViewModel
  • -CardViewModel
  • 查看项目
  • -CardView
  • StartApplication项目
  • -MainWindowView

依赖关系如下:StartApplication project - >查看项目 - > ViewModel项目 - >示范项目

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

现在我遇到的问题:如何创建CardView实例,如何将他们的DataContexts链接到CardViewModels以及如何在MainWindowView上安排/分配CardViews? ViewModel项目无法创建视图,因为它不依赖于View项目(将创建循环依赖项并打破模式)。如何在遵循MVVM模式的同时解决这个问题?

P.S。:CardViews需要精确定位x和y pos。这将需要一些复杂的计算,这些计算应该与相应的CardViewModel相对应。因此,我认为像网格这样的基本布局是不够的。

2 个答案:

答案 0 :(得分: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创建相应类型的视图实例。视图模型负责向用户呈现哪些对象;观点对他们如何展示负责。您不需要或不需要任何MVVM框架。 WPF的内置DataTemplate功能非常强大。不要相信任何认为你需要任何其他任何东西的人,这个项目的大小只有两个数量级。

答案 1 :(得分:0)

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

我有一个ViewManager类,它将视图类型链接到viewmodel类型。其中一个方法是ShowViewFor来处理这个任务,它需要一个viewmodel实例并且:

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

它还执行许多其他任务,例如跟踪打开视图,显示消息框和对话框等。

ViewManager可通过接口通过IOC容器使用,因此可以模拟单元测试。

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