如何修复由contextmenustrip不处置引起的内存泄漏

本文关键字:内存 泄漏 何修复 contextmenustrip | 更新日期: 2023-09-27 18:12:52

我在这里问了这个问题(我如何修复由ContextMenuStrip引用的对象引起的内存泄漏),并认为它已经得到了回答,但明显的修复没有工作。我对这个问题进行了搜索,发现了许多关于上下文菜单条导致内存泄漏的问题和博客,但没有找到具体的答案。我创建了一个小的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。菜单关闭后我该如何处理?

如何修复由contextmenustrip不处置引起的内存泄漏

将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类到this:

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)
    {
    }
}

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

我接受了lothing的建议,但做了一些修改。我的示例过于简单,没有显示实际应用程序中可能出现的所有不同对象类型和所有不同菜单构建。我需要快速修复,修改构建菜单的过程是不现实的。我修改了处理程序,如下所示,现在一次只有一个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);
    }

顺便说一句,我没有开发这个代码。这部分应用程序非常旧,我只尝试修复真正糟糕的东西,如内存泄漏。