一,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)了,模式不會改變。


