写在前面
又是很久没更新文章了,前几天在和群友们讨论框架的时候,有个人遇到了这样的问题,他说我主程序打开了一个子窗口,对子窗口进行了修改之后,关闭子窗口,当再次打开时子窗口显示的数据是改动之后的数据。当时我看到这个问题,我觉得不可思议,不可能啊,我从来没遇到过这样的情况啊。后来在一个大佬的讲解下我才明白了事情的缘由,如果你也遇到了一样的情况或者刚刚接触MVVM框架,那么请往下看。
传统模式
因为我接触WPF并不是从0开始,我是接手了一个前辈写了一半的程序,所以我在使用MVVM的时候默认就从模仿他的使用思路,这就导致我对MVVM框架中大多基础和细节都半知半解。原来我在创建View和ViewModel并关联他们的时候一直使用的是传统模式。
例如下面这段代码
//MainView.xaml.cs
...
MainViewModel viewmodel;
public MainView()
{
viewmodel = new MainViewModel();
this.DataContext = viewmodel;
}
...
这样写,是由View主动创建ViewModel实例,并依赖他。(View依赖ViewModel)
好处是,每次打开这个View时,它对应的ViewModel都是新的,但这也可能成为他的负担,如果有大量的子窗口被重复的打开关闭,那么整个系统的效率就会下降,而且可能会遇到这样的情况,需要某个窗口是单例,也就是说,不管你打开关闭几次,它里面的内容都是时间上连续的。
依赖注入
我用的是Mvvmlighting,在安装的时候会自动生成ViewModel文件夹,和MainViewModel.cs和ViewModelLocator.cs,按照我以前的想法直接不管ViewModelLocator.cs文件,然后在MainWindow.xaml.cs中直接实例化MainViewModel类,做一个数据源绑定。但是这个ViewModelLocator.cs是用来做什么的,下面我们来分析一下他的代码。
//第一部分,注释内容
/*
In App.xaml:
<Application.Resources>
<vm:ViewModelLocator xmlns:vm="clr-namespace:ATTest"
x:Key="Locator" />
</Application.Resources>
In the View:
DataContext="{Binding Source={StaticResource Locator}, Path=ViewModelName}"
You can also use Blend to do all this with the tool's support.
See http://www.galasoft.ch/mvvm
*/
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
namespace ATTest.ViewModel
{
/// <summary>
/// This class contains static references to all the view models in the
/// application and provides an entry point for the bindings.
/// </summary>
public class ViewModelLocator
{
//第二部分,构造函数
/// <summary>
/// Initializes a new instance of the ViewModelLocator class.
/// </summary>
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
// Create design time view services and models
SimpleIoc.Default.Register<IDataService, DesignDataService>();
}
else
{
// Create run time view services and models
SimpleIoc.Default.Register<IDataService, DataService>();
}
SimpleIoc.Default.Register<MainViewModel>();
}
//第三部分,我也不知道是啥
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public static void Cleanup()
{
// TODO Clear the ViewModels
}
}
}
我们分三个部分来讲解这个文件。
第一部分
注释部分,很简单的两个信息:
一,它被作为一个全局变量在App.xaml中被定义。
二,告诉你怎么在View中使用它。
第二部分
构造函数部分,也是简单的两句代码
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default) 这句代码创建了一个IOC容器(IOC:控制反转,其实我也不懂,反正就是一个容器,在这个容器里面存储ViewModel中所有类的实例)
SimpleIoc.Default.Register() 这句代码看方法名也知道了,将类注册到这个容器当中,方便容器对他们进行统一管理
第三部分
我也不知道叫啥的部分,就是为了获取容器中对应类的实例。
关联
<Window.DataContext>
<Binding Path="Main" Source="{StaticResource Locator}"></Binding>
</Window.DataContext>
这样我们就可以依赖注入了,然后绑定对应的控件的依赖项属性。
写在结尾
其实很多同学会问这样写即麻烦又复杂,而且效果也和传统模式一样,为什么还要学呢。首先,不管用不用我们都要了解,万一以后在团队开发中要求使用这样的IOC模式那不是操蛋了,其次,这样绑定有助于了解C#的新特性,将依赖注入都交给容器,而且在一些特定的情况下需要窗口单例模式,就能够使用这样的写法,还有一个特性就是,这样写可以在设计的时候显示你绑定的内容,这就很nice了,不用写完一部分内容,还要编译运行项目才能知道窗口中某些控件绑定的对不对,这就是今天的内容,与君共勉!