【6月日新计划32】WPF入门-事件聚合器IEvevtAggregator

177 阅读3分钟

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);

        }