[uwp]MVVM之MVVMLight,一個登陸註銷過程的簡單模擬

以前學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);
        }
    }
View Code

除了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>("確認註銷嗎?");
        }
    }
View Code

裏面有兩點可能形成困惑,一是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基本上就是這五點。

 

哦。。差點把源代碼忘了。。點擊」我是代碼「下載。

相關文章
相關標籤/搜索