以前學MVVM,從ViewModelBase,RelayCommand都是本身瞎寫,許多地方處理的很差,接觸到MVVMLigth後,就感受省事多了。windows
那麼久我如今學習MVVMLight的收穫,簡單完成如下一個Demoapp
Demo主要功能是:框架
用戶在登陸界面登錄,登陸成功後跳轉到另外一個頁面,同時把登陸時的用戶信息做爲參數傳遞過去,而後用戶能夠選擇註銷,註銷時會彈出對話框,讓用戶選擇是否真的註銷,若是是,就真的註銷,回退到 登陸頁面,不然就不作任何處理。async
功能很簡潔,接下來就用MVVMLight來實現,另外個人開發環境是vs2015,項目類型是windows10通用應用,因爲mvvmlight並未對win10通用應用項目作適配,因此不會像wpf項目那樣,在工程中自動添加ViewModel文件夾和全局資源ViewModelLocator,因此須要咱們手動添加,不過這個過程也很簡單。mvvm
一.爲項目安裝MVVMLightLibs(經過vs中的NuGet包管理器)ide
安裝成功後,就可使用了。函數
二.建立ViewModelLocator佈局
本身建立一個類ViewModelLocator能夠直接放在項目根目錄,也能夠放在ViewModel裏面,具體放哪兒仁者見仁,智者見智。咱們後面用到的頁面導航,ViewModel和View綁之間的定都是以這個類爲基礎學習
public class ViewModelLocator { public ViewModelLocator() { ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default); } }
如今看起來很簡單,待會會給他加東西的。this
三.肯定View
在這個demo中,咱們主要有LoginView和MainView兩個頁面,前者是登陸頁面,後者是登陸成功後的頁面。
LoginView的佈局代碼以下:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid Margin="24"> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <TextBox Grid.Row="0" Header="用戶名" Text="{Binding Username,Mode=TwoWay}"/> <TextBox Grid.Row="1" Header="密碼" Text="{Binding Password,Mode=TwoWay}"/> <TextBox Grid.Row="2"></TextBox> <Button Grid.Row="3" HorizontalAlignment="Stretch" Content="登陸" Command="{Binding LoginCommand}"></Button> </Grid> </Grid>
MainView的佈局代碼以下:
<Grid Margin="36" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <StackPanel> <TextBlock> <Run FontSize="36" Text="歡迎你:"></Run> <Run FontSize="72" Foreground="Purple" Text="{Binding Username,Mode=TwoWay}"></Run> </TextBlock> <TextBox></TextBox> </StackPanel> <Button Content="註銷" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Command="{Binding LogoffCommand}"></Button> </Grid>
佈局很簡單,也很好理解,可是咱們目前尚未把View和ViewModel綁定到一塊兒,由於咱們ViewModel還沒搞呢。
四.建立ViewModel
接下來就是ViewModel了。這個工程簡單,因此也只有兩個ViewModel,分別是LoginViewModel和MainViewModel,相信從名字就能看出他他們和View的對應關係了。
在LoginViewModel中,對應LoginView,添加Username和Password兩個屬性,而且添加一個命令LoginCommand.
必定要讓LoginViewModel繼承ViewModelBase。。(在紅色波浪線處敲擊鍵盤Shift+alt+F10自動添加命名空間哦)
public class LoginViewModel:ViewModelBase { private string _username; private string _password; public string Username { get { return _username; } set { Set(ref _username, value); } } public string Password { get { return _password; } set { Set(ref _password, value); } } public ICommand LoginCommand { get; set; } private void Login() { User model = new User { Username = Username.Trim(), Password = Password.Trim() }; if (true==AuthenticateHelper.Authenticate(model)) { var navigation = ServiceLocator.Current.GetInstance<INavigationService>(); navigation.NavigateTo("Main",model); ViewModelLocator.Main.User = model; } else { GalaSoft.MvvmLight.Messaging.Messenger.Default.Send<string>("用戶名或密碼錯誤!!!"); } } public LoginViewModel() { LoginCommand = new RelayCommand(Login); } }
除了Login方法的具體實現目前有些模糊外,其餘的理解起來都很容易。
至於MainViewModel就更簡單了
public class MainViewModel:ViewModelBase { private string _username; public string Username { get { return _username; } set { Set(ref _username, value); } } private Model.User _user; public User User { get { return _user; } set { _user = value; Username = value.Username; } } public MainViewModel() { Username = "CQ"; LogoffCommand = new RelayCommand(Logoff); } public ICommand LogoffCommand { get; set;} private void Logoff() { GalaSoft.MvvmLight.Messaging.Messenger.Default.Send<object>("確認註銷嗎?"); } }
裏面有兩點可能形成困惑,一是User屬性,它是一個Model類,用於頁面傳遞參數等;二是Logoff方法的實現,這個和Login方法同樣。待會兒再說。
五.Model
寫到這兒我本身都覺着一頭霧水了。。。沒辦法,繼續填坑
MVVM,咱們已經有了View和ViewModel,那麼最後一個Model呢?剛纔提過了,Model其實就是用來傳遞參數用一下,在這個demo中沒什麼大用處。
具體能夠下載源碼查看。
六.如今重頭戲纔來!!!
若是本身寫ViewModel和View的綁定,會怎麼寫呢?
大概是在View對應的.cs類構造函數中來一句
this.DataContext=new ViewModel();
這樣的確可行,可是在MVVMLight中,是經過SimpleIoc,用一個簡單的IOC容器來實現對實例的建立和銷燬。即有IOC容器爲咱們建立ViewModel實例,而後用它做爲View的數據源。
(這塊兒能夠具體百度一下。我也正在學習)
因此ViewModelLocator的代碼就成下面這個樣子了
public class ViewModelLocator { public ViewModelLocator() { ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default); SimpleIoc.Default.Register<LoginViewModel>(); SimpleIoc.Default.Register<MainViewModel>(); } public static LoginViewModel _login; public static LoginViewModel Login { get { if (_login == null) _login = ServiceLocator.Current.GetInstance<LoginViewModel>(); return _login; } } private static MainViewModel _main; public static MainViewModel Main { get { if (_main == null) _main = ServiceLocator.Current.GetInstance<MainViewModel>(); return _main; } } }
能夠看到,在構造函數中向SimpleIoc註冊了兩個給定的類型LoginViewModel和MainViewModel.
而後定義了兩個靜態的只讀屬性Main和Login。他們的就是用來和具體的View綁定用的。至於爲何是靜態的呢?目的是在頁面導航的時候方便傳遞參數而作的。
那麼究竟怎麼吧LoginView和Login綁定?(如下方法是mvvmlight框架默認的行爲,但在win10通用項目中沒有自動生成,因此手動來實現)
在App.xaml中添加一個全局資源,代碼以下
<Application x:Class="LoginDemo.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:LoginDemo" RequestedTheme="Light"> <Application.Resources> <ResourceDictionary> <local:ViewModelLocator x:Key="Locator"></local:ViewModelLocator> </ResourceDictionary> </Application.Resources> </Application>
這樣作的好處是在整個工程中均可以使用ViewModelLocator的實例。
接下來就是具體的綁定環節了。。咱們以LoginView和Login屬性的綁定爲例
LoginView中只須要添加以下代碼:
<Page.DataContext> <Binding Path="Login" Source="{StaticResource Locator}"></Binding> </Page.DataContext>
嗯,就這麼簡單,其實剛纔爲何把ViewModelLocator做爲全局資源,目的就是在其餘地方引用方便。
至此。demo已經能夠編譯運行了。(注意,在app.xaml.cs中,把啓動頁面設置爲LoginView)
可是卻並無實現登陸驗證和頁面導航的功能。。
不過不用擔憂,咱們主體框架已經搭好,其餘的都簡單了。
七.登陸驗證
在LoginViewModel中咱們見到了Login方法定義以下:
private void Login() { User model = new User { Username = Username.Trim(), Password = Password.Trim() }; if (true==AuthenticateHelper.Authenticate(model)) { var navigation = ServiceLocator.Current.GetInstance<INavigationService>(); navigation.NavigateTo("Main",model); ViewModelLocator.Main.User = model; } else { GalaSoft.MvvmLight.Messaging.Messenger.Default.Send<string>("用戶名或密碼錯誤!!!"); } }
首先把用戶名和密碼初始化一個User的實例,而後調用Authenticate方法驗證,若是驗證經過,就導航到MainView,若是失敗,就彈出一個消息框,說你失敗了!!!
由於是demo嘛。Authenticate我就簡單的用了一個if來判斷用戶名密碼是否和預約義的一致(預約義的就是「cq」,"cq"),一致就返回true,表示經過!不然就false..
這沒什麼好說的。重點仍是驗證經過後的導航和驗證失敗的消息提醒。
八.頁面導航
針對於導航,MVVMLight也有本身的一套方法,他提供了一個接口INavigationService和方法NavigationService,咱們要作的就是繼續到IOC容器中註冊「一個用於導航的工廠類型」
因而乎,ViewModelLocator變成了下面這樣
public class ViewModelLocator { public ViewModelLocator() { ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default); SimpleIoc.Default.Register<LoginViewModel>(); SimpleIoc.Default.Register<MainViewModel>(); var navigationService = this.InitNavigationService(); SimpleIoc.Default.Register(() => navigationService); } public INavigationService InitNavigationService() { NavigationService navigationService = new NavigationService(); navigationService.Configure("Login", typeof(LoginView)); navigationService.Configure("Main", typeof(MainView)); return navigationService; } public static LoginViewModel _login; public static LoginViewModel Login { get { if (_login == null) _login = ServiceLocator.Current.GetInstance<LoginViewModel>(); return _login; } } private static MainViewModel _main; public static MainViewModel Main { get { if (_main == null) _main = ServiceLocator.Current.GetInstance<MainViewModel>(); return _main; } } }
navigationService的Configure方法用來添加一個鍵值對,從而創建起一個字符串和View的對應關係。以便於在須要導航時,只須要傳遞一個對應的字符串,就能夠實現到具體頁面的導航。
var navigationService = this.InitNavigationService(); SimpleIoc.Default.Register(() => navigationService);
這兩行代碼是註冊」一個用於導航服務的工廠類型「到IOC容器中,須要導航的時候,經過ServiceLocator.Current.GetInstance<INavigationService>()獲取導航服務的工廠的實例,而後經過以前配置的不一樣的字符串來實現到不一樣頁面的額導航。
因此在登陸成功後,導航代碼以下:
var navigation = ServiceLocator.Current.GetInstance<INavigationService>(); navigation.NavigateTo("Main",model); ViewModelLocator.Main.User = model;//以前說定義Main爲靜態目的就是爲了方便傳遞參數,具體用途就在這兒
經過如上代碼,就能夠實現導航了。
九.怎麼彈出消息提示框呢?
在咱們code-behind模式中,咱們是這樣作的
await new MessageDialog("Hello world!").ShowAsync();
可是在MVVM中該怎麼作呢?顯然不能在ViewModel中寫吧。那應該就仍是寫在View對應的.cs文件中了。那麼寫在View對應的.cs中後,怎麼才能觸發它呢?
對此,咱們找MVVMLight的Messager來幫忙,能夠在Login中登陸失敗的if-else分支中,添加以下代碼
GalaSoft.MvvmLight.Messaging.Messenger.Default.Send<string>("用戶名或密碼錯誤!!!");
這表明發送的消息類型是字符串,消息內容就是」「號裏面的了。
只有發送不行啊。。。必須有個接收機制吧。。接收的怎麼寫呢?寫在哪兒呢?
在那兒接收,固然就寫在哪兒了。。須要在View中接收,就寫在View對應的Page的構造函數裏面。寫法以下:
public LoginView() { this.InitializeComponent(); GalaSoft.MvvmLight.Messaging.Messenger.Default.Register<string>(this, MessageBox); } private async void MessageBox(string msg) { await new MessageDialog(msg).ShowAsync(); }
對了,其實就是註冊一個對string類型消息的監聽(不知道用」監聽「這個詞好很差,實際是經過廣播進行的,具體能夠百度)
而後還要添加一個處理消息的委託方法MessageBox,沒錯,它裏面實現了彈出一個MessageDialog的功能。
值得注意的是,註冊消息的類型能夠是各類各樣的,但都是經過匹配具體的類型來實現消息廣播的。
九.上面是彈出一個消息框,那麼接下來就彈出一個能夠交互的消息框,啓示就一個確認和一個返回按鈕。。
按理來講,這個應該是點擊註銷的時候彈出的確認對話框,因此把註冊這個消息的代碼應該放在MainView對應得.cs文件中。
具體以下:
public MainView() { this.InitializeComponent(); GalaSoft.MvvmLight.Messaging.Messenger.Default.Register<object>(this,true, LogoffMessage); } public async void LogoffMessage(object param) { MessageDialog msg = new MessageDialog(param as string); UICommand yes = new UICommand("肯定", (o) => { var navigation = ServiceLocator.Current.GetInstance<INavigationService>(); navigation.GoBack(); }); UICommand no = new UICommand("返回", (o) => { }); msg.Commands.Add(yes); msg.Commands.Add(no); var re=await msg.ShowAsync(); if (re == yes) { GalaSoft.MvvmLight.Messaging.Messenger.Default.Unregister<object>(this, LogoffMessage); } }
和以前基本無差異,只不過是把以前的MessaeDialog多加了兩個UICommand而已。。
但有兩點要注意:
1.咱們註冊兩個消息,類型應該要不同,不然會串的。。。
2.但完成註銷工做後,記得取消對消息的監聽,不然第二次註冊的時候會彈出兩次對話框的!!
至此。整個Demo就完成了。。。嗯。感受寫的還挺爛的。
最後,我把關鍵點提一下:
1.ViewModelBase
2.RelayCommand
3.Messager
4.NavigationService
5.SimpleIoc基本上就是這五點。
哦。。差點把源代碼忘了。。點擊」我是代碼「下載。