您的当前位置:首页正文

WPF MVVM 入门教程

2020-10-16 来源:尚佳旅游分享网
假如你对C#已经有了比较好的了解,那么学习WPF不会太难。我学习WPF已经有一段时间了,但一直没有找到比较好的MVVM教程。希望这篇文章能达到这个目的。在学习任何技术之前,你都会想知道“学了有什么好处”。在我看来,我看到的几乎所有的WPF教程都会有以下的一个或多个不足:1.示例都是用XAML表示。

2.示例掩盖了那些可以让你使用起来更加方便的主要功能。

3.示例试图通过一些根本没什么用的知识点来炫耀WPF/XAML的技巧。4.xxx

所以,为了处理这些问题,我写了这篇文章,基于那篇在Google里输入“WPFTutorial”而得到的第一篇文章。这篇文章也许不是100%正确,或者不是“唯一

解”,但它将讲明我在6个月前得到的体会的主要思想。

我会先快速地介绍一些主题,然后通过一个示例来解释或证明每一个要点。因此,我不想试图让GUI很漂亮,那不是本文的重点。

Note:由于这篇教程特别的长,为了简洁,我省略了很多代码,所以,为了更好的理解这文章,请下载zip代码包。运行环境(.net4.0/vs2010)。

基本要点:

1.WPF中,最重要的就是数据绑定(databinding)。通常,你有一些数据集合,你

想要显示给用户,你就可以将数据绑定到XAML

2.WPF有两部分,XAML部分是描述你的GUI和特效,code-behind部分的.cs代码用

来联系XAML

3.最优雅同时也可能是最大限度地复用你的代码的方式就是使用‘MVVM’模式:

Model,View,ViewModel。这个的目标就是确保你的View中cs代码最小化,应该都以XAML的形式来显示。你要知道的主要知识点:

1.用来存储你的数据的集合是ObservableCollection<>.而不是list,也不是dictionary,但可以是ObservableCollection。单词“Observable(可观察的)”就是这里的主线:WPF

窗口要有‘观察’你数据集合的能力。集合类要实现

一些WPF要使用的必要接口。

2.每一个WPF控件都有一个DataContext以及集合控件拥有一个ItemsSource属性。

3.INotifyPropertyChanged接口将会在GUI与你的代码要传输数据时,大量的使用。

示例1:先做错之(大部分)

示例是开始的最好方式。我们将通过一个Song类来开始,而不是常用的Person类。我们可以将歌曲(Song)整理到唱片(Album)中,或者一个更大的集合,或者以艺术家(Artist)的方式来整理。以下是一个简单的Song类:

