1. Prism 簡介
Prism 是一個用于構建松耦合、可維護和可測驗的 XAML 應用的框架,它支持所有還活著的基于 XAML 的平臺,包括 WPF、Xamarin Forms、WinUI 和 Uwp Uno,Prism 提供了一組設計模式的實作,這些模式有助于撰寫結構良好且可維護的 XAML 應用程式,包括 MVVM、依賴項注入、命令、事件聚合器等,
Prism 是一個有10年以上歷史的框架,而上個月才剛發布了它的 8.0 版本,這意味著現在網上能找到的大部分 Prism 的資料都已經有點過時,連 官方檔案 也不例外,如果你需要詳細的檔案,除了官方檔案,我會推薦 RyzenAdorer 的 Prism 系列文章:
NET Core 3 WPF MVVM框架 Prism系列文章索引 - RyzenAdorer -
如果你不需要那么詳細的檔案,只需要一個入門的教程,那么我希望我寫的這兩篇文章可以幫到你,
2. Prism.Core、Prism.Wpf 和 Prism.Unity
從很久以前開始,臃腫 就是 Prism 被提起最多的標簽,畢竟比起 MVVMLight,Prism 實作的功能更多;對于初學者來說,剛打開 Prism 的檔案很可能會馬上選擇放棄,Prism 的檔案詳細到讓人望而卻步,例如多年前的舊版官方檔案的 其中一篇:

不是 6 分鐘,不是 16 分賬,是整整 60 分鐘,Prism 的舊檔案隨便打開一篇都嚇死人,而 Prism 的各種包更是多到離譜,例如幾年前的 Prism 6.3,其中 WPF 平臺的專案有這么多個:
- Prism.Wpf
- Prism.Autofac
- Prism.DryIoc
- Prism.Mef
- Prism.Ninject
- Prism.StructureMap
- Prism.Unity
所以臃腫是很多人對 Prism 的印象,
減肥是一個永恒的受歡迎的話題,對 Prism 也是一樣,相比 Prism 6.3,剛剛發布的 8.0 已經好很多了(雖然還是有很多個專案),例如 WPF 平臺的專案已經大幅刪減,只保留了 Prism.Wpf、Prism.DryIoc 和 Prism.Unity,也就是說現在 Prism 只支持 DryIoc 和 Unity 兩種 IOC 容器,這樣一來 Prism 專案的結構就很清晰了,
以 WPF 為例,核心的專案是 Prism.Core,它提供實作 MVVM 模式的核心功能以及部分各平臺公用的類,然后是 Prism.Wpf,它提供針對 Wpf 平臺的功能,包括導航、彈框等,最后由 Prism.Unity 指定 Unity 作為 IOC 容器,

