|
查看: 3216|回复: 7
|
[讨论][WPF][C#] MVVM 程式设计入门小教程。
[复制链接]
|
|
|
本帖最后由 宅男-兜着走 于 6-8-2010 07:04 PM 编辑
Model View ViewModel
From Wikipedia, the free encyclopedia
The Model View ViewModel (MVVM) is an architectural pattern used in software engineering that originated from Microsoft as a specialization of the Presentation Model design pattern introduced by Martin Fowler.[1] Largely based on the Model-view-controller pattern (MVC), MVVM is targeted at modern UI development platforms (Windows Presentation Foundation and Silverlight) in which there is a User Experience (UX) developer who has different requirements than a more “traditional” developer (i.e. oriented toward business logic and back end development). The View-Model of MVVM is “basically a value converter on steroids”[2] meaning that the View-Model is responsible for exposing the data objects from the Model in such a way that those objects are easily managed and consumed. In this respect, the View-Model is more Model than View, and handles most if not all of the View’s display logic (though the demarcation between what functions are handled by which layer is a subject of ongoing discussion[3] and exploration).
MVVM was designed to make use of specific functions in WPF to better facilitate the separation of View layer development from the rest of the pattern by removing virtually all “code behind” from the View layer.[4] Instead of requiring Interactive Designers to write View code, they can use the native WPF markup language XAML and create bindings to the ViewModel, which is written and maintained by application developers. This separation of roles allows Interactive Designers to focus on UX needs rather than programming or business logic, allowing for the layers of an application to be developed in multiple work streams.
原文来自: http://xiaoyinnet.blog.51cto.com/909896/196071
微软的WPF带来了新的技术体验,如Sliverlight、音频、视频、3D、动画……,这导致了软件UI层更加细节化、可定制化。同时,在技术层面,WPF也带来了诸如Binding、Dependency Property、Routed Events、Command、DataTemplate、ControlTemplate等新特性。MVVM(Model-View-ViewModel)框架的由来便是MVP(Model-View-Presenter)模式与WPF结合的应用方式时发展演变过来的一种新型架构框架。它立足于原有MVP框架并且把WPF的新特性揉合进去,以应对客户日益复杂的需求变化。
WPF的数据绑定与Presentation Model相集合是非常好的做法,使得开发人员可以将View和逻辑分离出来,但这种数据绑定技术非常简单实用,也是WPF所特有的,所以我们又称之为Model-View-ViewModel (MVVM)。这种模式跟经典的MVP(Model-View-Presenter)模式很相似,除了你需要一个为View量身定制的model,这个model就是ViewModel。ViewModel包含所有由UI特定的接口和属性,并由一个ViewModel 的视图的绑定属性,并可获得二者之间的松散耦合,所以需要在ViewModel 直接更新视图中编写相应代码。数据绑定系统还支持提供了标准化的方式传输到视图的验证错误的输入的验证。
MVVM=
Model , View , ViewModel
目前为止, 使用MVVM的人都明白 Model, View 是什么。 但是ViewModel 目前还真的是个谜,
20 多个开发员,每个都可能有不同的看法与使用的方法。
那么这里分享点我的小小小小小小心得。也建议学WPF的确实不使用 Prism, MEF, MVVM Light 的人也可以参考下他们的做法。
由于有点大工程, 所以这个只有Save Command。
= =
至于怎么连接资料库,方法也无差别, 希望大家勤力多多翻书, 这个只是针对 MVVM。
Note: 教程内用到 AttachedCommandBehavior 的 Library, 可以到这里下载,里面有 这个Project的最终样貌, AttachedCommandBehavior 的 Source Code
如果没有的话:-
http://cid-478d11236bf4657f.offi ... aspx/MVVM%20Article
更多关于 AttachedCommandBehavior v 2 :
http://marlongrech.wordpress.com ... ehavior-v2-aka-acb/
第一步: 首先打开个新专题。 然后输入你喜欢的名字。
- 我这里有lib , model, viewModel Solution Folder
- lib 我会放Library进去, AttachCommandBehavior.dll。
- model 会开个新的Class,Person.cs
- ViewModel 资料的 Collection, CRUD Method, Command。
请参考图片, Right Click 滑鼠然后 Add Folder。

第二步: Expand Refrence 的Folder, 然后 right click add reference -> Browse -> 找你Project 里面的(AttachCommandBehavior.dll)

如果做到了, 就会看到AttachCommandBehavior.dll 在你的 Reference List 里面了。

第三步: Right Click 刚才 Add 的 Folder, 然后 Add Class 进去,
- Model/ Person.cs
- ViewModel/ PersonViewModel.cs

第四步: 做个UI 出来, 我做成这样, 你喜欢怎样就怎样。
 - <Window x:Class="SampleProject.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Sample mvvm program" Height="400" Width="550" ResizeMode="NoResize"> <Grid x:Name="root"> <Grid.RowDefinitions> <RowDefinition Height="169*" /> <RowDefinition Height="193*" /> </Grid.RowDefinitions> <Button Height="23" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="54" Margin="0,0,12,8">Save</Button> <Label Margin="12,12,0,0" Height="28" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120">First Name</Label> <Label HorizontalAlignment="Left" Margin="12,46,0,0" Name="label2" Width="120" Height="28" VerticalAlignment="Top">Last Name</Label> <Label Margin="12,80,0,61" HorizontalAlignment="Left" Width="120">Age</Label> <Label Height="28" Margin="12,0,0,27" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="120">Telephone</Label> <TextBox Height="23" Margin="138,17,185,0" VerticalAlignment="Top" /> <TextBox Height="23" Margin="138,49,185,0" VerticalAlignment="Top" /> <TextBox Margin="138,82,185,64" /> <TextBox Height="23" Margin="138,0,185,29" VerticalAlignment="Bottom" /> <ListView Grid.Row="1"> <ListView.View> <GridView> <GridViewColumn Header="First Name" Width="100"/> <GridViewColumn Header="Last Name" Width="100"/> <GridViewColumn Header="Age" Width="80"/> <GridViewColumn Header="Tel" Width="200"/> </GridView> </ListView.View> </ListView> </Grid></Window>
复制代码 第五步: 打开你的 Window1.xaml.cs (Code Behind) 然后改点东西
-不需要太大的变动, 只要把 ViewModel Assign 给你的 DataContext 就好了。
-Code Behind 接下来也不会有什么逻辑, Code 的添加。
-可以放着不管丢去一边。
-View 可以说是暂时完成了。- using System.Windows;using SampleProject.ViewModel;
- namespace SampleProject{ /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { public Window1() { InitializeComponent(); DataContext = new PersonViewModel(); } }}
复制代码 第六步: 完成 Person.cs
-没什么特别, 一堆 Getter Setter, 跟一个Contructor。 Person.cs 就完成了。
-有必要的话, Validation 也能在这里做, 也可以Implement IDataErrorInfo, 看个人喜欢。
-那么可以关起来了, 也不会再打开了。- namespace SampleProject.Model{ public class Person { public string FirstName { get; set; } public string LastName {get;set;} public int Age { get; set; } public string TelPhone { get; set; }
- public Person(string fName, string lName, int age, string tel) { FirstName = fName; LastName = lName; Age = age; TelPhone = tel; }
- }}
复制代码 |
|
|
|
|
|
|
|
|
|
|

楼主 |
发表于 6-8-2010 07:03 PM
|
显示全部楼层
第七步: 完成PersonViewModel
-这里有两点需要注意下。
1. 记得 Implement INotifyPropertyChanged
2. 记得引用 AttachedCommandBehavior
- INotifyPropertyChanged 会通知你的UI, 当你的资料更改, 或Update 的话。
- AttachedCommandBehavior 下有 MVVM Template 的 DelegateCommand, DelegateCommand<T>
- 所以 ViewModel 有个Getter 可以读到资料, Setter 可以 Update 资料。
- 基本上就没什么特别的了, 可以关掉了。
- using SampleProject.Model;
- using System.ComponentModel;
- using System.Collections.ObjectModel;
- using AttachedCommandBehavior;
- using System.Windows.Input;
- using System;
- namespace SampleProject.ViewModel
- {
- public class PersonViewModel : INotifyPropertyChanged
- {
- #region fields
- ObservableCollection<Person> personCollection;
- Person person;
- DelegateCommand savePerson;
- #endregion
- #region properties
-
- // ListView Binding 用的。
- public ObservableCollection<Person> PersonCollection
- {
- get { return personCollection; }
- private set
- {
- personCollection = value;
- OnPropertyChanged("PersonCollection");
- }
- }
-
- // Form Binding 用。
- public Person Person {
- get
- {
- return person;
- }
- set
- {
- person = value;
- OnPropertyChanged("Person");
- }
- }
- #endregion
- #region Command
- public ICommand SavePerson
- {
- get
- {
- if(savePerson==null) savePerson= new DelegateCommand(new Action(SavePersonExecuted), new Func<bool>(SaveCanExecute));
- return savePerson;
- }
- }
- #endregion
- public PersonViewModel()
- {
- // 因为我没 资料库, 所以我就用这种愚蠢的添加方式 ==
- Person = new Person("", "" , 0, "");
- PersonCollection = new ObservableCollection<Person>();
- PersonCollection.Add(new Person("Junior", "Tee", 23, "012-222112211"));
- PersonCollection.Add(new Person("Johnson", "Marcus", 20, "012-132434331"));
- PersonCollection.Add(new Person("Jessie", "Tee", 20, "012-343443341"));
- }
- bool SaveCanExecute()
- {
- // ICommand 判断可执行, 还是不可以执行。
- return (!string.IsNullOrEmpty(Person.FirstName) && !string.IsNullOrEmpty(Person.LastName) && Person.Age != 0 && !string.IsNullOrEmpty(Person.TelPhone));
- }
- void SavePersonExecuted()
- {
- // 可执行的话, Save 进 People Collection
- PersonCollection.Add(Person);
- Person = new Person("", "", 0, "");
- }
- #region INotifyPropertyChanged Members
- public event PropertyChangedEventHandler PropertyChanged = delegate { };
- public void OnPropertyChanged(string propertyName)
- {
- PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
- }
- #endregion
- }
- }
复制代码 第八步: Binding 资料进 View
- 先来个示范, 我们把 PeopleCollection Bind 进 ListView 内。
- 然后 F5 run 看看, 如果看到演示图-1这样的, 证明你已经成功了。
- <ListView Grid.Row="2" ItemsSource="{Binding PersonCollection}">
- <ListView.View>
- <GridView>
- <GridViewColumn Header="First Name" Width="100" DisplayMemberBinding="{Binding FirstName}"/>
- <GridViewColumn Header="Last Name" Width="100" DisplayMemberBinding="{Binding LastName}"/>
- <GridViewColumn Header="Age" Width="80" DisplayMemberBinding="{Binding Age}"/>
- <GridViewColumn Header="Tel" Width="200" DisplayMemberBinding="{Binding TelPhone}"/>
- </GridView>
- </ListView.View>
- </ListView>
复制代码演示图 1

第九步: 完成添加资料的部分
-Binding 个别的Properties 给予各自的TextBox, UpdateSource 了就Trigger。
-同样的道理, Button Save 也给予添加 ViewModel 内的 Save Command。
-好了就 F5, 如果全部资料没填的话, Save Button 会Disable(演示图2)
-如果全部资料填满了, Save Button 就Enable了, SaveExecuted 就会启动, 加进你的PeopleCollection。
- <TextBox Height="23" Margin="138,17,185,0" VerticalAlignment="Top" Text="{Binding Path=Person.FirstName, UpdateSourceTrigger=PropertyChanged}"/>
- <TextBox Height="23" Margin="138,49,185,0" VerticalAlignment="Top" Text="{Binding Path=Person.LastName, UpdateSourceTrigger=PropertyChanged}"/>
- <TextBox Margin="138,82,185,64" Text="{Binding Path=Person.Age,UpdateSourceTrigger=PropertyChanged}"/>
- <TextBox Height="23" Margin="138,0,185,29" VerticalAlignment="Bottom" Text="{Binding Path=Person.TelPhone ,UpdateSourceTrigger=PropertyChanged}" />
- <Button Height="23" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="54" Margin="0,0,12,8"
- Command="{Binding SavePerson}"
- >Save</Button>
复制代码演示图2

演示图3

第十步: 引用 AttachedCommandBehavior v2 Library
- Command 这个Property 只有少数部分的 Control 能支持。你不可能每次都用 Button 了事。
- AttachedCommandBehavior 支持了大多数(可能所有) 的 Event, 本人还没测试完。 目前基本上该有的都有了。
- (例子) ListView 的 SelectionChanged , ListView是不会支持 Command的, 所以你只能用Custom 的 Command。
- 接下来引用 : xmlns:cmd="clr-namespace:AttachedCommandBehavior;assembly=AttachedCommandBehavior" 如下面的 Sample Code
- <Window x:Class="SampleProject.Window1"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:cmd="clr-namespace:AttachedCommandBehavior;assembly=AttachedCommandBehavior"
- Title="Sample mvvm program" Height="400" Width="550" ResizeMode="NoResize">
复制代码 第十一步: 使用 AttachedCommandBehavior。
- 我们拿会刚才的 Button 例子 Save Button。
- 删除掉 Command, 然后就使用之前的 cmd: (第十步引用的)
- F5 run project, 还是一样会的到之前的效果。
- 差别就在于, 不会显示出已经 Disable了, 但是资料没天满, 还是一样, 不能添加资料。
- <Button Height="23" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="54" Margin="0,0,12,8"
- cmd:CommandBehavior.Event="Click"
- cmd:CommandBehavior.Command="{Binding SavePerson}"
- >Save</Button>
复制代码 MVVM Prism视频教程:
Mike Taulty- 推荐他的教程, 我非常喜欢他的讲解, 很清楚, 又很简单。
http://channel9.msdn.com/posts/m ... Code-Towards-Unity/
Brob- 这个也还好, 只是影片很蒙, 看不清楚, 只能听到他的声音。
http://channel9.msdn.com/posts/a ... -shell-and-modules/
Erikmok - 这个也不错, 但是解释比较敷衍,视频很清下。
http://development-guides.silver ... o/Silverlight-Prism
Composite Application Guidance from microsoft patterns& practices.Prism 下载
http://compositewpf.codeplex.com/
没什么特别的东西, 5 个Library , + 几个Sample, 跟一本Documentation, 不下载你就不能使用 MVVM Prism Framework
OK, 就此停笔, 希望大家能多多指教以及讨论。 |
|
|
|
|
|
|
|
|
|
|
发表于 10-8-2010 08:31 AM
|
显示全部楼层
|
谢谢分享。非常详细。MVVM 是一个值得鼓励使用的Architecture,不过个人觉得只是Commanding的Implementation会让人混淆。ICommand, RoutedCommand, RoutedUICommand, 还有special implementation 好像这个AttachedCommandBehavior, 还有MS Magazine MVVM http://msdn.microsoft.com/en-us/magazine/dd419663.aspx 的RelayCommand, 如果没有记错MVVM Light 自己的Implementation 还真的需要些时间学习才能了解个别的不同。不过到头来还是主要的执行Method,和可执行与否的Predicate. 不管怎样,Commanding 还是非常重要的,尤其是对于Undo/Redo的Implementation. MVVM的朋友怎么看? |
|
|
|
|
|
|
|
|
|
|

楼主 |
发表于 11-8-2010 01:54 AM
|
显示全部楼层
谢谢分享。非常详细。MVVM 是一个值得鼓励使用的Architecture,不过个人觉得只是Commanding的Implementatio ...
GNey 发表于 10-8-2010 08:31 AM 
你是第一个回我贴的人, 高兴下, 终于给人看到了。
早期没用 Commanding 的, 因为麻烦, 看不懂。
索性使用 IView 这个接口 链接去 ViewModel。
后期发现这样的写法耦合度有点高。所以才学起了 Commanding,
使用 CAL Guidance Prism 来开发。
我采用的应该是比较简单的方式了==,
也鼓励各位 WPF 的KAKI 写MVVM。 |
|
|
|
|
|
|
|
|
|
|
发表于 11-8-2010 08:40 AM
|
显示全部楼层
|
注意到你是使用Prism Architecture. 它与基本的MVVM有什么不同呢?要详细读读你给的Link了。其实我一向对使用Third Party External Library有些保留,除非是不得已。不过通常一些小的Dialog我还是使用传统的方式把Object Pass in 去Dialog的DataContext,Dialog一些一次性的Button不一定使用Commanding. |
|
|
|
|
|
|
|
|
|
|

楼主 |
发表于 11-8-2010 12:33 PM
|
显示全部楼层
注意到你是使用Prism Architecture. 它与基本的MVVM有什么不同呢?要详细读读你给的Link了。其实我一向对使用 ...
GNey 发表于 11-8-2010 08:40 AM 
Prism 的最基本上要有 Shell, 然后就多过一个的Module, Service。
Shell 会Load Module 内的View,
通过 Unity 来Resolve View 所需要的 Service, ViewModel
等等,
没错, Constructor 需求的东西, 你只要一个简单的一句, Unity.Resolve<T>(); 就能Settle掉了 Construc 所需要的复杂要求。
其实如果使用 Prism, 有几个很好很方便的地方。
1. Unity。
2. EventAggregator。
3. Region 的控制。
就因为Unity。 我就觉得Prism很不错咯,
Prism 的目的应该只是帮我们简单化 MVVM
至于运作方面,我就不说了, Documentation 内写到很详细,
最重要的是!!入门好像没很难==
关于Dialog 我遇到的问题跟你一样。解决方法也差不多。
看了很不爽。正在想解决方法。 |
|
|
|
|
|
|
|
|
|
|
发表于 12-8-2010 08:05 AM
|
显示全部楼层
|
看了看你所给的Link发现Prism 的AttachCommand非常方便。有没有Article 和documentation? 因为看不习惯看Video的? |
|
|
|
|
|
|
|
|
|
|

楼主 |
发表于 12-8-2010 11:34 AM
|
显示全部楼层
|
|
|
|
|
|
|
|
| |
本周最热论坛帖子
|