# WPF-MVVMLight-Study
**Repository Path**: momj/wpf-mvvmlight-study
## Basic Information
- **Project Name**: WPF-MVVMLight-Study
- **Description**: MVVMLight学习
- **Primary Language**: C#
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 6
- **Forks**: 2
- **Created**: 2022-07-20
- **Last Updated**: 2025-04-22
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# MvvmLightDemo
## 1.MVVM概述
MVVM是Model-View-ViewModel的简写,主要目的是为了解耦视图(View)和模型(Model)。

MVVMLight是一个实现MVVM模式的轻量级框架(相对于Prism)
引用NuGet包:


安装成功后,会在我们新建的Wpf工程中自动生成ViewModel文件夹,里面包含**MainViewModel.cs**和**ViewModelLocator.cs**两个文件。
至此,一个基于MVVMLight框架的WPF项目基本搭建完成。
## 2.MVVMLight 初探
#### 分层概述
MVVM中,各个部分的职责如下:
**Model:**负责数据实体的结构处理,与ViewModel进行交互;
**View:**负责界面显示,与ViewModel进行数据和命令的交互;
**ViewModel:**负责前端视图业务级别的逻辑结构组织,并将其反馈给前端。
#### 框架初探
通过NuGet安装MVVM Light 框架后,我们新建的Wpf项目中会自动生成一个ViewModel文件夹,里面有MainViewModel.cs和ViewModelLocator.cs两个文件。
下面我们就首先分析下这两个文件的内容:
MainViewModel.cs文件分析:
MainViewModel.cs文件中只有一个类MainViewModel,该类是主窗口MainWindow对应的ViewModel,继承自类ViewModelBase
ViewModelBase类又继承类ObservableObject,同时实现ICleanup接口
ObservableObject类实现INotifyPropertyChanged接口,用于通知属性的改变
由此分析,我们可以得出以下一般结论:
**当我们定义一个自己的ViewModel时,一般让自定义ViewModel继承自ViewModelBase类,这样当ViewModel中属性发生变化的时候,就可以自动通知对应的VIew。**
ViewModelLocator.cs文件分析:
ViewModelLocator.cs文件中只有一个ViewModelLocator类,类中包括一个构造函数、一个类型为MainViewModel的Main属性、以及一个静态的Cleanup函数。
```c#
public class ViewModelLocator
{
///
/// Initializes a new instance of the ViewModelLocator class.
///
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
////if (ViewModelBase.IsInDesignModeStatic)
////{
//// // Create design time view services and models
//// SimpleIoc.Default.Register();
////}
////else
////{
//// // Create run time view services and models
//// SimpleIoc.Default.Register();
////}
SimpleIoc.Default.Register();
}
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance();
}
}
public static void Cleanup()
{
// TODO Clear the ViewModels
}
}
```
在构造函数中,创建了一个SimpleIoc类型的单实例,用于注册ViewModel,然后用ServiceLocator对这个SimpleIoc类型的单实例进行包裹,方便统一管理。
观察App.xaml文件,我们会发现ViewModelLocator类被生成资源字典并加入到了全局资源,所以每次App初始化的时候,就会去初始化ViewModelLocator类。
实际上,他是一个很基本的视图模型注入器,在构造器中把使用到的ViewModel统一注册,并生成单一实例。然后使用属性把它暴露出来,每当我们访问属性的时候,就会返回相应的ViewModel实例。
```xaml
```
由此分析,我们可以得出以下一般结论:
**当我们自定义一个ViewModel的时候,就可以在ViewModelLocator类的构造函数中对ViewModel进行注册,然后在该类中定义一个属性,用于返回我们的自定义ViewModel**
## 3.双向数据绑定
```xaml
```
在View中,我们分别让一个TextBox和一个TextBlock绑定WelcomeModel中的WelcomeMsg属性
当我们在TextBox中输入文本的时候,利用双向绑定更新WelcomeModel中的WelcomeMsg属性,WelcomeMsg属性又通过RaisePropertyChanged来激发属性变更的事件,通知UI,如此一来,界面上的TextBlock中的内容就会更新。
**但是**,当我们运行以上程序的时候,修改TextBox里的内容,TextBlock中的内容并没有实时更新,而是需要按键盘上的Tab键或者点击LostFocus按钮,让TextBox失去焦点。
这就引出了一个问题,当目标更新的时候,数据源是否更新以及更新的时机?
为了解决这个问题,我们可以使用Binding的UpdateSourceTrigger和Mode属性
UpdateSourceTrigger属性的作用:当做何种改变的时候通知数据源我们做了改变,默认值是LostFocus,这就解释了为什么我们上述程序需要TextBox失去焦点的时候,TextBlock的内容才会更新。
UpdateSourceTrigger可以取以下值:
| **枚举类型** | **说明** |
| --------------- | -------------------------------------- |
| Default | 默认值(默认LostFocus) |
| Explicit | 当应用程序调用 UpdateSource 方法时生效 |
| LostFocus | 失去焦点的时候触发 |
| PropertyChanged | 属性改变时立即触发 |
为了让TextBlock中的内容随着TextBox的内容实时变化,我们可以将绑定修改成如下格式:
```xaml
```
Mode属性取值如下:

**通过以上Demo,我们应达成以下共识:**
1)ViewModel => View更新的时候,我们一般在属性的**set块**中加入**RaisePropertyChanged**,它的作用是当数据源改变的时候,会触发PropertyChanged事件,通知UI属性发生了变更;
2)View => ViewModel 更新的时候,我们一般利用**Binding**的**UpdateSourceTrigger**和**Mode**属性。
## 4.RelayCommand初探
#### 概述
在MVVM Light框架中,主要通过命令绑定来进行事件的处理。
WPF中,命令是通过实现 ICommand 接口创建的。 ICommand 公开了两个方法(Execute 及 CanExecute)和一个事件(CanExecuteChanged)。
在MVVM Light框架中,RelayCommand类实现了ICommand 接口,用于完成命令绑定。
通过RelayCommand类的构造函数传入Action类型的Execute委托和Func类型的CanExecute委托,CanExecute委托用于表示当前命令是否可以执行,Execute委托则表示执行当前命令对应的方法。
通过命令绑定,解耦了View和ViewModel的行为交互,将视图的显示和业务逻辑分开。比如我们对界面上的某个按钮进行命令绑定,当点击按钮的时候,实际上进行操作是在对应的ViewModel下的所绑定的方法中执行的。
核心引用:**using GalaSoft.MvvmLight.CommandWpf;**

## 5.RelayCommand深究
#### (1).概述
有时候,单纯的命令绑定不一定能满足我们的开发需求,比如我们需要在命令绑定的时候传递一个参数,这个时候,我们就需要使用RelayCommand的泛型版本了。
RelayCommand的泛型版本的构造函数以下:
public RelayCommand(Action execute, bool keepTargetAlive = false);
public RelayCommand(Action execute, Func canExecute, bool keepTargetAlive = false);
构造函数传入的是委托类型的参数,Execute 和 CanExecute执行委托方法。
#### (2)带一个参数的命令绑定
```xaml
```
```C#
private RelayCommand