<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          .NET 5 WPF Prism 8.0 的升級指南

          共 8848字,需瀏覽 18分鐘

           ·

          2021-01-23 10:57

          轉(zhuǎn)自:RyzenAdorer
          cnblogs.com/ryzen/p/14301414.html

          前言


          曾經(jīng)以學習的目的寫了關(guān)于在 .NET Core 3.1 使用Prism的系列文章.NET Core 3 WPF MVVM 框架 Prism系列文章,也謝謝大家的支持。


          《.NET Core 3.0 WPF MVVM框架 Prism系列之模塊化》


          《.NET Core 3.0 WPF MVVM框架 Prism系列之事件聚合器》


          事實上當初的版本則是Prism 7.2.0.1442(7.2)版本,而現(xiàn)在也發(fā)布了.NET 5和最新的Prism 8.0.0.1909(8.0)版本,因此同樣的我想將之前的Prism Demo項目可以升級到最新。


          寫這篇文章的目的是自己也能學習一番,而更多的是回答那些在我Prism系列文章下面留下的我認為可以拿來一講一些問題,而有些問題我則是水平有限回答不了(真的不是不想回答)


          然后我拿之前的Prism Demo項目,WPF從.NET Core 3.1 升級到.NET 5其實非常簡單,無腦修改項目的TargetFramework 為 net 5.0-windows就行了,但是當 Prism 7.2 升級到 Prism 8.0,我發(fā)現(xiàn)build的時候報了很多錯誤,那么讓我們來看看究竟Prism 8.0 更新了些什么?


          一 、Prism 8.0 更新了什么?


          先來看下關(guān)于Prism 7.2和Prism 8.0的程序集引用情況,可推敲出一些不同:



          這里可能不會講述所有關(guān)于Prism 8.0更新的全部細節(jié),只是我認為可能主要的一些功能,我們可以看到Prism 8.0相比Prism 7.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可能會發(fā)生報錯,而我的目的則是一篇更新指南,關(guān)于Prism8.0更新的全部細節(jié),可以看官方在github的Prism8.0的ReleaseNote,這里還推薦dino.c大佬的有關(guān)Prism8.0的文章:[Windows] Prism 8.0 入門(上):Prism.Core和[Windows] Prism 8.0 入門(下):Prism.Wpf 和 Prism.Unity


          1、ContainerLocator.Current.Resolve函數(shù)去除


          ContainerLocator.Current.Resolve<T>
          //替換為
          ServiceLocator.Current.GetInstance<T>



          這可能是你遇到的第一個升級報錯,因為ContainerLocator.Current.Resolve這個api本來是在Prism.WPF下的CommonServiceLocator程序集下面的,8.0時候被砍了,在Prism.Core加上ServiceLocator.Current.GetInstance用于替換,切掉了CommonServiceLocator程序集,我覺得非常合理,因為該功能本身就應該是IOC里面的公共功能


          2、有關(guān)事件轉(zhuǎn)命令的程序集變化


          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 已經(jīng)被標記為棄用狀態(tài),而在Prism8.0更是直接刪除,推薦繼承PrismApplication(各平臺都支持),當然也可以選擇PrismBootstrapper (WPF獨有)


          4、IOC添加新注冊功能


          其實IOC這部分功能我不打算細講,因為其實不屬于Prism的特性功能,因為Prism默認支持兩個IOC擴展,也就是Unity和DryIoc的,而新添加的功能也是對應通過兩個IOC支持實現(xiàn)的,直接看代碼示例:


          public interface ITestService { }
          public interface ITest2Service { }
          public class TestService : ITestService, ITest2Service { }
          private static ITestService TestDelegate() =>new TestService();
          //添加支持注冊多服務對應單實現(xiàn)類的功能
          var services = new[] { typeof(ITestService), typeof(ITest2Service) };
          IContainerRegistry.RegisterManySingleton(services);//注冊成單例模式
          IContainerRegistry.RegisterMany(services);//注冊成瞬時模式
          //添加支持注冊服務為scope(范圍模式)
          IContainerRegistry.RegisterScoped(typeof(TestService))//單服務
          IContainerRegistry.RegisterScoped(typeof(TestService), typeof(TestService))//單服務
          IContainerRegistry.RegisterScoped();//單服務泛型版本
          IContainerRegistry.RegisterScoped(typeof(ITestService), typeof(TestService))//單服務單實現(xiàn)
          //添加支持通過委托方法注冊服務
          IContainerRegistry.Register(typeof(ITestService), TestDelegate)//注冊為瞬時模式
          IContainerRegistry.RegisterSingleton(typeof(ITestService), TestDelegate)//注冊為單例模式
          IContainerRegistry.RegisterScoped(typeof(ITestService), TestDelegate)//注冊為范圍模式


          5、添加了有關(guān)在void方法中異步等待Task的擴展方法


          你乍一看好像沒什么卵用,但是里面還是有說法的,我們來看一個例子,WPF界面MVVM異步讀取耗時數(shù)據(jù)加載界面,這里是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 _allMedicines=new ObservableCollection();
          public ObservableCollection 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("耗時操作完成");
          }


          這是正常我們會實現(xiàn)的方式,同樣的也確實不會出現(xiàn)跨線程問題(在非UI線程操作ObservableCollection集合會出現(xiàn)),關(guān)于async await在WPF不會出現(xiàn)跨線程問題,可以參考我的另外一篇文章異步函數(shù)async await在wpf都做了什么?,也同樣的在執(zhí)行耗時操作時候不會阻塞UI主線程,如果在最上層不用async void能否實現(xiàn)同樣的效果,這就是TaskExtension的意義了,下面只例舉非泛型版本TaskExtension的api,,實際還有泛型版本的TaskExtension,我們拿最多參數(shù)的重載方法來說明:


          public static class TaskExtensions
          {
          public static async void Await(this Task task, Action completedCallback, Action errorCallback, bool configureAwait)
          {
          try
          {
          await task.ConfigureAwait(configureAwait);
          completedCallback?.Invoke();
          }
          catch (Exception obj)
          {
          errorCallback?.Invoke(obj);
          }
          }
          }


          1、completedCallback:當前Task的回調(diào)函數(shù),指Task執(zhí)行的后續(xù)操作


          2、errorCallback:回調(diào)函數(shù)的異?;卣{(diào)函數(shù),回調(diào)函數(shù)異常后可以執(zhí)行


          3、configureAwait:指示回調(diào)函數(shù)是否在當前執(zhí)行上下文執(zhí)行,True為是,false為否


          我們可以把ExecuteLoadCommand方法修改下:


          void ExecuteLoadCommand()
          {
          //TaskExtension for async void Command
          ALongTask().Await( completedCallback:() =>
          {
          this.AllMedicines.AddRange(_medicineSerivce.GetAllMedicines());
          }, errorCallback:null,configureAwait:true);
          }


          該方式執(zhí)行效果和之前一樣,而且不用在void方法加上async 和方法內(nèi)部await就能實現(xiàn)異步等待操作,而這只是推薦在Command的Excuted Method使用,這也是官方推薦的,因為一般Excuted Method返回值只會是void


          二、回答一些問題


          如何在Prism使用AOP?


          其實AOP并不是屬于prism特有的功能,但是由于prism支持擴展IOC容器:Unity和DryIoc,只要其IOC容器本身支持,那就可以,由于默認Prism是以Unity為默認IOC容器,所以以Unity為例子:


          1、NuGet引用Unity AOP庫:Unity.Interception( 最新是5.11.1)


          2、在App.cs添加擴展AOP,代碼如下:


          protected override void RegisterTypes(IContainerRegistry containerRegistry)
          {
          var container = PrismIocExtensions.GetContainer(containerRegistry);
          container.AddNewExtension()//add Extension Aop
          //注冊服務和添加顯示攔截
          .RegisterType(new Interceptor(), new InterceptionBehavior())
          .RegisterType(new Interceptor(), new InterceptionBehavior())
          .RegisterType(new Interceptor(), new InterceptionBehavior());
          }


          3、新建類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 };
          }
          }


          4、為那些需要攔截的接口標上Attribute


          [LogHandler]
          public interface IMedicineSerivce
          {
          List GetAllMedicines();
          List GetRecipesByPatientId(int patientId);
          }
          [LogHandler]
          public interface IPatientService
          {
          List GetAllPatients();
          }
          [LogHandler]
          public interface IUserService
          {
          List 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


          當然這里篇幅有限,不可能講述有關(guān)太多Unity AOP的細節(jié),實際上Unity AOP功能非常強大,同樣支持通過配置文件來配置AOP和支持對不同類型方法的攔截,需要了解更多細節(jié)在這里可推薦該博文C#中AOP_使用Unity實現(xiàn)AOP


          是否所有事件和邏輯都在ViewModel處理?


          WPF是個數(shù)據(jù)驅(qū)動型程序,當使用MVVM框架如Prism或者MVVMLight的時候,我們會在ViewModel處理業(yè)務數(shù)據(jù)邏輯,通過Binding方式驅(qū)動前臺界面的顯示,如果處理邏輯是View相關(guān)的,例如對控件的樣式變化,鼠標移動控件等View邏輯相關(guān)的,這時候則推薦用依賴或者附加屬性,或在View的Code-behind的cs文件中事件來處理有關(guān)View的邏輯,不要為了所謂的MVVM而把一切邏輯都放在ViewModel處理,實則更加不靈活,反而跟之前的MVC都放在C中處理沒啥區(qū)別了


          其他問題?(待補充)


          三、源碼


          .NET5-Prsm8.0-Sample:https://github.com/ZhengDaoWang/PrismMetroSample/tree/NET5-Prism8.0


          四、參考


          • https://github.com/PrismLibrary/Prism


          • https://github.com/PrismLibrary/Prism/releases


          • C#中AOP_使用Unity實現(xiàn)AOP


          - EOF -


          瀏覽 70
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本偷拍自拍大香蕉 | 91 国产 爽 黄 在线 | 黄色电影一级免费 | 天天久久天堂 | 日皮视频|