Microsoft.Win32.SystemEvents 事件不适用于 WeakEventManager
Microsoft.Win32.SystemEvents events don't work with WeakEventManager
当我做的时候
WeakEventManager<SystemEvents, EventArgs>
.AddHandler(null, nameof(SystemEvents.DisplaySettingsChanged), OnDisplaySettingsChanged);
我的 OnDisplaySettingsChanged
从来没有被调用过。但是,如果我改为通过 SystemEvents.DisplaySettingsChanged += OnDisplaySettingsChanged
使用普通事件订阅,一切正常。
怎么回事?
原来是WeakEventManager
的错。当事件被触发时,它意味着 source
将是 null
对于静态事件源(代码摘自 reference source):
protected void DeliverEvent(object sender, EventArgs args)
{
ListenerList list;
object sourceKey = (sender != null) ? sender : StaticSource;
...
但是对于 SystemEvents
,sender
永远不会是 null
。相反,它传递 SystemEvents
、WeakEventManager
的私有实例,然后假设它是另一个它之前不知道的实例,并且不调用处理程序。
这是我想出的解决方法:
class EventProxy
{
private readonly Action<EventHandler> _subscribe;
private readonly Action<EventHandler> _unsubscribe;
public EventProxy(Action<EventHandler> subscribe, Action<EventHandler> unsubscribe)
{
_subscribe = subscribe;
_unsubscribe = unsubscribe;
}
private EventHandler _event;
public event EventHandler Event
{
add
{
if (_event == null)
_subscribe(OnEvent);
_event += value;
}
remove
{
// ReSharper disable once DelegateSubtraction
_event -= value;
if (_event == null)
_unsubscribe(OnEvent);
}
}
private void OnEvent(object sender, EventArgs args)
{
_event?.Invoke(this, args);
}
}
用法示例:
var proxy = new EventProxy(h => SystemEvents.DisplaySettingsChanged += h, h => SystemEvents.DisplaySettingsChanged -= h);
WeakEventManager<EventProxy, EventArgs>.AddHandler(proxy, nameof(EventProxy.Event), OnDisplaySettingsChanged);
一些解释:
SystemEvents
持有对 EventProxy
的强引用,后者持有对处理程序的弱引用(通过 WeakEventManager
)
- 当
WeakEventManager
订阅了AddHandler
里面的事件时,proxy订阅了原来的事件
EventProxy
充当静态事件和处理程序之间的代理,在原始事件触发时调用处理程序
- 处理程序被收集后,
WeakEventManager
最终会运行清理,发现处理程序已死并取消订阅
- 这将导致代理取消订阅原始事件,并最终被 GC 收集
当我做的时候
WeakEventManager<SystemEvents, EventArgs>
.AddHandler(null, nameof(SystemEvents.DisplaySettingsChanged), OnDisplaySettingsChanged);
我的 OnDisplaySettingsChanged
从来没有被调用过。但是,如果我改为通过 SystemEvents.DisplaySettingsChanged += OnDisplaySettingsChanged
使用普通事件订阅,一切正常。
怎么回事?
原来是WeakEventManager
的错。当事件被触发时,它意味着 source
将是 null
对于静态事件源(代码摘自 reference source):
protected void DeliverEvent(object sender, EventArgs args)
{
ListenerList list;
object sourceKey = (sender != null) ? sender : StaticSource;
...
但是对于 SystemEvents
,sender
永远不会是 null
。相反,它传递 SystemEvents
、WeakEventManager
的私有实例,然后假设它是另一个它之前不知道的实例,并且不调用处理程序。
这是我想出的解决方法:
class EventProxy
{
private readonly Action<EventHandler> _subscribe;
private readonly Action<EventHandler> _unsubscribe;
public EventProxy(Action<EventHandler> subscribe, Action<EventHandler> unsubscribe)
{
_subscribe = subscribe;
_unsubscribe = unsubscribe;
}
private EventHandler _event;
public event EventHandler Event
{
add
{
if (_event == null)
_subscribe(OnEvent);
_event += value;
}
remove
{
// ReSharper disable once DelegateSubtraction
_event -= value;
if (_event == null)
_unsubscribe(OnEvent);
}
}
private void OnEvent(object sender, EventArgs args)
{
_event?.Invoke(this, args);
}
}
用法示例:
var proxy = new EventProxy(h => SystemEvents.DisplaySettingsChanged += h, h => SystemEvents.DisplaySettingsChanged -= h);
WeakEventManager<EventProxy, EventArgs>.AddHandler(proxy, nameof(EventProxy.Event), OnDisplaySettingsChanged);
一些解释:
SystemEvents
持有对EventProxy
的强引用,后者持有对处理程序的弱引用(通过WeakEventManager
)- 当
WeakEventManager
订阅了AddHandler
里面的事件时,proxy订阅了原来的事件 EventProxy
充当静态事件和处理程序之间的代理,在原始事件触发时调用处理程序- 处理程序被收集后,
WeakEventManager
最终会运行清理,发现处理程序已死并取消订阅 - 这将导致代理取消订阅原始事件,并最终被 GC 收集