publicclassSong{#regionMembersstring_artistName;string_songTitle;#endregion#regionProperties///Theartistname.PublicstringArtistName{get{return_artistName;}set{_artistName=value;}}///}Thesongtitle.publicstringSongTitle{get{return_songTitle;}set{_songTitle=value;}}#endregion在WPF术语中,这就是我们的‘Model’,GUI就是我们的‘View’。神奇的地方在于,对它们俩之间进行数据绑定的是’ViewModel’,就是这个适配器,可以将我们的Model转化为WPF框架可以识别的东西。再次重申,这个Song类就是我们的’Model’。

由于我们创建的Song类是引用类型,在内存中进行复制是非常的轻便而且代价很低。我们可以也可以非常容易的创建我们的SongViewModel。然后,我们第一步要思考的是:我们要(可能)显示什么?假设我们只关心歌曲(song)的表演者,而不是歌曲名,那我们的SongViewModel就可以这样定义:

publicclassSongViewModel{Song_song;publicSongSong{get{return_song;}set{_song=value;}}publicstringArtistName{get{returnSong.ArtistName;}set{Song.ArtistName=value;}}}由于我们暴露了一个property在我们的ViewModel中,所以我们要显式地对song的表演者(ArtistName)对进行一个改变。通过以下代码:

SongViewModelsong=...;//...enablethedatabinding...//changethenamesong.ArtistName=\"Elvis\";//theguishouldchange在这里,我们通过声明的方式来创建viewmodel,例如,我们还得在XAML中声明这个ViewModel:

x:Class=\"Example1.MainWindow\"xmlns:local=\"clr-namespace:Example1\">以上这种方式与下面的这种code-behind方式是一样的:

publicpartialclassMainWindow:Window{SongViewModel_viewModel=newSongViewModel();publicMainWindow(){InitializeComponent();base.DataContext=_viewModel;}}然后在XAML中去除DataContext元素结点:

nodatacontext-->示例显示的样子是:

点击那个按钮不会有任何作用,因为还没有完全实现数据绑定。

数据绑定:

记住我在一开始所说的,我会让其中一个property显示出来,在这个例子中,我们希望显示ArtistName。我选择这个名字是因为它与WPF中的任何属性的名字都不像。在网络上,有无数的示例用Person这样的类,然后再在Person里定义个Name属性(而Name属性在.NETWPF中已经烂大街了)。也许那些文章的作用根本就没有意识到,那会对初学者造成很大的困扰。

关于数据绑定的教程,网上已经有一大堆了,这里我将不会对其进行单独讲解。我希望我们的例子可以很细微,以至让你知道是怎么回事。

为了绑定ArtistNameproperty到我们的SongViewModel,我们只要在xaml里简明地写下:

Binding关键字绑定到控件的内容,这里是一个Label,其通过它的

DataContext来返回ArtistName,如你所见,我们将会把SongViewModel的一个实例设置到Label的DataContext,因此,我们将会看到_songViewModel.ArtistName显示在Label上。

再次申明:点击按钮不会有任何效果,因为我们还没有完全实现数据绑定。GUI界面在property改变时,不会接收到任何通知。

示例2:INotifyPropertyChanged

这里,我们不得不实现那个“狡猾”的接口:

INotifyPropertyChanged。正如所说,任何一个实现了这个接口的类,在其property改变时,都会通知到他的监听者。所以,我们要对我们的SongViewModel进行一些小改动:

publicclassSongViewModel:INotifyPropertyChanged#regionConstruction///ConstructsthedefaultinstanceofaSongViewModelpublicSongViewModel(){_song=newSong{ArtistName=\"Unknown\",SongTitle=\"Unknown\"};}#endregion#regionMembersSong_song;#endregion#regionPropertiespublicSongSong{get{return_song;}set{_song=value;}}publicstringArtistName{get{returnSong.ArtistName;}set{if(Song.ArtistName!=value){Song.ArtistName=value;RaisePropertyChanged(\"ArtistName\");}}}#endregion#regionINotifyPropertyChangedMemberspubliceventPropertyChangedEventHandlerPropertyChanged;#endregion#regionMethodsprivatevoidRaisePropertyChanged(stringpropertyName){//takeacopytopreventthreadissuesPropertyChangedEventHandlerhandler=PropertyChanged;if(handler!=null){handler(this,newPropertyChangedEventArgs(propertyName));}}#endregion{}这里,发生了一些事情。首先,我们检查是否真的有property改变:这个在复杂对象时,会对性能有一点点帮忙。然后,如果值改变,我们调用

PropertyChanged事件通知监听者.现在,我们拥有一个Model,一个ViewModel。我们现在需要的就是定义我们的View。下面就是我们的MainWindow:为了测试以上的数据绑定,我们可以通过传统的方式来创建一个按钮,然后实现其OnClick事件,如下所示:publicpartialclassMainWindow:Window{#regionMembersSongViewModel_viewModel;int_count=0;#endregionpublicMainWindow(){InitializeComponent();//Wehavedeclaredtheviewmodelinstancedeclarativelyinthexaml.//Getthereferencetoithere,sowecanuseitinthebuttonclickevent._viewModel=(SongViewModel)base.DataContext;}privatevoidButtonUpdateArtist_Click(objectsender,RoutedEventArgse){++_count;_viewModel.ArtistName=string.Format(\"Elvis({0})\",_count);}}这个工作的很好,但这不是我们现在要应该使用WPF的方式,我们将‘更新表演者‘的逻辑添加在了code-behind的cs代码里。这不是我们现在应该使用的方式。Window类应该关心的是窗口状态,而不是这些个逻辑。第二个问题是,假如这时,我们想将按钮事件中的点击事件移动到不同的控件中,比如一个菜单列表中。这意味着我们要剪切,粘贴并且更改多处。下面是我们在点击按钮时的运行示图:示例3:命令(Commands)

直接绑定到GUI事件是有问题的。WPF提供了一个更好的方式,那就是ICommand。许多控件拥有Commandattribute。它遵循与Content和ItemsSouce是一样的绑定方式,除非你要将其绑定到一个返回值为ICommand的property上。对于平常的例子,我们只需要实现一个平常的类,就叫做‘RelayCommand’吧,它实现了ICommand接口。

ICommand需要用户实现两个方法:boolCanExecute和voidExecute。CanExecute方法其实就是问用户:“我是否能执行这个Command?”,这对于你控制你要执行的GUI操作的上下文(context)很有用。在我们的这个例子中,我们不关心这些,所以直接返回true就好了,当然这就意味着可以一直调用我们的‘Execute’方法。有时,当你将一个Command绑定到一个按钮,并且它只有在你选择了一个list中的项后才会被执行,这时,你就可以在CanExecute方法中实现其逻辑。

由于我们想要复用ICommand代码,所以就在RelayCommand方法中包含了所以可以利用的代码,这样不需要重复的写了。

为了展示复用ICommand是有多么的容易,我们将“更新表演者”这个Command绑定到一个按钮和一个菜单项上。要注意的是,这里我们不再专门绑定按钮的Click事件或者指定菜单的Click事件。

示例4:框架

如果你读到这里,你会发现,如果你以后要使用这种方式,以上的代码大多都是在重复:实现INPC(INotifyPropertyChanged)或创建Command.这其实就是大部分MVVM的样板,所以,我们可以将实现INPC放到一个基类中,我们把它叫做‘ObservableObject’。对于RelayCommand类,我们可以将其移动到我们的.net类库中。这就是所有的MVVM框架开始所做的(如:Prism,Caliburn).这里,我们将这些类移到一个小型的类库中,以便于我们将来的复用。运行起来后,效果与之前的一样:

示例5:Songs集合

就像我之前所说的,为了将一个项的集合显示在View(如XAML)中,我们需要使用一个ObservableCollection。在这个例子中,我们创建了一个AlbumViewModel,它可以很好地将songs整合到一起。这也像一个小型的song的数据库。一开始,你可能会要这样做:classAlbumViewModel{#regionMembers}ObservableCollection_songs=newObservableCollection();#endregion你可能会想:我现在已经有不同的viewmodel了,我想要以AlbumViewModel的方式来显示歌曲,而不是像SongViewModel那样。

我们再创建一些Icommands,并将其附加到一些按钮上:

publicICommandAddAlbumArtist{}publicICommandUpdateAlbumArtists{}在这个例子中,我们点击“AddArtist”工作正常。但是点击“UpdateArtistNames”却不工作。如果你查看MSDN,那在其黄色部分有显示

为了完全支持将绑定源对象中的数据值传送到绑定目标,在支持可绑定属性的集合中的每个对象都必须实现适当的属性更改通知机制,如INotifyPropertyChanged接口。运行例子,如下所示:

示例6:以正确的方式整合Songs

在这最后的例子里,我们让ObservableCollection的泛型对象指定为

SongViewModels而不是Song.

classAlbumViewModel{#regionMembersObservableCollection_songs=newObservableCollection();#endregion//codeelidedforbrevity}现在我们的例子可以正常运行,而且更为关键的是:我们的MainWindow.cs仍然为空。总结:实例化你的ViewModel最后值得再提一下的是:当你为一个XAML定义了一个ViewModel,由于你不能传给他任何参数,所以,你就得显式地实现它的默认构造函数。在什么地方加ViewModel完全取决于你,你也许可能认为在其code-behind的MainWindow.cs里加更方便,当然,也可以在XAML里加。其它的框架:还有很多MVVM框架,它们都很强大,它们是作用于WPF,WP7,Silverlight,或者他们之间的结合体。最后:希望这6个例子能让你看到,使用MVVM的方式来写WPF是多么的容易。以上我已经试图覆盖那些我认为会被忽略的点。引用:1.EffectiveC#2.FrameworkDesignGuidelines:Conventions,Idioms,andPatternsforReusable.NETLibraries3.C#4.0inaNutshell:TheDefinitiveReference4.WPF4Unleashed5.JoshSmith扩展阅读:作者:HelloWorldinMVVMLightin10MinutesMVVMLightUsingTwoViewsBarryLapthorn

因篇幅问题不能全部显示,请点此查看更多更全内容