假如你对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)。通常,你有一些数据集合,你想要显示给用户,你就可以将数据绑定到XAML2.WPF有两部分,XAML部分是描述你的GUI和特效,code-behind部分的.cs代码用来联系XAML3.最优雅同时也可能是最大限度地复用你的代码的方式就是使用‘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:Windowx:Class=Example1.MainWindowxmlns:local=clr-namespace:Example1Window.DataContext!--DeclarativelycreateaninstanceofourSongViewModel--local:SongViewModel//Window.DataContext以上这种方式与下面的这种code-behind方式是一样的:publicpartialclassMainWindow:Window{SongViewModel_viewModel=newSongViewModel();publicMainWindow(){InitializeComponent();base.DataContext=_viewModel;}}然后在XAML中去除DataContext元素结点:Windowx:Class=Example1.MainWindowxmlns:local=clr-namespace:Example1!--nodatacontext--示例显示的样子是:点击那个按钮不会有任何作用,因为还没有完全实现数据绑定。数据绑定:记住我在一开始所说的,我会让其中一个property显示出来,在这个例子中,我们希望显示ArtistName。我选择这个名字是因为它与WPF中的任何属性的名字都不像。在网络上,有无数的示例用Person这样的类,然后再在Person里定义个Name属性(而Name属性在.NETWPF中已经烂大街了)。也许那些文章的作用根本就没有意识到,那会对初学者造成很大的困扰。关于数据绑定的教程,网上已经有一大堆了,这里我将不会对其进行单独讲解。我希望我们的例子可以很细微,以至让你知道是怎么回事。为了绑定ArtistNameproperty到我们的SongViewModel,我们只要在xaml里简明地写下:LabelContent={BindingArtistName}/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:Windowx:Class=Example2.MainWindowxmlns=:x=:local=clr-namespace:Example2Title=Example2SizeToContent=WidthAndHeightResizeMode=NoResizeHeight=350Width=525Window.DataContext!--DeclarativelycreateaninstanceofourSongViewModel--local:SongViewModel//Window.DataContextGridGrid.RowDefinitionsRowDefinitionHeight=Auto/RowDefinitionHeight=Auto/RowDefinitionHeight=Auto//Grid.RowDefinitionsGrid.ColumnDefinitionsColumnDefinitionWidth=Auto/ColumnDefinitionWidth=Auto//Grid.ColumnDefinitionsLabelGrid.Column=0Grid.Row=0Content=Example2-thisworks!/LabelGrid.Column=0Grid.Row=1Content=Artist:/LabelGrid.Column=1Grid.Row=1Content={BindingArtistName}/ButtonGrid.Column=1Grid.Row=2Name=ButtonUpdateA