本文將介紹如何在.NET Core3環境下使用MVVM框架Prism的使用事件聚合器實作模塊間的通信
一.事件聚合器
?在上一篇 .NET Core 3 WPF MVVM框架 Prism系列之模塊化 我們留下了一些問題,就是如何處理同模塊不同表單之間的通信和不同模塊之間不同表單的通信,Prism提供了一種事件機制,可以在應用程式中低耦合的模塊之間進行通信,該機制基于事件聚合器服務,允許發布者和訂閱者之間通過事件進行通訊,且彼此之間沒有之間參考,這就實作了模塊之間低耦合的通信方式,下面參考官方的一個事件聚合器模型圖:

二.創建和發布事件
1.創建事件
?首先我們來處理同模塊不同表單之間的通訊,我們在PrismMetroSample.Infrastructure新建一個檔案夾Events,然后新建一個類PatientSentEvent,代碼如下:
PatientSentEvent.cs:
public class PatientSentEvent: PubSubEvent<Patient>
{
}
2.訂閱事件
?然后我們在病人詳細表單的PatientDetailViewModel類訂閱該事件,代碼如下:
PatientDetailViewModel.cs:
public class PatientDetailViewModel : BindableBase
{
IEventAggregator _ea;
IMedicineSerivce _medicineSerivce;
private Patient _currentPatient;
//當前病人
public Patient CurrentPatient
{
get { return _currentPatient; }
set { SetProperty(ref _currentPatient, value); }
}
private ObservableCollection<Medicine> _lstMedicines;
//當前病人的藥物串列
public ObservableCollection<Medicine> lstMedicines
{
get { return _lstMedicines; }
set { SetProperty(ref _lstMedicines, value); }
}
//建構式
public PatientDetailViewModel(IEventAggregator ea, IMedicineSerivce medicineSerivce)
{
_medicineSerivce = medicineSerivce;
_ea = ea;
_ea.GetEvent<PatientSentEvent>().Subscribe(PatientMessageReceived);//訂閱事件
}
//處理接受訊息函式
private void PatientMessageReceived(Patient patient)
{
this.CurrentPatient = patient;
this.lstMedicines = new ObservableCollection<Medicine>(_medicineSerivce.GetRecipesByPatientId(this.CurrentPatient.Id).FirstOrDefault().LstMedicines);
}
}
3.發布訊息
?然后我們在病人串列表單的PatientListViewModel中發布訊息,代碼如下:
PatientListViewModel.cs:
public class PatientListViewModel : BindableBase
{
private IApplicationCommands _applicationCommands;
public IApplicationCommands ApplicationCommands
{
get { return _applicationCommands; }
set { SetProperty(ref _applicationCommands, value); }
}
private List<Patient> _allPatients;
public List<Patient> AllPatients
{
get { return _allPatients; }
set { SetProperty(ref _allPatients, value); }
}
private DelegateCommand<Patient> _mouseDoubleClickCommand;
public DelegateCommand<Patient> MouseDoubleClickCommand =>
_mouseDoubleClickCommand ?? (_mouseDoubleClickCommand = new DelegateCommand<Patient>(ExecuteMouseDoubleClickCommand));
IEventAggregator _ea;
IPatientService _patientService;
/// <summary>
/// 建構式
/// </summary>
public PatientListViewModel(IPatientService patientService, IEventAggregator ea, IApplicationCommands applicationCommands)
{
_ea = ea;
this.ApplicationCommands = applicationCommands;
_patientService = patientService;
this.AllPatients = _patientService.GetAllPatients();
}
/// <summary>
/// DataGrid 雙擊按鈕命令方法
/// </summary>
void ExecuteMouseDoubleClickCommand(Patient patient)
{
//打開表單
this.ApplicationCommands.ShowCommand.Execute(FlyoutNames.PatientDetailFlyout);
//發布訊息
_ea.GetEvent<PatientSentEvent>().Publish(patient);
}
}
效果如下:

