
WPF Mvvmlight初探
什么是 Mvvmlight?
MVVM Light 是一个轻量级的 MVVM 框架,适用于 WPF、UWP、Xamarin 等多个平台。它由微软 MVP Laurent Bugnion 开发,旨在简化 MVVM 模式的实现,提高开发效率。MVVM Light 提供了核心的 MVVM 功能,包括数据绑定、命令处理、消息传递等
安装与配置
首先创建一个.net framework 4.7.2
框架的项目。
通过 NuGet 包管理器安装 MVVM Light:
Install-Package MvvmLight
安装完成后,项目中会自动生成 ViewModel 文件夹,包含 MainViewModel.cs 和 ViewModelLocator.cs 两个文件
在 App.xaml
中添加 ViewModelLocator
作为静态资源:
<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>
ViewModelLocator
通过依赖注入(IoC)容器管理 ViewModel
的实例,确保每个 ViewModel
的单例模式.
然后在MainWindow.xaml
中进行绑定。
<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>
核心功能与使用示例
数据绑定
在 MainViewModel.cs
中定义属性,并通过 ViewModelBase
类实现属性变更通知:
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
}
private string _name;
public string Name
{
get => _name;
set => Set(ref _name, value);
}
}
在 XAML 中绑定属性:
<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>
Set
方法会自动触发 PropertyChanged
事件,通知视图更新。
命令处理
使用 RelayCommand
处理用户交互:
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!";
}
}
在 XAML 中绑定命令:
<Button Content="Update Name" Command="{Binding UpdateNameCommand}" Margin="122,158,474,217" />
RelayCommand
是 ICommand
接口的实现,用于处理命令逻辑。
点了之后Name值发生了改变。
3. 消息传递
使用 Messenger 类实现 ViewModel 与前端 View 之间的通信,举个例子:
首先我们给TextBlock
添加名称修改如下所示:
<TextBlock Name="CC" Text="{Binding Name}" Margin="94,46,366,311" />
然后我们在前端页面按F7查看代码,将注册一个消息的方法,当获取到通知时改变一下CC控件的值:
public MainWindow()
{
InitializeComponent();
Messenger.Default.Register<NotificationMessage>(this, (message) =>
{
this.CC.Text = message.Notification;
});
}
然后我们修改一下MainViewModel
下面的UpdateName
方法,让它发送一个"Hello, MVVM Light!"
消息通知:
private void UpdateName()
{
//Name = "Hello, MVVM Light!";
//发送消息给View端
Messenger.Default.Send(new NotificationMessage("Hello NotificationMessage, MVVM Light!"));
}
然后我们点击一下发现它发生了改变。
简单依赖注入周期管理
在ViewModelLocator中这里有返回注入的实例,如果有传入Guid.NewGuid().ToString()
它将是瞬时的,每次的对象都不一样。
return ServiceLocator.Current.GetInstance<MainViewModel>(Guid.NewGuid().ToString());
我们再创建一个SubViewModel
,只想演示一下依赖注入时,当窗口关闭但实例仍然存在的案例示范。
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;
}
}
在ViewModelLocator中进行注册SubViewModel,并且一个Sub的单例注入。
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>();
}
}
创建一个SubWindow.xaml
窗体并对其进行绑定Sub
这个实例对象。
<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>
然后在MainWindow.xaml
窗体中添加一个按钮用于打开SubWindow.xaml
窗体。
<Button Content="测试" HorizontalAlignment="Left" Margin="453,185,0,0" VerticalAlignment="Top" Height="55" Width="160" Click="Button_Click_1"/>
private void Button_Click_1(object sender, RoutedEventArgs e)
{
new SubWindow().Show();
}
我们尝试运行一下,我们先打开我们的SubWindow
窗口,然后再关闭查看是否还会进行打印输出。
窗体关闭后我们发现实例并没有释放。
接下来我们将在ViewModelLocator
类中写一个静态方法进行释放其中注入的类型。
public static void Cleanup<T>()
where T : ViewModelBase
{
ServiceLocator.Current.GetInstance<T>().Cleanup();
// 清楚注册
SimpleIoc.Default.Unregister<T>();
// 再次注册,为了下一次使用的时候是空的
SimpleIoc.Default.Register<T>();
}
然后我们在SubWindow.xaml
类中添加SubWindow_Closed
事件,进行调用释放当前的依赖注入。
public partial class SubWindow : Window
{
public SubWindow()
{
InitializeComponent();
this.Closed += SubWindow_Closed;
}
private void SubWindow_Closed(object sender, EventArgs e)
{
ViewModelLocator.Cleanup<SubViewModel>();
}
}
再次测试我们发现当SubWindow
关闭的时候相关绑定的依赖注入实例也会被释放,并且不再打印输出了。
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

