Memory Not Freed if Windows Forms Application Uses ToolTip.SetToolTip

Dotyczy: .NET Framework 4.5

Symptoms


You have developed a .NET Windows Forms application which uses the ToolTip.SetToolTip method to associate a ToolTip control with a Windows Forms Control. When programmatically removing the associated Windows Forms control from the Form and Disposing of it, the control is not released from the managed heap. Over time, if repeatedly adding and removing controls that are associated with a ToolTip from using ToolTip.SetToolTip, memory usage continues to increase and may eventually result in a System.OutOfMemoryException.

Cause


When calling ToolTip.SetToolTip to associate a ToolTip with a Windows Forms control, the ToolTip object stores internal information, including a reference to the Control, within an internal HashTable. If using a single ToolTip control and associating it with numerous Controls, then a reference to each Control and its ToolTip information is stored within the HashTable. Neither calling a Control's Dispose method or removing it from the Controls collection of its container disassociates the Control from its ToolTip. Therefore in a scenario where the application is adding Controls dynamically to a Form, associating them with a ToolTip, and removing and Disposing of them; the Control is still rooted in memory and the application's managed memory heap will continue to grow over time.

Resolution


Depending on the application design, there are several ways to resolve this problem.

If the application is using one ToolTip control for many Windows Forms controls, then you can disassociate a particular Windows Forms control from the ToolTip by calling ToolTip.SetToolTip and passing a reference to the control and an empty string for the ToolTip caption. When an empty string is passed for the ToolTip caption, the SetToolTip method removes the reference to the Windows Forms control from within the ToolTip's internal HashTable. Note that you can also call the ToolTip.RemoveAll to remove all ToolTip text and disassociate the ToolTip control from all Windows Forms controls.

If the application is not associating multiple Windows Forms controls with a single ToolTip, then you can simply call the ToolTip's Dispose method.

More Information


This behavior is by design.

Consider the following sample code in a Windows Forms application. The code in the CreateControls() method creates a number of TextBox controls, calls ToolTip.SetToolTip to assign a ToolTip caption to each Control, and adds the Control to an internal ObservableCollection, and to the Controls collection. The code in the RemoveControls() method walks over the ObservableCollection, gets a reference to each Control and then removes it from the ObservableCollection and the Form's Controls collection, and then calls its Dispose method. This results in each of the TextBox controls being removed from the Form and Disposed, but the instance of each TextBox and associated ToolTipInfo are still rooted in the managed heap. Uncommenting the specified line in the RemoveControls() method disassociates the TextBox from the ToolTip and allows it to be garbage collected.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections.ObjectModel;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {

        ObservableCollection<TextBox> list;
        ToolTip toolTip;

        public Form1()
        {
            InitializeComponent();

            toolTip = new ToolTip();
            list = new ObservableCollection<TextBox>();
        }


        private void button1_Click(object sender, EventArgs e)
        {
            CreateControls();
        }
       
       
        private void button2_Click(object sender, EventArgs e)
        {
            RemoveControls();
        }

        private void CreateControls()
        {
            for (int i = 0; i < 100; i++)
            {
                TextBox t = new TextBox();
               
                toolTip.SetToolTip(t, i.ToString());
                t.Left = (t.Width * i) + 5;
                list.Add(t);
                this.Controls.Add(t);
            }
        }

        private void RemoveControls()
        {
            for (int i = list.Count-1;i>=0;i--)
            {
                TextBox tb = list[i];
                this.Controls.Remove(tb);
                list.Remove(tb);

 

  //Uncomment this line to disassociate the ToolTip control from the Windows Forms Control
             //   toolTip.SetToolTip(tb, "");
                tb.Dispose();
            }
        }
    }
}