0

我正在ToolStripMenu使用文件中的条目填充 a XML。每个ToolStripItem添加到菜单中的都会自动获得大约十几个分配给它的事件处理程序。用户可以选择更改XML和刷新菜单,使用以下命令清除所有内容:

menuStrip1.Items.Clear();

这将从 中重新填充XML并刷新表单。似乎所有这些动态项的事件处理程序从未从内存中删除。大多数项目不直接位于 menuStrip1 上,而是位于动态创建的子菜单中,这些子菜单位于menuStrip1.

尽管如此,我可以坐在那里刷新表单并且内存只是膨胀失控,5-10 MB每次刷新大约100 dynamic ToolStripItems. 可以快速达到数百 MB,这会使应用程序崩溃。

还加载其他内容,ToolStripItems例如属性和图标,但似乎是事件处理程序使用了大部分添加的内存。我注释掉了所有的处理程序,每次刷新时内存变得更加(但不是完全)可持续。

是否有必要枚举所有菜单和子菜单并单独释放每个事件处理程序以确保它不会留下来?

4

3 回答 3

1

如果您同时处理/删除事件处理程序和它订阅的对象,您应该没问题。否则,是的,您将不得不取消订阅每个处理程序。

或者看一下不同的订阅方式——不是编译器生成的事件。 有关示例,请参见http://paulstovell.com/blog/weakevents )

于 2014-02-19T02:31:24.193 回答
0

检查ItemRemoved菜单条的事件。它将返回一个从您的菜单条中删除的项目。您可以在此处处理它,也可以从此处删除每个处理程序。

更新

获得父菜单后,您可以使用递归函数调用删除所有子菜单的事件处理程序。

private void menuStrip1_ItemRemoved(object sender, ToolStripItemEventArgs e)
{
    foreach (object child in ((ToolStripMenuItem)e.Item).DropDownItems)
    {
        if (child.GetType().Name == "ToolStripMenuItem")
            RemoveHandler((ToolStripMenuItem)child);                
    }
}
private void RemoveHandler(ToolStripMenuItem item)
{
    if (item.HasDropDownItems)
    {
        foreach (Object dropdown in item.DropDownItems)
        {
            if (dropdown.GetType().Name == "ToolStripMenuItem")
                RemoveHandler((ToolStripMenuItem)dropdown);
        }
    }
    else
    {
        //item.Click -= new EventHandler(MenuItem_Clicked);
        //Remove event handlers
    }
}
于 2014-02-19T03:44:47.287 回答
0

我经常使用的一个技巧是创建一个private _cleanUp = New List<Action>()类级变量并将其用于清理事件处理程序。

因此,当我附加一个事件处理程序时,我还将分离代码添加到列表中,如下所示:

this.NameTextbox.Click += My_Handler;
_cleanUp.Add(() => this.NameTextbox.Click -= My_Handler);

然后在清理时,对于您的代码,我可以这样做:

_cleanUp.ForEach(a => a());
_cleanUp.Clear();
menuStrip1.Items.Clear();

然后我不必确切地记住添加了什么。我可以将任何我喜欢的清理代码添加到列表中。

于 2014-02-20T04:51:44.447 回答