0

我有一个使用 LayoutUpdated-events 并需要注册它们的应用程序。这是问题,我在执行 WeakEventManager 期间遇到了问题

internal class WeakLayoutUpdatedManager : WeakEventManager
{
    [..]

    private void OnLayoutUpdated(object sender, EventArgs e)
    {
        // NOTE: received sender is always null (by design of LayoutUpdated) 
        base.DeliverEvent(sender, e);
    }
}

这就是发生的事情:

  1. 我们总是收到 null 作为发件人(根据 LayoutUpdated 的设计)
  2. 该 null 被传递到 DeliverEvent
  3. DeliverEvent 无法查找正确的 ListenerList,因为它需要一个 sender != null 作为键

WPF中的失败查找:WeakEventManager // DeliverEvent // 行:359

object sourceKey = (sender != null) ? sender : StaticSource;
list = (ListenerList)Table[this, sourceKey];

我的问题是:有没有办法弱注册到 LayoutUpdated 事件?

我对 sender-parameter 不感兴趣,所以 LayoutUpdated 总是提供 null 对我来说是可以的(我使用常规“+=”的实际实现有效)。但是 WeakEventManager 基类依赖 sender-parameter 来跟踪 ListenerList。

4

1 回答 1

0

正如您发现的那样,null发件人突然WeakEventManager认为这是一个静态事件,这基本上使其与LayoutUpdated. 由于WeakEventManager.DeliverEvent是非虚拟的,因此您无法真正“修复”它。

因此,我认为您的选择是使用第三方弱事件管理器,或者自己编写一个,这应该不难(除非您需要通用解决方案)。

这个想法是打破事件源 ( UIElement) 和事件订阅者 ( Delegate.Target) 之间的硬引用,这可以通过使用对事件处理程序及其目标具有弱引用的中介类来实现。

这是一个适用于 type 事件的简单粗暴的示例EventHandler

public class WeakEventListener<TSender>
{
    private readonly EventHandler _handler;
    private readonly EventInfo _event;
    private readonly WeakReference<object> _target;

    // Helps to keep original EventHandler alive as long as its target isn't GCed.
    private readonly ConditionalWeakTable<object, EventHandler> _targetHandler =
        new ConditionalWeakTable<object, EventHandler>();

    public WeakEventListener(string eventName, EventHandler handler)
    {
        _handler = new EventHandler(DeliverEvent);
        _event = typeof(TSender).GetEvent(eventName);
        _target = new WeakReference<object>(handler.Target);
        _targetHandler.Add(handler.Target, handler);
    }

    public void Add(TSender source) => _event.AddEventHandler(source, _handler);

    public void Remove(TSender source) => _event.RemoveEventHandler(source, _handler);

    private void DeliverEvent(object sender, EventArgs args)
    {
        if (_target.TryGetTarget(out object target) &&
            _targetHandler.TryGetValue(target, out EventHandler handler))
        {
            handler(sender, args);
        }
    }
}

可以按如下方式使用:

var listener = new WeakEventListener<UIElement>(nameof(LayoutUpdated), OnLayoutUpdated);
listener.Add(uiElement);
于 2021-02-16T05:17:15.063 回答