從PRISM開始學WPF(六)MVVM(三)事件聚合器EventAggregator?
從PRISM開始學WPF(一)WPF?
從PRISM開始學WPF(二)Prism?
從PRISM開始學WPF(三)Prism-Region?
從PRISM開始學WPF(四)Prism-Module?
從PRISM開始學WPF(五)MVVM(一)ViewModel?
從PRISM開始學WPF(六)MVVM(二)Command?
從PRISM開始學WPF(七)MVVM(三)事件聚合器EventAggregator?
- Event aggregation. For communication across view models, presenters, or controllers when there is not a direct action-reaction expectation.
(⊙﹏⊙),Google一下:
事件聚合。在沒有直接的行動反應期望的情況下,跨視圖模型,演示者或控制者進行通信。
1是沒有直接行動反應期望,2跨視圖通信
在具體了解這個概念之前,先看一個例子:
通過簡介,很容易想到聊天窗口,當你在一個視圖A中輸入文字點擊發送之後,另外一個視圖B會接收到這個消息,並將文字輸出到屏幕上,而這個時候,視圖A並不關心誰將收到信息,只管提交,視圖B也不管是誰發來的消息,只管接收,並顯示。
關門,放代碼:
Setp1 在Shell窗口中,定義兩個Region,分別來展示發送視圖和接收視圖
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <ContentControl prism:RegionManager.RegionName="LeftRegion" /> <ContentControl Grid.Column="1" prism:RegionManager.RegionName="RightRegion" /> </Grid>
今天Typora更新了,代碼塊支持
xaml
格式,以前都是用xml-dtd
,下面統一使用xaml
XAML這應該是教程中出現的比較復雜的xaml,比較詳細的使用了Grid,這裏的Grid很像一個表格,在使用他布局之前,需要定義好列數,下面的代碼為Grid設置了兩個列
<Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions>
那麽能加行嗎?那是當然的了,下面是為Grid添加兩行,Height的Auto表示這個行的高度會根據內容高度進行適應:
<Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions>
在使用Grid的時候,需要為控件指定Grid的位置
Grid.Column="1"
,1就是下標,都是從0開始的,1就代表第二列,當你不指定具體位置的時候,默認將控件插入Grid的0行0列,上面的LeftRegion
就是在首行首列的位置。
Setp2 新建兩個Module,分別為ModuleA 和 ModuleB,ModuleA中的視圖用來發送信息,ModuleB中的視圖用來接收顯示信息。
Module的創建在第四節已經說明了
先看ModuleA的發送視圖:
MessageView.xaml
<StackPanel>
<TextBox Text="{Binding Message}" Margin="5"/>
<Button Command="{Binding SendMessageCommand}" Content="Send Message" Margin="5"/>
</StackPanel>
MessageView中定義了一個文本框,進行了數據綁定,然後是一個按鈕,綁定了一個SendMessageCommand命令。在我們點擊Send Message按鈕的時候,就會將Message顯示到接收視圖裏去。
再看ModuleB的顯示視圖:
MessageList.xaml
<Grid>
<ListBox ItemsSource="{Binding Messages}" />
</Grid>
就定義了一個ListBox來顯示Message。ItemsSource綁定的應該是一個集合,不然怎麽叫Source呢?
接下來,看下Prism怎麽實現跨視圖模型通訊:
首先,定義一個MessageSentEvent
類,繼承PubSubEvent<string>
,string是因為這個事件接收的payload是字符串類型,PubSubEvent<T>
類負責連接發布者和訂閱者,他負責維護訂閱者列表並處理事件派發給訂閱者。
using Prism.Events;
namespace UsingEventAggregator.Core
{
public class MessageSentEvent : PubSubEvent<string>
{
}
}
然後我們看下MessageViewModel:
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using UsingEventAggregator.Core;
namespace ModuleA.ViewModels
{
public class MessageViewModel : BindableBase
{
IEventAggregator _ea;
private string _message = "Message to Send";
public string Message
{
get { return _message; }
set { SetProperty(ref _message, value); }
}
public DelegateCommand SendMessageCommand { get; private set; }
public MessageViewModel(IEventAggregator ea)
{
_ea = ea;
SendMessageCommand = new DelegateCommand(SendMessage);
}
private void SendMessage()
{
_ea.GetEvent<MessageSentEvent>().Publish(Message);
}
}
}
先看我們熟悉的部分:
private string _message = "Message to Send";
public string Message
{
get { return _message; }
set { SetProperty(ref _message, value); }
}
這是<TextBox Text="{Binding Message}" Margin="5"/>
中的Message
然後定義了一個DelegateCommand
public DelegateCommand SendMessageCommand { get; private set; }
接下來就是EventAggregator部分了:
首先定義一個依賴註入點:
IEventAggregator _ea;
然後通過容器的構造註入來獲取容器的EventAggregator的實例,還定義了命令SendMessageCommand的回調函數SendMessage
。
public MessageViewModel(IEventAggregator ea)
{
_ea = ea;
SendMessageCommand = new DelegateCommand(SendMessage);
}
SendMessge中通過MessageSentEvent發布Payload,這裏Payload一定要匹配MessageSentEvent的Payload類型,上面我們繼承PubSubEvent<string>
時使用的String
,不然的話,這在編譯的時候就會拋出異常。
private void SendMessage()
{
_ea.GetEvent<MessageSentEvent>().Publish(Message);
}
接下來,我們讓ModuleB中的MessageListViewModel
獲取這個Payload,並進行一些操作:
using Prism.Events;
using Prism.Mvvm;
using System.Collections.ObjectModel;
using UsingEventAggregator.Core;
namespace ModuleB.ViewModels
{
public class MessageListViewModel : BindableBase
{
IEventAggregator _ea;
private ObservableCollection<string> _messages;
public ObservableCollection<string> Messages
{
get { return _messages; }
set { SetProperty(ref _messages, value); }
}
public MessageListViewModel(IEventAggregator ea)
{
_ea = ea;
Messages = new ObservableCollection<string>();
_ea.GetEvent<MessageSentEvent>().Subscribe(MessageReceived);
}
private void MessageReceived(string message)
{
Messages.Add(message);
}
}
}
代碼閱讀:
private ObservableCollection<string> _messages;
public ObservableCollection<string> Messages
{
get { return _messages; }
set { SetProperty(ref _messages, value); }
}
這是 <ListBox ItemsSource="{Binding Messages}" />
中的Messages,他的類型是ObservableCollection
,具體為什麽是 ObservableCollection而不是List!後面再說。
public MessageListViewModel(IEventAggregator ea)
{
_ea = ea;
Messages = new ObservableCollection<string>();
_ea.GetEvent<MessageSentEvent>().Subscribe(MessageReceived);
}
這裏訂閱了MessageSentEvent,並且處理Payload,處理Payload的方法是MessageReceived,這個方法在Messages新增一條記錄。
事件聚合器可以有多個發布者和多個訂閱者。
從PRISM開始學WPF(六)MVVM(三)事件聚合器EventAggregator?