其實這兩天關于寫這MVVM思路一直很混亂. 一方面也在整理回顧4月份項目中關于MVVM的使用,當時版本用Silverlight 3. 版本4釋出ICommand的支援多少對MVVM應實際應用上産生一定影響. 當然另外一個原因 我在不斷回顧過程也發現不少原來項目中存在的實際問題.這些問題出現同時也給我帶來一些的關于MVVM在Silverlight運用中思考. 我現在依然不能理清寫這篇文章思路. 也有可能整篇文章結構比較混亂, 但大都會針對MVVM在Silverlight項目中運用産生一些實際問題作出整理. 同時在技術群和MSN中每天也有不少Dvp會對MVVM使用提出不少問題. 是以這篇文章打算首先以一個簡單MVVM Demo切入, 然後再對項目中使用MVVM個細節問題點 進行逐個闡述.
本篇會幫助從MVVM了解進入實際編碼入門, 然後對實際運用細節提出我個人看法解決方式[當然有可能是不成熟 也歡迎提出你的見解]. 如果你認為對MVVM運用已經駕輕就熟 敬請飄過.
<1>為什麼要用MVVM?
關于這個問題 我們把它範圍更縮小一點 單一放到Silverlight中來看.一方面Silverlight帶來很多新的技術體驗動畫,3D 等 另外一方面我們在程式設計上UI層就更加細節化了.可定制.同時新特性Binding、Dependency Property、Routed Events、Command、DataTemplate的不斷出現, 也為原來适用WPF的MVP模式發生轉變, Silverlight需要一個适合自己的架構來"武裝"自己, 新特性的出現加速MVVM基于MVP中演變成型的速度. 這就導緻MVVM從MVP(Model-View-Presenter)模式與WPF結合的應用方式時發展演變過來的一種新型架構架構..
<2>如何運用MVVM?
既然MVVM出現時一個不斷演變過程, 那麼它的出現回給我們的原來Silverlight編碼産生哪些變化?這個問題我稍後會解釋.先讓我們學會使用這個MVVM.
<2.1>我們要做什麼?
為了達到完整示範使用MVVM整個過程,下面我會寫一個小型的DEMO來闡述如何使用MVVM. 在進入編碼前又必要了解一下我們這個程式要做什麼 具體需求如下:
現在有一個場景:
在工業批量生産中, 會使用多台自動化機器, 而人的作用就是這些自動化機器的管理者. 管理者通過程控機[監控自動化機器運作狀态一種機器] 實時動态采集多台自動化機器的運作狀态參數. 來保證機器正常運作. 說明整個控制流程圖:
<a href="http://blog.51cto.com/attachment/201201/134504238.png" target="_blank"></a>
其實這個是一個簡單單一流程. 反映到程式中 我們就要通過程控機Com通信端口采集多個自動化機器的運作參數并反映目前運作狀況. ok 需求明确了. 那就讓我們用MVVM在Silverlight模拟實作.
<2.2>進入編碼
MVVM具體進行拆分就可以分為M-V-VM 三個層次,M[Model]—V[View]—VM[ViewModel],第一個M代表資料實體Model, View也是UI. 那麼VM來做什麼?當我們有了漂亮View和可操作的Model. 使用者通過View來與程式進行互動.Silverlight提供了我們良好Binding特性, 我們可以把View中控件與Model的特定屬性進行Binding操作, 頁面資料就能直接回報到Model上來, 貌似很好,但是實際運用中我們發現, View中可能出現類型有多個, 而我們的Model定義屬性類型是唯一的, 同時頁面資料需要過濾轉換處理, 這就麻煩了, 我們隻能修改Model, 可是針對多個頁面難道我們修改多次嗎? 那麼也就違反OO程式設計第一原則ISP類單一職責. 不可取.同時這時的Model也變得臃腫龐大, 後期代碼維護代價足以能夠折磨你到"抓狂".
這個時候我們才想起有點冷落的VM了, 我們可以把View除了顯示以外所有活 都交給它來做. 它功能就充分展現了 隔離View與Model直接關聯,同時要滿足View所有向背景請求資料的需求. 備受冷落的VM終于有了用武之地了.清晰流程圖:
<a href="http://blog.51cto.com/attachment/201201/134509650.png" target="_blank"></a>
我們先通過利用程式檢索一下目前機器運作狀态,利用VS2010搭建一個Silverlight Application 結構如下:
<a href="http://blog.51cto.com/attachment/201201/134514527.png" target="_blank"></a>
從結構能清晰看出整體資料流向: 使用者操作View輸入資料,利用Silverlight 4支援的ICommand通過ViewModes通路DataSource資料源, 并把相關資料更新給ViewModel的屬性,View通過TwoWay雙向綁定看到更新資料..
定義View:
有了View我們再來封裝Model, 我們控制對象是機器Machine 封裝成Model:
public class ComMachine
{
public string Id { get; set; }
public string MachineName { get; set; }
public string State { get; set; }
}
緊跟着定義ViewModel:
/// <summary>
/// 測試單一MVVM架構資料流向
/// Author:chenkai Date:2010年8月10日15:35:43
/// </summary>
public class MachineViewModel:INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public MachineViewModel(List<Models.ComMachine> getcuslist)
{
this.customerlist = getcuslist;
this.queryCommand = new Command.QueryDataCommand(this);
}
//查詢資料
private List<Models.ComMachine> customerlist = null;
//搜尋關鍵字
private string searchKey = string.Empty;
public string SearchKey
get { return searchKey; }
set
{
searchKey = value;
if (PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs("SearchKey"));
}
//搜尋結果
private string searchResult = string.Empty;
public string SearchResult
get { return searchResult; }
{
searchResult = value;
this.PropertyChanged(this, new PropertyChangedEventArgs("SearchResult"));
//搜尋Command
private ICommand queryCommand = null;
public ICommand QueryCommand
get { return queryCommand; }
public void QueryDate()
if (!string.IsNullOrEmpty(this.searchKey))
Models.ComMachine QueryCustomer = null;
foreach (Models.ComMachine getcustomer in DataSource.GetCustomerList())
{
if (getcustomer.Id.Equals(this.searchKey))
{
QueryCustomer = getcustomer;
break;
}
}
if (QueryCustomer != null)
this.SearchResult = "機器名稱:" + QueryCustomer.MachineName + Environment.NewLine + "可用狀态:" + QueryCustomer.State;
else
MessageBox.Show("機器編号不存在!");
this.SearchKey = string.Empty;
else
MessageBox.Show("請輸入你要查詢的機器編号ID!");
}
ViewModel完全為View頁面做的量身定制的類,實作 INotifyPropertyChanged接口當對Viewmodel屬性值發生變更時便進行通知. 另外一個很重要地方是就是當View調用Command指令時,需要在ViewModel中進行關聯.
通過上面我們能看出View中實作的對ViewMode屬性Bingding和對查詢操作Command調用. 也就是說調用Binding和Command是可以寫在XAML中的,而真正定義Binding和Command實作描述的代碼卻在ViewModel中.
Command定義:
/// 封裝指令Command
/// </summary>
public class QueryDataCommand :ICommand
{
private ViewModels.MachineViewModel _CustomerViewModel;
public QueryDataCommand(ViewModels.MachineViewModel getcustomer)
{
this._CustomerViewModel = getcustomer;
}
//ICommand接口成員之一 确定在目前調用CMd狀态下是否允許其執行方法
public bool CanExecute(object parameter)
return true;//允許
//當出現影響是否應執行該指令的更改時發生
public event EventHandler CanExecuteChanged
add{ }
remove { }
public void Execute(object parameter)
//調用Command要執行的方法
this._CustomerViewModel.QueryDate();
}
SL 3中我們在定義并實作ICommand接口後, 調用往往放在類似事件中需要手動處理.這樣有什麼不好地方?
在沒有結合command patter前,用silverlight進行異步調用的确顯得很簡單,可問題在于如果應用程式再多一些即有多個異步調用的話,我們将就需要添加一些按鈕,而問題也就在于我們不能确定哪個調用屬于哪個按鈕,即行為與UI不能産生直接有效的映射關系,而Silverlight 4Command的全面支援也是完全解除這個調用障礙.
我在技術群中有人曾這樣直接問過我一個Command問題:
當一個Button按鈕有一個Click事件 同時還綁定一個背景調用的Command 當點選按鈕時問他們執行順序? 這個問題當時确實難住了我 平時這樣細節沒有注意.
經過測試發現調用順序是: 先調用Click事件 然後才通過程式對Command解析才調用Command 的Excute方法.
還有一個關于Command調用場景問題:
頁面有一個Button, 當該Button被點選的時候我們要完成一些操作, 将該操作封裝成一個Command并綁定到該Button上就可以了, 但如果我們要在Button被Load的時候執行另外一些操作呢?
本文轉自chenkaiunion 51CTO部落格,原文連結:http://blog.51cto.com/chenkai/764722