【问题标题】:C# MVVM How to dynamically create ViewsC# MVVM 如何动态创建视图
【发布时间】:2017-03-27 17:55:00
【问题描述】:

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

我创建了以下项目结构:

  • 模型项目
  • -MemoryCardModel30
  • -卡片
  • ViewModel 项目
  • -MainWindowViewModel
  • -CardViewModel
  • 查看项目
  • -卡片视图
  • StartApplication 项目
  • -主窗口视图

依赖如下:StartApplication项目->View项目->ViewModel项目->Model项目

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

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

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

【问题讨论】:

  • 您使用的是框架,您的方法是“模型优先”还是“视图优先”?此外,您也许可以让 WrapPanel 做您想做的事...msdn.microsoft.com/en-us/library/…
  • 这就是为什么我们在我的工作中使用 MVPVM 的原因:将模型链接到视图然后显示这些视图并不是一项非常适合 MVVM 的工作,但是是Presenter的工作
  • @BerinLoritsch 不,我目前没有使用任何框架。我想从一开始就学习这种模式。我只熟悉 EntityFramework 上下文中的“模型优先”,但不熟悉 MVVM。但是,在这种情况下,我从 ViewModel 开始,然后是模型,最后是视图。
  • 通常这种目标是通过在主视图模型上公开一组卡片视图模型、在视图端绑定ItemsControl(或一些派生词)并使用DataTemplate来实现的(通过ItemsControl.ItemTemplate设置)来定义单张卡片的外观。要以非平凡的方式安排项目,您可以将 ItemsControl.ItemsPanel 设置为您喜欢的 Panel - 无论是 WrapPanelGridCanvas 还是任何其他 Panel(在非常复杂的场景中,通常更容易编写自己的面板,而不是尝试让库存面板来做您的投标)。

标签: c# wpf design-patterns mvvm


【解决方案1】:

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 中执行此操作的规范方法。使用DataTemplates 创建适当类型的视图实例。视图模型负责应该向用户呈现哪些对象;视图负责它们的显示方式。您不需要或不需要任何 MVVM 框架。 WPF 内置的DataTemplate 功能非常强大。不要相信任何认为你在这个规模的两个数量级范围内的项目需要其他任何东西的人。

【讨论】:

  • 我不会把坐标放到VM层,而是把它们抽象出来,通过转换器在视图层解析。
  • @Rekshino 这是正确(或至少“正确”)的方法,我会适当地更新我的答案。
【解决方案2】:

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


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

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

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

ViewManager 可通过 IOC 容器通过接口获得,因此可以模拟它以进行单元测试。

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

【讨论】:

    猜你喜欢
    • 2022-11-28
    • 1970-01-01
    • 1970-01-01
    • 2015-05-02
    • 1970-01-01
    • 2012-05-18
    • 1970-01-01
    • 2021-10-06
    • 1970-01-01
    相关资源
    最近更新 更多