Prism框架中提供的事件聚合器,可实现多点传送发布/订阅功能,可以用于ViewModel与ViewModel、ViewModel与View以及模块与模块之间的通信。
1. 加載動畫
當我們呼叫後端Api,有響應時間的時候,最好有一個加載動畫,來反映api正在執行。
1.1 定義Event
namespace BlankApp1.Common.Animations
{
public class UpdateModel
{
public string Filter { get; set; }
public bool IsOpen { get; set; }
}
public class UpdateLoadingEvent : PubSubEvent<UpdateModel>
{
}
}
namespace BlankApp1.Extension.Aggregator
{
public class MessageModel
{
public string Filter { get; set; }
public string Message { get; set; }
}
public class MessageEvent : PubSubEvent<MessageModel>
{
}
}
1.2 Extension
定義擴展方法,Uploading和Register
namespace BlankApp1.Extensions
{
public static class DialogExtension
{
/// <summary>
/// 询问窗口
/// </summary>
/// <param name="dialogHost">指定的DialogHost会话主机</param>
/// <param name="title">标题</param>
/// <param name="content">询问内容</param>
/// <param name="dialogHostName">会话主机名称(唯一)</param>
/// <returns></returns>
public static async Task<IDialogResult> Question(this IDialogHostService dialogHost,
string title, string content, string dialogHostName = "Root"
)
{
DialogParameters param = new DialogParameters();
param.Add("Title", title);
param.Add("Content", content);
param.Add("dialogHostName", dialogHostName);
var dialogResult = await dialogHost.ShowDialog("MsgView", param, dialogHostName);
return dialogResult;
}
/// <summary>
/// 推送等待消息
/// </summary>
/// <param name="aggregator"></param>
/// <param name="model"></param>
public static void UpdateLoading(this IEventAggregator aggregator, UpdateModel model)
{
aggregator.GetEvent<UpdateLoadingEvent>().Publish(model);
}
/// <summary>
/// 訂閱等待消息
/// </summary>
/// <param name="aggregator"></param>
/// <param name="action"></param>
public static void Register(this IEventAggregator aggregator, Action<UpdateModel> action)
{
aggregator.GetEvent<UpdateLoadingEvent>().Subscribe(action);
}
/// <summary>
/// 訂閱提示消息
/// </summary>
/// <param name="aggregator"></param>
/// <param name="action"></param>
public static void RegisterMessage(this IEventAggregator aggregator,
Action<MessageModel> action, string filterName = "Main")
{
aggregator.GetEvent<MessageEvent>().Subscribe(action,
ThreadOption.PublisherThread, true, (m) =>
{
return m.Filter.Equals(filterName);
});
}
/// <summary>
/// 發佈提示消息
/// </summary>
/// <param name="aggregator"></param>
/// <param name="message"></param>
public static void SendMessage(this IEventAggregator aggregator, string message, string filterName = "Main")
{
aggregator.GetEvent<MessageEvent>().Publish(new MessageModel()
{
Filter = filterName,
Message = message,
});
}
}
}
加載動畫,在MianView實現註冊方法
namespace BlankApp1.Views
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow(IEventAggregator aggregator)
{
InitializeComponent();
//主頁註冊提示窗口
aggregator.RegisterMessage(arg =>
{
MainSnakeBar.MessageQueue.Enqueue(arg.Message);
});
//主頁登錄彈窗
aggregator.Register(arg => {
//DialogHost.IsOpen =true;
DialogHost.IsOpen = arg.IsOpen;
if (DialogHost.IsOpen) {
//後面new 的就是加載動畫
DialogHost.DialogContent = new LoadingAnimation01();
}
});
MenuBar.SelectionChanged += (s, e) =>
{
DrawerHost.IsLeftDrawerOpen = false;
};
btnMin.Click += (s, e) =>
{
this.WindowState = WindowState.Minimized;
};
};
};
};
1.3 再封裝一層
namespace BlankApp1.ViewModels
{
public class NavigationViewModel : BindableBase, INavigationAware
{
public readonly IContainerProvider containerProvider;
public readonly IEventAggregator aggregator;
public NavigationViewModel(IContainerProvider containerProvider) {
this.containerProvider = containerProvider;
aggregator = containerProvider.Resolve<IEventAggregator>();
}
/// <summary>
/// 是否重用窗口
/// </summary>
/// <param name="navigationContext"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public virtual bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public virtual void OnNavigatedFrom(NavigationContext navigationContext)
{
}
public virtual void OnNavigatedTo(NavigationContext navigationContext)
{
}
public void UpdateLoading(bool IsOpen)
{
aggregator.UpdateLoading(new Common.Animations.UpdateModel()
{
IsOpen = IsOpen
});
}
}
}
UpdateLoading(true)
UpdateLoading(false)
2. Simple Demo
這裡是新建了一個項目,使用了material design
2.1 Config app.xaml
加載material design
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<materialDesign:BundledTheme
BaseTheme="Light"
PrimaryColor="DeepPurple"
SecondaryColor="Lime" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
2.2 MainWindow
View
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel VerticalAlignment="Center" Orientation="Vertical">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<Button
Height="35"
Margin="5"
Command="{Binding ButtonCommand}"
CommandParameter="PublishedView"
Content="Publish" />
<Button
Height="35"
Margin="5"
Command="{Binding ButtonCommand}"
CommandParameter="SubscribeView"
Content="Subscribe" />
</StackPanel>
<Separator Height="5" Margin="0,20,0,0" />
</StackPanel>
<StackPanel Grid.Row="1">
<ContentControl prism:RegionManager.RegionName="{x:Static extension:EventRegionManager.MainWindowRegionName}" />
</StackPanel>
</Grid>
ViewModel
public class MainWindowViewModel : BindableBase
{
private readonly IRegionManager _regionManager;
private string _title = "Prism Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public DelegateCommand<string> ButtonCommand { get; set; }
public MainWindowViewModel(IRegionManager regionManager)
{
this._regionManager = regionManager;
ButtonCommand = new DelegateCommand<string>(ButtonExecute);
}
public void ButtonExecute(string obj)
{
Navigate(obj);
}
private void Navigate(string viewName)
{
_regionManager.Regions[EventRegionManager.MainWindowRegionName].RequestNavigate(viewName);
}
}
2.3 Extension
EventRegionManager
public class EventRegionManager
{
public static string MainWindowRegionName = "MainWindowRegion";
}
MessageEvent
public class MessageModel
{
public string Message { get; set; }
}
public class MessageEvent :PubSubEvent<MessageModel>
{
}
DialogExtension
public static class DialogExtension
{
public static void RegisterMessage(this IEventAggregator aggregator,
Action<MessageModel> action)
{
//UI線程先執行
aggregator.GetEvent<MessageEvent>().Subscribe(action,ThreadOption.UIThread);
}
/// <summary>
/// 发送提示消息
/// </summary>
/// <param name="aggregator"></param>
/// <param name="message"></param>
public static void SendMessage(this IEventAggregator aggregator, string message)
{
aggregator.GetEvent<MessageEvent>().Publish(new MessageModel()
{
Message = message
});
}
}
2.4 Publisher
創建view。按鈕點擊發佈消息
public class PublishedViewModel : BindableBase
{
public readonly IEventAggregator aggregator;
private string message = "Send a message form publisher to subscriber";
public string Message
{
get { return message; }
set { message = value; RaisePropertyChanged(); }
}
public DelegateCommand SendMsssageCommand { get; set; }
public PublishedViewModel(IEventAggregator aggregator)
{
this.aggregator = aggregator;
SendMsssageCommand = new DelegateCommand(SendMethod);
}
public void SendMethod()
{
aggregator.SendMessage(Message);
}
}
2.5 Subscribe
訂閱者頁面加載後,直接訂閱消息,發佈者發送消息後,頁面自動顯示
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel HorizontalAlignment="Center">
<TextBlock Text="我是訂閱者"
FontSize="15"
FontWeight="Bold" />
</StackPanel>
<StackPanel Grid.Row="1">
<TextBox Margin="10"
Width="500"
Height="60"
Text="{Binding Message,Mode=TwoWay}"
Style="{StaticResource MaterialDesignOutlinedTextBox}"/>
</StackPanel>
</Grid>
public class SubscribeViewModel : BindableBase
{
public readonly IEventAggregator aggregator;
private string message;
public string Message
{
get { return message; }
set { message = value; RaisePropertyChanged(); }
}
public SubscribeViewModel(IEventAggregator aggregator)
{
this.aggregator = aggregator;
aggregator.RegisterMessage(arg => {
Message = arg.Message;
});
}
}
3. ThreadOption
遇到搞不定的,就要先到哪個線程先執行
public static void RegisterMessage(this IEventAggregator aggregator,
Action<MessageModel> action)
{
aggregator.GetEvent<MessageEvent>().Subscribe(action,ThreadOption.UIThread);
}
public enum ThreadOption
{
//
// 摘要:
// The call is done on the same thread on which the Prism.Events.PubSubEvent`1 was
// published.
PublisherThread,
//
// 摘要:
// The call is done on the UI thread.
UIThread,
//
// 摘要:
// The call is done asynchronously on a background thread.
BackgroundThread
}
4.Focus
使用事件聚合器來聚焦焦點。在頁面對應的cs文件裡面開啟IEventAggregator
在ViewModel中給IEventAggregator發送key
public FocusView(IEventAggregator aggregator)
{
InitializeComponent();
aggregator.RegisterMessage(arg =>
{
FocusPallet.IsEnabled = false;
}, key1Name);
aggregator.RegisterMessage(arg =>
{
FocusPallet.IsEnabled = true;
FocusPallet.SelectAll();
FocusPallet.Focus();
}, key2Name);
}