События Microsoft.Win32.SystemEvents не работают с WeakEventManager - PullRequest
0 голосов
/ 08 мая 2018

Когда я делаю

WeakEventManager<SystemEvents, EventArgs>
    .AddHandler(null, nameof(SystemEvents.DisplaySettingsChanged), OnDisplaySettingsChanged);

Мой OnDisplaySettingsChanged никогда не вызывается. Однако, если я вместо этого использую обычную подписку на события через SystemEvents.DisplaySettingsChanged += OnDisplaySettingsChanged, все работает нормально.

Что происходит?

1 Ответ

0 голосов
/ 08 мая 2018

Оказывается, это ошибка WeakEventManager. Когда событие вызывается, это означает, что source будет null для статических источников события (фрагмент кода из справочного источника ):

protected void DeliverEvent(object sender, EventArgs args)
{
    ListenerList list;
    object sourceKey = (sender != null) ? sender : StaticSource;
    ...

Но sender никогда не будет null для SystemEvents. Вместо этого он передает частный экземпляр 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, прокси-сервер подписывается на исходное событие
  • EventProxy действует как прокси между статическим событием и обработчиком, вызывая обработчик всякий раз, когда происходит исходное событие
  • После того, как обработчик будет собран, WeakEventManager в конечном итоге запустит очистку, обнаружит, что обработчик мертв, и отписаться
  • Это приведет к тому, что прокси откажется от подписки на исходное событие и, в конце концов, получит GC
...