Правильная очистка пользовательских элементов управления WPF

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

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

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

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

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

Ответ 1

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

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

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

Ответ 2

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

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

Ответ 3

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

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

        base.OnVisualParentChanged(oldParent);
    }

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

Ответ 4

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