Pytanie Prawidłowe czyszczenie elementów sterujących użytkownika WPF


Jestem stosunkowo nowy w WPF, a niektóre rzeczy z nim są całkiem obce dla mnie. Po pierwsze, w przeciwieństwie do Windows Forms, hierarchia sterowania WPF nie obsługuje IDisposable. W Windows Forms, jeśli kontrola użytkownika korzystała z dowolnych zarządzanych zasobów, bardzo łatwo było wyczyścić zasoby, przesłoniwszy metodę Dispose, którą zaimplementował każdy z nich.

W WPF historia nie jest taka prosta. Szukałem tego przez kilka godzin i napotkałem dwa podstawowe motywy:

Pierwszym tematem jest Microsoft wyraźnie stwierdzający, że WPF nie implementuje IDisposable, ponieważ formanty WPF nie mają niezarządzanych zasobów. Choć może to być prawdą, wydaje się, że całkowicie pominęli fakt, że rozszerzenia użytkowników do ich hierarchii klas WPF mogą rzeczywiście korzystać z zarządzanych zasobów (bezpośrednio lub pośrednio poprzez model). Nie implementując IDisposable, firma Microsoft skutecznie usunęła jedyny gwarantowany mechanizm, dzięki któremu niezarządzane zasoby używane przez niestandardowy kontrolek lub okno WPF mogą zostać wyczyszczone.

Po drugie, znalazłem kilka odniesień do Dispatchera.ShutdownStarted. Próbowałem użyć zdarzenia ShutdownStarted, ale wydaje się, że nie uruchamia się dla każdej kontrolki. Mam pęczku WPF UserControl's, że mam wdrożony program obsługi dla ShutdownStarted, i nigdy nie jest wywoływana. Nie jestem pewien, czy działa tylko w systemie Windows, czy może w klasie aplikacji WPF. Jednak nie jest on prawidłowo uruchamiany, a przeciążam otwarte obiekty PerformanceCounter za każdym razem, gdy aplikacja się zamyka.

Czy istnieje lepsza alternatywa dla czyszczenia niezarządzanych zasobów niż zdarzenie Dispatcher.ShutdownStarted? Czy istnieje jakaś sztuczka do implementacji IDisposable, tak aby wywołać Dispose? Wolałbym uniknąć używając finalizatora, o ile to możliwe.


21
2017-10-11 08:56


pochodzenie




Odpowiedzi:


Obawiam się, że Dispatcher.ShutdownStarted naprawdę wydaje się być jedynym mechanizmem WPF zapewniającym usuwanie zasobów w UserControls. (Zobacz bardzo podobne pytanie Zapytałem chwilę temu).

Innym sposobem podejścia do problemu jest przeniesienie wszystkich dostępnych zasobów (jeśli w ogóle możliwe) z kodu do oddzielnych klas (takich jak ViewModel przy użyciu wzorca MVVM). Następnie na wyższym poziomie można obsługiwać zamykanie głównego okna i powiadamiać wszystkie modele ViewModels za pośrednictwem klasy Messenger.

Jestem zaskoczony, że nie dostałeś zdarzenia Dispatcher.ShutdownStarted. Czy kontrolki UserControls są wtedy podłączone do okna najwyższego poziomu?


12
2017-10-11 13:16



+1 za przeniesienie dostępnych zasobów z kodu. Jednym z kluczowych punktów nauki dla WPF jest minimalizacja kodu, aby wykorzystać siłę i ekspresję architektury łączenia danych. Bolesna rzecz do nauczenia się (krzywa uczenia się przypomina wspinaczkę na klifie), ale satysfakcjonująca, gdy "dostajesz" sposób myślenia WPF. - Greg D
Wszystkie dostępne zasoby są w rzeczywistości w ViewModel, które same są identyfikowalne. Jestem naprawdę zdezorientowany, dlaczego zdarzenie Dispatcher.ShutdownStarted nie jest uruchomione. Kontrola licznika wydajności (i powiązany z nim ViewModel) są rzeczywiście dołączone do wykresu WPF, ponieważ są osadzone w <Grid> w <TabControl>. - jrista
@Greg D: Generalnie otrzymuję model WPF. Zacząłem używać MVVM, gdy tylko poznałem podstawy WPF, a mój CodeBehind jest prawie tak samo nagi, jak to tylko możliwe (po prostu domyślny konstruktor i jego wywołanie InitializeComponent). Komponowalność i możliwości tworzenia plików WPF są oszałamiające i nigdy nie wrócę do formularzy Windows, jeśli będę miał wybór. - jrista


Interfejs IDisposable ma (prawie) żadnego znaczenia w ramach WPF, ponieważ mechanizm różni się od WinForm. W WPF musisz pamiętać drzewo wizualne i logiczne: to jest fundamentalne.
Tak więc każdy obiekt wizualny na ogół żyje jako dziecko jakiegoś innego obiektu. Podstawą mechanizmu budowania WPF jest dołączenie obiektu wizualnego w sposób hierarchiczny, a następnie odłączanie i niszczenie, gdy nie są użyteczne.

Myślę, że możesz sprawdzić OnVisualParentChanged metoda narażona od czasu UIElement: ta metoda jest wywoływana, gdy obiekt wizualny jest podłączony i gdy jest odłączony. To może być odpowiednie miejsce do usuwania niezarządzanych obiektów (gniazd, plików itp.).


10
2017-10-11 16:05



Dzięki za podpowiedź na temat OnVisualParentChanged. Zagram z tym i zobaczę, czy to pomoże rozwiązać mój problem. - jrista


Szukałem tego również i po przetestowaniu różnych opcji zaimplementowałem rozwiązanie venezia

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

        base.OnVisualParentChanged(oldParent);
    }

Zdałem sobie z tego sprawę, gdy zadzwonił rodzic Children.Clear() Metoda i miały już elementy dodane do dzieci, DependencyObject miał wartość. Ale kiedy rodzic dodał element (Children.Add(CustomControl)) i dzieci były puste DependencyObject miał wartość zerową.


6
2017-12-28 18:36



Zmieniłem go na if (Parent == null), więc jeśli przeniosę kontrolę do innego kontenera, to nie ulegnie samozniszczeniu - Sean


Podczas gdy inni dali ci naprawdę przydatne informacje na temat tego problemu, jest trochę informacji, których możesz nie mieć, które wyjaśnią wiele o tym, dlaczego nie ma IDisposable. Zasadniczo, WPF (i Silverlight) intensywnie wykorzystuje WeakReferences - pozwala to na odwołanie się do obiektu, który GC nadal może gromadzić.


0
2017-10-11 20:53



Dzięki za wgląd Pete. Ciekaw jestem, czy masz jakieś linki, które wyjaśniają to bardziej szczegółowo? Jestem ciekawy, jak intensywne korzystanie z WeakReferences nie powoduje problemów. Mogą być potężnym narzędziem w sytuacjach niszowych ... ale nie mogę sobie wyobrazić, w jaki sposób są one używane w WPF. - jrista