WPF NET5 Prism8.0的升级指南
前言
曾经我以学习的目的写了关于在.NET Core3.1使用Prism的系列文章.NET Core 3 WPF MVVM框架 Prism系列文章索引,也谢谢大家的支持,事实上当初的版本则是Prism7.2.0.1442(7.2)版本,而现在也发布了.NET5和最新的Prism8.0.0.1909(8.0)版本,因此同样的我想将之前的Prism Demo项目可以升级到最新,写这篇文章的目的是自己也能学习一番,而更多的是回答那些在我Prism系列文章下面留下的我认为可以拿来一讲一些问题,而有些问题我则是水平有限回答不了(真的不是不想回答)
然后我拿之前的Prism Demo项目,WPF从.NET Core3.1升级到.NET 5其实非常简单,无脑修改项目的TargetFramework
为net5.0-windows
就行了,但是当Prism7.2升级到Prism8.0,我发现build的时候报了很多错误,那么让我们来看看究竟Prism8.0更新了些啥
一 .Prism8.0更新了什么?
我们先来看下关于Prism7.2和Prism8.0的程序集引用情况,可推敲出一些不同:
这里可能不会讲述所有关于Prism8.0更新的全部细节,只是我认为可能主要的一些功能,我们可以看到Prism8.0相比Prism7.2,在Prism.WPF
中去除了System.Windows.Interactivity
和CommonServiceLocator
程序集,引入了Microsoft.Xaml.Behaviors.Wpf
,实际上Prism8.0做了以下整合:
- 用
Microsoft.Xaml.Behaviors.Wpf
替换System.Windows.Interactivity
CommonServiceLocator
整合入Prism.Core
之中
因为你从旧版本更新到Prism8.0可能会发生报错,而我的目的则是一篇更新指南,关于Prism8.0更新的全部细节,可以看官方在github的Prism8.0的ReleaseNote,这里还推荐Dior大佬的有关Prism8.0的文章:[Windows] Prism 8.0 入门(上):Prism.Core和[Windows] Prism 8.0 入门(下):Prism.Wpf 和 Prism.Unity
1.ContainerLocator.Current.Resolve函数去除:
ContainerLocator.Current.Resolve<T>
//替换为
ServiceLocator.Current.GetInstance<T>
这可能是你遇到的第一个升级报错,因为ContainerLocator.Current.Resolve<T>
这个api本来是在Prism.WPF
下的CommonServiceLocator
程序集下面的,8.0时候被砍了,在Prism.Core
加上ServiceLocator.Current.GetInstance<T>
用于替换,切掉了CommonServiceLocator
程序集,我觉得非常合理,因为该功能本身就应该是IOC里面的公共功能
2.有关事件转命令的程序集变化:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
//替换为
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
这可能是你遇到的第二个升级报错,由于用Microsoft.Xaml.Behaviors.Wpf
替换System.Windows.Interactivity
,因此,xaml的xmlns也需要对应更改
3.去除 Bootstrapper :
public partial class App : Bootstrapper
//替换成
public partial class App : PrismApplication //(推荐)其他平台也支持
//or
public partial class App : PrismBootstrapper //WPF独有
这可能是你遇到的第三个升级报错,我们在App.cs中都会集成一个底层类用于注册或者配置,其实在Prism7.2的时候Bootstrapper
已经被标记为弃用状态,而在Prism8.0更是直接删除,推荐继承PrismApplication
(各平台都支持),当然也可以选择PrismBootstrapper
(WPF独有)
4.IOC添加新注册功能:
其实IOC这部分功能我不打算细讲,因为其实不属于Prism的特性功能,因为Prism默认支持两个IOC扩展,也就是Unity和DryIoc的,而新添加的功能也是对应通过两个IOC支持实现的,直接看代码示例:
public interface ITestService { }
public interface ITest2Service { }
public class TestService : ITestService, ITest2Service { }
private static ITestService TestDelegate() =>new TestService();
//添加支持注册多服务对应单实现类的功能
var services = new[] { typeof(ITestService), typeof(ITest2Service) };
IContainerRegistry.RegisterManySingleton<TestService>(services);//注册成单例模式
IContainerRegistry.RegisterMany<TestService>(services);//注册成瞬时模式
//添加支持注册服务为scope(范围模式)
IContainerRegistry.RegisterScoped(typeof(TestService))//单服务
IContainerRegistry.RegisterScoped(typeof(TestService), typeof(TestService))//单服务
IContainerRegistry.RegisterScoped<TestService>();//单服务泛型版本
IContainerRegistry.RegisterScoped(typeof(ITestService), typeof(TestService))//单服务单实现
//添加支持通过委托方法注册服务
IContainerRegistry.Register(typeof(ITestService), TestDelegate)//注册为瞬时模式
IContainerRegistry.RegisterSingleton(typeof(ITestService), TestDelegate)//注册为单例模式
IContainerRegistry.RegisterScoped(typeof(ITestService), TestDelegate)//注册为范围模式
5.添加了有关在void方法中异步等待Task的扩展方法:
你乍一看好像没什么卵用,但是里面还是有说法的,我们来看一个例子,WPF界面MVVM异步读取耗时数据加载界面,这里是xaml的简化代码::
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid Grid.Row="1" IsReadOnly="True" ItemsSource="{Binding AllMedicines}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Type" Binding="{Binding Type}"/>
<DataGridTextColumn Header="Unit" Binding="{Binding Unit}"/>
</DataGrid.Columns>
</DataGrid>
ViewModel简化代码:
private ObservableCollection<Medicine> _allMedicines=new ObservableCollection<Medicine>();
public ObservableCollection<Medicine> AllMedicines
{
get { return _allMedicines; }
set { _allMedicines = value; }
}
private DelegateCommand _loadCommand;
public DelegateCommand LoadCommand =>
_loadCommand ?? (_loadCommand = new DelegateCommand(ExecuteLoadCommand));
async void ExecuteLoadCommand()
{
await ALongTask();
this.AllMedicines.AddRange(_medicineSerivce.GetAllMedicines());
}
private async Task ALongTask()
{
await Task.Delay(3000);//模拟耗时操作
Debug.WriteLine("耗时操作完成");
}
这是正常我们会实现的方式,同样的也确实不会出现跨线程问题(在非UI线程操作ObservableCollection
集合会出现),关于async await在WPF不会出现跨线程问题,可以参考我的另外一篇文章异步函数async await在wpf都做了什么?,也同样的在执行耗时操作时候不会阻塞UI主线程,如果在最上层不用async void能否实现同样的效果,这就是TaskExtension
的意义了,下面只例举非泛型版本TaskExtension
的api,,实际还有泛型版本的TaskExtension
,我们拿最多参数的重载方法来说明:
public static class TaskExtensions
{
public static async void Await(this Task task, Action completedCallback, Action<Exception> errorCallback, bool configureAwait)
{
try
{
await task.ConfigureAwait(configureAwait);
completedCallback?.Invoke();
}
catch (Exception obj)
{
errorCallback?.Invoke(obj);
}
}
}
1.completedCallback:当前Task的回调函数,指Task执行的后续操作
2.errorCallback:回调函数的异常回调函数,回调函数异常后可以执行
3.configureAwait:指示回调函数是否在当前执行上下文执行,True为是,false为否
我们可以把ExecuteLoadCommand方法修改下:
void ExecuteLoadCommand()
{
//TaskExtension for async void Command
ALongTask().Await( completedCallback:() =>
{
this.AllMedicines.AddRange(_medicineSerivce.GetAllMedicines());
}, errorCallback:null,configureAwait:true);
}
该方式执行效果和之前一样,而且不用在void方法加上async 和方法内部await就能实现异步等待操作,而这只是推荐在Command的Excuted Method使用,这也是官方推荐的,因为一般Excuted Method返回值只会是void
二.回答一些问题
如何在Prism使用AOP?
其实AOP并不是属于prism特有的功能,但是由于prism支持扩展IOC容器:Unity和DryIoc,只要其IOC容器本身支持,那就可以,由于默认Prism是以Unity为默认IOC容器,所以以Unity为例子:
NuGet引用Unity AOP库:Unity.Interception(最新是5.11.1)
在App.cs添加扩展AOP,代码如下:
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
var container = PrismIocExtensions.GetContainer(containerRegistry);
container.AddNewExtension<Interception>()//add Extension Aop
//注册服务和添加显示拦截
.RegisterType<IMedicineSerivce, MedicineSerivce>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<PolicyInjectionBehavior>())
.RegisterType<IPatientService, PatientService>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<PolicyInjectionBehavior>())
.RegisterType<IUserService, UserService>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<PolicyInjectionBehavior>()); }
新建类
LogHandler
继承ICallHandler
用于处理拦截逻辑和特性LogHandlerAttribute
,模拟记录Log,:public class LogHandler : ICallHandler
{
public int Order { get ; set ; } public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
Debug.WriteLine("-------------Method Excute Befored-------------");
Debug.WriteLine($"Method Name:{input.MethodBase.Name}");
if (input.Arguments.Count>0)
{
Debug.WriteLine("Arguments:");
for (int i = 0; i < input.Arguments.Count; i++)
{
Debug.WriteLine($"parameterName:{input.Arguments.ParameterName(i)},parameterValue:{input.Arguments[i]}");
}
}
var methodReturn = getNext()(input, getNext);
Debug.WriteLine("-------------Method Excute After-------------");
if (methodReturn.Exception!=null)
{
Debug.WriteLine($"Exception:{methodReturn.Exception.Message} \n");
}
else
{
Debug.WriteLine($"Excuted Successed \n");
}
return methodReturn;
}
} public class LogHandlerAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new LogHandler() { Order = this.Order };
}
}
为那些需要拦截的接口标上Attribute
[LogHandler]
public interface IMedicineSerivce
{
List<Medicine> GetAllMedicines();
List<Recipe> GetRecipesByPatientId(int patientId);
}
[LogHandler]
public interface IPatientService
{
List<Patient> GetAllPatients();
}
[LogHandler]
public interface IUserService
{
List<User> GetAllUsers();
}效果如下:
Vs输出:
-------------Method Excute Befored-------------
Method Name:GetAllMedicines
-------------Method Excute After-------------
Excuted Successed
-------------Method Excute Befored-------------
Method Name:GetRecipesByPatientId
Arguments:
parameterName:patientId,parameterValue:1
-------------Method Excute After-------------
Excuted Successed
-------------Method Excute Befored-------------
Method Name:GetRecipesByPatientId
Arguments:
parameterName:patientId,parameterValue:2
-------------Method Excute After-------------
Excuted Successed
-------------Method Excute Befored-------------
Method Name:GetRecipesByPatientId
Arguments:
parameterName:patientId,parameterValue:3
-------------Method Excute After-------------
Excuted Successed
-------------Method Excute Befored-------------
Method Name:GetRecipesByPatientId
Arguments:
parameterName:patientId,parameterValue:4
-------------Method Excute After-------------
Excuted Successed
当然这里篇幅有限,不可能讲述有关太多Unity AOP的细节,实际上Unity AOP功能非常强大,同样支持通过配置文件来配置AOP和支持对不同类型方法的拦截,需要了解更多细节在这里可推荐该博文C#中AOP_使用Unity实现AOP
是否所有事件和逻辑都在ViewModel处理?
WPF是个数据驱动型程序,当使用MVVM框架如Prism或者MVVMLight的时候,我们会在ViewModel处理业务数据逻辑,通过Binding方式驱动前台界面的显示,如果处理逻辑是View相关的,例如对控件的样式变化,鼠标移动控件等View逻辑相关的,这时候则推荐用依赖或者附加属性,或在View的Code-behind的cs文件中事件来处理有关View的逻辑,不要为了所谓的MVVM而把一切逻辑都放在ViewModel处理,实则更加不灵活,反而跟之前的MVC都放在C中处理没啥区别了
其他问题?(待补充)
三.源码
四.参考
https://github.com/PrismLibrary/Prism
https://github.com/PrismLibrary/Prism/releases
C#中AOP_使用Unity实现AOP
WPF NET5 Prism8.0的升级指南的更多相关文章
- Apache ShardingSphere 5.0.0 内核优化及升级指南
经过近两年时间的优化和打磨,Apache ShardingSphere 5.0.0 GA 版终于在本月正式发布,相比于 4.1.1 GA 版,5.0.0 GA 版在内核层面进行了大量的优化.首先,基于 ...
- Spring Boot 2.0 升级指南
Spring Boot 2.0 升级指南 前言 Spring Boot已经发布2.0有5个月多,多了很多新特性,一些坑也慢慢被填上,最近有空,就把项目中Spring Boot 版本做了升级,顺便整理下 ...
- Angular4.0.0正式发布,附新特性及升级指南
本文首发地址:Angular4.0.0正式发布,附新特性及升级指南 作者|孙薇 编辑|尾尾 经历了6个RC版本之后,Angular项目组终于发布了新版,即正式版 Angular 4.0.0.新版的 A ...
- 企业IT管理员IE11升级指南【17】—— F12 开发者工具
企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...
- 企业IT管理员IE11升级指南【15】—— 代理自动配置脚本
企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...
- 企业IT管理员IE11升级指南【5】—— 不跟踪(DNT)例外
企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...
- 企业IT管理员IE11升级指南【6】—— Internet Explorer 11面向IT专业人员的常见问题
企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...
- 企业IT管理员IE11升级指南【7】—— Win7和Win8.1上的IE11功能对比
企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...
- webpack4升级指南
webpack4升级指南 鉴于图书项目编译速度极慢的情况(项目里面module太多了,编译慢很正常)且最近需求不多(很少出现的空挡期).所以我觉得搞一波webpack升级,看看有没有帮助.webpac ...
随机推荐
- C++将整型值转换为字符串
最简单的方法是使用一个字符串流(stringstream):#include<iostream>#include<string>#include<sstream>u ...
- MetaException(message:Could not connect to meta store using any of the URIs provided. Most recent failure: org.apache.thrift.transport.TTransportException: java.net.ConnectException: 拒绝连接 (Connection
hive在hive-site.xml中配置hive.metastore.uris属性,后启动异常 hive异常 [fan@master hive-0.13.1-cdh5.3.6]$ bin/hive ...
- 06-flask-文件上传案例
前端代码 Demo.html <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...
- 关于Java Integer和Long直接比较
Integer和Long不能直接equals比较会返回False Long.class源码 ` public boolean equals(Object obj) { if (obj instance ...
- Restful API 接口设计标准及规范
Restful API 接口设计标准以及规范 RESTful概念 理解和评估以网络为基础的应用软件的架构设计,得到一个功能强.性能好.适宜通信的架构.REST指的是一组架构约束条件和原则." ...
- 用Wireshark对Android应用的网络流量进行抓包
通过Wireshark.Charles.Burpsuite等工具分析网络流量的过程,又叫做抓包. 为何需要抓包 测试手机应用(如搜狗号码通.搜狗手机浏览器)的功能时,经常遇到与网络交互的场景,这时候我 ...
- Swift3.0学习之Button
1.根据字符串的长度确定Button的长度(button的高度给定) let hight:CGFloat = 30.0 let size1 = CGSize(width: getLabWidth(la ...
- java 去掉html标签 使用正则表达式删除HTML标签。
import java.util.regex.Matcher; import java.util.regex.Pattern; public class HTMLSpirit{ public stat ...
- jmeter+jdk环境配置
一:JDK 安装配置:https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html ,下载JDK8 安装 1. ...
- 初探JAVA内部类细节一
定义: 可以将一个类的定义放在另一个类的内部 这就是内部类.--摘自java编程思想 一般实现方式: public class SimpleInnerClass { class Content { p ...