是否可以为 UIElement.LayoutUpdated 实施 WeakEventManager?
Is it possible to implement a WeakEventManager for UIElement.LayoutUpdated?
我有一个使用 LayoutUpdated-events 的应用程序,需要将它们注册为 weak。这是问题,我在执行 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);
}
}
事情就是这样:
- 我们总是收到 null 作为发件人(根据 LayoutUpdated 的设计)
- null 被传递到 DeliverEvent
- DeliverEvent 无法查找正确的 ListenerList,因为它需要发送者 != null 作为键
WPF: WeakEventManager // DeliverEvent // Line: 359
中的查找失败
object sourceKey = (sender != null) ? sender : StaticSource;
list = (ListenerList)Table[this, sourceKey];
我的问题是:有没有办法弱注册 LayoutUpdated 事件?
我对 sender 参数不感兴趣,所以我认为 LayoutUpdated 始终提供 null(我使用常规“+=”的实际实现有效)。但是 WeakEventManager base-class 依赖于 sender-parameter 来跟踪 ListenerLists。
正如您自己发现的那样,null
发件人打破 WeakEventManager
认为这是一个静态事件,这基本上使其与 LayoutUpdated
不兼容。由于 WeakEventManager.DeliverEvent
是非虚拟的,您无法真正“修复”它。
所以我认为您的选择是使用第三方 weak event manager,或者自己编写一个,这应该不会那么困难(除非您需要通用解决方案)。
想法是打破事件源 (UIElement
) 和事件订阅者 (Delegate.Target
) 之间的硬引用,这可以通过使用中介 class 来实现对事件处理程序及其目标的弱引用。
这是一个简单的示例,可以处理类型为 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);
我有一个使用 LayoutUpdated-events 的应用程序,需要将它们注册为 weak。这是问题,我在执行 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);
}
}
事情就是这样:
- 我们总是收到 null 作为发件人(根据 LayoutUpdated 的设计)
- null 被传递到 DeliverEvent
- DeliverEvent 无法查找正确的 ListenerList,因为它需要发送者 != null 作为键
WPF: WeakEventManager // DeliverEvent // Line: 359
中的查找失败object sourceKey = (sender != null) ? sender : StaticSource;
list = (ListenerList)Table[this, sourceKey];
我的问题是:有没有办法弱注册 LayoutUpdated 事件?
我对 sender 参数不感兴趣,所以我认为 LayoutUpdated 始终提供 null(我使用常规“+=”的实际实现有效)。但是 WeakEventManager base-class 依赖于 sender-parameter 来跟踪 ListenerLists。
正如您自己发现的那样,null
发件人打破 WeakEventManager
认为这是一个静态事件,这基本上使其与 LayoutUpdated
不兼容。由于 WeakEventManager.DeliverEvent
是非虚拟的,您无法真正“修复”它。
所以我认为您的选择是使用第三方 weak event manager,或者自己编写一个,这应该不会那么困难(除非您需要通用解决方案)。
想法是打破事件源 (UIElement
) 和事件订阅者 (Delegate.Target
) 之间的硬引用,这可以通过使用中介 class 来实现对事件处理程序及其目标的弱引用。
这是一个简单的示例,可以处理类型为 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);