即使已精簡了這么多,Prism 還是有很多功能,兩篇文章也不足以講解全部內容,所以我只會介紹最常用到的入門知識,這篇文章首先介紹 Prism.Core 的主要功能,
3. Prism.Core
Prism.Core 可以單獨安裝,目前最新的版本是 8.0.0.1909:
Install-Package Prism.Core -Version 8.0.0.1909
除了一些各個平臺都用到的零零碎碎的公用類,作為一個 MVVM 庫 Prism.Core 主要提供了下面三方面的功能:
- BindableBase 和 ErrorsContainer
- Commanding
- Event Aggregator
這些功能已經覆寫了 MVVM 的核心功能,如果只需要與具體平臺無關的 MVVM 功能,可以只安裝 Prism.Core,
4. BindableBase 和 ErrorsContainer
資料系結是 MVVM 的核心元素之一,為了使系結的資料可以和 UI 互動,資料型別必須繼承 INotifyPropertyChanged, BindableBase 實作了 INotifyPropertyChanged 最簡單的封裝,它的使用如下:
public class MockViewModel : BindableBase
{
private string _myProperty;
public string MyProperty
{
get { return _myProperty; }
set { SetProperty(ref _myProperty, value); }
}
}
其中 SetProperty 判斷 _myProperty 和 value 是否相等,如果不相等就為 _myProperty 賦值并觸發 OnPropertyChanged 事件,
除了 INotifyPropertyChanged,系結機制中另一個十分有用的介面是 INotifyDataErrorInfo,它用于公開資料驗證的結果,Prism 提供了 ErrorsContainer 以便管理及通知資料驗證的錯誤資訊,要使用 ErrorsContainer,可以先寫一個類似這樣的基類:
public class DomainObject : BindableBase, INotifyDataErrorInfo
{
public ErrorsContainer<string> _errorsContainer;
protected ErrorsContainer<string> ErrorsContainer
{
get
{
if (_errorsContainer == null)
_errorsContainer = new ErrorsContainer<string>(s => one rrorsChanged(s));
return _errorsContainer;
}
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public void one rrorsChanged(string propertyName)
{
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
}
public IEnumerable GetErrors(string propertyName)
{
return ErrorsContainer.GetErrors(propertyName);
}
public bool HasErrors
{
get { return ErrorsContainer.HasErrors; }
}
}
然后就可以在派生類中通過 ErrorsContainer.SetErrors 和 ErrorsContainer.ClearErrors 管理資料驗證的錯誤資訊:
public class MockValidatingViewModel : DomainObject
{
private int mockProperty;
public int MockProperty
{
get
{
return mockProperty;
}
set
{
SetProperty(ref mockProperty, value);
if (mockProperty < 0)
ErrorsContainer.SetErrors(() => MockProperty, new string[] { "value cannot be less than 0" });
else
ErrorsContainer.ClearErrors(() => MockProperty);
}
}
}
5. Commanding
ICommand 同樣是 MVVM 模式的核心元素,DelegateCommand 實作了 ICommand 介面,它最基本的使用形式如下,其中 DelegateCommand 建構式中的第二個引數 canExecuteMethod 是可選的:
public DelegateCommand SubmitCommand { get; private set; }
public CheckUserViewModel()
{
SubmitCommand = new DelegateCommand(Submit, CanSubmit);
}
private void Submit()
{
//implement logic
}
private bool CanSubmit()
{
return true;
}
另外它還有泛型的版本:
public DelegateCommand<string> SubmitCommand { get; private set; }
public CheckUserViewModel()
{
SubmitCommand = new DelegateCommand<string>(Submit, CanSubmit);
}
private void Submit(string parameter)
{
//implement logic
}
private bool CanSubmit(string parameter)
{
return true;
}
通常 UI 會根據 ICommand 的 CanExecute 函式的回傳值來判斷觸發此 Command 的 UI 元素是否可用,CanExecute 回傳 DelegateCommand 建構式中的第二個引數 canExecuteMethod 的回傳值,如果不傳入這個引數,則 CanExecute 一直回傳 True,
如果 CanExecute 的回傳值有變化,可以呼叫 RaiseCanExecuteChanged 函式,它會觸發 CanExecuteChanged 事件并通知 UI 元素重新判斷系結的 ICommand 是否可用,除了主動呼叫 RaiseCanExecuteChanged,DelegateCommand 還可以用 ObservesProperty 和 ObservesCanExecute 兩種形式監視屬性,定于屬性的 PropertyChanged 事件并改變 CanExecute:
private bool _isEnabled;
public bool IsEnabled
{
get { return _isEnabled; }
set { SetProperty(ref _isEnabled, value); }
}
private bool _canSave;
public bool CanSave
{
get { return _canSave; }
set { SetProperty(ref _canSave, value); }
}
public CheckUserViewModel()
{
SubmitCommand = new DelegateCommand(Submit, CanSubmit).ObservesProperty(() => IsEnabled);
//也可以寫成串聯方式
SubmitCommand = new DelegateCommand(Submit, CanSubmit).ObservesProperty(() => IsEnabled).ObservesProperty<bool>(() => CanSave);
SubmitCommand = new DelegateCommand(Submit).ObservesCanExecute(() => IsEnabled);
}
6. Event Aggregator
本來Event Aggregator(事件聚合器)或 Messenger 之類的組件本來并不是 MVVM 的一部分,不過現在也成了 MVVM 框架的一個重要元素,解耦是 MVVM 的一個重要目標,'EventAggregator' 則是實作解耦的重要工具,在 MVVM 中,對于 View 和與他匹配的 ViewModel 之間的互動,可以使用 INotifyProperty 和 Icommand;而對于必須通信的不同 ViewModel 或模塊,為了使它們之間實作低耦合,可以使用 Prism 中的 EventAggregator,如下圖所示,Publisher 和 Scbscriber 之間沒有直接關聯,它們通過 Event Aggregator 獲取 PubSubEvent 并發送及接收訊息:

要使用 EventAggregator,首先需要定義 PubSubEvent:
public class TickerSymbolSelectedEvent : PubSubEvent<string>{}
發布方和訂閱方都通過 EventAggregator 索取 PubSubEvent,在 ViewModel中通常都是通過依賴注入獲取一個 IEventAggregator:
public class MainPageViewModel
{
IEventAggregator _eventAggregator;
public MainPageViewModel(IEventAggregator ea)
{
_eventAggregator = ea;
}
}
發送方的操作很簡單,只需要 通過 GetEvent 拿到 PubSubEvent,把訊息發布出去,然后拍拍屁股走人,其它的責任都不用管:
_eventAggregator.GetEvent<TickerSymbolSelectedEvent>().Publish("STOCK0");
訂閱方是真正使用這些訊息并負責任的人,下面是最簡單的通過 Subscribe 訂閱事件的代碼:
public class MainPageViewModel
{
public MainPageViewModel(IEventAggregator ea)
{
ea.GetEvent<TickerSymbolSelectedEvent>().Subscribe(ShowNews);
}
void ShowNews(string companySymbol)
{
//implement logic
}
}
除了基本的呼叫方式,Subscribe 函式還有其它可選的引數:
public virtual SubscriptionToken Subscribe(Action action, ThreadOption threadOption, bool keepSubscriberReferenceAlive)
其中 threadOption 指示收到訊息后在哪個執行緒上執行第一個引數定義的 action,它有三個選項:
- PublisherThread,和發布者保持在同一個執行緒上執行,
- UIThread,在 UI 執行緒上執行,
- BackgroundThread,在后臺執行緒上執行,
第三個引數 keepSubscriberReferenceAlive 默認為 false,它指示該訂閱是否為強參考,
- 設定為 false 時,參考為弱參考,用完可以不用管,
- 設定為 true 時,參考為強參考,用完需要使用
Unsubscribe取消訂閱,
下面代碼是一段訂閱及取消訂閱的示例:
public class MainPageViewModel
{
TickerSymbolSelectedEvent _event;
public MainPageViewModel(IEventAggregator ea)
{
_event = ea.GetEvent<TickerSymbolSelectedEvent>();
_event.Subscribe(ShowNews);
}
void Unsubscribe()
{
_event.Unsubscribe(ShowNews);
}
void ShowNews(string companySymbol)
{
//implement logic
}
}
7. 生產力工具
如果覺得屬性和 DelegateCommand 的定義有些啰嗦,可以試試安裝這個工具:Prism Template Pack,它提供了一些實用的代碼段和一些 Project 和 Item 的模板,例如,安裝此工具后可以通過 cmd 代碼段快速生成一個完整的 DelegateCommand 代碼:
private DelegateCommand _fieldName;
public DelegateCommand CommandName =>
_fieldName ?? (_fieldName = new DelegateCommand(ExecuteCommandName));
void ExecuteCommandName()
{
}
更多代碼段定義請參考官方檔案:Productivity Tools Prism
8. 結語
Prism.Core 最初由 Microsoft Patterns and Practices 團隊創建,現在轉移到社區,雖然 Prism 框架非常成熟(還有點臃腫),支持插件和定位控制元件的區域,但 Prism.Core 很輕,僅包含幾個常用的型別,這篇文章已經把 Prism.Core 中最常用的類盡可能簡單地介紹過一遍,這足夠用完創建一個基于 MVVM 框架的專案,
Prism 的更多功能將在下一篇文章中介紹,
9. 參考
https://github.com/PrismLibrary/Prism
https://prismlibrary.com/docs/index.html
INotifyPropertyChanged 介面
INotifyDataErrorInfo 介面
ICommand 介面
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/231247.html
標籤:.NET技术
