WPF Mvvmlight初探 电脑版发表于:2025/4/15 15:45  ># WPF Mvvmlight初探 [TOC] 什么是 Mvvmlight? ------------ tn2>MVVM Light 是一个轻量级的 MVVM 框架,适用于 WPF、UWP、Xamarin 等多个平台。它由微软 MVP Laurent Bugnion 开发,旨在简化 MVVM 模式的实现,提高开发效率。MVVM Light 提供了核心的 MVVM 功能,包括数据绑定、命令处理、消息传递等 安装与配置 ------------ tn2>首先创建一个`.net framework 4.7.2`框架的项目。  tn2>通过 NuGet 包管理器安装 MVVM Light: ```bash Install-Package MvvmLight ``` tn2>安装完成后,项目中会自动生成 ViewModel 文件夹,包含 MainViewModel.cs 和 ViewModelLocator.cs 两个文件  tn2>在 `App.xaml` 中添加 `ViewModelLocator` 作为静态资源: ```xml <Application x:Class="WpfMvvmLightExample.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfMvvmLightExample" xmlns:vm="clr-namespace:WpfMvvmLightExample.ViewModel" StartupUri="View/MainWindow.xaml"> <Application.Resources> <ResourceDictionary> <vm:ViewModelLocator x:Key="Locator" /> </ResourceDictionary> </Application.Resources> </Application> ``` tn2>`ViewModelLocator` 通过依赖注入(IoC)容器管理 `ViewModel` 的实例,确保每个 `ViewModel` 的单例模式. 然后在`MainWindow.xaml`中进行绑定。 ```xml <Window x:Class="WpfLearningMvvmlightApp.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:WpfLearningMvvmlightApp" mc:Ignorable="d" DataContext="{Binding Source={StaticResource Locator},Path=Main}" Title="MainWindow" Height="450" Width="800"> <Grid> </Grid> </Window> ``` 核心功能与使用示例 ------------ ### 数据绑定 tn2>在 `MainViewModel.cs` 中定义属性,并通过 `ViewModelBase` 类实现属性变更通知: ```csharp public class MainViewModel : ViewModelBase { public MainViewModel() { } private string _name; public string Name { get => _name; set => Set(ref _name, value); } } ``` tn2>在 XAML 中绑定属性: ```xml <Window x:Class="WpfLearningMvvmlightApp.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:WpfLearningMvvmlightApp" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <TextBlock Text="{Binding Name}" Margin="94,46,366,311" /> </Grid> </Window> ``` tn2>`Set` 方法会自动触发 `PropertyChanged` 事件,通知视图更新。 ### 命令处理 tn2>使用 `RelayCommand` 处理用户交互: ```csharp public class MainViewModel : ViewModelBase { public MainViewModel() { UpdateNameCommand = new RelayCommand(UpdateName); } private string _name; public string Name { get => _name; set => Set(ref _name, value); } public RelayCommand UpdateNameCommand { get; } private void UpdateName() { Name = "Hello, MVVM Light!"; } } ``` tn2>在 XAML 中绑定命令: ```xml <Button Content="Update Name" Command="{Binding UpdateNameCommand}" Margin="122,158,474,217" /> ``` tn2>`RelayCommand` 是 `ICommand` 接口的实现,用于处理命令逻辑。  tn2>点了之后Name值发生了改变。  ### 3. 消息传递 tn2>使用 Messenger 类实现 ViewModel 与前端 View 之间的通信,举个例子: 首先我们给`TextBlock`添加名称修改如下所示: ```xml <TextBlock Name="CC" Text="{Binding Name}" Margin="94,46,366,311" /> ``` tn2>然后我们在前端页面按F7查看代码,将注册一个消息的方法,当获取到通知时改变一下CC控件的值: ```csharp public MainWindow() { InitializeComponent(); Messenger.Default.Register<NotificationMessage>(this, (message) => { this.CC.Text = message.Notification; }); } ``` tn2>然后我们修改一下`MainViewModel`下面的`UpdateName`方法,让它发送一个`"Hello, MVVM Light!"`消息通知: ```csharp private void UpdateName() { //Name = "Hello, MVVM Light!"; //发送消息给View端 Messenger.Default.Send(new NotificationMessage("Hello NotificationMessage, MVVM Light!")); } ```  tn2>然后我们点击一下发现它发生了改变。  简单依赖注入周期管理 ------------ tn2>在ViewModelLocator中这里有返回注入的实例,如果有传入`Guid.NewGuid().ToString()`它将是瞬时的,每次的对象都不一样。 ```csharp return ServiceLocator.Current.GetInstance<MainViewModel>(Guid.NewGuid().ToString()); ``` tn2>我们再创建一个`SubViewModel`,只想演示一下依赖注入时,当窗口关闭但实例仍然存在的案例示范。 ```csharp public class SubViewModel : ViewModelBase { public static volatile int i = 0; volatile bool flag = true; public SubViewModel() { Task.Run(() => { while (flag) { Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}Logger:日志启动!"); Thread.Sleep(1000); } }); i++; Console.WriteLine($"初始化{i}"); } public override void Cleanup() { base.Cleanup(); flag = false; } } ``` tn2>在ViewModelLocator中进行注册SubViewModel,并且一个Sub的单例注入。 ```csharp public ViewModelLocator() { ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default); SimpleIoc.Default.Register<MainViewModel>(); SimpleIoc.Default.Register<SubViewModel>(); } public MainViewModel Main { get { return ServiceLocator.Current.GetInstance<MainViewModel>(Guid.NewGuid().ToString()); } } public SubViewModel Sub { get { return ServiceLocator.Current.GetInstance<SubViewModel>(); } } ``` tn2>创建一个`SubWindow.xaml`窗体并对其进行绑定`Sub`这个实例对象。 ```xml <Window x:Class="WpfLearningMvvmlightApp.SubWindow" 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:WpfLearningMvvmlightApp" mc:Ignorable="d" DataContext="{Binding Source={StaticResource Locator},Path=Sub}" Title="SubWindow" Height="450" Width="800"> <Grid> </Grid> </Window> ``` tn2>然后在`MainWindow.xaml`窗体中添加一个按钮用于打开`SubWindow.xaml`窗体。 ```xml <Button Content="测试" HorizontalAlignment="Left" Margin="453,185,0,0" VerticalAlignment="Top" Height="55" Width="160" Click="Button_Click_1"/> ``` ```csharp private void Button_Click_1(object sender, RoutedEventArgs e) { new SubWindow().Show(); } ``` tn2>我们尝试运行一下,我们先打开我们的`SubWindow`窗口,然后再关闭查看是否还会进行打印输出。   tn2>窗体关闭后我们发现实例并没有释放。 接下来我们将在`ViewModelLocator`类中写一个静态方法进行释放其中注入的类型。 ```csharp public static void Cleanup<T>() where T : ViewModelBase { ServiceLocator.Current.GetInstance<T>().Cleanup(); // 清楚注册 SimpleIoc.Default.Unregister<T>(); // 再次注册,为了下一次使用的时候是空的 SimpleIoc.Default.Register<T>(); } ``` tn2>然后我们在`SubWindow.xaml`类中添加`SubWindow_Closed`事件,进行调用释放当前的依赖注入。 ```csharp public partial class SubWindow : Window { public SubWindow() { InitializeComponent(); this.Closed += SubWindow_Closed; } private void SubWindow_Closed(object sender, EventArgs e) { ViewModelLocator.Cleanup<SubViewModel>(); } } ``` tn2>再次测试我们发现当`SubWindow`关闭的时候相关绑定的依赖注入实例也会被释放,并且不再打印输出了。 