草庐IT

c# - MVVM 和 View/ViewModel 层次结构

coder 2023-11-09 原文

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

背景

我正在制作的游戏是数独。数独有一个包含 9x9 格子的棋盘。我有三个型号 - Game , Board , 和 Tile .当Game创建后,它会自动创建一个 Board , 当 Board被创建,它创建 81 (9x9) Tiles .

1. 有了层级的 View ,对应的 View 模型是如何创建的?

为了匹配模型的层次结构,我想要一个 View 层次结构( GameView 包含一个 BoardView ,其中包含 81 TileViews )。在 XAML 中,使用用户控件创建这种 View 层次结构非常容易,但我不明白 View 模型是如何创建的。

在我看到的示例中,用户控件的数据上下文通常设置为 View 模型(使用 ViewModelLocator 作为源),它创建 View 模型的新实例。如果您有平面 View ,这似乎很有效,但如果您有层次结构,它似乎也会变得困惑。是否GameView创建 GameViewModel并留给它的 BoardView child 创建一个BoardViewModel ?如果是这样,如何GameViewModelBoardViewModel沟通?可以BoardViewModel将层次结构备份到 GameViewModel ?

2. View 模型如何获取模型数据?

在 iOS 中,我首先使用服务来获取 Game预先填充数据的模型。然后我会创建一个 GameViewController View Controller (负责创建 View )并传递 Game到它。在 MVVM 中,我看到让 View 负责创建自己的 View 模型(理想情况下使用 ViewModelLocator )的值(value),但我不明白该 View 模型如何获取模型。

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

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

不仅GameViewModel需要引用 Game型号,但 BoardViewModel以某种方式创建的(参见问题 1)需要引用 Board属于Game的型号模型。所有 Tiles 也是如此.所有这些信息是如何沿链条传递的?我可以完全在 XAML 中完成这么多繁重的工作,还是必须在代码中进行某种绑定(bind)或其他初始化?

呼!

我感谢您提供的任何建议,即使它不是完整的答案。我也很想找到与我的项目有类似挑战的任何 MVVM 项目示例。万分感谢!

最佳答案

我将首先创建一个类来开始应用程序。通常我把这个类叫做 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 组件。从现在开始,您的 ViewModel 是应用程序 .在使用 MVVM 时记住这一点很重要。您的 View 只是一个用户友好的界面,允许用户与 View 模型进行交互。它们实际上不被视为应用程序代码的一部分。

例如,您的 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 ,您可以指定 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>
    

    它会使用 3x3 UniformGrid 绘制一个板子,每个单元格包含您 Squares 的内容大批。如果您的 BoardViewModel.Squares属性恰好是 TileModel 的数组对象,那么每个网格单元将包含一个 TileModel ,您可以再次使用隐式 DataTemplate告诉 WPF 如何绘制每个 TileModel
    现在至于你如何ViewModel获取其实际数据对象,这取决于您。我更喜欢将所有数据访问抽象为一个类,例如 Repository ,还有我的 ViewModel只需调用类似 SodokuRepository.GetSavedGame(gameId); 的电话.它使应用程序易于测试和维护。

    无论您如何获取数据,请记住 ViewModelModels是你的应用程序,所以他们应该负责获取数据。不要在 View 中这样做.我个人喜欢保留我的 Model用于仅保存数据的普通对象的层,因此只能从我的 ViewModel 执行数据访问操作。

    用于ViewModels之间的通信,我其实有一个article on my blog关于那个。总而言之,使用消息系统,例如 Microsoft Prism 的 EventAggregator或 MVVM Light 的 Messenger .它们就像一种寻呼系统:任何类都可以订阅接收特定类型的消息,任何类都可以广播消息。

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

    我想另一种方法是将处理程序从一个类附加到另一个类,例如调用 CurrentBoardViewModel.ExitCommand += Exit;来自 ShellViewModel ,但我觉得这很麻烦,更喜欢使用消息传递系统。

    无论如何,我希望这能回答您的一些问题,并为您指明正确的方向。祝你的项目好运:)

    关于c# - MVVM 和 View/ViewModel 层次结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12315445/

    有关c# - MVVM 和 View/ViewModel 层次结构的更多相关文章

    1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

      我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

    2. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

      我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

    3. ruby-on-rails - 渲染另一个 Controller 的 View - 2

      我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

    4. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

      我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

    5. ruby-on-rails - 如何在 Rails View 上显示错误消息? - 2

      我是rails的新手,想在form字段上应用验证。myviewsnew.html.erb.....模拟.rbclassSimulation{:in=>1..25,:message=>'Therowmustbebetween1and25'}end模拟Controller.rbclassSimulationsController我想检查模型类中row字段的整数范围,如果不在范围内则返回错误信息。我可以检查上面代码的范围,但无法返回错误消息提前致谢 最佳答案 关键是您使用的是模型表单,一种显示ActiveRecord模型实例属性的表单。c

    6. c# - 如何在 ruby​​ 中调用 C# dll? - 2

      如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

    7. C# 到 Ruby sha1 base64 编码 - 2

      我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

    8. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

      给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

    9. ruby-on-rails - 复数 for fields_for has_many 关联未显示在 View 中 - 2

      目前,Itembelongs_toCompany和has_manyItemVariants。我正在尝试使用嵌套的fields_for通过Item表单添加ItemVariant字段,但是使用:item_variants不显示该表单。只有当我使用单数时才会显示。我检查了我的关联,它们似乎是正确的,这可能与嵌套在公司下的项目有关,还是我遗漏了其他东西?提前致谢。注意:下面的代码片段中省略了不相关的代码。编辑:不知道这是否相关,但我正在使用CanCan进行身份验证。routes.rbresources:companiesdoresources:itemsenditem.rbclassItemi

    10. 基于C#实现简易绘图工具【100010177】 - 2

      C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

    随机推荐