Xamarin.Android和UWP之MVVM的简单使用

Posted Catcher Wong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Xamarin.Android和UWP之MVVM的简单使用相关的知识,希望对你有一定的参考价值。

0x01 前言

就目前而言,MVVM可以说是挺流行的,无论是web端还是移动端,web端的主要代表angularjs,avalonjs等,

移动端(xamarin,uwp)的代表应该是mvvmlight,mvvmcross等,

我们的主题是移动端,所以主要讲mvvmlight,mvvmcross,这篇主要讲MvvmLight,下篇讲MvvmCross。

还是以Demo的形式来谈使用。

 

0x02 简单的MVVM(mvvmlight) Demo

先来个web版最简单的MVVM效果,然后在按xamarin.android->uwp的顺序做一样效果的demo

注:这个效果是基于 avalonjs的

 

下面来看看我们的第一个例子(Xamarin.Android):

新建一个Android项目Catcher.MVVMDemo.Day01DroidByMvvmLight

通过NuGet安装相关组件(MvvmLight)。

然后编写我们的Main.axml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:orientation="vertical"
 4     android:layout_width="fill_parent"
 5     android:layout_height="fill_parent">
 6     <EditText
 7         android:layout_width="fill_parent"
 8         android:layout_height="wrap_content"
 9         android:id="@+id/et_input" />
10     <TextView
11         android:layout_width="fill_parent"
12         android:layout_height="wrap_content"
13         android:id="@+id/tv_input" />
14 </LinearLayout>

 

然后去修改MainActivity

 1 using Android.App;
 2 using Android.OS;
 3 using Android.Widget;
 4 using GalaSoft.MvvmLight.Helpers;
 5 using GalaSoft.MvvmLight.Views;
 6 namespace Catcher.MVVMDemo.Day01DroidByMvvmLight
 7 {
 8     [Activity(Label = "MvvmLightDemo", MainLauncher = true, Icon = "@drawable/icon")]
 9     public class MainActivity : ActivityBase
10     {        
11         EditText etInput;
12         TextView tvInput;
13         protected override void OnCreate(Bundle bundle)
14         {
15             base.OnCreate(bundle);
16             SetContentView(Resource.Layout.Main);
17             etInput = FindViewById<EditText>(Resource.Id.et_input);
18             tvInput = FindViewById<TextView>(Resource.Id.tv_input);
19             
20             this.SetBinding(() => etInput.Text, () => tvInput.Text);
21         }
22     }
23 }

 

MainActivity是继承ActivityBase,同时将输入的值绑定在TextView上。

效果图如下:

 

第二个例子(UWP):

新建一个Universal Windows项目:Catcher.MVVMDemo.Day01UWP

修改我们的MainPage.xaml

 1 <Page
 2     x:Class="Catcher.MVVMDemo.Day01UWP.MainPage"
 3     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 4     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 5     xmlns:local="using:Catcher.MVVMDemo.Day01UWP"
 6     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 7     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 8     mc:Ignorable="d">
 9     
10     <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
11         <StackPanel VerticalAlignment="Top">
12             <TextBox x:Name="txtName"/>
13             <TextBlock Text="{Binding ElementName=txtName,Path=Text}"/>
14         </StackPanel>
15     </Grid>
16 </Page>

 

这里直接在页面通过Binding来绑定了。相比Android简洁了不少。

效果如下:

 

到这里,这两个简单的例子已经OK了,你是不是也想动手试试呢!

不过这两个例子并没有涉及到Mvvm主要的东西。至少连ViewModel的影子都还没出现呢。

0x03 MVVM(mvvmlight) 登陆Demo

开始之前,我们新建一个类库项目Catcher.MVVMDemo.Day01Core

这个类库是后面的2个例子都要用到的,处理我们的ViewModel。

通过NuGet安装MvvmLight

在ViewModel文件夹下面添加一个LoginViewModel

 1 using GalaSoft.MvvmLight;
 2 using GalaSoft.MvvmLight.Command;
 3 using GalaSoft.MvvmLight.Messaging;
 4 using GalaSoft.MvvmLight.Views;
 5 using Microsoft.Practices.ServiceLocation;
 6 using System.Diagnostics;
 7 namespace Catcher.MVVMDemo.Day01Core.ViewModel
 8 {
 9     public class LoginViewModel : ViewModelBase
10     {
11         public LoginViewModel()
12         {
13         }
14         private string _name;
15         public string Name
16         {
17             get
18             {
19                 return _name;
20             }
21             set
22             {
23                 _name = value; 
24                 //RaisePropertyChanged("Name");
25                 RaisePropertyChanged(() => Name);
26             }
27         }
28         private string _password;
29         public string Password
30         {
31             get
32             {
33                 return _password;
34             }
35             set
36             {
37                 _password = value;
38                 RaisePropertyChanged(() => Password);
39             }
40         }
41         /// <summary>
42         /// login command
43         /// </summary>
44         public RelayCommand LoginCommand
45         {
46             get
47             {
48                 return new RelayCommand(() => Login());
49             }
50         }
51         /// <summary>
52         /// login
53         /// </summary>
54         private void Login()
55         {
56             //Valid the user
57             if (Name == "catcher" && Password == "123")
58             {
59                 var nav = ServiceLocator.Current.GetInstance<INavigationService>();
60                 nav.NavigateTo("Main");
61             }
62             else
63             {
64                 var dialog = ServiceLocator.Current.GetInstance<IDialogService>();
65                 dialog.ShowMessage(
66                     "check your name and password",
67                     "infomation",
68                     "OK",
69                     null);
70             }
71         }
72     }
73 }

