Каков самый безопасный способ очистить свойство Controls контейнера и убедиться, что элементы управления правильно размещены?

В приложении WinForms у меня есть несколько случаев, когда я добавляю элемент управления в контейнер в ответ на действие пользователя (panel.Controls.Add(new CustomControl(...))), а затем очищаю панель (panel.Controls.Clear()) и повторно использую ее.

В рабочей среде приложение иногда выдает исключение, связанное с ошибками GDI или невозможностью загрузить файл ImageList. Обычно это происходит на машинах с ограниченными ресурсами и с пользователями, интенсивно использующими приложение в течение дня. Кажется довольно очевидным, что у меня есть утечка дескриптора GDI и что я должен удалять элементы управления, которые очищаются из контейнера, однако любые объяснения, которые я могу найти, расплывчаты относительно того, где и когда элемент управления должен быть удален.

Должен ли я удалять дочерние элементы управления сразу после очистки контейнера? Что-то вроде:

var controls = new List<Control>(_panel.Controls.Cast<Control>());
_panel.Controls.Clear();
foreach (var c in controls) c.Dispose();

Или я должен отслеживать элементы управления в списке и вызывать dispose в методе Dispose() контейнера? Такие как:

List<Control> _controlsToDispose = new List<Control>();
void ClearControls()
{
    _controlsToDispose.AddRange(_panel.Controls.Cast<Control>());
    _panel.Controls.Clear();
}
void Dispose()
{
    ...
    foreach (var c in _controlsToDispose) c.Dispose();
}

person Rebecca Scott    schedule 29.03.2011    source источник


Ответы (2)


Вариант 2 представляет еще один список, который вам нужно будет очистить, и для этих элементов потребуется больше памяти. Я бы предпочел вариант 1 с попыткой перехвата, обернутой вокруг кода, который вы упомянули.

person sajoshi    schedule 29.03.2011
comment
Значит, нет необходимости размещать элементы управления в методе контейнера Dispose()? Я смогу это сделать, как только освобожу контейнер? - person Rebecca Scott; 29.03.2011

После (достаточно эффективного) исправления всех случаев, когда мое приложение не удаляло очищенные элементы управления, я могу сделать несколько выводов:

  • Иногда я предварительно создавал список элементов управления, хранящийся, например, в свойстве Tag набора ListViewItem или TreeViewItem. Они не должны удаляться при очистке, но весь список должен быть повторен и ((Control)item.Tag).Dispose() вызван в родительском методе Dispose().
  • Если элемент управления не будет использоваться снова, что может случиться, когда я создаю его на лету, его следует удалить при удалении из контейнера.
  • При очистке и добавлении элементов управления «на лету» вам необходимо учитывать жизненный цикл элементов управления, чтобы определить, следует ли удалять их немедленно, отложить его до удаления родителя или не беспокоиться об этом.
  • У меня была ситуация, когда я удалил элемент управления, чтобы отобразить сообщение "Загрузка...", а затем снова вернул элемент управления в ответ на завершение потока. Я добавил вызов для удаления элемента управления, когда удалял его, что вызывало ошибки при попытке добавить его снова. Из-за проблемы с потоками отлаживать было непросто. Дело в том, что жизненный цикл может зависеть от потоков, отличных от потока пользовательского интерфейса. В данном случае речь шла о 20 секундах после отображения формы, так что, по крайней мере, элемент управления все еще существовал. Управление ситуацией, когда элемент управления может быть уничтожен, а потоки все еще хотят на него ссылаться, вероятно, является случаем слабых событий.

Мне не удалось найти какие-либо передовые практики или рекомендации по управлению жизненным циклом и утилизацией элементов управления. Я предполагаю, что правило заключается в том, что если элемент управления не заканчивает свою жизнь, вложенный в удаленный элемент управления, его необходимо удалить вручную, когда он не будет использоваться снова, или в методе Dispose() родительского элемента управления в последний.

person Community    schedule 18.04.2011