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

发布时间:2026/9/10 12:38:44

鉴于之前已经询问过 Prism 的多项功能(包括异步验证),我将提供一个精简且聚焦的回答,基于 WPF 环境,避免重复之前的内容 Prism 异步验证的示例。鉴于之前已经询问过 Prism 的多项功能包括异步验证我将提供一个精简且聚焦的回答基于 WPF 环境避免重复之前的内容如INotifyDataErrorInfo和模拟 API 的异步验证示例。本次示例将展示一个不同场景的异步验证例如验证密码强度通过异步 API 调用包括代码、测试代码、适用场景和使用方法。如果您有特定需求如与导航/区域的集成、更复杂的验证逻辑或特定测试场景请进一步说明我会定制答案。一、异步验证概述Prism 通过 WPF 的INotifyDataErrorInfo接口支持异步验证结合BindableBase和异步方法async/await实现动态输入验证。异步验证适用于需要远程或复杂逻辑的场景如 API 调用检查密码强度并通过错误通知机制更新 UI。主要特性异步处理支持耗时验证如 API 调用。错误通知通过INotifyDataErrorInfo提供动态错误反馈。MVVM 集成与 Prism 的 MVVM 模式无缝结合。模块化支持验证逻辑可定义在模块中。二、适用场景异步验证适用于远程验证如通过 API 检查密码强度或唯一性。复杂规则如验证密码是否符合安全策略长度、字符类型。实时反馈输入时异步验证并显示错误或状态。模块化应用模块中的视图模型需要独立异步验证。表单提交确保提交前所有字段通过异步验证。三、异步验证示例以下是一个异步验证密码强度的示例假设通过 API 检查密码是否满足要求。1. 项目配置安装 Prism NuGet 包如Prism.DryIoc。创建 Prism 应用程序继承PrismApplication.确保视图模型继承BindableBase。2. 定义模拟 API 服务模拟异步 API 调用以验证密码强度IPasswordValidationService.csusingSystem.Threading.Tasks;namespaceWpfApp.Services{publicinterfaceIPasswordValidationService{Task(boolIsValid,stringMessage)ValidatePasswordAsync(stringpassword);}}PasswordValidationService.csusingSystem.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.Length8)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.csusingPrism.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;privatereadonlyDictionarystring,Liststring_errorsnewDictionarystring,Liststring();publicstringPassword{get_password;set{if(SetProperty(ref_password,value)){ValidatePasswordAsync();}}}publicboolIsValidating{get_isValidating;setSetProperty(ref_isValidating,value);}publicstringValidationMessage{get_validationMessage;setSetProperty(ref_validationMessage,value);}publicDelegateCommandSubmitCommand{get;}publicMainWindowViewModel(IPasswordValidationServicevalidationService){_validationServicevalidationService;SubmitCommandnewDelegateCommand(OnSubmit,CanSubmit);}privateasyncvoidValidatePasswordAsync(){_errors.Remove(nameof(Password));IsValidatingtrue;ValidationMessageValidating...;try{var(isValid,message)await_validationService.ValidatePasswordAsync(_password);if(!isValid)AddError(nameof(Password),message);ValidationMessagemessage;}finally{IsValidatingfalse;RaiseErrorsChanged(nameof(Password));SubmitCommand.RaiseCanExecuteChanged();}}privatevoidOnSubmit(){ValidationMessageForm submitted successfully!;}privateboolCanSubmit(){return!HasErrors!IsValidating;}privatevoidAddError(stringpropertyName,stringerror){if(!_errors.ContainsKey(propertyName))_errors[propertyName]newListstring();_errors[propertyName].Add(error);}privatevoidRaiseErrorsChanged(stringpropertyName){ErrorsChanged?.Invoke(this,newDataErrorsChangedEventArgs(propertyName));}publicboolHasErrors_errors.Any();publiceventEventHandlerDataErrorsChangedEventArgsErrorsChanged;publicIEnumerableGetErrors(stringpropertyName){return_errors.ContainsKey(propertyName)?_errors[propertyName]:null;}}}4. 定义视图在MainWindow.xaml中应用异步验证并显示状态Window x:ClassWpfApp.MainWindow xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml xmlns:localclr-namespace:WpfApp.Converters TitleMainWindow Height450 Width800 Window.Resources local:ErrorConverter x:KeyErrorConverter/ local:BooleanToVisibilityConverter x:KeyBoolToVisibilityConverter/ Style TargetTypeTextBox Style.Triggers Trigger PropertyValidation.HasError ValueTrue Setter PropertyToolTip Value{Binding RelativeSource{RelativeSource Self}, Path(Validation.Errors)[0].ErrorContent}/ Setter PropertyBorderBrush ValueRed/ /Trigger /Style /Style /Window.Resources Grid StackPanel Margin10 TextBox Width200 Margin5 TextBox.Text Binding PathPassword UpdateSourceTriggerPropertyChanged ValidatesOnNotifyDataErrorsTrue/ /TextBox.Text TextBox.ToolTip Binding Path(Validation.Errors) RelativeSource{RelativeSource Self} Converter{StaticResource ErrorConverter}/ /TextBox.ToolTip /TextBox TextBlock Text{Binding ValidationMessage} FontSize14 Margin5/ TextBlock TextValidating... Visibility{Binding IsValidating, Converter{StaticResource BoolToVisibilityConverter}} Margin5/ Button ContentSubmit Command{Binding SubmitCommand} Width100 Margin5/ /StackPanel /Grid /WindowErrorConverter.csusingSystem;usingSystem.Globalization;usingSystem.Windows.Controls;usingSystem.Windows.Data;namespaceWpfApp.Converters{publicclassErrorConverter:IValueConverter{publicobjectConvert(objectvalue,TypetargetType,objectparameter,CultureInfoculture){if(valueisReadOnlyObservableCollectionValidationErrorerrorserrors.Any())returnstring.Join(\n,errors.Select(ee.ErrorContent));returnnull;}publicobjectConvertBack(objectvalue,TypetargetType,objectparameter,CultureInfoculture){thrownewNotImplementedException();}}}BooleanToVisibilityConverter.csusingSystem;usingSystem.Globalization;usingSystem.Windows;usingSystem.Windows.Data;namespaceWpfApp.Converters{publicclassBooleanToVisibilityConverter:IValueConverter{publicobjectConvert(objectvalue,TypetargetType,objectparameter,CultureInfoculture){return(valueisboolisVisibleisVisible)?Visibility.Visible:Visibility.Collapsed;}publicobjectConvertBack(objectvalue,TypetargetType,objectparameter,CultureInfoculture){returnvalueisVisibilityvisibilityvisibilityVisibility.Visible;}}}5. 配置应用程序在App.xaml.cs中注册服务usingPrism.Ioc;usingSystem.Windows;usingWpfApp.Services;namespaceWpfApp{publicpartialclassApp:PrismApplication{protectedoverrideWindowCreateShell(){returnContainer.ResolveMainWindow();}protectedoverridevoidRegisterTypes(IContainerRegistrycontainerRegistry){containerRegistry.RegisterIPasswordValidationService,PasswordValidationService();}}}四、测试代码以下是使用 MSTest 和 Moq 的单元测试示例测试异步验证逻辑1. 测试依赖安装 NuGet 包Microsoft.NET.Test.SdkMSTest.TestFrameworkMSTest.TestAdapterMoqPrism.DryIoc2. 测试代码测试 PasswordValidationServiceusingMicrosoft.VisualStudio.TestTools.UnitTesting;usingSystem.Threading.Tasks;usingWpfApp.Services;[TestClass]publicclassPasswordValidationServiceTests{privatePasswordValidationService_service;[TestInitialize]publicvoidSetup(){_servicenewPasswordValidationService();}[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);}}测试 MainWindowViewModelusingMicrosoft.VisualStudio.TestTools.UnitTesting;usingMoq;usingSystem.Linq;usingSystem.Threading.Tasks;usingWpfApp.Services;usingWpfApp.ViewModels;[TestClass]publicclassMainWindowViewModelTests{privateMockIPasswordValidationService_validationServiceMock;privateMainWindowViewModel_viewModel;[TestInitialize]publicvoidSetup(){_validationServiceMocknewMockIPasswordValidationService();_viewModelnewMainWindowViewModel(_validationServiceMock.Object);}[TestMethod]publicvoidPassword_Empty_AddsError(){_viewModel.Password;Assert.IsTrue(_viewModel.HasErrors);varerrors_viewModel.GetErrors(nameof(_viewModel.Password))asListstring;Assert.IsTrue(errors.Contains(Password cannot be empty.));}[TestMethod]publicasyncTaskPassword_Invalid_AddsError(){_validationServiceMock.Setup(ss.ValidatePasswordAsync(weak)).ReturnsAsync((false,Password must be at least 8 characters.));_viewModel.Passwordweak;awaitTask.Delay(1500);// 等待异步验证完成Assert.IsTrue(_viewModel.HasErrors);varerrors_viewModel.GetErrors(nameof(_viewModel.Password))asListstring;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(ss.ValidatePasswordAsync(Password123)).ReturnsAsync((true,Password is strong.));_viewModel.PasswordPassword123;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(ss.ValidatePasswordAsync(Password123)).ReturnsAsync((true,Password is strong.));boolisValidatingChangedfalse;_viewModel.PropertyChanged(s,e){if(e.PropertyNamenameof(_viewModel.IsValidating))isValidatingChangedtrue;};_viewModel.PasswordPassword123;awaitTask.Delay(1500);// 等待异步验证完成Assert.IsTrue(isValidatingChanged);Assert.IsFalse(_viewModel.IsValidating);}}3. 测试说明PasswordValidationService 测试验证有效密码返回正确结果。验证短密码和无大写字母的密码返回正确错误。MainWindowViewModel 测试验证空密码触发错误。验证无效密码触发异步错误。验证有效密码无错误且命令可执行。验证异步验证期间IsValidating属性变化。五、注意事项异步处理使用async void在属性设置中触发异步验证但需谨慎处理异常。考虑使用防抖debounce技术避免频繁触发异步调用。UI 反馈使用IsValidating和BooleanToVisibilityConverter显示验证进度。使用ErrorConverter显示错误消息。模块化集成异步验证逻辑可定义在模块中与区域或导航结合。性能优化避免短时间内多次触发异步验证。使用缓存或延迟机制优化 API 调用。测试注意使用Task.Delay模拟异步等待确保测试覆盖异步逻辑。使用 Moq 模拟异步服务。六、总结Prism 异步验证通过INotifyDataErrorInfo和async/await实现动态输入验证适合远程验证如密码强度检查和复杂业务规则。结合 Prism 的 MVVM 支持异步验证可集成到模块化应用中。上述代码示例展示了密码强度的异步验证测试代码验证了核心功能。如果您需要更具体的内容例如与区域/导航/事件聚合器的集成、其他异步验证场景或特定测试用例请提供细节我会进一步优化答案
延伸阅读

更多相关文章

2026/9/4 6:16:35

CANN Ascend C SIMT半精度转换函数

__half2uint_rz 【免费下载链接】asc-devkit 本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。 项目地址: https://gitcode…

2026/9/10 18:54:07

Hadoop与3D打印结合的制造业大数据分析实践

1. 项目背景与核心价值 当制造业遇上大数据和增材制造技术,一场生产效率革命正在悄然发生。这个项目将Hadoop分布式计算框架与3D打印技术相结合,构建了一套面向制造领域的数据分析解决方案。在实际生产环境中,我们每天需要处理来自数百台3D打…

2026/9/10 18:54:07

基于深度学习的人脸门禁与IPC安防监控系统实战指南

简介:面向深度学习与智能安防方向的毕业设计、课程设计开发者,这份源码项目融合人脸门禁与IPC网络摄像监控,可完成实时人脸识别、身份验证、异常告警等典型安防场景,适用于RKNN嵌入式平台快速验证。压缩包共71个文件,大…

2026/9/10 18:54:07

大模型API请求全链路:从Harness编排到KV Cache优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/10 18:49:07

医疗知识图谱问答系统实战:Python+Neo4j实现KBQA

简介:这是一份面向课程设计与知识图谱入门学习的医疗知识图谱问答系统Python工程,适合希望快速搭建轻量级问答Demo的开发者。项目包含设计报告Word、完整源码、医疗数据与运行截图,围绕“构建医疗知识图谱—实现简单对话系统”展开&#xff0…

2026/9/10 16:39:38

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/10 11:16:38

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/10 0:00:55

目录对比去重实战:用哈希算法精准清理重复文件

我电脑里现在还有一块换了三次机的“数据墓地”硬盘,里面存着2016年以前所有旧笔记本的完整备份。平时不觉得有什么,直到前阵子想把它整理归档,发现同一个安装包、同一批照片、同一份论文草稿,在几个不同的备份目录里反复出现。更…

2026/9/10 0:00:55

Leaflet离线地图完整Demo合集:内网部署与坐标纠偏实战

简介:这是一份面向Web GIS开发者的LeafLet离线地图示例合集,帮助开发者快速掌握离线地图从搭建到交互的完整流程。压缩包共723个文件,大小14.06MB,以319个js脚本、175个html页面和29个css样式文件为主体,配合png/svg图…

2026/9/10 0:00:55

MATLAB读取Rinex 3.02观测文件:多系统GNSS数据解析实战

简介:基于MATLAB开发的Rinex3.02版观测文件(o文件)读取代码包,面向卫星定位导航方向的学习者与研究人员,用于解决新版观测文件的数据解析、历元提取与时间转换问题。压缩包共4个文件,包含两个m脚本、一个19…

2026/9/10 12:32:02

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/10 15:19:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/10 15:49:53

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码