这里的登陆是写死了一个用户名和密码。

同时,修改我们的ViewModelLocator,添加我们LoginViewModel的信息

 1 using GalaSoft.MvvmLight.Ioc;
 2 using Microsoft.Practices.ServiceLocation;
 3 namespace Catcher.MVVMDemo.Day01Core.ViewModel
 4 {
 5     public class ViewModelLocator
 6     {
 7         public ViewModelLocator()
 8         {
 9             //provider
10             ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
11             //view model 
12             SimpleIoc.Default.Register<MainViewModel>();
13             SimpleIoc.Default.Register<LoginViewModel>();
14         }
15         public MainViewModel Main
16         {
17             get
18             {
19                 return ServiceLocator.Current.GetInstance<MainViewModel>();
20             }
21         }
22         public LoginViewModel LoginViewModel
23         {
24             get
25             {
26                 return ServiceLocator.Current.GetInstance<LoginViewModel>();
27             }
28         }
29         public static void Cleanup()
30         {
31         }
32     }
33 }

到这里,我们将ViewModel的相关处理做好了。

下面两个例子就是添加一个登陆页面,提供验证,登陆成功就跳转到我们前面两个例子的页面,不成功就弹框提示。

第三个例子(Xamarin.Android):

在刚才的Catcher.MVVMDemo.Day01DroidByMvvmLight中,添加一个App.cs,主要是注册一些东西

 1 using Catcher.MVVMDemo.Day01Core.ViewModel;
 2 using GalaSoft.MvvmLight.Views;
 3 using GalaSoft.MvvmLight.Ioc;
 4 namespace Catcher.MVVMDemo.Day01DroidByMvvmLight
 5 {
 6     public static class App
 7     {
 8         private static ViewModelLocator _locator;
 9         public static ViewModelLocator Locator
10         {
11             get
12             {
13                 if (_locator == null)
14                 {                    
15                     var nav = new NavigationService();
16                     nav.Configure("Main", typeof(MainActivity));
17                    
18                     SimpleIoc.Default.Register<INavigationService>(() => nav);
19                     //the dialog
20                     SimpleIoc.Default.Register<IDialogService, DialogService>();
21                     _locator = new ViewModelLocator();
22                 }
23                 return _locator;
24             }
25         }
26     }
27 }

 

添加一个login.axml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:orientation="vertical"
 4     android:layout_width="match_parent"
 5     android:layout_height="match_parent">
 6     <EditText
 7         android:layout_width="match_parent"
 8         android:layout_height="wrap_content"
 9         android:hint="enter your name"
10         android:id="@+id/et_name" />
11     <EditText
12         android:layout_width="match_parent"
13         android:layout_height="wrap_content"
14         android:inputType="textPassword"
15         android:hint="enter your password"
16         android:id="@+id/et_pwd" />
17     <Button
18         android:layout_width="match_parent"
19         android:layout_height="wrap_content"
20         android:text="Login"
21         android:id="@+id/btn_login" />
22 </LinearLayout>

 

添加一个LoginActivity,与LoginViewModel相适配。

 1 using Android.App;
 2 using Android.OS;
 3 using Android.Widget;
 4 using Catcher.MVVMDemo.Day01Core.ViewModel;
 5 using GalaSoft.MvvmLight.Helpers;
 6 using GalaSoft.MvvmLight.Views;
 7 namespace Catcher.MVVMDemo.Day01DroidByMvvmLight
 8 {
 9     [Activity(Label = "Login", MainLauncher = true, Icon = "@drawable/icon")]
10     public class LoginActivity : ActivityBase
11     {
12         /// <summary>
13         /// the view model
14         /// </summary>
15         public LoginViewModel VM
16         {
17             get { return App.Locator.LoginViewModel; }
18         }
19         protected override void OnCreate(Bundle savedInstanceState)
20         {
21             base.OnCreate(savedInstanceState);
22             SetContentView(Resource.Layout.login);
23             EditText etName = FindViewById<EditText>(Resource.Id.et_name);
24             EditText etPassword = FindViewById<EditText>(Resource.Id.et_pwd);
25             Button btnLogin = FindViewById<Button>(Resource.Id.btn_login);
26             //binding
27             this.SetBinding(() => VM.Name, etName, () => etName.Text, BindingMode.TwoWay);
28             this.SetBinding(() => VM.Password, etPassword, () => etPassword.Text, BindingMode.TwoWay);
29             //button click
30             btnLogin.SetCommand("Click", VM.LoginCommand);
31         }
32     }
33 }

