尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

鉴于之前已经询问过 Prism 的多项功能(包括异步验证),我将提供一个精简且聚焦的回答,基于 WPF 环境,避免重复之前的内容

鉴于之前已经询问过 Prism 的多项功能(包括异步验证),我将提供一个精简且聚焦的回答,基于 WPF 环境,避免重复之前的内容
📅 发布时间:2026/7/18 10:59:20

Prism 异步验证的示例。鉴于之前已经询问过 Prism 的多项功能(包括异步验证),我将提供一个精简且聚焦的回答,基于 WPF 环境,避免重复之前的内容(如INotifyDataErrorInfo和模拟 API 的异步验证示例)。本次示例将展示一个不同场景的异步验证(例如验证密码强度通过异步 API 调用),包括代码、测试代码、适用场景和使用方法。如果您有特定需求(如与导航/区域的集成、更复杂的验证逻辑或特定测试场景),请进一步说明,我会定制答案。


一、异步验证概述

Prism 通过 WPF 的INotifyDataErrorInfo接口支持异步验证,结合BindableBase和异步方法(async/await)实现动态输入验证。异步验证适用于需要远程或复杂逻辑的场景(如 API 调用检查密码强度),并通过错误通知机制更新 UI。主要特性:

  • 异步处理:支持耗时验证(如 API 调用)。
  • 错误通知:通过INotifyDataErrorInfo提供动态错误反馈。
  • MVVM 集成:与 Prism 的 MVVM 模式无缝结合。
  • 模块化支持:验证逻辑可定义在模块中。

二、适用场景

异步验证适用于:

  1. 远程验证:如通过 API 检查密码强度或唯一性。
  2. 复杂规则:如验证密码是否符合安全策略(长度、字符类型)。
  3. 实时反馈:输入时异步验证并显示错误或状态。
  4. 模块化应用:模块中的视图模型需要独立异步验证。
  5. 表单提交:确保提交前所有字段通过异步验证。

三、异步验证示例

以下是一个异步验证密码强度的示例,假设通过 API 检查密码是否满足要求。

1. 项目配置
  • 安装 Prism NuGet 包(如Prism.DryIoc)。
  • 创建 Prism 应用程序,继承PrismApplication.
  • 确保视图模型继承BindableBase。
2. 定义模拟 API 服务

模拟异步 API 调用以验证密码强度:

IPasswordValidationService.cs:

usingSystem.Threading.Tasks;namespaceWpfApp.Services{publicinterfaceIPasswordValidationService{Task<(boolIsValid,stringMessage)>ValidatePasswordAsync(stringpassword);}}

PasswordValidationService.cs:

