在作MVVM各類框架對比以前,我以爲有必要先本身作一個簡單的MVVM實現案例比較好,這樣就能夠看到本身實現的時候有那些不方便的地方。而各類框架又是怎麼解決咱們這些麻煩的。git
案例介紹:用戶登陸畫面,沒有輸入用戶ID數據時按鈕不可用,輸入用戶ID數據後按鈕可使用。點擊按鈕獲取用戶名信息。github
案例下載:https://github.com/NewBLife/UWP/tree/master/MvvmDemoexpress
一、建立UWP空項目框架
將False改爲True,這應該都懂的函數
二、構建項目結構佈局
按照MVVM模式思想,通常都會包含Views,ViewModels,Models,若是項目比較複雜在ViewModel和Model之間還會有Service層。測試
複雜系統狀況:this
三、建立Binding基類spa
MVVM核心技能之一:綁定。若是是單向顯示數據的話直接使用類屬性就能夠,若是想交互雙向綁定的實現INotifyPropertyChanged接口。其中的PropertyChanged事件會通知UI改變綁定值狀態。設計
代碼以下:
using System; using System.ComponentModel; using System.Runtime.CompilerServices; namespace MvvmDemo.Common { /// <summary> /// Viewmodel基類,屬性雙向綁定基礎 /// </summary> public class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; /// <summary> /// 屬性變動通知 /// </summary> /// <param name="propertyName">屬性名</param> public void NotifyPropertyChanged([CallerMemberName] String propertyName = "") { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } }
四、建立Command基類
MVVM核心技能二:ICommand。這個的存在使咱們的UI邏輯能夠搬遷到其餘地方處理,給自動化單體測試與分工設計帶來了可能。按鈕的Click事件,Combox選擇,列表選擇等等均可以使用Command形式綁定到ViewModel的Command屬性作處理。
代碼以下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; namespace MvvmDemo.Common { public class DelegateCommand<T>: ICommand { /// <summary> /// 命令 /// </summary> private Action<T> _Command; /// <summary> /// 命令能否執行判斷 /// </summary> private Func<T, bool> _CanExecute; /// <summary> /// 可執行判斷結束後通知命令執行 /// </summary> public event EventHandler CanExecuteChanged; /// <summary> /// 構造函數 /// </summary> /// <param name="command">命令</param> public DelegateCommand(Action<T> command):this(command,null) { } /// <summary> /// 構造函數 /// </summary> /// <param name="command">命令</param> /// <param name="canexecute">命令可執行判斷</param> public DelegateCommand(Action<T> command,Func<T,bool> canexecute) { if(command==null) { throw new ArgumentException("command"); } _Command = command; _CanExecute = canexecute; } /// <summary> /// 命令執行判斷 /// </summary> /// <param name="parameter">判斷數據</param> /// <returns>斷定結果(True:可執行,False:不可執行)</returns> public bool CanExecute(object parameter) { return _CanExecute == null ? true : _CanExecute((T)parameter); } /// <summary> /// 執行命令 /// </summary> /// <param name="parameter">參數</param> public void Execute(object parameter) { _Command((T)parameter); } } }
五、建立ViewModel
脫離UI的數據處理中心。讓咱們能夠單獨編寫它的測試程序來完成UI測試。主要目的就是將DB數據整合爲用戶想看的數據。
代碼以下:
using MvvmDemo.Common; using MvvmDemo.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MvvmDemo.ViewModels { public class MainViewModel : ViewModelBase { private string _userId; private string _userName; private DelegateCommand<string> _loginCommand; /// <summary> /// 用戶名 /// </summary> public string UserId { get { return _userId; } set { _userId = value; NotifyPropertyChanged(); } } /// <summary> /// 用戶名 /// </summary> public string UserName { get { return _userName; } set { _userName = value; NotifyPropertyChanged(); } } /// <summary> /// 登錄命令 /// </summary> public DelegateCommand<string> LoginCommand { get { return _loginCommand ??(_loginCommand=new DelegateCommand<string>( s=> { UserName = new UserModel().GetUserName(s); }, s=>!string.IsNullOrEmpty(s) )); } } } }
六、建立View設置綁定
綁定6.0後有X:Bind,以前有Binding。x:Bind是編譯時肯定綁定對象,Binding是實行時肯定綁定對象。總得來講X:Bind的速度比Binding快,在程序運行前就能發現綁定錯誤的問題。
這裏直接就拿MainView作例子,更改下佈局
<Page x:Class="MvvmDemo.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:MvvmDemo" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" VerticalAlignment="Center" HorizontalAlignment="Center"> <Grid.RowDefinitions> <RowDefinition Height="50"/> <RowDefinition Height="50"/> <RowDefinition Height="50"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <TextBlock Text="用戶ID:" Grid.Row="0" Grid.Column="0" Width="100" /> <TextBlock Text="用戶名:" Grid.Row="1" Grid.Column="0" Width="100" /> <TextBox x:Name="txtUserID" Grid.Row="0" Grid.Column="1" Width="150" Text="{x:Bind VM.UserId,Mode=OneWay}" /> <TextBlock x:Name="txbUserName" Grid.Row="1" Grid.Column="1" Width="150" Text="{x:Bind VM.UserName,Mode=OneWay}" /> <Button x:Name="btnLogin" Content="Login" Grid.Row="2" Grid.ColumnSpan="2" Width="150" HorizontalAlignment="Center" Command="{x:Bind VM.LoginCommand,Mode=OneWay}" CommandParameter="{Binding Text,ElementName=txtUserID,Mode=OneWay}"/> </Grid> </Page>
後臺添加代碼
/// <summary> /// 可用於自身或導航至 Frame 內部的空白頁。 /// </summary> public sealed partial class MainPage : Page { public MainViewModel VM =>new MainViewModel(); public MainPage() { this.InitializeComponent(); this.DataContext = VM; } }
七、建立Model
因爲這裏沒有複雜邏輯,就不添加Service了。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MvvmDemo.Models { public class UserModel { public string GetUserName(string userid) { return string.Format("取得成功:{0}",userid); } } }
八、實行結果
輸入內容前按鈕自動不可用:
輸入內容後按鈕自動可用:
點擊Login按鈕:
九、總結
這種就是簡單的登錄實現,涉及到了MVVM的View,Viewmodel,Model,以及XBind的用法。6.0後有X:Bind,以前有Binding。x:Bind是編譯時肯定綁定對象,Binding是實行時肯定綁定對象。總得來講X:Bind的速度比Binding快,在程序運行前就能發現綁定錯誤的問題。登錄頁面的按鈕是隨着輸入自動判斷是否可用,這個就是Icommand一個方法Canexcute的做用。可見本身實現Mvvm模式也不是很難的事情,不過隨着項目的複雜加深就會有不少問題,好比Viewmode之間通訊等,這個時候纔是MVVM框架的優點所在。