Удаление динамически созданных элементов управления в C # - PullRequest
6 голосов
/ 06 января 2010

У меня есть программа, которая добавляет серию «бликов» на график:

PictureBox blip = new PictureBox();
blip.Location = new Point(blipHours, blipAltitude);
blip.Size = new Size(6, 6);
blip.BackColor = System.Drawing.Color.Lime;
blip.Text = "";
blip.Name = callsign;
this.Controls.Add(blip);
this.Controls.SetChildIndex(blip, 0);
  1. Как сделать так, чтобы кнопка удаляла все "сообщения", созданные с помощью этого кода?

  2. Есть ли способ изменить цвет фона блика, когда его имя равно определенному callsign? Каждое сообщение связано с выделением в ListBox, и я хотел бы изменить цвет сообщения, когда пользователь выбирает его.

Ответы [ 5 ]

25 голосов
/ 06 января 2010

Все забывают о очень важной детали: вы должны удалить () элемент управления, иначе он утечет навсегда:

for (int ix = this.Controls.Count - 1; ix >= 0; ix--) {
    if (this.Controls[ix] is PictureBox) this.Controls[ix].Dispose();
}

Я уделю больше внимания предложению forever , в комментариях много шума по этому поводу, класс Control не ведет себя как любой другой класс .NET. Элемент управления поддерживается его свойством Handle. Который хранит родную ручку Windows. Пока существует собственное окно, объект Control не может быть уничтожен.

Это требует искусственного поддержания объекта в живых, когда вы используете Clear () или Remove () и удаляете элемент управления из его родителя. Winforms использует так называемое «окно парковки» в качестве хоста таких элементов управления. Это обычное родное окно, как и любое другое, его просто не видно. Его работа - быть родителем таких осиротевших элементов управления.

Окно парковки допускает множество хитрых трюков, которые обычно очень сложно сделать в Windows. Например, вы можете включать и выключать свойство ShowInTaskbar во время выполнения. Свойство окна, которое обычно можно указать только при создании окна (стиль WS_EX_APPWINDOW, указанный в вызове CreateWindowEx ()). Winforms могут сделать это даже после того, как вы создали окно, перемещая элементы управления формы в окно парковки, разрушая окно, создавая его снова и перемещая элементы управления назад. Ухоженная.

Но с не очень аккуратным зависанием, который является темой этого ответа, если вы удалите элемент управления и не вызовите его метод Dispose (), то он продолжит выживать в окне парковки , Навсегда. Истинная утечка. Ничего, что может сделать с этим сборщик мусора, он видит действительную ссылку на объект. Довольно грубое нарушение контракта IDisposable, вызов Dispose () является необязательным, но для класса Control это , а не .

К счастью, такую ​​ошибку довольно легко диагностировать, она не требует специальных инструментов, вы можете увидеть утечку на вкладке "Процессы" в диспетчере задач. Добавьте столбец «Объекты пользователя».

4 голосов
/ 06 января 2010
this.Controls.Clear();
2 голосов
/ 06 января 2010

Это удалит все элементы управления PictureBox из конкретного контейнера (в вашем случае я предполагаю график).

 for (int i = this.Controls.Count - 1; i >= 0; i--)
            {
                PictureBox control = this.Controls[i] as PictureBox;
                if (control == null)
                    continue;

                control.Dispose();
            }
2 голосов
/ 06 января 2010

Возможно, вы захотите добавить сообщение в список, а затем, когда пользователь нажмет кнопку «Очистить», просто переберите список, удалите сообщение из коллекции элементов управления и очистите список.

С точки зрения изменения цвета фона, почему бы вам не использовать оператор if?

blip.BackColor = callsign == "SpecialSign"? System.Drawing.Color.Red : System.Drawing.Color.Lime
0 голосов
/ 31 мая 2014

Похоже, Ханс Пассант тоже забыл очень важную деталь (или, возможно, он просто добавил к существующим ответам, а не представил полный ответ). В любом случае, вот что я должен был сделать, чтобы невидеть и избавиться от своих динамических элементов управления:

Panel p = tp.Controls[panelName] as Panel;
p.Controls.Clear();
for (int i = 0; i < p.Controls.Count; i++)
{
    p.Controls[i].Dispose();
}
...