如何正确释放ToolStripDropDown中托管的控件?
解决ToolStripDropDown托管TextBox的内存泄漏问题
问题根源
你遇到的内存泄漏是因为ToolStripDropDown关闭时不会自动释放内部的ToolStripControlHost及其托管的TextBox,且未清理事件绑定导致对象被事件句柄引用无法被GC回收。
正确的释放步骤
需要在ToolStripDropDown的Closed事件中显式清理所有资源,包括移除事件绑定、释放子控件、销毁宿主和弹窗本身:
- 在创建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(); }
- 实现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
相关产品推荐
相关产品推荐

