When I do
WeakEventManager<SystemEvents, EventArgs>
.AddHandler(null, nameof(SystemEvents.DisplaySettingsChanged), OnDisplaySettingsChanged);
My OnDisplaySettingsChanged
never gets called. However, if I instead use normal event subscribtion via SystemEvents.DisplaySettingsChanged += OnDisplaySettingsChanged
everything works fine.
What's going on?
Turns out it's WeakEventManager
's fault. When the event is fired, it implies that source
will be null
for static event sources (code excerpt from the reference source):
protected void DeliverEvent(object sender, EventArgs args)
{
ListenerList list;
object sourceKey = (sender != null) ? sender : StaticSource;
...
But sender
is never null
for SystemEvents
. Instead it passes a private instance of SystemEvents
, WeakEventManager
then assumes it's another instance it didn't previously know about and doesn't call the handler.
Here's the workaround I came up with:
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);
}
}
Usage example:
var proxy = new EventProxy(h => SystemEvents.DisplaySettingsChanged += h, h => SystemEvents.DisplaySettingsChanged -= h);
WeakEventManager<EventProxy, EventArgs>.AddHandler(proxy, nameof(EventProxy.Event), OnDisplaySettingsChanged);
Some explanation:
SystemEvents
holds a strong reference to EventProxy
, which holds a weak reference to the handler (via WeakEventManager
)WeakEventManager
subscribes to the event inside AddHandler
, the proxy subscribes to the original eventEventProxy
acts as a proxy between the static event and the handler, invoking the handler whenever the original event firesWeakEventManager
will eventually run a cleanup, discover that the handler is dead and unsubscribe