Правильная очистка пользовательских элементов управления WPF - PullRequest
21 голосов
/ 11 октября 2009

Я относительно новичок в WPF, и некоторые вещи с ним довольно чужды для меня. Например, в отличие от Windows Forms, иерархия элементов управления WPF не поддерживает IDisposable. В Windows Forms, если пользовательский элемент управления использовал какие-либо управляемые ресурсы, было очень легко очистить ресурсы, переопределив метод Dispose, который реализован в каждом элементе управления.

В WPF история не так проста. Я искал это несколько часов и столкнулся с двумя основными темами:

Первая тема - Microsoft четко заявляет, что WPF не реализует IDisposable, потому что элементы управления WPF не имеют неуправляемых ресурсов. Хотя это может быть правдой, они, похоже, полностью упустили тот факт, что пользовательские расширения в их иерархии классов WPF действительно могут использовать управляемые ресурсы (прямо или косвенно через модель). Не внедрив IDisposable, Microsoft фактически удалила единственный гарантированный механизм, с помощью которого можно очищать неуправляемые ресурсы, используемые пользовательским элементом управления WPF или окном.

Во-вторых, я нашел несколько ссылок на Dispatcher.ShutdownStarted. Я пытался использовать событие ShutdownStarted, но, похоже, оно не срабатывает для каждого элемента управления. У меня есть куча пользовательских элементов управления WPF, в которых я реализовал обработчик для ShutdownStarted, и он никогда не вызывается. Я не уверен, работает ли он только для Windows или, возможно, класса приложений WPF. Однако он не запускается должным образом, и я пропускаю открытые объекты PerformanceCounter каждый раз, когда приложение закрывается.

Есть ли лучшая альтернатива очистке неуправляемых ресурсов, чем событие Dispatcher.ShutdownStarted? Есть ли какая-то хитрость для реализации IDisposable, чтобы вызывался метод Dispose? Я бы предпочел избегать использования финализатора, если это вообще возможно.

Ответы [ 4 ]

12 голосов
/ 11 октября 2009

Боюсь, что Dispatcher.ShutdownStarted действительно является единственным механизмом, который WPF обеспечивает для удаления ресурсов в UserControls. (См. похожий вопрос , который я задал некоторое время назад).

Другой способ решения этой проблемы - переместить все ваши одноразовые ресурсы (если это вообще возможно) из кода позади и в отдельные классы (такие как ViewModel при использовании шаблона MVVM). Затем на более высоком уровне вы можете обработать закрытие вашего основного окна и уведомить все ViewModels через класс Messenger.

Я удивлен, что вы не получили событие Dispatcher.ShutdownStarted. Ваши UserControls прикреплены к окну верхнего уровня в то время?

10 голосов
/ 11 октября 2009

Интерфейс IDisposable не имеет (почти) никакого значения в WPF, поскольку механизм отличается от Winforms. В WPF вы должны иметь в виду визуальное и логическое дерево: это фундаментально.
Итак, любой визуальный объект обычно живет как дочерний по отношению к какому-то другому объекту. Основой механизма построения WPF является иерархическое присоединение визуальных объектов, а затем отсоединение и уничтожение, когда они бесполезны.

Я думаю, что вы можете проверить метод OnVisualParentChanged, доступный после UIElement: этот метод вызывается либо когда присоединен визуальный объект, либо когда отсоединен. Это может быть подходящим местом для удаления неуправляемых объектов (сокетов, файлов и т. Д.).

6 голосов
/ 28 декабря 2012

Я тоже искал это, и после тестирования разных вариантов я реализовал решение venezia

protected override void OnVisualParentChanged(DependencyObject oldParent)
    {
        if (oldParent != null)
        {
            MyOwnDisposeMethod(); //Release all resources here
        }

        base.OnVisualParentChanged(oldParent);
    }

Я понял, что когда родительский вызов вызывает Children.Clear() Метод и уже добавлены элементы в Children, DependencyObject имеет значение. Но когда родитель добавил элемент (Children.Add(CustomControl)), а потомки были пусты, DependencyObject был равен нулю.

0 голосов
/ 12 октября 2009

В то время как другие предоставили вам действительно полезную информацию об этой проблеме, есть немного информации, которой у вас может не быть, которая многое объяснит, почему не существует IDisposable. По сути, WPF (и Silverlight) интенсивно использует WeakReferences - это позволяет вам ссылаться на объект, который GC все еще может собирать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...