个人技术分享

介绍

MVVMLight是一个实现MVVM模式的轻量级框架(相对于Prism),能够更好的帮助我们开发WPF Windows Phone、Windows 8、SilverLight相关项目。

安装

在NuGet 工具箱上搜索MVVMLight进行下载

下载后它帮你生成ViewModel文件夹,里面包含MainViewModel和ViewModelLocator文件,然后需要自己建立一个完整的架构目录。

当安装完成之后由于版本原因在ViewModelLocator文件中的using引用会报错进而导致程序无法执行,所以需要修改引用。

将报错的using Microsoft.Practices.ServiceLocation; 引用修改为using CommonServiceLocator;

分层架构

1、View负责前端展示,与ViewModel进行数据和命令的交互。

2、ViewModel,负责前端视图业务级别的逻辑结构组织,并将其反馈给前端。

3、Model,主要负责数据实体的结构处理,与ViewModel进行交互。

MVVMLight架构的使用

1.在Model写一个实体对象用于和ViewModel交互

在MVVMLight中使用ObservableObject类实现实时监听数据变更。

ObservableObject,这个父类的作用就是保证能够检测属性是否被改变。它实现了INotifyPropertyChanged接口,通过触发PropertyChanged事件达到通知UI更改的目的,所以我们在定义实体对象的时候,只需要调用RaisePropertyChanged(PropertyName)就可以进行属性更改通知了。

public class WelcomeModel : ObservableObject
        {
            private String introduction;
            public String Introduction
            {
                get { return introduction; }
                set { introduction = value; RaisePropertyChanged(()=>Introduction); }
            }
        }

2.写一个ViewModel用于和View交互

此类继承了ViewModelBase父类,ViewModelBase同时继承 ObservableObject类和ICleanup接口。所以他同样有INotifyPropertyChanged接口的能力,能够通过触发PropertyChanged事件达到通知View的目的;构造函数中对 Welcome 属性进行了实例化。

public class WelcomeViewModel:ViewModelBase
        {
            /// <summary>
            /// 构造函数
            /// </summary>
            public WelcomeViewModel()
            {
                Welcome = new WelcomeModel() { Introduction = "Hello World!" };
            }
            #region 属性
    
            private WelcomeModel welcome;
            /// <summary>
            /// 欢迎词属性
            /// </summary>
            public WelcomeModel Welcome
            {
                get { return welcome; }
                set { welcome = value; RaisePropertyChanged(()=>Welcome); }
    
                M1 = new RelayCommand(f1);// 给指令赋值
            }
            
            public RelayCommand M1 { get;set; }
            // 回调函数 无返回值
            public void f1()
            {
                MessageBox.Show("f1");
            }
            #endregion
        }

3.写一个View,来显示数据和交互ViewModel。

TextBlock 绑定了 Welcome.Introduction,所以应该显示Welcome对象下的Introduction属性。   

<Window x:Class="MVVMLightDemo.View.WelcomeView"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="WelcomeView" Height="300" Width="300">
        <Grid>
            <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" >
                <TextBlock Text="{Binding Welcome.Introduction}" FontSize="30" ></TextBlock>
                <Button Width="200" Command="{Binding M1}" Height="40">点击</Button>
            </StackPanel>
        </Grid>
    </Window>

把 WelcomeViewModel 赋值给当前视图的数据上下文。所以可以在当前视图中使用ViewModel中所有的公开属性和命令。

    using MVVMLightDemo.ViewModel;
    using System.Windows;
    
    namespace MVVMLightDemo.View
    {
        /// <summary>
        /// Interaction logic for WelcomeView.xaml
        /// </summary>
        public partial class WelcomeView : Window
        {
            public WelcomeView()
            {
                InitializeComponent();
                this.DataContext = new WelcomeViewModel();
            }
        }
    }

构造器

所以每次App初始化的时候,就会去初始化ViewModelLocator类。

实际上他就是一个很基本的视图模型注入器。在构造器中把使用到的ViewModel统一注册,并生成单一实例。然后使用属性把它暴露出来,每当我们访问属性的时候,就会返回相应的ViewModel实例。

第一步在APP.xaml中添加引用

