項目用例:實現登錄界面(后台是遠程JAVA服務),項目結構分層為BL(純業務邏輯),UI(純界面顯示),
(轉載請注明來源:cnblogs coder-fang)
1. 首先在BL層引入 mvvm light包,之后會在BL在自動生成相關Locator,主要用於定位服務與ViewModel
2.我們在項目中創建了一個BaseViewModel,主要是存儲共享服務與共享方法等:

public class BaseVM : ViewModelBase { //這是其它業務ViewModel可能會用到的服務類,全部在Locator中注冊 public ConfigService configService = ServiceLocator.Current.GetInstance<ConfigService>(); public BackendService backendService = ServiceLocator.Current.GetInstance<BackendService>(); public SysInfoService sysInfoService = ServiceLocator.Current.GetInstance<SysInfoService>(); public ReadCardService readCardService = ServiceLocator.Current.GetInstance<ReadCardService>(); public BaseVM() { } public void ShowMsg(String msg) { DispatcherHelper.CheckBeginInvokeOnUI(() => { Messenger.Default.Send<String>(msg, "ShowMessage"); }); } public void SendMsg<T>(String token,T msg) { DispatcherHelper.CheckBeginInvokeOnUI(() => { Messenger.Default.Send<T>(msg, token); }); } }
3.創建LoginViewModel:

public class LoginVM:BaseVM { //登錄按鈕綁定的命令 public RelayCommand LoginCmd { get; private set; } private string _username; //綁定是否在后台登錄中,用於前端是否顯示進度條 private bool _isLogining = false; public Boolean IsLogining { get { return _isLogining; } set { _isLogining = value; RaisePropertyChanged(() => IsLogining); } } public String UserName { get { return _username; } set { _username = value; RaisePropertyChanged(() => UserName); } } private string _password; public String Password { get { return _password; } set { _password = value; RaisePropertyChanged(() => Password); } } public LoginVM() { //創建命令綁定的業務邏輯 LoginCmd = new RelayCommand(() => { if (String.IsNullOrEmpty(_username) || String.IsNullOrEmpty(_password)) { ShowMsg("請正確填寫用戶名與密碼"); return; } IsLogining = true; new Task(() => { //這是后台API接口服務,遠程調用java服務 backendService.Login(_username, _password, (data) =>//接口調用成功 { var obj = data as JObject; if (!((string)obj["code"]).Equals("200")) { ShowMsg((string)obj["msg"]); } else {//登錄成功 JObject result = (JObject)obj["result"]; sysInfoService.usertype = (UserType)Int32.Parse((string)result["type"]); //此處通過Mvvmlight機制進行異步消息通知,將成功的消息發送出去 SendMsg<String>("LoginOK",sysInfoService.usertype.ToString()); } IsLogining = false; }, (err) =>//接口調用失敗 { ShowMsg(err as String); IsLogining = false; }); }).Start(); }, () => { return !IsLogining; }); } }
注:這里關鍵代碼是異步消息通知(mvvmlight的強大機制),將成功消息發送即返回,在UI中或單元測試中進行消息處理,完全解耦了業務。
並且通過消息機制可以使用帶回調的消息體,實現通知回調功能(例如尋問用戶 點擊確定或取消 等類似功能),這里不再演示。
4.在locator中注冊服務與viewmodel:

public class BLLocator { public BLLocator() { ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default); //binding vm SimpleIoc.Default.Register<LoginVM>(); //binding service SimpleIoc.Default.Register<BackendService>(); SimpleIoc.Default.Register<ConfigService>(); SimpleIoc.Default.Register<SysInfoService>(); SimpleIoc.Default.Register<ReadCardService>(); } public LoginVM LoginVM { get { return ServiceLocator.Current.GetInstance<LoginVM>(); } } public static void Cleanup() { // TODO Clear the ViewModels } }
注:這里注冊了一些系統使用的服務和所有的viewmodel,機制類似於java中的spring,進行實例的管理,UI中直接綁定即可使用。
筆者在完成此筆記時,項目中已完成並進行了界面測試的服務與VM(使用的模式是一個UI對應一個VM)如下:
5.在UI項目下的app.xaml加入資源如下:
<!--這里使得在程序初始時實例化locator-->
<ResourceDictionary> <vm:BLLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:BL;assembly=BL" /> </ResourceDictionary>
6.創建Login ui,只需將相關datacontext綁定即可:

<Window x:Class="UI.Login" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:res="clr-namespace:UI.Properties" xmlns:utils="clr-namespace:UI.Utils" WindowStartupLocation="CenterScreen" Height="270.462" Width="393.461" Title="{x:Static res:Resources.LoginTitle}" DataContext="{Binding Source={StaticResource Locator},Path=LoginVM}" ResizeMode="NoResize" > <Window.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary> <utils:VisibleConvert x:Key="ShownConvert"></utils:VisibleConvert> </ResourceDictionary> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Window.Resources> <StackPanel Orientation="Vertical" Margin="10"> <Image Source="Resources/logoh.png" Height="80"></Image> <StackPanel Orientation="Horizontal" Margin="30,5"> <Label Width="70" HorizontalContentAlignment="Right">用戶名:</Label> <TextBox Width="240" Text="{Binding Path=UserName,NotifyOnTargetUpdated=True,UpdateSourceTrigger=PropertyChanged}"></TextBox> </StackPanel> <StackPanel Orientation="Horizontal" Margin="30,5"> <Label Width="70" HorizontalContentAlignment="Right" xml:space="preserve">密 碼:</Label> <PasswordBox Name="passBox" Width="240" PasswordChanged="PasswordBox_PasswordChanged"></PasswordBox> </StackPanel> <StackPanel Orientation="Horizontal" Margin="30,5,35,5"> <Button Command="{Binding LoginCmd}" Height="26" Width="53" Content="登錄" Margin="80,0,0,0" IsDefault="True"/> </StackPanel> <StackPanel> <ProgressBar IsIndeterminate="True" Visibility="{Binding Path=IsLogining, Converter={StaticResource ShownConvert}}" SmallChange="1" Maximum="50" EllipseDiameter="10" EllipseOffset="4" RenderTransformOrigin="0,0" Cursor="None" /> </StackPanel> </StackPanel> <Window>
注:這里使用了WPF強大的綁定機制,當然password做了一些特殊的處理,因為密碼框無法直接做綁定
圖中的圓點就是進度條
7.最后在Login.xaml.cs中響應VM發來的消息:

public partial class Login : Window { public Login() { InitializeComponent(); //監聽ViewModel發來的異步消息 Messenger.Default.Register<String>(this, "LoginOK", LoginOK); ; this.Unloaded+=(s,e)=>{ Messenger.Default.Unregister<String>("LoginOK"); }; } private void LoginOK(String msg) { //根據服務器返回的賬號角色進入不同的主界面 if(msg.Equals("CY")) { CYViews.MainWindow main = new CYViews.MainWindow(); this.Close(); main.Show(); } else if(msg.Equals("DY")) { DYViews.DYMainWindow main = new DYViews.DYMainWindow(); this.Close(); main.Show(); } else { ShowMessageBox("此角色暫不支持桌面端"); } } }
至此,使用Mvvmlight完成Login已經完成,通過代碼看出,BL層可以完全不與UI進行耦合,並且可以進行獨立的單元測試,包括需要用戶確認等對話框也完全可以以消息及回調的形式進行傳遞,也是可以獨立測試的。