WPF中实现Eventbus

前言

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

EventBus

事件实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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;
}
}
}

事件处理类接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122

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

项目中使用

数据

1
2
3
4
5
6
7
8
9
10
11
namespace SchoolClient.Socket
{
public class SocketEvent : EventData
{
public ZWsMsgVo Msg { get; set; }

public SocketEvent()
{
}
}
}

项目中Event接收类

1
2
3
4
5
6
7
8
class MyClass : IEventHandler<SocketEvent>
{
public void HandleEvent(SocketEvent eventData)
{
var msg = JsonConvert.SerializeObject(eventData);
Console.WriteLine("EventBus:"+ msg);
}
}

只要调用

1
ZEventBus.Instance.RegisterAll();

或者

1
ZEventBus.Instance.Register<SocketEvent>(GetType());

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

发送事件

1
ZEventBus.Instance.PostEvent<SocketEvent>(msgEvent);

问题

注意这个代码

1
2
3
4
5
6
MethodInfo methodInfo = handler.GetMethod("HandleEvent");
if (methodInfo != null)
{
object obj = Activator.CreateInstance(handler);
methodInfo.Invoke(obj, new object[] { eventData });
}

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

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

答案是不能

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

1
obj.GetType();

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

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