MVVM 模板的好例子 [英] Good examples of MVVM Template

查看:19
本文介绍了MVVM 模板的好例子的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在使用 Microsoft MVVM 模板,发现缺少详细示例令人沮丧.包含的 ContactBook 示例显示了很少的命令处理,我发现的唯一其他示例来自 MSDN 杂志文章,其中的概念相似,但使用的方法略有不同,并且仍然没有任何复杂性.是否有任何不错的 MVVM 示例至少显示基本的 CRUD 操作和对话框/内容切换?

I am currently working with the Microsoft MVVM template and find the lack of detailed examples frustrating. The included ContactBook example shows very little Command handling and the only other example I've found is from an MSDN Magazine article where the concepts are similar but uses a slightly different approach and still lack in any complexity. Are there any decent MVVM examples that at least show basic CRUD operations and dialog/content switching?

大家的建议真的很有用,我会开始整理一份好的资源清单

Everyone's suggestions were really useful and I will start compiling a list of good resources

框架/模板

有用的文章

截屏视频

其他图书馆

  • WPF Disciples' improved Mediator Pattern implementation(I highly recommend this for applications that have more complex navigation)
  • MVVM Light Toolkit Messenger

推荐答案

不幸的是,没有一款出色的 MVVM 示例应用程序可以做所有事情,而且有很多不同的方法来做事情.首先,您可能想要熟悉其中一个应用程序框架(Prism 是一个不错的选择),因为它们为您提供了方便的工具,例如依赖项注入、命令、事件聚合等,可以轻松尝试适合您的不同模式.

Unfortunately there is no one great MVVM example app that does everything, and there are a lot of different approaches to doing things. First, you might want to get familiar with one of the app frameworks out there (Prism is a decent choice), because they provide you with convenient tools like dependency injection, commanding, event aggregation, etc to easily try out different patterns that suit you.

棱镜释放:
http://www.codeplex.com/CompositeWPF

它包括一个相当不错的示例应用程序(股票交易者)以及许多较小的示例和操作方法.至少它很好地展示了人们用来使 MVVM 实际工作的几种常见子模式.我相信他们有 CRUD 和对话的例子.

It includes a pretty decent example app (the stock trader) along with a lot of smaller examples and how to's. At the very least it's a good demonstration of several common sub-patterns people use to make MVVM actually work. They have examples for both CRUD and dialogs, I believe.

Prism 不一定适用于每个项目,但熟悉它是件好事.

Prism isn't necessarily for every project, but it's a good thing to get familiar with.

CRUD:这部分非常简单,WPF 双向绑定使编辑大多数数据变得非常容易.真正的技巧是提供一个模型,使设置 UI 变得容易.至少您要确保您的 ViewModel(或业务对象)实现 INotifyPropertyChanged 以支持绑定,并且您可以将属性直接绑定到 UI 控件,但您可能还想实现 IDataErrorInfo 用于验证.通常,如果您使用某种 ORM 解决方案,则设置 CRUD 非常简单.

CRUD: This part is pretty easy, WPF two way bindings make it really easy to edit most data. The real trick is to provide a model that makes it easy to set up the UI. At the very least you want to make sure that your ViewModel (or business object) implements INotifyPropertyChanged to support binding and you can bind properties straight to UI controls, but you may also want to implement IDataErrorInfo for validation. Typically, if you use some sort of an ORM solution setting up CRUD is a snap.

本文演示了简单的 crud 操作:http://dotnetslackers.com/articles/wpf/WPFDataBindingWithLINQ.aspx

This article demonstrates simple crud operations: http://dotnetslackers.com/articles/wpf/WPFDataBindingWithLINQ.aspx

它建立在 LinqToSql 之上,但这与示例无关 - 重要的是您的业务对象实现了 INotifyPropertyChanged(由 LinqToSql 生成的类执行).MVVM 不是那个例子的重点,但我认为在这种情况下并不重要.

It is built on LinqToSql, but that is irrelevant to the example - all that is important is that your business objects implement INotifyPropertyChanged (which classes generated by LinqToSql do). MVVM is not the point of that example, but I don't think it matters in this case.

