之前学MVVM,从ViewModelBase,RelayCommand都是自己瞎写,许多地方处理的不好,接触到MVVMLigth后,就感觉省事多了。

那么久我现在学习MVVMLight的收获,简单完成以下一个Demo

Demo主要功能是:

  用户在登录界面登陆,登录成功后跳转到另一个页面,同时把登录时的用户信息作为参数传递过去,然后用户可以选择注销,注销时会弹出对话框,让用户选择是否真的注销,如果是,就真的注销,回退到               登录页面,否则就不做任何处理。

功能很简洁,接下来就用MVVMLight来实现,另外我的开发环境是vs2015,项目类型是windows10通用应用,由于mvvmlight并未对win10通用应用项目做适配,所以不会像wpf项目那样,在工程中自动添加ViewModel文件夹和全局资源ViewModelLocator,所以需要我们手动添加,不过这个过程也很简单。

一.为项目安装MVVMLightLibs(通过vs中的NuGet包管理器)

 

  安装成功后,就可以使用了。

二.创建ViewModelLocator

自己创建一个类ViewModelLocator可以直接放在项目根目录,也可以放在ViewModel里面,具体放哪儿仁者见仁,智者见智。我们后面用到的页面导航,ViewModel和View绑之间的定都是以这个类为基础

   public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
}
}

现在看起来很简单,待会会给他加东西的。

三.确定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基本上就是这五点。

哦。。差点把源代码忘了。。点击”我是代码“下载。

[uwp]MVVM之MVVMLight,一个登录注销过程的简单模拟的更多相关文章

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

    在前几章介绍了不少MVVM以及Mvvmlight实例,那实际企业开发中将以那种架构开发比较好?怎样分层开发才能节省成本? 本文特别分享实际企业项目开发中使用过的项目架构,欢迎参照使用!有不好的地方欢迎 ...

  2. WPF MVVM从入门到精通2:实现一个登录窗口

    原文:WPF MVVM从入门到精通2:实现一个登录窗口   WPF MVVM从入门到精通1:MVVM模式简介 WPF MVVM从入门到精通2:实现一个登录窗口 WPF MVVM从入门到精通3:数据绑定 ...

  3. win10 uwp MVVM 轻量框架

    如果在开发过程,遇到多个页面之间,需要传输信息,那么可能遇到设计的问题.如果因为一个页面内包含多个子页面和多个子页面之间的通信问题找不到一个好的解决方法,那么请看本文.如果因为ViewModel代码越 ...

  4. 传智播客JavaWeb day07、day08-自定义标签(传统标签和简单标签)、mvc设计模式、用户注册登录注销

    第七天的课程主要是讲了自定义标签.简单介绍了mvc设计模式.然后做了案例 1. 自定义标签 1.1 为什么要有自定义标签 前面所说的EL.JSTL等技术都是为了提高jsp的可读性.可维护性.方便性而取 ...

  5. MVVM、MVVMLight、MVVMLight Toolkit之我见

    原文:MVVM.MVVMLight.MVVMLight Toolkit之我见 我想,现在已经有不少朋友在项目中使用了MVVMLight了吧,如果你正在做WPF,Silverlight,Windows ...

  6. UWP开发之Mvvmlight实践七:如何查找设备(Mobile模拟器、实体手机、PC)中应用的Log等文件

    在开发中或者后期测试乃至最后交付使用的时候,如果应用出问题了我们一般的做法就是查看Log文件.上章也提到了查看Log文件,这章重点讲解下如何查看Log文件?如何找到我们需要的Packages安装包目录 ...

  7. 如何快速使用Access实现一个登录验证界面?

    大三上学期期末总结,嗯,没错,上学期,写在新学期开始,hhhh. 上学期末的时候信管班的一个同学问我会不会Access,能不能它实现一个登录验证界面,说实话,之前对Access只是有所耳闻,随便敷衍了 ...

  8. Django框架基于session的登录/注销实现

    博主最近想基于Django框架开发一个测试平台,第一版先实现查看下载自动化的测试报告文件 第一步:前端框架 网上选择一款开源boostrap的前端框架 AdminLTE,这里给个链接  https:/ ...

  9. 简单两步快速实现shiro的配置和使用,包含登录验证、角色验证、权限验证以及shiro登录注销流程(基于spring的方式,使用maven构建)

    前言: shiro因为其简单.可靠.实现方便而成为现在最常用的安全框架,那么这篇文章除了会用简洁明了的方式讲一下基于spring的shiro详细配置和登录注销功能使用之外,也会根据惯例在文章最后总结一 ...

随机推荐

  1. vconsole使用

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...

  2. 记录在Python2.7 x64 bit 下 PyQt5.8的编译过程

    由于工作需要使用python下面的Qt库.PyQt现在只提供针对Python3.X系列的PyQt,所有需要自己手动编译.防止忘记,特意写下随笔记录备忘. 工 作  环境:Python版本:Python ...

  3. css字体中英文对照表(转)

     在css文件中,我们常看到有些字体名称变成了乱码,这是由于网页开发者将中文字体的名字直接写成了中文,而css文件本身没有声明字符编码方式,查看时就出现了乱码.为了避免这种乱码状况出现,可以将css文 ...

  4. open方法读写文件

    vb使用open方法读写文件 (一)打开和关闭文件 1.顺序文件 打开顺序文件,我们可以使用Open语句.它的格式如下: Open pathname For [Input |Output |Appen ...

  5. 新手C#类、对象、字段、方法的学习2018.08.05

    类:具有相似属性和方法的对象的集合,如“人”是个类. 对象(实例):对象是具体的看得见摸得着的,如“张三”是“人”这个类的对象.(new Person()开辟了堆空间中,=开辟了栈空间,变量P存放在该 ...

  6. Hadoop之MapReduce学习笔记(一)

    主要内容:mapreduce整体工作机制介绍:wordcont的编写(map逻辑 和 reduce逻辑)与提交集群运行:调度平台yarn的快速理解以及yarn集群的安装与启动. 1.mapreduce ...

  7. Docker私有仓库registry的搭建及使用

    前言 由于Docker Hub公共仓库很多时候使用这并不是很方便,大分部因为网络的问题可能拉取的时候会很慢或者拉取不到,所以搭建一个本地的私有仓库. 准备 由于此篇文章是在Kubernetes集群安装 ...

  8. SELinux导致的docker启动失败

    安装docker yum install -y docker 启动docker systemctl start docker 报错 Job for docker.service failed beca ...

  9. unity3d开发实战《啪啪三国》技术详解!

     去年11月,上海火溶网络CEO王伟峰以其第一款3d手游产品<啪啪三国>为例,着重讲解了unity3D手机网游开发的经验,其中涉及了团队组成.人员要求.常见的unity3d开发遇到的坑及解 ...

  10. Python 入门学习(贰)文件/文件夹正则表达式批量重命名工具

    基于 Udacity 的 Python 入门课程 Programming Foundations with Python 基于 Python 2.7 思路 Project 2 是一个去除文件名中所有数 ...