如何修复ContextMenuStrips未处理导致的内存泄漏问题

时间:2015-09-14 19:15:10

标签: c# winforms memory-leaks

我在这里问了这个问题(How do I fix a memory leak caused by an object referenced by a ContextMenuStrip)并认为它已被回答,但显而易见的修复无效。我已经搜索了这个问题,并发现了很多关于因ContextMenuStrips导致内存泄漏的问题和博客,但没有找到具体的答案。我创建了一个小型的WinForms测试应用程序来展示这个问题。创建项目后,我在设计器中向Form1添加了一个TreeView,并按如下方式修改Form1.cs:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        treeView1.NodeMouseClick += OnNodeMouseClick;

        var object1 = new MyObject("Object1");
        var object2 = new MyObject("Object2");
        var object3 = new MyObject("Object3");

        var treeNode1 = new TreeNode(object1.Name) { Tag = object1 };
        var treeNode2 = new TreeNode(object2.Name) { Tag = object2 };
        var treeNode3 = new TreeNode(object3.Name) { Tag = object3 };
        treeView1.Nodes.Add(treeNode1);
        treeView1.Nodes.Add(treeNode2);
        treeView1.Nodes.Add(treeNode3);
    }

    private void OnNodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
    {
        if (treeView1.SelectedNode == null)
            return;

        if (e.Button != MouseButtons.Right)
            return;

        MyObject targetObject = (MyObject)e.Node.Tag;

        if (targetObject == null)
            return;

        var point = new Point(e.X + 20, e.Y);
        var popupMenu = targetObject.PopupMenu;
        popupMenu.Show(this, point);
    }
}

我也有MyObject类:

class MyObject
{
    public MyObject(string name)
    {
        Name = name;
    }
    public string Name { get; set; }
    public ContextMenuStrip PopupMenu
    {
        get
        {
            ContextMenuStrip myPopup = new ContextMenuStrip();
            myPopup.Items.Add(ItemDelete);
            return myPopup;
        }
    }

    public ToolStripMenuItem ItemDelete
    {
        get
        {
            ToolStripMenuItem itemDelete = new ToolStripMenuItem("Delete " + Name);
            itemDelete.Click += ItemDelete_Click;
            return itemDelete;
        }
    }

    public void ItemDelete_Click(object sender, EventArgs e)
    {
    }
}

我没有实现删除方法,因为不需要查看问题。每当您右键单击树中的对象时,都会创建一个新的ContextMenuStrip。菜单永远不会被删除。在我的实际应用程序中,上下文菜单可以引用一些大对象。 dotMemory中显示的保留路径具有UserPreferenceChangeEventHandler引用的ContextMenuStrip。关闭后如何处理菜单?

2 个答案:

答案 0 :(得分:1)

将OnNodeMouseClick更改为:

    private void OnNodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
    {
        if (treeView1.SelectedNode == null)
            return;
        if (e.Button != MouseButtons.Right)
            return;
        MyObject targetObject = (MyObject)e.Node.Tag;
        if (targetObject == null)
            return;
        e.Node.ContextMenuStrip = MyObject.SharedPopup;
    }

你的MyObject类:

class MyObject
{
    private static ContextMenuStrip sharedPopup;
    public static ContextMenuStrip SharedPopup
    {
        get
        {
            if (null == sharedPopup)
            {
                sharedPopup = new ContextMenuStrip();
                ToolStripMenuItem deleteItem = new ToolStripMenuItem();
                deleteItem.Click += ItemDelete_Click;
                deleteItem.Paint += deleteItem_Paint;
                sharedPopup.Items.Add(deleteItem);
            }
            return sharedPopup;
        }
    }

    static void deleteItem_Paint(object sender, PaintEventArgs e)
    {
        ToolStripMenuItem item = sender as ToolStripMenuItem;
        item.Text = "Delete " + ((item.Owner as ContextMenuStrip).SourceControl as TreeView).SelectedNode.Text;
    }

    public MyObject(string name)
    {
        Name = name;
    }
    public string Name { get; set; }

    static void ItemDelete_Click(object sender, EventArgs e)
    {

    }
}

这会重复使用相同的上下文菜单,只需在需要时更新文本。

答案 1 :(得分:0)

我接受了Loathing的建议,但进行了修改。我的示例太简单了,没有显示所有不同的对象类型以及实际应用程序中可能出现的所有不同的菜单构建。我需要快速修复,修改构建菜单的过程并不实用。我修改了我的处理程序,如下所示,现在一次只有一个ContextMenuStrip。创建新的时,旧的将被删除。

    private void OnNodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
    {
        if (treeView1.SelectedNode == null)
            return;

        if (e.Button != MouseButtons.Right)
            return;

        MyObject targetObject = (MyObject)e.Node.Tag;

        if (targetObject == null)
            return;

        var point = new Point(e.X + 20, e.Y);
        var popupMenu = targetObject.PopupMenu;

        if (ContextMenuStrip != null)
        {
            ContextMenuStrip.Dispose();
        }

        ContextMenuStrip = popupMenu;
        ContextMenuStrip.Show(this, point);
    }
顺便说一下,我没有开发这段代码。应用程序的这一部分非常陈旧,我只是尝试修复内存泄漏等非常糟糕的事情。