一,MVVM理論知識
從上一篇文章中,我們已經知道,WPF技術的主要特點是數據驅動UI,所以在使用WPF技術開發的過程中是以數據為核心的,WPF提供了數據綁定機制,當數據發生變化時,WPF會自動發出通知去更新UI。
我們使用模式,一般是想達到高內聚低耦合。在WPF開發中,經典的編程模式是MVVM,是為WPF量身定做的模式,該模式充分利用了WPF的數據綁定機制,最大限度地降低了Xmal文件和CS文件的耦合度,也就是UI顯示和邏輯代碼的耦合度,如需要更換界面時,邏輯代碼修改很少,甚至不用修改。與WinForm開發相比,我們一般在后置代碼中會使用控件的名字來操作控件的屬性來更新UI,而在WPF中通常是通過數據綁定來更新UI;在響應用戶操作上,WinForm是通過控件的事件來處理,而WPF可以使用命令綁定的方式來處理,耦合度將降低。
我們可以通過下圖來直觀的理解MVVM模式:
View就是用xaml實現的界面,負責與用戶交互,接收用戶輸入,把數據展現給用戶。
ViewModel,一個C#類,負責收集需要綁定的數據和命令,聚合Model對象,通過View類的DataContext屬性綁定到View,同時也可以處理一些UI邏輯。
Model,就是系統中的對象,可包含屬性和行為。一般,View對應一個ViewModel,ViewModel可以聚合N個Model,ViewModel可以對應多個View,Model不知道View和ViewModel的存在。
二,MVVM示例講解
這個示例是為了讓大家直觀地了解MVVM的編程模式,關於其中用到的數據綁定和命令等知識,在后面的文章會專門討論。
- 首先定義NotificationObject類。目的是綁定數據屬性。這個類的作用是實現了INotifyPropertyChanged接口。WPF中類要實現這個接口,其屬性成員才具備通知UI的能力,數據綁定的知識,后面詳細討論。
using System.ComponentModel; namespace WpfFirst { class NotificationObject : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public void RaisePropertyChanged(string propertyName) { if (this.PropertyChanged != null) { this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } }
2.定義DelegateCommand類。目的是綁定命令屬性。這個類的作用是實現了ICommand接口,WPF中實現了ICommand接口的類,才能作為命令綁定到UI。命令的知識,后面詳細討論。
using System; using System.Collections.Generic; using System.Windows.Input; namespace WpfFirst { class DelegateCommand : ICommand { //A method prototype without return value. public Action<object> ExecuteCommand = null; //A method prototype return a bool type. public Func<object, bool> CanExecuteCommand = null; public event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) { if (CanExecuteCommand != null) { return this.CanExecuteCommand(parameter); } else { return true; } } public void Execute(object parameter) { if (this.ExecuteCommand != null) this.ExecuteCommand(parameter); } public void RaiseCanExecuteChanged() { if (CanExecuteChanged != null) { CanExecuteChanged(this, EventArgs.Empty); } } } }
3.開始定義Model類。一個屬性成員"WPF",它就是數據屬性,的有通知功能,它改變后,會知道通知UI更新。一個方法“Copy”,用來改變屬性“WPF”的值,它通過命令的方式相應UI事件。
using System.ComponentModel; using System.Windows.Input; namespace WpfFirst { class Model : NotificationObject { private string _wpf = "WPF"; public string WPF { get { return _wpf; } set { _wpf = value; this.RaisePropertyChanged("WPF"); } } public void Copy(object obj) { this.WPF += " WPF"; } } }
4.定義ViewModel類。定義了一個命令屬性"CopyCmd",聚合了一個Model對象"model"。這里的關鍵是,給CopyCmd命令指定響應命令的方法是model對象的“Copy”方法。
using System; namespace WpfFirst { class ViewModel { public DelegateCommand CopyCmd { get; set; } public Model model { get; set; } public ViewModel() { this.model = new Model(); this.CopyCmd = new DelegateCommand(); this.CopyCmd.ExecuteCommand = new Action<object>(this.model.Copy); } } }
5.定義View.
MainWindow.xaml代碼:我們能看到,TextBlock控件的text屬性,綁定在model對象的WPF屬性上; Button的click事件通過命令綁定到CopyCmd命令屬性。
<Window x:Class="WpfFirst.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <StackPanel VerticalAlignment="Center" > <TextBlock Text="{Binding model.WPF}" Height="208" TextWrapping="WrapWithOverflow"></TextBlock> <Button Command="{Binding CopyCmd}" Height="93" Width="232">Copy</Button> </StackPanel> </Grid> </Window>
MainWindow.xaml.cs代碼:它的工作知識把ViewModel對象賦值到DataContext屬性,指定View的數據源就是這個ViewModel。
using System.Windows; namespace WpfFirst { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.DataContext = new ViewModel(); } } }
6.運行結果。每當我們點擊按鈕,界面就是被更新了,因為Copy方法改變了WFP屬性的值。
寫這個簡單的例子,就是為了直觀地了解MVVM的編程模式。在實際開發中,不管程序有多復雜,也就是增加Model, View, ViewModel,和其他的一些輔助類(Helpers or Services)了,模式不會改變。