1

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?

torvin
  • 6,515
  • 1
  • 37
  • 52

1 Answers1

2

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)
  • When WeakEventManager subscribes to the event inside AddHandler, the proxy subscribes to the original event
  • EventProxy acts as a proxy between the static event and the handler, invoking the handler whenever the original event fires
  • After the handler gets collected, WeakEventManager will eventually run a cleanup, discover that the handler is dead and unsubscribe
  • This will cause the proxy to unsubscribe from the original event, and, eventually, get collected by GC
torvin
  • 6,515
  • 1
  • 37
  • 52