【问题标题】:Microsoft.Win32.SystemEvents events don't work with WeakEventManagerMicrosoft.Win32.SystemEvents 事件不适用于 WeakEventManager
【发布时间】:2024-05-03 12:25:03
【问题描述】:

当我这样做时

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

我的OnDisplaySettingsChanged 永远不会被调用。但是,如果我改为通过SystemEvents.DisplaySettingsChanged += OnDisplaySettingsChanged 使用普通事件订阅,一切正常。

发生了什么事?

【问题讨论】:

    标签: wpf weak-events weakeventmanager


    【解决方案1】:

    原来是WeakEventManager 的错。当事件被触发时,这意味着对于静态事件源,source 将是null(代码摘自reference source):

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

    但是sender 永远不会是null 对于SystemEvents。相反,它传递了SystemEventsWeakEventManager 的私有实例,然后假定它是另一个它以前不知道的实例并且不调用处理程序。

    这是我想出的解决方法:

    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 收集

    【讨论】: