简体   繁体   中英

Microsoft.Win32.SystemEvents events don't work with WeakEventManager

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 )
  • 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM