ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

在.NET Framework项目中利用MVVM Toolkit源生成器告别样板代码

在.NET Framework项目中利用MVVM Toolkit源生成器告别样板代码 在开发 .NET Framework 项目时手动实现 MVVM 模式中的属性通知和命令绑定往往伴随着大量重复的样板代码这不仅降低了开发效率也容易引入错误。如果你正在寻找一种既能保持代码整洁又能显著提升开发速度的方案那么 Microsoft.Toolkit.Mvvm 库又称 MVVM Toolkit的生成器功能绝对是你的不二之选。本文将为你详细拆解如何在传统的 .NET Framework 项目中集成并使用 MVVM Toolkit 的源生成器功能从环境配置、基础使用到高级技巧手把手带你告别繁琐的INotifyPropertyChanged手动实现。1. 背景与核心概念为什么需要 MVVM Toolkit 生成器在深入实操之前我们有必要厘清几个核心概念理解它们能帮你更好地运用后续的工具。MVVM 模式这是 Model-View-ViewModel 的缩写是一种广泛应用于 WPF、UWP、Xamarin.Forms 和 .NET MAUI 等 XAML 系 UI 框架的设计模式。其核心思想是将用户界面逻辑View与业务逻辑和数据Model分离开来而 ViewModel 则作为连接二者的桥梁负责处理视图的展示逻辑和用户交互命令。手动实现的痛点在传统的 MVVM 实现中ViewModel 通常需要实现INotifyPropertyChanged接口以便在属性值变更时通知视图更新。这意味着你需要为每个需要绑定的属性编写如下代码private string _userName; public string UserName { get _userName; set { if (_userName ! value) { _userName value; OnPropertyChanged(); // 还需要实现这个方法 } } }对于命令ICommand则需要创建实现了该接口的类如RelayCommand。当项目庞大时这种重复劳动不仅枯燥而且OnPropertyChanged方法中的属性名硬编码使用nameof稍好但仍有风险极易因重命名而失效导致 bug。MVVM Toolkit 的解决方案Microsoft.Toolkit.Mvvm 是一个现代、高效、平台无关的 MVVM 库。它提供了一套丰富的组件而其最大的亮点之一就是源生成器。源生成器是 C# 9.0/10.0 引入的编译器功能它能在编译过程中分析你的代码并自动生成额外的源代码如INotifyPropertyChanged的实现、命令属性等。你只需要通过简单的属性标记编译器就会为你生成完整、正确且高性能的样板代码。核心优势代码极简用[ObservableProperty]和[RelayCommand]等属性标记字段或方法即可自动生成完整属性或命令。强类型安全生成器基于编译时分析自动生成正确的属性名字符串彻底告别手写nameof可能带来的拼写错误或重构遗漏。高性能生成的代码是高度优化的避免了反射等运行时开销。开发体验佳在 Visual Studio 中你可以直接导航到生成的文件查看生成的代码调试体验与手写代码无异。2. 环境准备与版本说明要在 .NET Framework 项目中使用 MVVM Toolkit 的生成器功能需要满足特定的环境要求因为源生成器依赖于较新的编译器功能。必须满足的条件项目类型.NET Framework 4.6.1、4.7、4.8 或更高版本。虽然 .NET Framework 4.5.2 等旧版本广泛存在如网络热词中提到的安装问题但 MVVM Toolkit 的源生成器特性要求最低为.NET Framework 4.6.1或.NET Standard 2.0后者可被 .NET Framework 4.6.1 引用。如果你的项目是旧版本请先升级目标框架。开发环境Visual Studio 2022 (推荐)这是最理想的环境对 C# 源生成器和现代 .NET 开发支持最完善。Visual Studio 2019 (版本 16.8 或更高)需要安装 .NET 5.0 SDK 以获得必要的编译器支持。C# 语言版本项目必须使用C# 9.0 或更高版本。源生成器是 C# 9.0 引入的功能。NuGet 包需要安装Microsoft.Toolkit.Mvvm包。请注意确保安装的是7.0.0 或更高版本早期版本可能不包含完整的源生成器支持或 API 有所不同。版本兼容性对照表你的项目目标框架推荐的 MVVM Toolkit 版本注意事项.NET Framework 4.6.1 - 4.87.x 或 8.x确保项目语言版本为 C# 9.0。可能需要手动编辑.csproj文件。.NET Standard 2.07.x 或 8.x.NET Framework 4.6.1 项目可引用此标准库。语言版本要求同上。.NET Core 3.1 / .NET 5/6/7/88.x (最新稳定版)拥有最好的兼容性和性能。重要提示如果你的开发环境在安装 .NET Framework 或 SDK 时遇到问题例如网络热词中提到的“安装未成功 尚未安装 .net framework 4.5.2”请务必通过微软官方渠道下载并安装正确的版本。对于 .NET Framework 项目我们主要关注目标框架版本而非系统已安装的运行库版本。项目属性中的目标框架设置才是关键。3. 创建项目与配置环境让我们从一个干净的 WPF 应用程序开始演示完整的集成流程。3.1 创建新的 WPF 项目打开 Visual Studio 2022。选择“创建新项目” - 搜索“WPF” - 选择“WPF 应用程序(.NET Framework)”注意模板选择器上方的下拉菜单确保选中“C#”和“所有平台”。点击“下一步”。在“配置新项目”页面输入项目名称如MvvmToolkitDemo选择合适的位置。关键步骤在“框架”下拉框中务必选择.NET Framework 4.6.1或更高版本如 4.7.2, 4.8。这是支持源生成器的前提。点击“创建”。3.2 升级 C# 语言版本默认情况下.NET Framework 项目可能使用较旧的 C# 语言版本。我们需要将其显式设置为 C# 9.0 或更高。在解决方案资源管理器中右键单击你的项目 - 选择“编辑项目文件”。这将打开.csproj文件。在PropertyGroup标签内添加或修改LangVersion元素。如果不存在就新增一行。Project SdkMicrosoft.NET.Sdk PropertyGroup OutputTypeWinExe/OutputType TargetFrameworknet48/TargetFramework !-- 你的目标框架 -- !-- 添加以下行设置语言版本 -- LangVersionlatest/LangVersion !-- 或使用 9.0, 10.0 等具体版本 -- UseWPFtrue/UseWPF /PropertyGroup /Project使用latest可以让项目自动使用 Visual Studio 支持的最新 C# 版本这通常是安全的。保存文件。3.3 安装 Microsoft.Toolkit.Mvvm NuGet 包在解决方案资源管理器中右键单击项目 - 选择“管理 NuGet 程序包”。在打开的“NuGet 包管理器”窗口中切换到“浏览”选项卡。在搜索框中输入Microsoft.Toolkit.Mvvm。在搜索结果中选择正确的包确保作者是Microsoft。在右侧版本选择器中选择最新的稳定版本如 8.2.0。务必确认版本号 7.0.0。点击“安装”。接受任何许可协议。安装完成后你的项目引用中会添加Microsoft.Toolkit.Mvvm。同时由于它依赖Microsoft.Toolkit.Diagnostics和Microsoft.Toolkit.Mvvm.SourceGenerators源生成器包这些也会被自动引入。你可以在“依赖项”-“分析器”下看到Microsoft.Toolkit.Mvvm.SourceGenerators这证明源生成器已就绪。4. 核心功能实战从零编写一个 ViewModel我们将创建一个简单的用户登录界面 ViewModel涵盖 ObservableProperty、RelayCommand 等核心功能。4.1 创建 ViewModel 基类与模型首先创建一个继承自ObservableObject的 ViewModel 基类。ObservableObject是 MVVM Toolkit 提供的基类它实现了INotifyPropertyChanged接口。我们的具体 ViewModel 将继承它。在项目中新建一个文件夹ViewModels然后添加一个类ViewModelBase.cs// 文件路径ViewModels/ViewModelBase.cs using Microsoft.Toolkit.Mvvm.ComponentModel; namespace MvvmToolkitDemo.ViewModels { /// summary /// ViewModel 基类继承自 ObservableObject 以获得属性通知功能。 /// 所有具体的 ViewModel 都应继承此类。 /// /summary public abstract class ViewModelBase : ObservableObject { // 这里可以添加所有 ViewModel 共用的逻辑例如导航、对话框服务等。 // 目前留空即可。 } }接着创建一个简单的用户模型User.cs放在Models文件夹// 文件路径Models/User.cs namespace MvvmToolkitDemo.Models { public class User { public string Username { get; set; } public string Password { get; set; } // 注意实际应用中密码不应以明文存储和传输 } }4.2 使用[ObservableProperty]自动生成属性现在创建主要的LoginViewModel.cs。我们将使用源生成器的核心特性。// 文件路径ViewModels/LoginViewModel.cs using Microsoft.Toolkit.Mvvm.ComponentModel; using Microsoft.Toolkit.Mvvm.Input; using MvvmToolkitDemo.Models; using System.Threading.Tasks; using System.Windows; namespace MvvmToolkitDemo.ViewModels { public partial class LoginViewModel : ViewModelBase // 继承我们自己的基类 { // 1. 使用 [ObservableProperty] 标记私有字段 // 生成器会自动为其生成一个名为 UserName 的公共属性并实现完整的属性通知逻辑。 [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(LoginCommand))] // 关联命令的可用性 private string _userName string.Empty; [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(LoginCommand))] private string _password string.Empty; [ObservableProperty] private bool _isLoading false; // 2. 生成的属性是公共的可以直接在 XAML 中绑定。 // 例如会生成public string UserName { get _userName; set SetProperty(ref _userName, value); } // SetProperty 方法来自 ObservableObject它负责比较值并触发 PropertyChanged 事件。 // 构造函数 public LoginViewModel() { // 命令初始化在构造函数中完成 } } }关键点解析[ObservableProperty]这个属性标记在一个私有字段上通常以下划线_开头。源生成器会分析这个字段并自动生成一个对应的公共属性。属性名是字段名去掉下划线和首字母小写后的形式例如_userName-UserName。[NotifyCanExecuteChangedFor]这个属性可以附加在[ObservableProperty]上用于指定当这个属性值改变时需要通知哪个RelayCommand重新评估其CanExecute状态。这在实现登录按钮的启用/禁用逻辑时非常有用。partial class注意我们将LoginViewModel声明为partial分部类。这是因为源生成器会将自动生成的代码写入到另一个同名的分部类文件中通常位于obj目录下的*.g.cs文件。你必须将主类声明为partial编译器才能将两部分合并。如何查看生成的代码在 Visual Studio 中编译项目后你可以将鼠标悬停在[ObservableProperty]上或者右键点击UserName属性 - 选择“转到定义”(F12)如果导航到LoginViewModel.g.cs这样的文件里面就是生成的代码。这有助于你理解背后的机制。4.3 使用[RelayCommand]自动生成命令接下来我们添加登录和取消命令。MVVM Toolkit 提供了AsyncRelayCommand来方便地处理异步操作。在LoginViewModel.cs的类中继续添加// 文件路径ViewModels/LoginViewModel.cs (续) namespace MvvmToolkitDemo.ViewModels { public partial class LoginViewModel : ViewModelBase { // ... 之前的属性字段 ... // 3. 使用 [RelayCommand] 标记方法自动生成 ICommand 属性。 // 此命令关联到 LoginAsync 方法。 [RelayCommand(CanExecute nameof(CanLogin))] private async Task LoginAsync() { // 模拟一个异步登录过程 IsLoading true; try { await Task.Delay(1500); // 模拟网络请求 // 这里应该是实际的登录逻辑例如调用服务 if (UserName Admin Password 123456) // 简单演示 { MessageBox.Show($登录成功欢迎{UserName}。); // 实际项目中这里可能导航到主页面 } else { MessageBox.Show(用户名或密码错误。); } } finally { IsLoading false; } } // 4. CanExecute 判断逻辑的方法 private bool CanLogin() { // 当用户名和密码都不为空且不在加载状态时按钮才可用 return !string.IsNullOrWhiteSpace(UserName) !string.IsNullOrWhiteSpace(Password) !IsLoading; } // 5. 另一个简单的同步命令示例 [RelayCommand] private void Cancel() { UserName string.Empty; Password string.Empty; // 实际项目中这里可能关闭窗口或清除状态 } } }关键点解析[RelayCommand]标记在一个方法上会自动生成一个名为LoginCommand的ICommand属性方法名 “Command”。这个命令在执行时会调用被标记的方法。CanExecute参数通过CanExecute nameof(CanLogin)指定了一个判断命令是否可执行的方法。当CanLogin()返回false时绑定该命令的 UI 控件如按钮会自动变为禁用状态。我们之前用[NotifyCanExecuteChangedFor]将UserName和Password属性与LoginCommand关联这样当它们变化时命令的CanExecute状态会自动重新评估。异步支持被标记的方法可以是async Task类型。生成的AsyncRelayCommand会自动处理异步操作并在执行期间防止重复执行默认行为。IsLoading属性在这里用于在UI上显示加载状态并且也参与了CanLogin的判断。命令命名生成命令的属性名遵循{MethodName}Command的规则。例如LoginAsync方法生成LoginCommandCancel方法生成CancelCommand。4.4 在 XAML 视图中进行数据绑定现在我们将 ViewModel 与 ViewMainWindow连接起来。首先修改MainWindow.xaml.cs的构造函数设置其DataContext// 文件路径MainWindow.xaml.cs using MvvmToolkitDemo.ViewModels; using System.Windows; namespace MvvmToolkitDemo { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // 将 ViewModel 实例设置为窗口的数据上下文 this.DataContext new LoginViewModel(); } } }然后修改MainWindow.xaml设计一个简单的登录界面并绑定我们生成的属性和命令!-- 文件路径MainWindow.xaml -- Window x:ClassMvvmToolkitDemo.MainWindow xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml xmlns:dhttp://schemas.microsoft.com/expression/blend/2008 xmlns:mchttp://schemas.openxmlformats.org/markup-compatibility/2006 mc:Ignorabled TitleMVVM Toolkit 演示 - 登录 Height300 Width400 Grid Margin20 Grid.RowDefinitions RowDefinition HeightAuto/ RowDefinition HeightAuto/ RowDefinition HeightAuto/ RowDefinition HeightAuto/ RowDefinition Height*/ /Grid.RowDefinitions Grid.ColumnDefinitions ColumnDefinition WidthAuto/ ColumnDefinition Width*/ /Grid.ColumnDefinitions !-- 用户名 -- TextBlock Grid.Row0 Grid.Column0 Text用户名: Margin5 VerticalAlignmentCenter/ TextBox Grid.Row0 Grid.Column1 Text{Binding UserName, UpdateSourceTriggerPropertyChanged} Margin5 Height25/ !-- 密码 -- TextBlock Grid.Row1 Grid.Column0 Text密码: Margin5 VerticalAlignmentCenter/ PasswordBox x:NamePasswordBox Grid.Row1 Grid.Column1 Margin5 Height25/ !-- 注意PasswordBox 的 Password 属性不是依赖属性不能直接绑定。 通常使用附加行为或转换器这里为简化在代码后台处理。 -- !-- 加载指示器 -- ProgressBar Grid.Row2 Grid.ColumnSpan2 Height10 Margin5 IsIndeterminateTrue Visibility{Binding IsLoading, Converter{StaticResource BooleanToVisibilityConverter}}/ TextBlock Grid.Row2 Grid.ColumnSpan2 HorizontalAlignmentCenter VerticalAlignmentCenter Text登录中... Visibility{Binding IsLoading, Converter{StaticResource BooleanToVisibilityConverter}}/ !-- 按钮 -- StackPanel Grid.Row3 Grid.ColumnSpan2 OrientationHorizontal HorizontalAlignmentCenter Margin0,20,0,0 Button Content登录 Command{Binding LoginCommand} Margin10 Width80 Height30/ Button Content取消 Command{Binding CancelCommand} Margin10 Width80 Height30/ /StackPanel !-- 状态提示 -- TextBlock Grid.Row4 Grid.ColumnSpan2 TextWrappingWrap Margin5 Run Text提示/ LineBreak/ Run Text1. 使用 [ObservableProperty] 自动生成 UserName 和 Password 属性。/ LineBreak/ Run Text2. 使用 [RelayCommand] 自动生成 LoginCommand 和 CancelCommand。/ LineBreak/ Run Text3. 登录按钮状态随输入框内容和加载状态自动改变。/ LineBreak/ Run Text4. 用户名: Admin, 密码: 123456 可模拟登录成功。/ /TextBlock /Grid /Window注意上面的 XAML 使用了一个BooleanToVisibilityConverter这是 WPF 内置的转换器但需要在资源中声明。同时PasswordBox的绑定需要特殊处理。为了示例完整我们更新MainWindow.xaml的顶部并添加代码后台的密码绑定首先在Window标签内添加资源字典Window.Resources BooleanToVisibilityConverter x:KeyBooleanToVisibilityConverter/ /Window.Resources然后修改MainWindow.xaml.cs实现密码的手动绑定这是一个常见的 MVVM 中处理 PasswordBox 的方案// 文件路径MainWindow.xaml.cs (更新) using MvvmToolkitDemo.ViewModels; using System.Windows; using System.Windows.Controls; namespace MvvmToolkitDemo { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); var viewModel new LoginViewModel(); this.DataContext viewModel; // 手动处理 PasswordBox 的密码绑定因为 Password 不是依赖属性 PasswordBox.PasswordChanged (sender, args) { // 这里假设 ViewModel 的 Password 属性有公共 setter。 // 由于我们使用了 [ObservableProperty]它确实生成了公共的 Password 属性。 if (this.DataContext is LoginViewModel vm) { vm.Password PasswordBox.Password; } }; // 可选当 ViewModel 的 Password 属性从其他位置改变时同步到 UI例如 Cancel 命令 // 这需要更复杂的双向绑定机制本例为简化暂不实现。 } } }4.5 运行与验证现在按下F5运行项目。初始状态登录按钮应为禁用状态因为用户名和密码为空。输入内容在用户名和密码框中输入任意文本。你会发现随着你的输入登录按钮会自动变为启用状态。这得益于[NotifyCanExecuteChangedFor]和CanLogin方法的协同工作。执行登录输入用户名Admin和密码123456点击“登录”按钮。按钮会立即变为禁用因为IsLoading变为true并显示进度条和“登录中...”文本。大约 1.5 秒后弹出“登录成功”消息框同时 UI 恢复。执行取消点击“取消”按钮用户名和密码框会被清空登录按钮再次变为禁用。错误情况输入错误的用户名密码会弹出错误提示。整个过程你没有手动编写任何PropertyChanged事件触发代码或ICommand的实现类所有样板代码都由源生成器在编译时自动、正确地生成。5. 高级特性与最佳实践掌握了基础用法后我们来看一些更高级的特性和在实际项目中的最佳实践。5.1 使用[AlsoNotifyChangeFor]和[AlsoNotifyCanExecuteFor]有时一个属性的变化会影响另一个计算属性或命令的状态。除了[NotifyCanExecuteChangedFor]MVVM Toolkit 还提供了更通用的[AlsoNotifyChangeFor]和[AlsoNotifyCanExecuteFor]。假设我们有一个FullName属性由FirstName和LastName计算得出public partial class AdvancedViewModel : ObservableObject { [ObservableProperty] [AlsoNotifyChangeFor(nameof(FullName))] // 当 FirstName 变化时也通知 FullName 属性更新 private string _firstName; [ObservableProperty] [AlsoNotifyChangeFor(nameof(FullName))] private string _lastName; // 这是一个计算属性没有对应的后台字段也不需要 [ObservableProperty] public string FullName ${FirstName} {LastName}; [ObservableProperty] [AlsoNotifyCanExecuteFor(nameof(SaveCommand))] // 当 Email 变化时重新评估 SaveCommand private string _email; [RelayCommand(CanExecute nameof(CanSave))] private void Save() { // 保存逻辑 } private bool CanSave() !string.IsNullOrEmpty(FullName) !string.IsNullOrEmpty(Email); }5.2 依赖注入与服务集成在实际项目中ViewModel 不应直接创建服务或弹窗。应通过构造函数注入依赖。定义接口例如ILoginService、IDialogService。在 ViewModel 中注入public partial class LoginViewModel : ViewModelBase { private readonly ILoginService _loginService; private readonly IDialogService _dialogService; // 通过构造函数注入 public LoginViewModel(ILoginService loginService, IDialogService dialogService) { _loginService loginService; _dialogService dialogService; } [RelayCommand] private async Task LoginAsync() { IsLoading true; try { var result await _loginService.AuthenticateAsync(UserName, Password); if (result.IsSuccess) { await _dialogService.ShowMessageAsync(登录成功, $欢迎{result.User.DisplayName}。); } else { await _dialogService.ShowErrorAsync(登录失败, result.ErrorMessage); } } finally { IsLoading false; } } }然后在应用启动时如App.xaml.cs使用你喜欢的 DI 容器如 Microsoft.Extensions.DependencyInjection注册服务和 ViewModel并负责创建主窗口。5.3 处理生成的代码与调试生成的代码文件位于obj/Debug/net48或对应配置和框架目录下的*.g.cs文件中。在 Visual Studio 中你可以通过以下方式查看在“解决方案资源管理器”中点击“显示所有文件”按钮。展开obj-Debug-net48-source-generators目录路径可能因工具链版本略有不同。找到以你的 ViewModel 命名的.g.cs文件。调试你可以像调试自己手写的代码一样在标记了[RelayCommand]的方法或[ObservableProperty]属性的set访问器通过转到定义找到生成的部分中设置断点。源生成器的设计使得调试体验非常自然。5.4 性能与内存考量源生成器在编译时生成代码因此没有运行时反射开销性能等同于手写的高质量样板代码。AsyncRelayCommand内部使用了CancellationTokenSource和状态管理能有效防止异步命令的重复执行并安全地处理任务取消。对于大量属性使用[ObservableProperty]比手动实现更不易出错且代码更简洁可维护性更高。6. 常见问题与排查思路在使用过程中你可能会遇到一些典型问题。下表列出了常见现象、原因及解决方案问题现象可能原因排查与解决思路编译错误CS1061 “XXXViewModel”不包含“XXXCommand”的定义1. 未将 ViewModel 类声明为partial。2. 未安装或未正确引用Microsoft.Toolkit.MvvmNuGet 包特别是源生成器包。3. C# 语言版本低于 9.0。1. 确保 ViewModel 类有partial关键字。2. 检查“依赖项”下的“分析器”确认Microsoft.Toolkit.Mvvm.SourceGenerators存在。3. 检查项目文件.csproj中的LangVersion确保设置为latest或9.0以上。属性绑定不更新 UI1. 未继承ObservableObject。2. 绑定的属性名拼写错误区分大小写。3. 在 XAML 中绑定了私有字段如_userName而不是生成的公共属性UserName。1. 确保 ViewModel 继承自ObservableObject或你的ViewModelBase。2. 检查 XAML 中Binding的Path是否与生成的属性名完全一致。3.务必绑定到公共属性名即字段名去掉下划线首字母大写的名字。命令按钮始终为禁用状态1.CanExecute指定的方法始终返回false。2. 未使用[NotifyCanExecuteChangedFor]或[AlsoNotifyCanExecuteFor]将相关属性与命令关联。3. 命令的CanExecute逻辑有误。1. 调试CanExecute方法检查其返回值。2. 确保影响命令状态的属性都通过特性关联到了命令。3. 检查CanExecute方法中的逻辑条件。生成的属性或命令在智能感知中不出现1. 项目未成功编译。2. Visual Studio 的 IntelliSense 缓存问题。1. 尝试重新编译整个项目 (CtrlShiftB)。2. 关闭并重新打开 Visual Studio 解决方案。3. 尝试清理解决方案并重新生成。在 .NET Framework 4.5.2 项目中无法使用MVVM Toolkit 的源生成器要求最低 .NET Framework 4.6.1 或 .NET Standard 2.0。升级项目目标框架至 .NET Framework 4.6.1 或更高版本。这是硬性要求。AsyncRelayCommand执行时 UI 卡死在异步命令方法中在await之前执行了长时间运行的同步代码阻塞了 UI 线程。确保耗时的操作如 CPU 密集型计算、同步 IO放在Task.Run中执行或者使用真正的异步 API。async/await本身不会创建新线程它只是提供一种方便的异步编程模型。7. 工程化建议与扩展方向当你决定在大型项目中使用 MVVM Toolkit 生成器时遵循一些最佳实践能让项目更健壮。项目结构组织采用清晰的文件夹结构如Views、ViewModels、Models、Services、Converters、Behaviors等。为每个 View 创建对应的 ViewModel。基类与通用功能就像我们创建的ViewModelBase可以在这里集中处理通用功能如导航请求、消息通知、对话框服务接口等。验证结合System.ComponentModel.DataAnnotations或FluentValidation等库进行数据验证。可以在属性的setter中通过生成的部分方法或命令的执行方法中进行验证。消息传递对于跨 ViewModel 的通信可以考虑使用Microsoft.Toolkit.Mvvm.Messaging中的弱引用消息机制避免内存泄漏和强耦合。测试由于 ViewModel 不依赖 UI 框架且命令和属性都是普通的 .NET 对象非常适合进行单元测试。使用 MSTest、xUnit 或 NUnit 可以轻松测试命令执行和属性变更逻辑。代码分析器MVVM Toolkit 包含一些代码分析器会在你编写代码时给出提示例如建议对命令方法使用Async后缀等。遵循这些建议能使代码更规范。将 MVVM Toolkit 的生成器功能引入到 .NET Framework 项目中是一次显著的生产力解放。它用极简的注解取代了冗长的样板代码借助编译器的力量保证了代码的正确性和性能。从今天展示的登录示例出发你可以将其应用到项目所有的数据绑定和命令场景中。开始时你可能会习惯性地想去手写OnPropertyChanged但一旦适应了这种声明式的编程风格你会发现代码更清晰、更专注业务逻辑、维护成本也更低。
返回列表