曾經我以學習的目的寫了關於在.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更新了些啥html
咱們先來看下關於Prism7.2和Prism8.0的程序集引用狀況,可推敲出一些不一樣:git
這裏可能不會講述全部關於Prism8.0更新的所有細節,只是我認爲可能主要的一些功能,咱們能夠看到Prism8.0相比Prism7.2,在Prism.WPF
中去除了System.Windows.Interactivity
和CommonServiceLocator
程序集,引入了Microsoft.Xaml.Behaviors.Wpf
,實際上Prism8.0作了如下整合:github
Microsoft.Xaml.Behaviors.Wpf
替換System.Windows.Interactivity
CommonServiceLocator
整合入Prism.Core
之中由於你從舊版本更新到Prism8.0可能會發生報錯,而個人目的則是一篇更新指南,關於Prism8.0更新的所有細節,能夠看官方在github的Prism8.0的ReleaseNote,這裏還推薦dino.c大佬的有關Prism8.0的文章:[Windows] Prism 8.0 入門(上):Prism.Core和[Windows] Prism 8.0 入門(下):Prism.Wpf 和 Prism.Unityexpress
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裏面的公共功能bootstrap
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也須要對應更改c#
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獨有)windows
其實IOC這部分功能我不打算細講,由於其實不屬於Prism的特性功能,由於Prism默認支持兩個IOC擴展,也就是Unity和DryIoc的,而新添加的功能也是對應經過兩個IOC支持實現的,直接看代碼示例:api
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)//註冊爲範圍模式
你乍一看好像沒什麼卵用,可是裏面仍是有說法的,咱們來看一個例子,WPF界面MVVM異步讀取耗時數據加載界面,這裏是xaml的簡化代碼::app
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
其實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
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