4.實作多訂閱多發布
?同理,我們實作搜索后的Medicine添加到當前病人串列中也是跟上面步驟一樣,在Events檔案夾創建事件類MedicineSentEvent:
MedicineSentEvent.cs:
public class MedicineSentEvent: PubSubEvent<Medicine>
{
}
?在病人詳細表單的PatientDetailViewModel類訂閱該事件:
PatientDetailViewModel.cs:
public PatientDetailViewModel(IEventAggregator ea, IMedicineSerivce medicineSerivce)
{
_medicineSerivce = medicineSerivce;
_ea = ea;
_ea.GetEvent<PatientSentEvent>().Subscribe(PatientMessageReceived);
_ea.GetEvent<MedicineSentEvent>().Subscribe(MedicineMessageReceived);
}
/// <summary>
// 接受事件訊息函式
/// </summary>
private void MedicineMessageReceived(Medicine medicine)
{
this.lstMedicines?.Add(medicine);
}
?在藥物串列表單的MedicineMainContentViewModel也訂閱該事件:
MedicineMainContentViewModel.cs:
public class MedicineMainContentViewModel : BindableBase
{
IMedicineSerivce _medicineSerivce;
IEventAggregator _ea;
private ObservableCollection<Medicine> _allMedicines;
public ObservableCollection<Medicine> AllMedicines
{
get { return _allMedicines; }
set { SetProperty(ref _allMedicines, value); }
}
public MedicineMainContentViewModel(IMedicineSerivce medicineSerivce,IEventAggregator ea)
{
_medicineSerivce = medicineSerivce;
_ea = ea;
this.AllMedicines = new ObservableCollection<Medicine>(_medicineSerivce.GetAllMedicines());
_ea.GetEvent<MedicineSentEvent>().Subscribe(MedicineMessageReceived);//訂閱事件
}
/// <summary>
/// 事件訊息接受函式
/// </summary>
private void MedicineMessageReceived(Medicine medicine)
{
this.AllMedicines?.Add(medicine);
}
}
在搜索Medicine表單的SearchMedicineViewModel類發布事件訊息:
SearchMedicineViewModel.cs:
IEventAggregator _ea;
private DelegateCommand<Medicine> _addMedicineCommand;
public DelegateCommand<Medicine> AddMedicineCommand =>
_addMedicineCommand ?? (_addMedicineCommand = new DelegateCommand<Medicine>(ExecuteAddMedicineCommand));
public SearchMedicineViewModel(IMedicineSerivce medicineSerivce, IEventAggregator ea)
{
_ea = ea;
_medicineSerivce = medicineSerivce;
this.CurrentMedicines = this.AllMedicines = _medicineSerivce.GetAllMedicines();
}
void ExecuteAddMedicineCommand(Medicine currentMedicine)
{
_ea.GetEvent<MedicineSentEvent>().Publish(currentMedicine);//發布訊息
}
效果如下:

然后我們看看現在Demo專案的事件模型和程式集參考情況,如下圖:

?我們發現PatientModule和MedicineModule兩個模塊之間做到了通訊,但卻不相互參考,依靠參考PrismMetroSample.Infrastructure程式集來實作間接依賴關系,實作了不同模塊之間通訊且低耦合的情況
三.取消訂閱事件
?Prism還提供了取消訂閱的功能,我們在病人詳細表單提供該功能,PatientDetailViewModel加上這幾句:
PatientDetailViewModel.cs:
private DelegateCommand _cancleSubscribeCommand;
public DelegateCommand CancleSubscribeCommand =>
_cancleSubscribeCommand ?? (_cancleSubscribeCommand = new DelegateCommand(ExecuteCancleSubscribeCommand));
void ExecuteCancleSubscribeCommand()
{
_ea.GetEvent<MedicineSentEvent>().Unsubscribe(MedicineMessageReceived);
}
效果如下:

四.幾種訂閱方式設定
?我們在Demo已經通過訊息聚合器的事件機制,實作訂閱者和發布者之間的通訊,我們再來看看,Prim都有哪些訂閱方式,我們可以通過PubSubEvent類上面的Subscribe函式的其中最多引數的多載方法來說明:
Subscribe.cs:
public virtual SubscriptionToken Subscribe(Action<TPayload> action, ThreadOption threadOption, bool keepSubscriberReferenceAlive, Predicate<TPayload> filter);
1.action引數
其中action引數則是我們接受訊息的函式
2.threadOption引數
ThreadOption型別引數threadOption是個列舉型別引數,代碼如下:
ThreadOption.cs
public enum ThreadOption
{
/// <summary>
/// The call is done on the same thread on which the <see cref="PubSubEvent{TPayload}"/> was published.
/// </summary>
PublisherThread,
/// <summary>
/// The call is done on the UI thread.
/// </summary>
UIThread,
/// <summary>
/// The call is done asynchronously on a background thread.
/// </summary>
BackgroundThread
}
三種列舉值的作用:
- PublisherThread:默認設定,使用此設定能接受發布者傳遞的訊息
- UIThread:可以在UI執行緒上接受事件
- BackgroundThread:可以在執行緒池在異步接受事件
3.keepSubscriberReferenceAlive引數
默認keepSubscriberReferenceAlive為false,在Prism官方是這么說的,該引數指示訂閱使用弱參考還是強參考,false為弱參考,true為強參考:
- 設定為true,能夠提升短時間發布多個事件的性能,但是要手動取消訂閱事件,因為事件實體對保留對訂閱者實體的強參考,否則就算表單關閉,也不會進行GC回收.
- 設定為false,事件維護對訂閱者實體的弱參考,當表單關閉時,會自動取消訂閱事件,也就是不用手動取消訂閱事件
4.filter引數
?filter是一個Predicate
PatientDetailViewModel.cs:
_ea.GetEvent<MedicineSentEvent>().Subscribe(MedicineMessageReceived,
ThreadOption.PublisherThread,false,medicine=>medicine.Name=="當歸"|| medicine.Name== "瓊漿玉露");
效果如下:

五.原始碼
?最后,附上整個demo的源代碼:PrismDemo原始碼
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/8179.html
標籤:WPF