usingSystem.Text.RegularExpressions;usingSystem.Threading.Tasks;namespaceWpfApp.Services{publicclassPasswordValidationService:IPasswordValidationService{publicasyncTask<(boolIsValid,stringMessage)>ValidatePasswordAsync(stringpassword){// 模拟 API 延迟awaitTask.Delay(1000);if(string.IsNullOrEmpty(password))return(false,"Password cannot be empty.");if(password.Length<8)return(false,"Password must be at least 8 characters.");if(!Regex.IsMatch(password,@"[A-Z]"))return(false,"Password must contain at least one uppercase letter.");if(!Regex.IsMatch(password,@"[0-9]"))return(false,"Password must contain at least one number.");return(true,"Password is strong.");}}}
3. 定义视图模型

实现INotifyDataErrorInfo和异步验证逻辑:

MainWindowViewModel.cs:

usingPrism.Commands;usingPrism.Mvvm;usingSystem.Collections;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Linq;usingSystem.Threading.Tasks;usingWpfApp.Services;namespaceWpfApp.ViewModels{publicclassMainWindowViewModel:BindableBase,INotifyDataErrorInfo{privatereadonlyIPasswordValidationService_validationService;privatestring_password;privatebool_isValidating;privatestring_validationMessage;privatereadonlyDictionary<string,List<string>>_errors=newDictionary<string,List<string>>();publicstringPassword{get=>_password;set{if(SetProperty(ref_password,value)){ValidatePasswordAsync();}}}publicboolIsValidating{get=>_isValidating;set=>SetProperty(ref_isValidating,value);}publicstringValidationMessage{get=>_validationMessage;set=>SetProperty(ref_validationMessage,value);}publicDelegateCommandSubmitCommand{get;}publicMainWindowViewModel(IPasswordValidationServicevalidationService){_validationService=validationService;SubmitCommand=newDelegateCommand(OnSubmit,CanSubmit);}privateasyncvoidValidatePasswordAsync(){_errors.Remove(nameof(Password));IsValidating=true;ValidationMessage="Validating...";try{var(isValid,message)=await_validationService.ValidatePasswordAsync(_password);if(!isValid)AddError(nameof(Password),message);ValidationMessage=message;}finally{IsValidating=false;RaiseErrorsChanged(nameof(Password));SubmitCommand.RaiseCanExecuteChanged();}}privatevoidOnSubmit(){ValidationMessage="Form submitted successfully!";}privateboolCanSubmit(){return!HasErrors&&!IsValidating;}privatevoidAddError(stringpropertyName,stringerror){if(!_errors.ContainsKey(propertyName))_errors[propertyName]=newList<string>();_errors[propertyName].Add(error);}privatevoidRaiseErrorsChanged(stringpropertyName){ErrorsChanged?.Invoke(this,newDataErrorsChangedEventArgs(propertyName));}publicboolHasErrors=>_errors.Any();publiceventEventHandler<DataErrorsChangedEventArgs>ErrorsChanged;publicIEnumerableGetErrors(stringpropertyName){return_errors.ContainsKey(propertyName)?_errors[propertyName]:null;}}}
4. 定义视图

在MainWindow.xaml中应用异步验证并显示状态:

<Window x:Class="WpfApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApp.Converters" Title="MainWindow" Height="450" Width="800"> <Window.Resources> <local:ErrorConverter x:Key="ErrorConverter"/> <local:BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter"/> <Style TargetType="TextBox"> <Style.Triggers> <Trigger Property="Validation.HasError" Value="True"> <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/> <Setter Property="BorderBrush" Value="Red"/> </Trigger> </Style> </Style> </Window.Resources> <Grid> <StackPanel Margin="10"> <TextBox Width="200" Margin="5"> <TextBox.Text> <Binding Path="Password" UpdateSourceTrigger="PropertyChanged" ValidatesOnNotifyDataErrors="True"/> </TextBox.Text> <TextBox.ToolTip> <Binding Path="(Validation.Errors)" RelativeSource="{RelativeSource Self}" Converter="{StaticResource ErrorConverter}"/> </TextBox.ToolTip> </TextBox> <TextBlock Text="{Binding ValidationMessage}" FontSize="14" Margin="5"/> <TextBlock Text="Validating..." Visibility="{Binding IsValidating, Converter={StaticResource BoolToVisibilityConverter}}" Margin="5"/> <Button Content="Submit" Command="{Binding SubmitCommand}" Width="100" Margin="5"/> </StackPanel> </Grid> </Window>

ErrorConverter.cs:

usingSystem;usingSystem.Globalization;usingSystem.Windows.Controls;usingSystem.Windows.Data;namespaceWpfApp.Converters{publicclassErrorConverter:IValueConverter{publicobjectConvert(objectvalue,TypetargetType,objectparameter,CultureInfoculture){if(valueisReadOnlyObservableCollection<ValidationError>errors&&errors.Any())returnstring.Join("\n",errors.Select(e=>e.ErrorContent));returnnull;}publicobjectConvertBack(objectvalue,TypetargetType,objectparameter,CultureInfoculture){thrownewNotImplementedException();}}}

BooleanToVisibilityConverter.cs:

usingSystem;usingSystem.Globalization;usingSystem.Windows;usingSystem.Windows.Data;namespaceWpfApp.Converters{publicclassBooleanToVisibilityConverter:IValueConverter{publicobjectConvert(objectvalue,TypetargetType,objectparameter,CultureInfoculture){return(valueisboolisVisible&&isVisible)?Visibility.Visible:Visibility.Collapsed;}publicobjectConvertBack(objectvalue,TypetargetType,objectparameter,CultureInfoculture){returnvalueisVisibilityvisibility&&visibility==Visibility.Visible;}}}
5. 配置应用程序

在App.xaml.cs中注册服务:

usingPrism.Ioc;usingSystem.Windows;usingWpfApp.Services;namespaceWpfApp{publicpartialclassApp:PrismApplication{protectedoverrideWindowCreateShell(){returnContainer.Resolve<MainWindow>();}protectedoverridevoidRegisterTypes(IContainerRegistrycontainerRegistry){containerRegistry.Register<IPasswordValidationService,PasswordValidationService>();}}}

四、测试代码

以下是使用 MSTest 和 Moq 的单元测试示例,测试异步验证逻辑:

1. 测试依赖

安装 NuGet 包:

  • Microsoft.NET.Test.Sdk
  • MSTest.TestFramework
  • MSTest.TestAdapter
  • Moq
  • Prism.DryIoc
2. 测试代码

测试 PasswordValidationService:

usingMicrosoft.VisualStudio.TestTools.UnitTesting;usingSystem.Threading.Tasks;usingWpfApp.Services;[TestClass]publicclassPasswordValidationServiceTests{privatePasswordValidationService_service;[TestInitialize]publicvoidSetup(){_service=newPasswordValidationService();}[TestMethod]publicasyncTaskValidatePasswordAsync_ValidPassword_ReturnsValid(){var(isValid,message)=await_service.ValidatePasswordAsync("Password123");Assert.IsTrue(isValid);Assert.AreEqual("Password is strong.",message);}[TestMethod]publicasyncTaskValidatePasswordAsync_ShortPassword_ReturnsInvalid(){var(isValid,message)=await_service.ValidatePasswordAsync("Pass123");Assert.IsFalse(isValid);Assert.AreEqual("Password must be at least 8 characters.",message);}[TestMethod]publicasyncTaskValidatePasswordAsync_NoUppercase_ReturnsInvalid(){var(isValid,message)=await_service.ValidatePasswordAsync("password123");Assert.IsFalse(isValid);Assert.AreEqual("Password must contain at least one uppercase letter.",message);}}

测试 MainWindowViewModel:

usingMicrosoft.VisualStudio.TestTools.UnitTesting;usingMoq;usingSystem.Linq;usingSystem.Threading.Tasks;usingWpfApp.Services;usingWpfApp.ViewModels;[TestClass]publicclassMainWindowViewModelTests{privateMock<IPasswordValidationService>_validationServiceMock;privateMainWindowViewModel_viewModel;[TestInitialize]publicvoidSetup(){_validationServiceMock=newMock<IPasswordValidationService>();_viewModel=newMainWindowViewModel(_validationServiceMock.Object);}[TestMethod]publicvoidPassword_Empty_AddsError(){_viewModel.Password="";Assert.IsTrue(_viewModel.HasErrors);varerrors=_viewModel.GetErrors(nameof(_viewModel.Password))asList<string>;Assert.IsTrue(errors.Contains("Password cannot be empty."));}[TestMethod]publicasyncTaskPassword_Invalid_AddsError(){_validationServiceMock.Setup(s=>s.ValidatePasswordAsync("weak")).ReturnsAsync((false,"Password must be at least 8 characters."));_viewModel.Password="weak";awaitTask.Delay(1500);// 等待异步验证完成Assert.IsTrue(_viewModel.HasErrors);varerrors=_viewModel.GetErrors(nameof(_viewModel.Password))asList<string>;Assert.IsTrue(errors.Contains("Password must be at least 8 characters."));Assert.AreEqual("Password must be at least 8 characters.",_viewModel.ValidationMessage);}[TestMethod]publicasyncTaskPassword_Valid_NoErrors(){_validationServiceMock.Setup(s=>s.ValidatePasswordAsync("Password123")).ReturnsAsync((true,"Password is strong."));_viewModel.Password="Password123";awaitTask.Delay(1500);// 等待异步验证完成Assert.IsFalse(_viewModel.HasErrors);Assert.AreEqual("Password is strong.",_viewModel.ValidationMessage);Assert.IsTrue(_viewModel.SubmitCommand.CanExecute(null));}[TestMethod]publicasyncTaskIsValidating_SetDuringValidation(){_validationServiceMock.Setup(s=>s.ValidatePasswordAsync("Password123")).ReturnsAsync((true,"Password is strong."));boolisValidatingChanged=false;_viewModel.PropertyChanged+=(s,e)=>{if(e.PropertyName==nameof(_viewModel.IsValidating))isValidatingChanged=true;};_viewModel.Password="Password123";awaitTask.Delay(1500);// 等待异步验证完成Assert.IsTrue(isValidatingChanged);Assert.IsFalse(_viewModel.IsValidating);}}
3. 测试说明
  • PasswordValidationService 测试:
    • 验证有效密码返回正确结果。
    • 验证短密码和无大写字母的密码返回正确错误。
  • MainWindowViewModel 测试:
    • 验证空密码触发错误。
    • 验证无效密码触发异步错误。
    • 验证有效密码无错误且命令可执行。
    • 验证异步验证期间IsValidating属性变化。

五、注意事项

  1. 异步处理:
    • 使用async void在属性设置中触发异步验证,但需谨慎处理异常。
    • 考虑使用防抖(debounce)技术避免频繁触发异步调用。
  2. UI 反馈:
    • 使用IsValidating和BooleanToVisibilityConverter显示验证进度。
    • 使用ErrorConverter显示错误消息。
  3. 模块化集成:
    • 异步验证逻辑可定义在模块中,与区域或导航结合。
  4. 性能优化:
    • 避免短时间内多次触发异步验证。
    • 使用缓存或延迟机制优化 API 调用。
  5. 测试注意:
    • 使用Task.Delay模拟异步等待,确保测试覆盖异步逻辑。
    • 使用 Moq 模拟异步服务。

六、总结

Prism 异步验证通过INotifyDataErrorInfo和async/await实现动态输入验证,适合远程验证(如密码强度检查)和复杂业务规则。结合 Prism 的 MVVM 支持,异步验证可集成到模块化应用中。上述代码示例展示了密码强度的异步验证,测试代码验证了核心功能。

如果您需要更具体的内容(例如与区域/导航/事件聚合器的集成、其他异步验证场景或特定测试用例),请提供细节,我会进一步优化答案!

相关新闻

  • DiffusionGemma-26B-A4B-it-mxfp8 故障排除:常见问题与解决方案大全
  • 3步安装LizzieYzy:围棋AI分析的终极免费指南
  • 3DTopia在游戏开发中的应用:快速创建游戏资产的完整指南

最新新闻

  • minimal-functional-fox社区指南:如何获得帮助与分享你的定制成果
  • 种子搜索终极革命:如何用Jackett一站式管理700+种子站点
  • 深入解析McBSP串行通信:架构、数据流与嵌入式音频应用实践
  • meilisearch-php完全指南:如何构建闪电般的PHP搜索应用
  • 如何在本地预览Next-on-Netlify应用:Netlify CLI实战教程
  • Tachometer 与置信区间:理解统计显著性在 Web 性能测试中的重要性

日新闻

  • 宝珀中国官方售后服务中心|官方热线和维修地址权威信息声明(2026年7月更新) - 宝珀官方售后服务中心
  • # 2026年北京知识产权律师推荐怎么选?看这五点关键不踩雷 - 本地品牌推荐
  • 2026实测教程:生成的拼豆图纸不满意怎么修改才省事 - 省事研究所

周新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号