在做MVVM各種框架對比之前,我覺得有必要先自己做一個簡單的MVVM實現案例比較好,這樣就可以看到自己實現的時候有那些不方便的地方。而各種框架又是怎麼解決我們這些麻煩的。
1、創建UWP空項目
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));
}
}
}
}
4、創建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);
}
}
}
5、創建ViewModel
代碼如下:
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)
));
}
}
}
}
6、創建View設置綁定
這裡直接就拿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;
}
}
7、創建Model
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);
}
}
}
8、實行結果
輸入內容前按鈕自動不可用:

輸入內容後按鈕自動可用:

點擊Login按鈕:

9、總結
這種就是簡單的登陸實現,涉及到了MVVM的View,Viewmodel,Model。這裡按鈕是自動判斷是否可用,這個判斷實現就在我們的Viewmodel。例子中涉及到了Xbind,這個和Binding是有區別的,具體說明下節在續。可見自己實現Mvvm模式也不是很難的事情,不過隨著項目的復雜加深就會有很多問題,比如Viewmode之間通信等。