Утечка памяти объектов GDI: Custom ToolStripControlHost в ContextMenuStrip не удаляется

У меня есть различные настраиваемые контекстные меню, основанные на ToolStripControlHost. Они завернуты внутрь ContextMenuStrip и размещены в заголовках столбцов DataGridView (в зависимости от некоторых условий) следующим образом:

        if (this.DGV.Columns[DGVColname] != null)
        {
            ContextMenuStrip cstr = null;
            // there is always only one item in context menu
            // I tried disposing in different manners, this is an example of my efforts
            if (this.DGV.Columns[DGVColname].HeaderCell.ContextMenuStrip != null)
            {
                cstr = this.DGV.Columns[DGVColname].HeaderCell.ContextMenuStrip;

                if (cstr.Items[0] != null)
                {
                    cstr.Items[0].Dispose();
                    cstr.Items.Clear();
                }
            }
            else
            {
                cstr = new ContextMenuStrip();
                cstr.Opened += new EventHandler(cstr_Opened);
            }

            TextBoxToolStrip tsHost = new TextBoxToolStrip();

            tsHost.Size = new System.Drawing.Size(172, 20);
            tsHost.TextChanged += new EventHandler(myToolStrip_TextChanged);

            cstr.ShowCheckMargin = false;
            cstr.ShowImageMargin = false;
            cstr.Margin = new Padding(0);
            cstr.Padding = new Padding(0);
            cstr.Items.Add(tsHost);

            cstr.MaximumSize = new Size(tsHost.Width + 10, tsHost.Height + 10);
            cstr.Size = cstr.MaximumSize;
            cstr.LayoutStyle = ToolStripLayoutStyle.Flow;

            DGV.Columns[DGVColname].HeaderCell.ContextMenuStrip = cstr;
        }

Несмотря на то, что я вызываю dispose и/или устанавливаю все, что я могу получить, на нулевые значения, количество объектов GDI для процесса моего приложения по-прежнему постоянно увеличивается. У меня есть 20 столбцов с меню в моем DataGridView, и я получаю +30 или +34 (точно) каждый раз, когда вызывается код.

TextBoxToolStrip в этом примере расширяет ToolStripControlHost и содержит один TextBox:

   public class TextBoxToolStrip : ToolStripControlHost
    {
        // .... some string or bool Properties here ....

        public TextBox TextBoxControl
        {
            get { return Control as TextBox; }
        }

        public TextBoxToolStrip()
            : base(new TextBox())
        {
            this.TextBoxControl.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
            | System.Windows.Forms.AnchorStyles.Right)));

            this.TextBoxControl.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
            this.TextBoxControl.Location = new System.Drawing.Point(0, 3);
            this.TextBoxControl.ReadOnly = false;
            this.TextBoxControl.Font =
                new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));

            this.TextBoxControl.Size = new System.Drawing.Size(172, 20);
            this.Size = new System.Drawing.Size(172, 20);

            this.TextBoxControl.TabIndex = 0;
            this.TextBoxControl.TextAlign = System.Windows.Forms.HorizontalAlignment.Left;

            this.MouseHover += new EventHandler(TextBoxToolStrip_Enter);

            this.AutoSize = false;
            this.TextBoxControl.PreviewKeyDown += new PreviewKeyDownEventHandler(TextBoxPreviewKeyDown);
            this.TextBoxControl.KeyDown += new KeyEventHandler(TextBoxControl_KeyDown);
        }

        protected override void OnSubscribeControlEvents(Control control)
        {
            base.OnSubscribeControlEvents(control);

            TextBox tb = control as TextBox;

            if (tb != null)
            {
                tb.TextChanged += new EventHandler(OnTextChanged);
            }
        }

        protected override void OnUnsubscribeControlEvents(Control control)
        {
            base.OnUnsubscribeControlEvents(control);

            TextBox tb = control as TextBox;

            if (tb != null)
            {
                tb.TextChanged -= OnTextChanged;
            }
        }

        private void TextBoxToolStrip_Enter(object sender, EventArgs e)
        {
            this.Focus();
        }

        private void TextBoxPreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            if (e.KeyCode == Keys.Menu)
                e.IsInputKey = true;
        }

        private void TextBoxControl_KeyDown(object sender, KeyEventArgs e)
        {
            TextBox txb = sender as TextBox;
            ToolStripDropDown tsd = (ToolStripDropDown)txb.Parent;

            if (e.KeyCode == Keys.Enter)
            {
                tsd.Close();
            }
        }

        /// <summary>
        /// Expose TextChanged event
        /// </summary>
        public new event EventHandler TextChanged;

        private void OnTextChanged(object sender, EventArgs e)
        {
            if (TextChanged != null)
            {
                TextChanged(this, e);
            }
        }

ВОПРОС:

Как я могу правильно избавиться от контекстного меню?


person Arie    schedule 28.11.2013    source источник
comment
Вы никогда не утилизируете саму CMS. OnSubscribeControlEvents() выглядит как хороший источник утечек GC. Используйте приличный профилировщик памяти.   -  person Hans Passant    schedule 28.11.2013


Ответы (1)


Оказалось, что это не события, статические ссылки или что-то в этом роде. У меня ToolStripControlHosts bsed на TreeView, и оказалось, что текут только они. Похоже, что TreeView счастливо пропускает ресурсы сам по себе: когда для свойства CheckBoxes установлено значение true, дескрипторы растровых изображений, используемые для рисования отмеченных и неотмеченных флажков, не освобождаются должным образом при удалении TreeView (в моем случае их оставалось 4 каждый раз). случае), и это вызывает утечку памяти объектов GDI. Мне пришлось удалить список изображений флажка вручную.

Проблема и решение описаны здесь< /а>. Хотя здесь я использовал свойство вместо события:

        public new bool CheckBoxes
        {
            get
            {
                return base.CheckBoxes;
            }
            set
            {
                if (base.CheckBoxes == false)
                {
                    base.CheckBoxes = true;

                    IntPtr handle = SendMessage(this.Handle, TVM_GETIMAGELIST, new
                    IntPtr(TVSIL_STATE), IntPtr.Zero);
                        if (handle != IntPtr.Zero)
                            _checkboxImageList = handle;
                }
            }
        }

Событие StyleChanged в моем случае не работает.

person Arie    schedule 02.12.2013