本文演示数据验证
http:///blogs.msdn.com/wpfsdk/archive/2007/10/02/data-validation-in-3-5.aspx

同样,大多数 ORM 解决方案生成的类已经实现了 IDataErrorInfo,并且通常提供一种机制来轻松添加自定义验证规则.

Again, most ORM solutions generate classes that already implement IDataErrorInfo and typically provide a mechanism to make it easy to add custom validation rules.

大多数情况下,您可以将某个 ORM 创建的对象(模型)包装在一个 ViewModel 中,该模型包含它和保存/删除命令 - 您已经准备好将 UI 直接绑定到模型的属性.

Most of the time you can take an object(model) created by some ORM and wrap it in a ViewModel that holds it and commands for save/delete - and you're ready to bind UI straight to the model's properties.

视图看起来像这样(ViewModel 有一个属性 Item 保存模型,就像在 ORM 中创建的一个类):

The view would look like something like this (ViewModel has a property Item that holds the model, like a class created in the ORM):

<StackPanel>
   <StackPanel DataContext=Item>
      <TextBox Text="{Binding FirstName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
      <TextBox Text="{Binding LastName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
   </StackPanel>
   <Button Command="{Binding SaveCommand}" />
   <Button Command="{Binding CancelCommand}" />
</StackPanel>

对话框:对话框和 MVVM 有点棘手.我更喜欢在对话框中使用 Mediator 方法的风格,您可以在这个 StackOverflow 问题中阅读更多关于它的信息:
WPF MVVM 对话框示例

Dialogs: Dialogs and MVVM are a bit tricky. I prefer to use a flavor of the Mediator approach with dialogs, you can read a little more about it in this StackOverflow question:
WPF MVVM dialog example

我常用的方法,不是很经典的MVVM,可以总结如下:

My usual approach, which is not quite classic MVVM, can be summarized as follows:

对话框 ViewModel 的基类,用于公开提交和取消操作的命令、让视图知道对话框已准备好关闭的事件,以及您在所有对话框中需要的任何其他内容.

A base class for a dialog ViewModel that exposes commands for commit and cancel actions, an event to lets the view know that a dialog is ready to be closed, and whatever else you will need in all of your dialogs.

对话框的通用视图 - 这可以是一个窗口,也可以是一个自定义的模态"覆盖类型控件.从本质上讲,它是一个内容展示器,我们将视图模型转储到其中,它处理关闭窗口的连接 - 例如,在数据上下文更改时,您可以检查新的 ViewModel 是否从您的基类继承,如果是,订阅相关的关闭事件(处理程序将分配对话结果).如果您提供替代的通用关闭功能(例如 X 按钮),您应该确保也在 ViewModel 上运行相关的关闭命令.

A generic view for your dialog - this can be a window, or a custom "modal" overlay type control. At its heart it is a content presenter that we dump the viewmodel into, and it handles the wiring for closing the window - for example on data context change you can check if the new ViewModel is inherited from your base class, and if it is, subscribe to the relevant close event (the handler will assign the dialog result). If you provide alternative universal close functionality (the X button, for instance), you should make sure to run the relevant close command on the ViewModel as well.

在需要为 ViewModel 提供数据模板的地方,它们可以非常简单,特别是因为您可能将每个对话框的视图封装在单独的控件中.ViewModel 的默认数据模板将如下所示:

Somewhere you need to provide data templates for your ViewModels, they can be very simple especially since you probably have a view for each dialog encapsulated in a separate control. The default data template for a ViewModel would then look something like this:

<DataTemplate DataType="{x:Type vmodels:AddressEditViewModel}">
   <views:AddressEditView DataContext="{Binding}" />
</DataTemplate>

对话框视图需要访问这些,否则它不知道如何显示 ViewModel,除了共享对话框 UI 之外,它的内容基本上是这样的:

The dialog view needs to have access to these, because otherwise it won't know how to show the ViewModel, aside from the shared dialog UI its contents are basically this:

<ContentControl Content="{Binding}" />

隐式数据模板会将视图映射到模型,但谁启动它?

The implicit data template will map the view to the model, but who launches it?

这是不那么 mvvm 的部分.一种方法是使用全局事件.我认为更好的做法是使用通过依赖注入提供的事件聚合器类型设置——这样事件对容器是全局的,而不是整个应用程序.Prism 在容器语义和依赖注入方面使用了 unity 框架,总体来说我还是挺喜欢 Unity 的.

This is the not-so-mvvm part. One way to do it is to use a global event. What I think is a better thing to do is to use an event aggregator type setup, provided through dependency injection - this way the event is global to a container, not the whole app. Prism uses the unity framework for container semantics and dependency injection, and overall I like Unity quite a bit.

通常,根窗口订阅此事件是有意义的 - 它可以打开对话框并将其数据上下文设置为通过引发的事件传入的 ViewModel.

Usually, it makes sense for the root window to subscribe to this event - it can open the dialog and set its data context to the ViewModel that gets passed in with a raised event.

以这种方式进行设置后,ViewModel 可以要求应用程序打开一个对话框并在那里响应用户操作,而无需了解有关 UI 的任何信息,因此在大多数情况下,MVVM 特性仍然完整.

Setting this up in this way lets ViewModels ask the application to open a dialog and respond to user actions there without knowing anything about the UI so for the most part the MVVM-ness remains complete.

然而,有时用户界面必须提高对话框,这会使事情变得有点棘手.例如,考虑对话框位置是否取决于打开它的按钮的位置.在这种情况下,当您请求打开对话框时,您需要一些特定于 UI 的信息.我通常会创建一个单独的类来保存 ViewModel 和一些相关的 UI 信息.不幸的是,一些耦合似乎不可避免.

There are times, however, where the UI has to raise the dialogs, which can make things a bit trickier. Consider for example, if the dialog position depends on the location of the button that opens it. In this case you need to have some UI specific info when you request a dialog open. I generally create a separate class that holds a ViewModel and some relevant UI info. Unfortunately some coupling seems unavoidable there.

引发需要元素位置数据的对话框的按钮处理程序的伪代码:

Pseudo code of a button handler that raises a dialog which needs element position data:

ButtonClickHandler(sender, args){
    var vm = DataContext as ISomeDialogProvider; // check for null
    var ui_vm = new ViewModelContainer();
    // assign margin, width, or anything else that your custom dialog might require
    ...
    ui_vm.ViewModel = vm.SomeDialogViewModel; // or .GetSomeDialogViewModel()
    // raise the dialog show event
}

对话框视图将绑定到位置数据,并将包含的 ViewModel 传递给内部 ContentControl.ViewModel 本身仍然对 UI 一无所知.

The dialog view will bind to position data, and pass the contained ViewModel to the inner ContentControl. The ViewModel itself still doesn't know anything about the UI.

一般来说,我不使用 ShowDialog() 方法的 DialogResult 返回属性或期望线程阻塞直到对话框关闭.非标准模态对话框并不总是这样工作,在复合环境中,您通常不希望事件处理程序以任何方式阻塞.我更喜欢让 ViewModel 处理这个问题——ViewModel 的创建者可以订阅它的相关事件,设置提交/取消方法等,所以不需要依赖这个 UI 机制.

In general I don't make use of the DialogResult return property of the ShowDialog() method or expect the thread to block until the dialog is closed. A non-standard modal dialog doesn't always work like that, and in a composite environment you often don't really want an event handler to block like that anyhow. I prefer to let the ViewModels deal with this - the creator of a ViewModel can subscribe to its relevant events, set commit/cancel methods, etc, so there is no need to rely on this UI mechanism.

所以,而不是这个流程:

So instead of this flow:

// in code behind
var result = somedialog.ShowDialog();
if (result == ...

我使用:

// in view model
var vm = new SomeDialogViewModel(); // child view model
vm.CommitAction = delegate { this.DoSomething(vm); } // what happens on commit 
vm.CancelAction = delegate { this.DoNothing(vm); } // what happens on cancel/close (optional)
// raise dialog request event on the container

我更喜欢这种方式,因为我的大多数对话框都是非阻塞的伪模态控件,而且这样做似乎比解决它更简单.也易于单元测试.

I prefer it this way because most of my dialogs are non-blocking pseudo-modal controls and doing it this way seems more straightforward than working around it. Easy to unit test as well.

这篇关于MVVM 模板的好例子的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