本文的原文链接如下:
http://www.codeproject.com/Articles/165368/WPF-MVVM-Quick-Start-Tutorial
本文并不是对原文的逐句翻译,为了便于大家理解,我尽量翻译的通俗易懂点,所以有所增删。
如果你是新手,开始本文前,请阅读前两篇博文。
http://www.cnblogs.com/zhangzhi19861216/archive/2013/03/19/WPF-MVM.html
http://www.cnblogs.com/zhangzhi19861216/archive/2013/03/19/2969474.html
这次我们从例4开始,并且把原文的例5和例6合在一起将.
例4:框架
现在,如果你详尽的阅读此文,你可能会注意到很多是重复的代码。例如实现INotifyPropertyChanged接口、创建命令。我们可以把实现了INotifyPropertyChanged接口的类封装为一个基类,命名为ObservableObject, 和RelayCommand类一样,把它们放到一个类库(MicroMvvm)当中,以便其它的类库调用。ObservableObject类和RelayCommand类是相当基础的类,它们是重构的必然结果。
译者附加:
查看MicroMvvm类库RelayCommand.cs中的RelayCommand<T> 类,与之前(例3)的RelayCommand不太一样,因为例3及以前只是为了做一个简单点示例。
Action委托变成了Action<T>
而Func<Boolean> 变成了Predicate<T>
关于他们的区别,我在这里给一个连接:http://www.cnblogs.com/Wendy_Yu/archive/2011/06/08/2075526.html
而继承INotifyPropertyChanged接口的类ObservableObject还添加了属性验证方法VerifyPropertyName。
详情请根据本文地脚的连接下载源码。
例5:Songs的集合,我们应该怎样做
正如我之前所说,为了在您的视图(即XAML,本文为MainWindow.xaml)当中显示集合中的数据,你需要使用一个ObservableCollection。在这个例子中,我们创建了一个AlbumViewModel类,它收集了一些人们了解的歌曲。我们还创建了一个简单的歌曲资料库——SongDatabase类,这个类只是为了让我们可以迅速生产一些歌曲信息。
你的第一次尝试可能如下(AlbumViewModel.cs):
class AlbumViewModel { #region Members ObservableCollection<Song> _songs = new ObservableCollection<Song>(); #endregion }
你可能会想:“我现在有一个不同的View Model,我想通过AlbumViewModel显示歌曲,而不是SongViewModel”。
我们依旧创建一些ICommands命令并且把它们附件在一些按钮上。
public ICommand AddAlbumArtist {} public ICommand UpdateAlbumArtists {}
下载本文示例(本文左下角),运行Example5.
在这个例子当中,点击“Add Artis”按钮工作正常,但是点击“Update Artist Names”按钮就不是那么乐观了—没有成功。这是为什么呢?请看下面解释:
MSDN:“为了完全支持将绑定源对象中的数据值传送到绑定目标,在支持可绑定属性的集合中的每个对象都必须实现适当的属性更改通知机制,
如 INotifyPropertyChanged 接口。”
译者附加:
我们来看看AlbumViewModel源码:
class AlbumViewModel { #region Members private SongDatabase _database = new SongDatabase(); ObservableCollection<Song> _songs = new ObservableCollection<Song>(); #endregion #region Properties public ObservableCollection<Song> Songs { get { return _songs; } set { _songs = value; } } #endregion #region Construction public AlbumViewModel() { for (int i = 0; i < 3; ++i) { _songs.Add(new Song { ArtistName = _database.GetRandomArtistName, SongTitle = _database.GetRandomSongTitle }); } } #endregion #region Commands void UpdateAlbumArtistsExecute() { if (_songs == null) return; foreach (var song in _songs) { song.ArtistName = _database.GetRandomArtistName; } } bool CanUpdateAlbumArtistsExecute() { return true; } public ICommand UpdateAlbumArtists { get { return new RelayCommand(UpdateAlbumArtistsExecute, CanUpdateAlbumArtistsExecute); } } void AddAlbumArtistExecute() { if (_songs == null) return; _songs.Add(new Song { ArtistName = _database.GetRandomArtistName, SongTitle = _database.GetRandomSongTitle }); } bool CanAddAlbumArtistExecute() { return true; } public ICommand AddAlbumArtist { get { return new RelayCommand(AddAlbumArtistExecute, CanAddAlbumArtistExecute); } } #endregion
当我们点击“Add Artis”按钮时,触发AddAlbumArtist命令,调用AddAlbumArtistExecute方法给动态集合ObservableCollection<Song> _songs = new ObservableCollection<Song>()添加一个Song歌曲。
由于MainWindow.xaml中ListView的ItemsSource绑定的是Songs:
<ListView Grid.Column="1" Grid.Row="2" ItemsSource="{Binding Songs}" > <ListView.ItemTemplate> <DataTemplate> <StackPanel> <Label Content="{Binding ArtistName}" /> <Label Content="{Binding SongTitle}" FontSize="10" /> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView>
动态集合Songs增加了,数据驱动UI,UI也跟着增加歌曲集合。
而当我们点击“Update Artist Names”按钮时,触发UpdateAlbumArtists命令,调用UpdateAlbumArtistsExecute()更改每一个歌曲的艺术家名:
void UpdateAlbumArtistsExecute() { if (_songs == null) return; foreach (var song in _songs) { song.ArtistName = _database.GetRandomArtistName; } }
哎呦,根据MSDN上面的解释,ArtistName没实现INotifyPropertyChanged接口呀,怪不得ArtistName更新不了。
那我们应该怎么办呢? 想起来了,我们不是有SongViewModel吗? 在我们的SongViewModel类里面拥有实现了INotifyPropertyChanged接口的ArtistName。
现在清晰了,把AlbumViewModel中的Song类,跟改为SongViewModel类,代码如下(例6:Example6):
class AlbumViewModel { #region Members private SongDatabase _database = new SongDatabase(); ObservableCollection<SongViewModel> _songs = new ObservableCollection<SongViewModel>(); #endregion #region Properties public ObservableCollection<SongViewModel> Songs { get { return _songs; } set { _songs = value; } } #endregion #region Construction public AlbumViewModel() { for (int i = 0; i < 3; ++i) { _songs.Add(new SongViewModel { ArtistName = _database.GetRandomArtistName, SongTitle = _database.GetRandomSongTitle }); } } #endregion #region Commands void UpdateAlbumArtistsExecute() { if (_songs == null) return; foreach (var song in _songs) { song.ArtistName = _database.GetRandomArtistName; } } bool CanUpdateAlbumArtistsExecute() { return true; } public ICommand UpdateAlbumArtists { get { return new RelayCommand(UpdateAlbumArtistsExecute, CanUpdateAlbumArtistsExecute); } } void AddAlbumArtistExecute() { if (_songs == null) return; _songs.Add(new SongViewModel { ArtistName = _database.GetRandomArtistName, SongTitle = _database.GetRandomSongTitle }); } bool CanAddAlbumArtistExecute() { return true; } public ICommand AddAlbumArtist { get { return new RelayCommand(AddAlbumArtistExecute, CanAddAlbumArtistExecute); } } #endregion
现在让我们运行程序,看看好了没,嗯,没问题。
后记:
这篇博文就算翻译完了。
希望这六个例子能给读者用MVVM写WPF应用提供帮助。
如果您发现这篇文章对你有帮助,请支持一下。
如果您发现有什么错误,请指正。
接下来我还会在闲暇时间翻译一些其它文章,希望大家前来学习交流。
源码下载:http://files.cnblogs.com/zhangzhi19861216/MVVMQuickStartTutorial.rar