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

如何正确释放ToolStripDropDown中托管的控件?

解决ToolStripDropDown托管TextBox的内存泄漏问题

问题根源

你遇到的内存泄漏是因为ToolStripDropDown关闭时不会自动释放内部的ToolStripControlHost及其托管的TextBox,且未清理事件绑定导致对象被事件句柄引用无法被GC回收。

正确的释放步骤

需要在ToolStripDropDown的Closed事件中显式清理所有资源,包括移除事件绑定、释放子控件、销毁宿主和弹窗本身:

  1. 在创建Popup时注册Closed事件
    修改DisplayFilter方法,确保弹窗关闭时触发清理逻辑:
private void DisplayFilter(DataGridViewCellMouseEventArgs e)
{
    int minWidth = MINIMUM_FILTER_WIDTH > Columns[e.ColumnIndex].Width ? MINIMUM_FILTER_WIDTH : Columns[e.ColumnIndex].Width;

    CueTextBox TxtFilter = new CueTextBox
    {
        Cue = "Filter",
        Size = new Size(minWidth, 30),
        Tag = Columns[e.ColumnIndex].Name
    };
    // 绑定筛选提交事件(你原代码中应该存在这行,否则Disposed里的移除无意义)
    TxtFilter.KeyPress += CmdFilter_Submit;
    
    var popup = new ToolStripDropDown
    {
        AutoSize = false,
        Margin = Padding.Empty,
        Padding = Padding.Empty
    };
    // 注册Closed事件用于资源清理
    popup.Closed += Popup_Closed;

    ToolStripControlHost host = new ToolStripControlHost(TxtFilter)
    {
        Margin = Padding.Empty,
        Padding = Padding.Empty,
        AutoSize = false,
        Size = TxtFilter.Size
    };

    popup.Size = TxtFilter.Size;
    popup.Items.Add(host);
    popup.Show(this, GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).X, GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Bottom);
    TxtFilter.Focus();
}
  1. 实现Popup的Closed事件处理方法
    添加以下方法,在弹窗关闭时彻底清理所有控件和事件绑定:
private void Popup_Closed(object sender, ToolStripDropDownClosedEventArgs e)
{
    var popup = sender as ToolStripDropDown;
    if (popup == null) return;

    // 先移除自身的Closed事件绑定,避免内存引用残留
    popup.Closed -= Popup_Closed;

    // 反向遍历弹窗内的所有宿主控件,避免索引异常
    for (int i = popup.Items.Count - 1; i >= 0; i--)
    {
        var hostItem = popup.Items[i];
        var host = hostItem as ToolStripControlHost;
        if (host != null)
        {
            // 获取托管的筛选文本框
            var filterBox = host.Control as CueTextBox;
            if (filterBox != null)
            {
                // 移除筛选提交事件绑定
                filterBox.KeyPress -= CmdFilter_Submit;
                // 释放文本框的子控件(如果存在)
                for (int ix = filterBox.Controls.Count - 1; ix >= 0; ix--)
                {
                    filterBox.Controls[ix].Dispose();
                }
                // 释放文本框本身
                filterBox.Dispose();
            }
            // 释放ToolStripControlHost
            host.Dispose();
        }
        // 从弹窗中移除项
        popup.Items.Remove(hostItem);
    }
    // 释放ToolStripDropDown本身
    popup.Dispose();
}

关键要点

  • 事件绑定必须清理:事件句柄会持有对控件的强引用,必须显式移除才能让GC回收对象
  • 释放顺序从内到外:先清理TextBox的资源,再释放Host,最后销毁Popup,避免嵌套引用残留
  • 显式调用Dispose:WinForms嵌套控件不会自动销毁内部托管对象,必须手动调用Dispose()释放非托管资源

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 00:05:32