Оказывается, это ошибка 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