其中还依赖了一个微软的DLL——Microsoft.Practices.ServiceLocation.dll。
在使用过程中,**可能遇到的问题:**就是可能是由于MvvmLight没有及时更新,如果将图中的CommonServiceLocator升级到最新的2.x版本之后,它所依赖的ServiceLication的名称空间变为了CommonServiceLocator,导致安装MvvmLight之后自动生成的代码就出现了错误,如果你使用了最新版本,那么就需要手动修改一下这个名称空间:
安装完成MvvmLight之后,系统会自动生成一个ViewModel文件夹以及MainViewModel和ViewModelLocator两个文件,并将ViewModelLocator添加到了全局资源App.xaml中:
<Application x:Class="MvvmLightDemo.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:MvvmLightDemo" StartupUri="MainWindow.xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" d1p1:Ignorable="d"
xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006">
<Application.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:MvvmLightDemo.ViewModel" />
ResourceDictionary>
Application.Resources>
Application>
这个类型有什么作用,要把它放到全局资源中?
///
/// This class contains static references to all the view models in the
/// application and provides an entry point for the bindings.
/// 这个类型的用处就是包含了所有的View和ViewModel之间的关联,使得View和ViewModel不再直接引用;
///
public class ViewModelLocator
{
///
/// Initializes a new instance of the ViewModelLocator class.
///
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
// 在Visual Studio中设计预览Xaml文件的时候,在这里注册会起作用;
SimpleIoc.Default.Register<MainViewModel>();
}
else
{
// 在运行时,在这里注册会起作用;
SimpleIoc.Default.Register<MainViewModel>();
}
// 更多的时候,默认在这里注册就可以了;
SimpleIoc.Default.Register<MainViewModel>();
}
///
/// 这里对应的是View中DataContext需要的依赖属性;
/// 就是这个Main属性,关联起了View和ViewModel;
/// DataContext="{Binding Source={StaticResource Locator}, Path=Main}
///
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public static void Cleanup()
{
// TODO Clear the ViewModels
}
}
它继承自父类ObservableObject,实现了INotifyPropertyChanged和ICleanup,以实现:
在使用的过程中,需要我们调用ViewModelBase为我们提供的Set函数在属性的Set当中调用,即可实现通知View层的效果:
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
public class MainViewModel : ViewModelBase
{
private string m_LabelShow;
public string Label1Show
{
get { return m_LabelShow; }
// MvvmLight实现的Set方法,好处就是不用自己实现RaisePropertyChanged函数了;
set { Set(ref m_LabelShow, value); }
}
///
/// Initializes a new instance of the MainViewModel class.
///
public MainViewModel()
{
Label1Show = "ShowOne";
}
}
至于MvvmLight的实现原理,以下代码可以表明MvvmLight到底做了些什么:
///
/// 写一个一个轻量级MVVM框架,以帮助理解MVVM到底是什么;
/// INotifyPropertyChanged接口实现了当属性发生变化的时候,可以及时发出通知
///
public class MvvmBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
///
/// 属性发生改变时调用该方法发出通知;
///
/// 属性名称
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
protected virtual void SetAndNotifyIfChanged<T>(string propertyName, ref T oldValue, T newValue)
{
if (oldValue == null && newValue == null) return;
if (oldValue != null && oldValue.Equals(newValue)) return;
if (newValue != null && newValue.Equals(oldValue)) return;
oldValue = newValue;
RaisePropertyChanged(propertyName);
}
}
RelayCommand翻译过来就是“依赖命令”,相对于依赖属性,该类型主要作用就是让ViewModel层的类型具有处理依赖命令的能力。如果一个控件中包含了一个Icommand接口,那么就可以在MvvmLight架构下的VM层使用RelayCommand对依赖命令进行实现了
以下这个例子是利用RelayCommand实现一个前进后退按钮,可以导航控制Page页:
利用MvvmLight实现的优势就是:
View端:
<Window x:Class="MvvmLightDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MvvmLightDemo"
xmlns:vm="clr-namespace:MvvmLightDemo.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
DataContext="{Binding Source={StaticResource Locator}, Path=Main}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10*">RowDefinition>
<RowDefinition Height="90*">RowDefinition>
Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0" >
<Button Command="{Binding NavToPrePage}">Nav To PrePageButton>
<Button Command="{Binding NavToNextPage}">Nav To NextPageButton>
StackPanel>
<Frame Content="{Binding MainWindowFrame}" Grid.Row="1" NavigationUIVisibility="Hidden">Frame>
Grid>
Window>
ViewModel端:
public class MainViewModel : ViewModelBase
{
///
/// 导航到下一页面按钮的依赖命令;
///
public RelayCommand NavToNextPage { get; set; }
///
/// 导航到上一页面按钮的依赖命令;
///
public RelayCommand NavToPrePage { get; set; }
///
/// 主页Frame;
///
private Frame m_MainWindowFrame;
public Frame MainWindowFrame
{
get { return m_MainWindowFrame; }
set { Set(ref m_MainWindowFrame, value); }
}
///
/// 主标签页;
///
private Page m_MPage;
public Page MPage
{
get { return m_MPage; }
set { Set(ref m_MPage, value); }
}
///
/// 附标签页;
///
private Page m_AttachPage;
public Page AttachPage
{
get { return m_AttachPage; }
set { Set(ref m_AttachPage, value); }
}
///
/// Initializes a new instance of the MainViewModel class.
///
public MainViewModel()
{
NavToNextPage = new RelayCommand(NavNextPage); // 导航到下一个Page页;
NavToPrePage = new RelayCommand(NavPrePage);
MPage = new MainPage();
AttachPage = new PageTwo();
MainWindowFrame = new Frame();
MainWindowFrame.NavigationUIVisibility = System.Windows.Navigation.NavigationUIVisibility.Hidden;
MainWindowFrame.Content = MPage;
}
///
/// 处理点击下一页;
///
private void NavNextPage()
{
MainWindowFrame.Content = AttachPage;
}
///
/// 处理点击上一页;
///
private void NavPrePage()
{
MainWindowFrame.Content = MPage;
}
}
源代码可参见:https://github.com/visiontrail/CSharpKnowledge 在工程MvvmLightDemo工程当中,使用VS2015/VS2017编译