Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

Автор вопроса: jristaОтветов: 5

Я относительно новичок в 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. Однако он не срабатывает должным образом, и я сливаю открытые объекты PerformanceCounter каждый раз, когда приложение закрывается.

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

disposewpfuser-controlsfinalizer

Ответы

Ответ 1 · рейтинг 13 · Mark Heath

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

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

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

Ответ 2 · рейтинг 11 · venezia

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

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

Ответ 3 · рейтинг 8 · Jaime Marín

Я тоже это искал и после тестирования разных вариантов реализовал решение 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 · Pete OHanlon

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

Ответ 5 · fsbflavio

У меня возникли трудности, когда я использовал какое-то соединение с базой данных, используя какой-нибудь драйвер реализации IDbConnection или Entity Framework.

Я обнаружил, что в этих случаях рекомендуется сохранять одно соединение / контекст объекта для каждого окна, чтобы иметь возможность отслеживать изменения / транзакции. (ссылка)

Итак, я переопределил OnClosing:

protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
      base.OnClosing(e);
      this._context.Dispose();
}

Контекст может быть моей ViewModel / Control, который реализует IDisposable для очистки ресурсов.

Пример использования OnClosing для удаления ресурсов (ссылка )

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы