Facebook Sharer
选择您要替换的背景颜色:
【农历新年】背景图片:
个性化设定
 注册  找回密码
查看: 5865|回复: 16
打印 上一主题 下一主题

[讨论][WPF][C#] [原创] MVVM 程式设计入门小教程。

[复制链接]

46

主题

6

好友

6456

积分

百变名嘴

Rank: 13Rank: 13Rank: 13Rank: 13

跳转到指定楼层
1#
发表于 2010-8-6 06:47 PM |只看该作者 |倒序浏览
本帖最后由 宅男-兜着走 于 2010-8-6 06:57 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 出来, 我做成这样, 你喜欢怎样就怎样。

  1. <Window x:Class="SampleProject.Window1"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     Title="Sample mvvm program" Height="400" Width="550" ResizeMode="NoResize">
  5.     <Grid x:Name="root">
  6.         <Grid.RowDefinitions>
  7.             <RowDefinition Height="169*" />
  8.             <RowDefinition Height="193*" />
  9.         </Grid.RowDefinitions>
  10.         <Button Height="23" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="54" Margin="0,0,12,8">Save</Button>
  11.         <Label Margin="12,12,0,0" Height="28" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120">First Name</Label>
  12.         <Label HorizontalAlignment="Left" Margin="12,46,0,0" Name="label2" Width="120" Height="28" VerticalAlignment="Top">Last Name</Label>
  13.         <Label Margin="12,80,0,61" HorizontalAlignment="Left" Width="120">Age</Label>
  14.         <Label Height="28" Margin="12,0,0,27" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="120">Telephone</Label>
  15.         <TextBox Height="23" Margin="138,17,185,0" VerticalAlignment="Top" />
  16.         <TextBox Height="23" Margin="138,49,185,0" VerticalAlignment="Top" />
  17.         <TextBox Margin="138,82,185,64" />
  18.         <TextBox Height="23" Margin="138,0,185,29" VerticalAlignment="Bottom" />
  19.         <ListView Grid.Row="1">
  20.             <ListView.View>
  21.                 <GridView>
  22.                     <GridViewColumn Header="First Name" Width="100"/>
  23.                     <GridViewColumn Header="Last Name" Width="100"/>
  24.                     <GridViewColumn Header="Age" Width="80"/>
  25.                     <GridViewColumn Header="Tel" Width="200"/>
  26.                 </GridView>
  27.             </ListView.View>
  28.         </ListView>
  29.     </Grid>
  30. </Window>
复制代码
第五步: 打开你的 Window1.xaml.cs (Code Behind) 然后改点东西
-不需要太大的变动, 只要把 ViewModel Assign 给你的 DataContext 就好了。
-Code Behind 接下来也不会有什么逻辑, Code 的添加。
-可以放着不管丢去一边。
-View 可以说是暂时完成了。

  1. using System.Windows;
  2. using SampleProject.ViewModel;

  3. namespace SampleProject
  4. {
  5.     /// <summary>
  6.     /// Interaction logic for Window1.xaml
  7.     /// </summary>
  8.     public partial class Window1 : Window
  9.     {
  10.         public Window1()
  11.         {
  12.             InitializeComponent();
  13.             DataContext = new PersonViewModel();
  14.         }
  15.     }
  16. }
复制代码
第六步: 完成 Person.cs
-没什么特别, 一堆 Getter Setter, 跟一个Contructor。 Person.cs 就完成了。
-有必要的话, Validation 也能在这里做, 也可以Implement IDataErrorInfo, 看个人喜欢。
-那么可以关起来了, 也不会再打开了。

  1. namespace SampleProject.Model
  2. {
  3.     public class Person
  4.     {
  5.         public string FirstName { get; set; }
  6.         public string LastName {get;set;}
  7.         public int Age { get; set; }
  8.         public string TelPhone { get; set; }

  9.         public Person(string fName, string lName, int age, string tel)
  10.         {
  11.             FirstName = fName;
  12.             LastName = lName;
  13.             Age = age;
  14.             TelPhone = tel;
  15.         }

  16.     }
  17. }
复制代码




已有 1 人评分积分 论坛通知 收起 理由
goodday + 2 + 1 感谢分享

总评分: 积分 + 2  论坛通知 + 1   查看全部评分

收藏收藏0

46

主题

6

好友

6456

积分

百变名嘴

Rank: 13Rank: 13Rank: 13Rank: 13

2#
发表于 2010-8-6 06:47 PM |只看该作者
本帖最后由 宅男-兜着走 于 2010-8-6 06:54 PM 编辑

第七步: 完成PersonViewModel
-这里有两点需要注意下。
   1. 记得 Implement INotifyPropertyChanged
   2. 记得引用 AttachedCommandBehavior
- INotifyPropertyChanged 会通知你的UI, 当你的资料更改, 或Update 的话。
- AttachedCommandBehavior 下有 MVVM Template 的 DelegateCommand, DelegateCommand<T>
- 所以 ViewModel 有个Getter 可以读到资料, Setter 可以 Update 资料。
- 基本上就没什么特别的了, 可以关掉了。

  1. using SampleProject.Model;
  2. using System.ComponentModel;
  3. using System.Collections.ObjectModel;
  4. using AttachedCommandBehavior;
  5. using System.Windows.Input;
  6. using System;

  7. namespace SampleProject.ViewModel
  8. {
  9.     public class PersonViewModel : INotifyPropertyChanged
  10.     {
  11.         #region fields
  12.         ObservableCollection<Person> personCollection;
  13.         Person person;
  14.         DelegateCommand savePerson;
  15.         #endregion

  16.         #region properties
  17.          
  18.         // ListView Binding 用的。
  19.         public ObservableCollection<Person> PersonCollection
  20.         {
  21.             get { return personCollection; }

  22.             private set
  23.             {
  24.                 personCollection = value;
  25.                 OnPropertyChanged("PersonCollection");
  26.             }
  27.         }
  28.          
  29.         // Form Binding 用。
  30.         public Person Person {
  31.             get
  32.             {
  33.                 return person;
  34.             }
  35.             set
  36.             {
  37.                 person = value;
  38.                 OnPropertyChanged("Person");
  39.             }
  40.         }

  41.         #endregion

  42.         #region Command

  43.         public ICommand SavePerson
  44.         {
  45.             get
  46.             {
  47.                 if(savePerson==null) savePerson= new DelegateCommand(new Action(SavePersonExecuted), new Func<bool>(SaveCanExecute));
  48.                 return savePerson;
  49.             }
  50.         }
  51.         #endregion

  52.         public PersonViewModel()
  53.         {
  54. // 因为我没 资料库, 所以我就用这种愚蠢的添加方式 ==
  55.             Person = new Person("", "" , 0, "");
  56.             PersonCollection = new ObservableCollection<Person>();
  57.             PersonCollection.Add(new Person("Junior", "Tee", 23, "012-222112211"));
  58.             PersonCollection.Add(new Person("Johnson", "Marcus", 20, "012-132434331"));
  59.             PersonCollection.Add(new Person("Jessie", "Tee", 20, "012-343443341"));
  60.         }

  61.         bool SaveCanExecute()
  62.         {
  63. // ICommand 判断可执行, 还是不可以执行。
  64.             return (!string.IsNullOrEmpty(Person.FirstName) && !string.IsNullOrEmpty(Person.LastName) && Person.Age != 0 && !string.IsNullOrEmpty(Person.TelPhone));
  65.         }

  66.         void SavePersonExecuted()
  67.         {
  68. // 可执行的话, Save 进 People Collection
  69.             PersonCollection.Add(Person);
  70.             Person = new Person("", "", 0, "");
  71.         }


  72.         #region INotifyPropertyChanged Members

  73.         public event PropertyChangedEventHandler PropertyChanged = delegate { };

  74.         public void OnPropertyChanged(string propertyName)
  75.         {
  76.             PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  77.         }

  78.         #endregion
  79.     }
  80. }
复制代码
第八步: Binding 资料进 View
- 先来个示范, 我们把 PeopleCollection Bind 进 ListView 内。
- 然后 F5 run 看看, 如果看到演示图-1这样的, 证明你已经成功了。

  1.   <ListView Grid.Row="2" ItemsSource="{Binding PersonCollection}">
  2.             <ListView.View>
  3.                 <GridView>
  4.                     <GridViewColumn Header="First Name" Width="100" DisplayMemberBinding="{Binding FirstName}"/>
  5.                     <GridViewColumn Header="Last Name" Width="100" DisplayMemberBinding="{Binding LastName}"/>
  6.                     <GridViewColumn Header="Age" Width="80" DisplayMemberBinding="{Binding Age}"/>
  7.                     <GridViewColumn Header="Tel" Width="200" DisplayMemberBinding="{Binding TelPhone}"/>
  8.                 </GridView>
  9.             </ListView.View>
  10.         </ListView>
复制代码
演示图 1


第九步: 完成添加资料的部分
-Binding 个别的Properties 给予各自的TextBox, UpdateSource 了就Trigger。
-同样的道理, Button Save 也给予添加 ViewModel 内的 Save Command。
-好了就 F5, 如果全部资料没填的话, Save Button 会Disable(演示图2)
-如果全部资料填满了, Save Button 就Enable了, SaveExecuted 就会启动, 加进你的PeopleCollection。

  1.         <TextBox Height="23" Margin="138,17,185,0" VerticalAlignment="Top" Text="{Binding Path=Person.FirstName, UpdateSourceTrigger=PropertyChanged}"/>
  2.         <TextBox Height="23" Margin="138,49,185,0" VerticalAlignment="Top" Text="{Binding Path=Person.LastName, UpdateSourceTrigger=PropertyChanged}"/>
  3.         <TextBox Margin="138,82,185,64" Text="{Binding Path=Person.Age,UpdateSourceTrigger=PropertyChanged}"/>
  4.         <TextBox Height="23" Margin="138,0,185,29" VerticalAlignment="Bottom" Text="{Binding Path=Person.TelPhone ,UpdateSourceTrigger=PropertyChanged}" />

  5.         <Button Height="23" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="54" Margin="0,0,12,8"
  6.           Command="{Binding SavePerson}"
  7.           >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

  1. <Window x:Class="SampleProject.Window1"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     xmlns:cmd="clr-namespace:AttachedCommandBehavior;assembly=AttachedCommandBehavior"
  5.     Title="Sample mvvm program" Height="400" Width="550" ResizeMode="NoResize">
复制代码
第十一步: 使用 AttachedCommandBehavior。
- 我们拿会刚才的 Button 例子 Save Button。
- 删除掉 Command, 然后就使用之前的 cmd: (第十步引用的)
- F5 run project, 还是一样会的到之前的效果。
- 差别就在于, 不会显示出已经 Disable了, 但是资料没天满, 还是一样, 不能添加资料。

  1.         <Button Height="23" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="54" Margin="0,0,12,8"
  2.                 cmd:CommandBehavior.Event="Click"
  3.                 cmd:CommandBehavior.Command="{Binding SavePerson}"
  4.                 >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, 就此停笔, 希望大家能多多指教以及讨论。


回复

使用道具 举报

15

主题

0

好友

999

积分

青铜长老

Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7

3#
发表于 2010-8-6 07:35 PM |只看该作者
楼主真的是太牛B啦~请问这是什么来的??


回复

使用道具 举报

46

主题

6

好友

6456

积分

百变名嘴

Rank: 13Rank: 13Rank: 13Rank: 13

4#
发表于 2010-8-6 07:43 PM |只看该作者
楼主真的是太牛B啦~请问这是什么来的??
片翼の墮天使 发表于 2010-8-6 07:35 PM


Composite Application , MVVM 架构咯==

兄弟== 。。。。
你当我无聊没事干吧。


回复

使用道具 举报

15

主题

0

好友

999

积分

青铜长老

Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7

5#
发表于 2010-8-6 08:10 PM |只看该作者
教主出来造福百姓~我一定会支持的~~


回复

使用道具 举报

46

主题

6

好友

6456

积分

百变名嘴

Rank: 13Rank: 13Rank: 13Rank: 13

6#
发表于 2010-8-6 08:20 PM |只看该作者
回复 5# 片翼の墮天使


    我晕== 。。。 这个只是纯粹分享nia~ 还有就是讨论。 给你说到好像神如此。


回复

使用道具 举报

203

主题

0

好友

2800

积分

认证卖家

Rank: 50Rank: 50Rank: 50Rank: 50Rank: 50

7#
发表于 2010-8-6 10:20 PM |只看该作者
谢谢分享... 但是没有一个是我看得懂的...


回复

使用道具 举报

46

主题

6

好友

6456

积分

百变名嘴

Rank: 13Rank: 13Rank: 13Rank: 13

8#
发表于 2010-8-6 11:28 PM |只看该作者
回复 7# 理想の情人


    都说了 == 当我无聊没事干。


回复

使用道具 举报

14

主题

2

好友

734

积分

青铜长老

Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7

9#
发表于 2010-8-7 07:08 PM |只看该作者
精华啊楼主~感谢分享~~~~


回复

使用道具 举报

13

主题

0

好友

2113

积分

白金长老

Rank: 10

10#
发表于 2010-8-7 10:38 PM |只看该作者
衰仔,我的靓仔 也开发着 WPF 的
你几时下来和他交流???
JL 那边有地方睡哦


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

JBTALKS.CC |联系我们 |隐私政策 |Share

GMT+8, 2024-11-26 12:55 AM , Processed in 0.103829 second(s), 28 queries .

Powered by Discuz! X2.5

© 2001-2012 Comsenz Inc.

Ultra High-performance Dedicated Server powered by iCore Technology Sdn. Bhd.
Domain Registration | Web Hosting | Email Hosting | Forum Hosting | ECShop Hosting | Dedicated Server | Colocation Services
本论坛言论纯属发表者个人意见,与本论坛立场无关
Copyright © 2003-2012 JBTALKS.CC All Rights Reserved
合作联盟网站:
JBTALKS 马来西亚中文论坛 | JBTALKS我的空间 | ICORE TECHNOLOGY SDN. BHD.
回顶部