WPF中实现Eventbus

2020-07-14 10:32:29 浏览数 (2)

前言

在开发Android或Vue前端的时候,我们可能数据产生后需要在多个页面接收,这时候可以用EventBus来实现,那么WPF开发能用EventBus吗?我没就来尝试一下!

EventBus

事件实体类

代码语言:javascript复制
using System;

namespace SchoolClient.Utils.EventBus
{
    /// <summary>
    /// 定义事件源接口,所有的事件源都要实现该接口
    /// </summary>
    public interface IEventData
    {
        /// <summary>
        /// 事件发生的时间
        /// </summary>
        DateTime EventTime { get; set; }

        /// <summary>
        /// 触发事件的对象
        /// </summary>
        object EventSource { get; set; }
    }

    public class EventData : IEventData
    {
        /// <summary>
        /// 事件发生的时间
        /// </summary>
        public DateTime EventTime { get; set; }

        /// <summary>
        /// 触发事件的对象
        /// </summary>
        public Object EventSource { get; set; }

        public EventData()
        {
            EventTime = DateTime.Now;
        }
    }
}

事件处理类接口

代码语言:javascript复制
namespace SchoolClient.Utils.EventBus
{
    public interface IEventHandler
    {
    }

    public interface IEventHandler<TEventData> : IEventHandler where TEventData : IEventData
    {
        /// <summary>
        /// 事件处理器实现该方法来处理事件
        /// </summary>
        /// <param name="eventData"></param>
        void HandleEvent(TEventData eventData);
    }
}

EventBus

代码语言:javascript复制

using SchoolClient.Utils.EventBus;

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;

namespace SchoolClient.Utils
{
    /// <summary>
    /// 事件总线
    /// </summary>
    public class ZEventBus
    {
        public static ZEventBus Instance = new ZEventBus();

        /// <summary>
        /// 定义线程安全集合
        /// </summary>
        private readonly ConcurrentDictionary<Type, List<Type>> _eventAndHandlerMapping;

        public ZEventBus()
        {
            _eventAndHandlerMapping = new ConcurrentDictionary<Type, List<Type>>();
        }

        // 注册所有的事件
        public void RegisterAll() {
            Assembly assembly = Assembly.GetEntryAssembly();
            foreach (var type in assembly.GetTypes())
            {
                if (typeof(IEventHandler).IsAssignableFrom(type))//判断当前类型是否实现了IEventHandler接口
                {
                    Type handlerInterface = type.GetInterface("IEventHandler`1");//获取该类实现的泛型接口
                    if (handlerInterface != null)
                    {
                        Type eventDataType = handlerInterface.GetGenericArguments()[0]; // 获取泛型接口指定的参数类型
                        if (_eventAndHandlerMapping.ContainsKey(eventDataType))
                        {
                            List<Type> handlerTypes = _eventAndHandlerMapping[eventDataType];
                            handlerTypes.Add(type);
                            _eventAndHandlerMapping[eventDataType] = handlerTypes;
                        }
                        else
                        {
                            var handlerTypes = new List<Type> { type };
                            _eventAndHandlerMapping[eventDataType] = handlerTypes;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 手动绑定事件源与事件处理
        /// </summary>
        /// <typeparam name="TEventData"></typeparam>
        /// <param name="eventHandler"></param>
        public void Register<TEventData>(Type eventHandler)
        {
            List<Type> handlerTypes;
            if (_eventAndHandlerMapping.ContainsKey(typeof(TEventData)))
            {
                handlerTypes = _eventAndHandlerMapping[typeof(TEventData)];
            }
            else {
                handlerTypes = new List<Type>();
            }
            if (!handlerTypes.Contains(eventHandler))
            {
                handlerTypes.Add(eventHandler);
            }
            _eventAndHandlerMapping[typeof(TEventData)] = handlerTypes;
        }

        /// <summary>
        /// 手动解除事件源与事件处理的绑定
        /// </summary>
        /// <typeparam name="TEventData"></typeparam>
        /// <param name="eventHandler"></param>
        public void UnRegister<TEventData>(Type eventHandler)
        {
            if (_eventAndHandlerMapping.ContainsKey(typeof(TEventData))) {
                List<Type> handlerTypes = _eventAndHandlerMapping[typeof(TEventData)];
             
                if (handlerTypes.Contains(eventHandler))
                {
                    handlerTypes.Remove(eventHandler);
                    _eventAndHandlerMapping[typeof(TEventData)] = handlerTypes;
      
                }
            } 
        }

        /// <summary>
        /// 根据事件源触发绑定的事件处理
        /// </summary>
        /// <typeparam name="TEventData"></typeparam>
        /// <param name="eventData"></param>
        public void PostEvent<TEventData>(TEventData eventData) where TEventData : IEventData
        {

            if (_eventAndHandlerMapping.ContainsKey(eventData.GetType()))
            {
                List<Type> handlers = _eventAndHandlerMapping[eventData.GetType()];
                if (handlers != null && handlers.Count > 0)
                {
                    foreach (var handler in handlers)
                    {
                        MethodInfo methodInfo = handler.GetMethod("HandleEvent");
                        if (methodInfo != null)
                        {
                            object obj = Activator.CreateInstance(handler);
                            methodInfo.Invoke(obj, new object[] { eventData });
                        }
                    }
                }
            }
        }
    }
}

项目中使用

数据

代码语言:javascript复制
namespace SchoolClient.Socket
{
    public class SocketEvent : EventData
    {
        public ZWsMsgVo Msg { get; set; }

        public SocketEvent()
        {
        }
    }
}

项目中Event接收类

代码语言:javascript复制
class MyClass : IEventHandler<SocketEvent>
{
    public void HandleEvent(SocketEvent eventData)
    {
        var msg = JsonConvert.SerializeObject(eventData);
        Console.WriteLine("EventBus:"  msg); 
    }
}

只要调用

代码语言:javascript复制
ZEventBus.Instance.RegisterAll();

或者

代码语言:javascript复制
ZEventBus.Instance.Register<SocketEvent>(GetType());

这两种的任何一种方式注册后,都会收到事件

发送事件

代码语言:javascript复制
ZEventBus.Instance.PostEvent<SocketEvent>(msgEvent);

问题

注意这个代码

代码语言:javascript复制
MethodInfo methodInfo = handler.GetMethod("HandleEvent");
if (methodInfo != null)
{
    object obj = Activator.CreateInstance(handler);
    methodInfo.Invoke(obj, new object[] { eventData });
}

我们可以看出上面的实现的原理是通过反射找到类里的方法,然后创建类的实例调用其中的HandleEvent方法,所以问题就来了,我们没法用在Window对应的类上,因为该类无法在非UI线程中创建。

那么是不是可以直接保留类实例的引用,然后进行调用呢?

答案是不能

在运行时Window的子类通过一下方式获取Type

代码语言:javascript复制
obj.GetType();

获取到的都是System.RuntimeType,也无法通过强转调用其中的方法。

所以目前还未实现能在Window的子类中使用EventBus

0 人点赞