<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />

    <Application x:Class="MVVMLightDemo.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 StartupUri="View/WelcomeView.xaml"
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                 d1p1:Ignorable="d"
                 xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006"
                 xmlns:vm="clr-namespace:MVVMLightDemo.ViewModel" >
      <Application.Resources>
        <ResourceDictionary>
                <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
        </ResourceDictionary>
      </Application.Resources>
    </Application>

第二步在ViewModelLocator类中注册对象并且实例化

下文中实例化的MainViewModel就是用于和View交互ViewModel中的类

namespace MVVMLightDemo.ViewModel
    {
        public class ViewModelLocator
        {
            /// <summary>
            /// Initializes a new instance of the ViewModelLocator class.
            /// </summary>
            public ViewModelLocator()
            {
                ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
    
                // 1.先注册对应的vm对象
                SimpleIoc.Default.Register<MainViewModel>();
            }
            // 2.实例化
            #region 实例化
            public MainViewModel Main
            {
                get
                {
                    return ServiceLocator.Current.GetInstance<MainViewModel>();
                }
            }
    
            #endregion
    
            public static void Cleanup()
            {
                // TODO Clear the ViewModels
            }
        }
    }

第三步在xaml中绑定

DataContext="{Binding Source= {StaticResource Locator},Path=Main}"

我们可以将后台CS中的 this.DataContext = new WelcomeViewModel(); 可以去掉了,直接在WelcomeView中这样写:

DataContext="{Binding Source={StaticResource Locator},Path=Welcome}",

这种写法当我们绑定到数据,编译之后就会立马呈现

服务端开发人员可以专心写ViewModel的业务逻辑代码,UI开发人员可以专注设计视图了,同样 ViewModel可以绑定到不同的视图上,所以从这边就可以体现出他其中的三个重要特性:低耦合、可重用性、独立开发。 ViewModelLocator 类中还有个 ClearnUp()方法,主要目的用来清除ViewModel实例的。ViewModelBase继承了GalaSoft.MvvmLight.ICleanup接口,并在自己的类中写好了Cleanup()虚方法。所以我们在实例ViewModel类中可以重写Cleanup()来达到清除当前实例的目的。

伟大的双向绑定

场景: 当数据发生变化时 数据源跟着发生变化 进而触发数据更新,另一条绑定数据源的数据也会跟着发生变化

下文中UpdateSourceTrigger属性的作用是 当做何种改变的时候通知数据源我们做了改变。

     <StackPanel Orientation="Vertical" Margin="10,10,0,0">
                <StackPanel Margin="0,10,0,0" Orientation="Horizontal" >
                    <TextBlock Text="Hello " ></TextBlock>
                    <TextBlock Text="{Binding UserInfo.UserName}" ></TextBlock>
                </StackPanel>
    
                <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal" >
                </StackPanel>
    
            </StackPanel>

(当修改输入框的内容的时候,对应绑定数据相应改变,并触发对UI的修改,所以下面那行文字也相应改变改变。)

UpdateSourceTrigger属性:

枚举类型 效果  
Default 默认值(默认为LostFocuse)
Explicit 当应用程序调用 UpdateSource 方法时生效
LostFocus 失去焦点的时候触发
PropertyChanged 数据属性改变的时候触发

这边我们直接使用 PropertyChanged,当UI数据改变的时候,我们再通知到数据源去做修改。

还有一个属性就是Mode,他有五个参数:

枚举类型 效果
OneWay 源发生变化,数据就会从源流向目标
OneTime 绑定会将数据从源发送到目标;但是,仅当启动了应用程序或 DataContext 发生更改时才会如此操作,因此,它不会侦听源中的更改通知。
OneWayToSource 绑定会将数据从目标发送到源
TwoWay 绑定会将源数据发送到目标,但如果目标属性的值发生变化,则会将它们发回给源
Default 绑定的模式根据实际情况来定,如果是可编辑的就是TwoWay,只读的就是OneWay

这边明显有很多种选择,明确一点的是,我们是想把View上的变化同步到ViewModel(Target => Source),所以使用OneWayToSource、TwoWay、Default或者不写都可以。严谨点应该使用OneWayToSource。因为是文本框,属于可以编辑控件,所以 Default指向的是TwoWay。下面还有一个TextBlock,仅仅用于显示的,所以不需要目标对源的修改,无需指定就默认是OneWay,当源改变的时候,会通知它进行修改。

MVVMLight架构详细介绍学习网站

https://www.cnblogs.com/wzh2010/category/937606.html