この記事は転載から
著者:Ryzen Adorer
前の記事:. NET Core 3 WPF MVVMフレームワークPrismシリーズ用のイベントアグリゲーター
原文へのリンク:https//www.cnblogs.com/ryzen/p/12196619.html
この記事では、. NET Core3環境でMVVMフレームワークPrismのイベントアグリゲーターを使用してモジュール間通信を実装する方法を説明します。
1. イベントアグリゲーター
在上一篇 [].NET Core 3 WPF MVVM 框架 Prism 系列之模块化](https://dotnet9.com/2023/06/Modularization-of-the-Prism-family-of-the-dotNET-Core-3-WPF-MVVM-framework) 我们留下了一些问题,就是如何处理同模块不同窗体之间的通信和不同模块之间不同窗体的通信,Prism 提供了一种事件机制,可以在应用程序中低耦合的模块之间进行通信,该机制基于事件聚合器服务,允许发布者和订阅者之间通过事件进行通讯,且彼此之间没有之间引用,这就实现了模块之间低耦合的通信方式,下面引用官方的一个事件聚合器模型图:

2. イベントの作成と発行
2.1.イベントの作成
まず、モジュールと異なるフォーム間の通信を処理します。PrismMetroSample.InfrastructureにEventsフォルダを作成し、次にPatientSentEventというクラスを作成します。
PatientSentEvent.cs:
public class PatientSentEvent: PubSubEvent<Patient>
{
}
2.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);
}
}
2.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);
}
}
効果は以下のとおり。

2.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);
}
}
Search 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);//发布消息
}
効果は以下のとおり。

次に、デモプロジェクトのイベントモデルとアセンブリ参照を次の図に示します。

我々はPatientModuleとMedicineModuleの2つのモジュール間で通信を行っているが,相互に参照せず,PrismMetroSample.Infrastructureアセンブリを参照することにより間接的な依存関係を実現し,異なるモジュール間の通信と低結合を実現していることを発見した.
3. イベントの購読解除
Prismにはサブスクリプション解除機能もあり、PatientDetailViewModelとPatientDetailViewModelには以下のような機能があります。
PatientDetailViewModel.cs:
private DelegateCommand _cancleSubscribeCommand;
public DelegateCommand CancleSubscribeCommand =>
_cancleSubscribeCommand ?? (_cancleSubscribeCommand = new DelegateCommand(ExecuteCancleSubscribeCommand));
void ExecuteCancleSubscribeCommand()
{
_ea.GetEvent<MedicineSentEvent>().Unsubscribe(MedicineMessageReceived);
}
効果は以下のとおり。

4. 複数のサブスクリプションモード
デモでは、メッセージアグリゲーターのイベントメカニズムを使用してサブスクライバとパブリッシャーの間の通信を実装しました。PubSubEventクラスのSubscribe関数の最も多くのパラメータを持つオーバーロードメソッドを使用して、Primがどのようなサブスクライバを持つかを見てみましょう。
Subscribe.cs:
public virtual SubscriptionToken Subscribe(Action<TPayload> action, ThreadOption threadOption, bool keepSubscriberReferenceAlive, Predicate<TPayload> filter);
4.1. actionパラメータ#actionパラメータ#
actionパラメータはメッセージを受け取る関数です。
4.2. threadパラメータ
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
}
3つの列挙値の役割:
- PublisherThread:パブリッシャーからのメッセージを受け入れるデフォルト設定
- UIThread:UIスレッドでイベントを受け付けます。
- BackgroundThread:スレッドプール内で非同期にイベントを受け付ける
4.3. keepriberReferveパラメータ
デフォルトのkeepSubscriberReferenceAliveはfalseです。Prismでは公式にそう言われています。このパラメータはサブスクリプションが弱い参照を使用するか強い参照を使用するか、falseは弱い参照を、trueは強い参照を使用するかを示します。
- trueに設定すると、短時間で複数のイベントをパブリッシュするパフォーマンスが向上しますが、イベントインスタンスペアはサブスクライバインスタンスへの強い参照を保持するため、イベントを手動でサブスクライブを解除します。
- falseに設定すると、イベントはサブスクライバインスタンスへの弱い参照を保持し、フォームが閉じられると自動的にイベントをサブスクライバインスタンスにサブスクライバインスタンスを自動的にサブスクライバインスタンスにサブスクライバインスタンスを削除します。
4.4. filterパラメーター
filterはPredicateのジェネリックデリゲートパラメータで、戻り値はブール値で、サブスクライブフィルタリングに使用できます。このデモではPatientDetailViewModelサブスクライブを変更します。
PatientDetailViewModel.cs:
_ea.GetEvent<MedicineSentEvent>().Subscribe(MedicineMessageReceived,
ThreadOption.PublisherThread,false,medicine=>medicine.Name=="当归"|| medicine.Name== "琼浆玉露");
効果は以下のとおり。

V.ザ·フーソースコードソース
最后,附上整个 demo 的源代码:PrismDemo 源码