You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WinUI 3自定义控件事件处置:如何正确取消事件订阅?

解决WinUI 3自定义控件Popup事件订阅/取消订阅问题

问题根源

WinUI 3的Unloaded事件并非控件销毁的标志——当控件被临时移出视觉树(比如布局调整、父控件切换可视状态)时就会触发,所以在这个事件里取消订阅会导致控件后续正常使用时事件失效。

正确解决方案

1. 每次模板应用时先取消旧订阅,再订阅新事件

OnApplyTemplate可能会被多次调用(比如模板刷新、控件样式变更),所以每次获取新的Popup实例前,必须先取消旧实例的事件订阅,避免重复订阅和内存泄漏:

private Popup? _popup;
private Button? toggleButton;
private ListView? listView;
private Grid? popupGrid;
private TextBlock? textBlock;

protected override void OnApplyTemplate()
{
    base.OnApplyTemplate();
    
    // 先取消旧Popup的事件订阅
    if (_popup != null)
    {
        _popup.Closed -= Popup_Closed;
        _popup.Opened -= Popup_Opened;
    }

    // 获取模板中的控件实例
    toggleButton = GetTemplateChild("PART_ToggleButton") as Button;
    _popup = GetTemplateChild("PART_Popup") as Popup;
    listView = GetTemplateChild("PART_ListView") as ListView;
    popupGrid = GetTemplateChild("PART_PopupGrid") as Grid;
    textBlock = GetTemplateChild("PART_ContentTextBlock") as TextBlock;

    // 订阅新Popup的事件
    if (_popup != null)
    {
        _popup.Closed += Popup_Closed;
        _popup.Opened += Popup_Opened;
    }
}

2. 在控件销毁时取消订阅

通过实现IDisposable接口,在控件真正被销毁时清理事件订阅,确保资源释放:

public sealed class YourCustomControl : Control, IDisposable
{
    // ... 上述成员变量和OnApplyTemplate代码 ...

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~YourCustomControl()
    {
        Dispose(false);
    }

    private void Dispose(bool disposing)
    {
        if (disposing)
        {
            // 清理托管资源:取消Popup事件订阅
            if (_popup != null)
            {
                _popup.Closed -= Popup_Closed;
                _popup.Opened -= Popup_Opened;
                _popup = null;
            }
            // 可在此处清理其他托管资源
        }
        // 若有非托管资源,在此处清理
    }
}

备选方案:使用WeakEventManager避免内存泄漏

如果担心忘记手动取消订阅,可使用WeakEventManager来订阅事件,它不会因未取消订阅导致内存泄漏:

// 订阅事件
if (_popup != null)
{
    WeakEventManager<Popup, object>.AddHandler(_popup, nameof(Popup.Opened), Popup_Opened);
    WeakEventManager<Popup, object>.AddHandler(_popup, nameof(Popup.Closed), Popup_Closed);
}

// 取消订阅
if (_popup != null)
{
    WeakEventManager<Popup, object>.RemoveHandler(_popup, nameof(Popup.Opened), Popup_Opened);
    WeakEventManager<Popup, object>.RemoveHandler(_popup, nameof(Popup.Closed), Popup_Closed);
}

内容的提问来源于stack exchange,提问作者Bevara Satyasairam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 09:22:41