VM通过App.cs里面的来获取。

两个输入框的绑定方式设为TwoWay。

按钮的点击事件设为LoginViewModel的LoginCommand。

最后去掉MainActivity的MainLauncher=true

 

效果图如下:

 

第四个例子(UWP):

在刚才的Catcher.MVVMDemo.Day01UWP中,添加一个LoginPage.xaml

 1 <Page
 2     x:Class="Catcher.MVVMDemo.Day01UWP.LoginPage"
 3     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 4     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 5     xmlns:local="using:Catcher.MVVMDemo.Day01UWP"    
 6     xmlns:vm="using:Catcher.MVVMDemo.Day01Core.ViewModel"
 7     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 8     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 9     mc:Ignorable="d">
10     <Page.DataContext>
11         <vm:LoginViewModel />
12     </Page.DataContext>
13     <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
14         <Grid.RowDefinitions>
15             <RowDefinition Height="*"></RowDefinition>
16             <RowDefinition Height="*"></RowDefinition>
17             <RowDefinition Height="*"></RowDefinition>
18             <RowDefinition Height="*"></RowDefinition>
19             <RowDefinition Height="5*"></RowDefinition>
20         </Grid.RowDefinitions>
21         <TextBox Grid.Row="1" Margin="15" Height="20" Text="{Binding Name,Mode=TwoWay}" PlaceholderText="enter you name" />
22         <PasswordBox Grid.Row="2" Margin="15" Height="20" Password="{Binding Password,Mode=TwoWay}" PasswordChar="*" PlaceholderText="enter your password" />
23         <Button Grid.Row="3" Margin="15,10" Content="Login" Command="{Binding LoginCommand}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"   />
24     </Grid>
25 </Page>

通过Page.DataContext设置了ViewModel

对TextBox,PasswordBox和button进行了相应的绑定。

 

然后修改App.xaml.cs中的OnLaunched方法,主要是启动页面和注册MvvmLight的东西

 1        protected override void OnLaunched(LaunchActivatedEventArgs e)
 2         {
 3 #if DEBUG
 4             if (System.Diagnostics.Debugger.IsAttached)
 5             {
 6                 this.DebugSettings.EnableFrameRateCounter = true;
 7             }
 8 #endif
 9             Frame rootFrame = Window.Current.Content as Frame;
10             if (rootFrame == null)
11             {
12                 rootFrame = new Frame();
13                 rootFrame.NavigationFailed += OnNavigationFailed;
14                 if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
15                 {
16                 }
17                 
18                 Window.Current.Content = rootFrame;
19             }
20             if (e.PrelaunchActivated == false)
21             {
22                 if (rootFrame.Content == null)
23                 {                    
24                     rootFrame.Navigate(typeof(LoginPage), e.Arguments);
25                 }
26                 
27                 Window.Current.Activate();
28                 //
29                 ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
30                 var navigationService = new NavigationService();
31                 navigationService.Configure("Login", typeof(LoginPage));
32                 navigationService.Configure("Main", typeof(MainPage));
33                 SimpleIoc.Default.Register<INavigationService>(() => navigationService);
34                 SimpleIoc.Default.Register<IDialogService, DialogService>();
35             }
36         }

 

效果图:

 

0x04 简单总结

对于Android来说,主要以下几个点:

1.Activity是继承了MvvmLight自己实现的ActivityBase,具体如下:

 1 namespace GalaSoft.MvvmLight.Views
 2 {
 3     public class ActivityBase : Activity
 4     {
 5         public ActivityBase();
 6         public static ActivityBase CurrentActivity { get; }
 7         public static void GoBack();
 8         protected override void OnResume();
 9     }
10 }

2.ViewModel继承ViewModelBase这个抽象类,在深究必然离不开INotifyPropertyChanged这个接口。

1    public abstract class ViewModelBase : ObservableObject, ICleanup
2 
3     public class ObservableObject : INotifyPropertyChanged

3.在ViewModelLocator里面通过SimpleIoc注册我们的ViewModel,当然也可以用Autofac等。

4.SetBinding和SetCommand的应用,可以看看具体的实现

 

对UWP来说,除了公共部分,与Android的区别就是在xaml中绑定了属性和“事件”。

 

下一篇会讲讲MvvmCross的简单使用。

 

以上是关于Xamarin.Android和UWP之MVVM的简单使用的主要内容,如果未能解决你的问题,请参考以下文章

UWP开发之Mvvmlight实践九:基于MVVM的项目架构分享

无法使用 Xamarin Android/Uwp 在 USB 闪存驱动器中写入文件

Xamarin.Forms 手势密码实现

我可以在不使用gradle的情况下使用Xamarin.Android中的Android数据绑定库吗?不是MVVM for dotnet

带有原生 Android 工具栏的 Xamarin 表单

win10 uwp MVVM入门