Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Zrozumienie wewnętrznego zachowania obiektów WPF pomoże Ci dokonać odpowiednich kompromisów między funkcjonalnością a wydajnością.
Nie usuwaj programów obsługi zdarzeń w obiektach może zachować żywość obiektów
Delegat, który obiekt przekazuje do jego zdarzenia, jest w rzeczywistości odwołaniem do tego obiektu. W związku z tym programy obsługi zdarzeń mogą utrzymywać obiekty aktywne dłużej niż oczekiwano. Podczas czyszczenia obiektu, który zarejestrował się w celu nasłuchiwania zdarzenia obiektu, niezbędne jest usunięcie tego delegata przed zwolnieniem obiektu. Utrzymywanie niepotrzebnych obiektów przy życiu zwiększa użycie pamięci aplikacji. Jest to szczególnie istotne, gdy obiekt jest korzeniem drzewa logicznego lub drzewa wizualnego.
WPF wprowadza słaby wzorzec odbiornika zdarzeń, które mogą być przydatne w sytuacjach, gdy relacje okresu istnienia obiektu między źródłem i odbiornikiem są trudne do śledzenia. Niektóre istniejące zdarzenia WPF używają tego wzorca. Jeśli implementujesz obiekty z niestandardowymi zdarzeniami, ten wzorzec może być dla ciebie użyteczny. Aby uzyskać szczegółowe informacje, zobacz Słabe wzorce zdarzeń.
Istnieje kilka narzędzi, takich jak CLR Profiler i Podgląd zestawu roboczego, które mogą zawierać informacje na temat użycia pamięci określonego procesu. Profiler CLR zawiera wiele bardzo przydatnych widoków profilu alokacji, w tym histogram przydzielonych typów, grafy alokacji i wywołań, linię czasową pokazującą procesy odzyskiwania pamięci różnych generacji oraz wynikowy stan sterty zarządzanej po tych procesach, a także drzewo wywołań przedstawiające alokacje poszczególnych metod i ładowanie zestawów. Aby uzyskać więcej informacji, zobacz Performance.
Właściwości i obiekty zależności
Ogólnie rzecz biorąc, uzyskiwanie dostępu do właściwości zależności DependencyObject nie jest wolniejsze niż uzyskiwanie dostępu do właściwości CLR. Chociaż istnieje niewielkie obciążenie związane z wydajnością ustawiania wartości właściwości, uzyskanie wartości jest tak szybkie, jak pobieranie wartości z właściwości CLR. Obniżenie obciążenia związanego z małą wydajnością polega na tym, że właściwości zależności obsługują niezawodne funkcje, takie jak powiązanie danych, animacja, dziedziczenie i stylizacja. Aby uzyskać więcej informacji, zobacz Przegląd właściwości zależności.
Optymalizacje właściwości zależności
Właściwości zależności w aplikacji należy definiować bardzo ostrożnie. Jeśli DependencyProperty ma wpływ tylko na opcje metadanych typu renderowania, a nie inne opcje metadanych, takie jak AffectsMeasure, należy oznaczyć je jako takie, przesłaniając jego metadane. Aby uzyskać więcej informacji na temat zastępowania lub uzyskiwania metadanych właściwości, zobacz Metadane właściwości zależności.
Może być bardziej wydajne, jeśli obsługa zmiany właściwości ręcznie unieważnia procesy pomiaru, rozmieszczania i renderowania, gdy nie wszystkie zmiany właściwości faktycznie wpływają na te procesy. Na przykład możesz zdecydować się na ponowne renderowanie tła tylko wtedy, gdy wartość jest większa niż ustawiony limit. W takim przypadku procedura obsługi zmiany właściwości unieważniłaby renderowanie tylko wtedy, gdy wartość przekroczy ustawiony limit.
Tworzenie właściwości DependencyProperty dziedziczonej nie jest bezpłatne
Domyślnie zarejestrowane właściwości zależności nie są dziedziczone. Można jednak jawnie ustawić dowolną właściwość, którą można dziedziczyć. Chociaż jest to przydatna funkcja, uczynienie właściwości dziedziczną wpływa na wydajność, wydłużając czas unieważniania właściwości.
Ostrożnie używaj programu RegisterClassHandler
Pamiętaj, że chociaż wywołanie RegisterClassHandler pozwala na zapisanie stanu wystąpienia, program obsługi jest wywoływany dla każdego wystąpienia, co może powodować problemy z wydajnością. Używaj RegisterClassHandler tylko wtedy, gdy aplikacja wymaga zapisania stanu wystąpienia.
Ustawianie wartości domyślnej dla właściwości DependencyProperty podczas rejestracji
Podczas tworzenia DependencyProperty, która wymaga wartości domyślnej, ustaw tę wartość, korzystając z domyślnych metadanych przekazanych jako parametr do metody Register w DependencyProperty. Użyj tej techniki zamiast ustawiać wartość właściwości w konstruktorze lub w każdym wystąpieniu elementu.
Ustawianie wartości PropertyMetadata przy użyciu funkcji Register
Podczas tworzenia DependencyPropertymożna ustawić PropertyMetadata przy użyciu metod Register lub OverrideMetadata. Mimo że obiekt może mieć konstruktor statyczny do wywołania OverrideMetadata, nie jest to optymalne rozwiązanie i będzie miało wpływ na wydajność. Aby uzyskać najlepszą wydajność, ustaw PropertyMetadata podczas wywołania do Register.
Obiekty z możliwością zamrażania
Freezable jest specjalnym typem obiektu, który ma dwa stany: rozmrożony i zamrożony. Zamrażanie obiektów zawsze, gdy jest to możliwe, poprawia wydajność aplikacji i zmniejsza jej zestaw roboczy. Aby uzyskać więcej informacji, zobacz Przegląd obiektów z możliwością zamrażania.
Każdy Freezable ma zdarzenie Changed, które jest wywoływane za każdym razem, gdy się ono zmienia. Jednak powiadomienia o zmianach są kosztowne pod względem wydajności aplikacji.
Rozważmy następujący przykład, w którym każdy Rectangle używa tego samego obiektu Brush:
rectangle_1.Fill = myBrush;
rectangle_2.Fill = myBrush;
rectangle_3.Fill = myBrush;
// ...
rectangle_10.Fill = myBrush;
rectangle_1.Fill = myBrush
rectangle_2.Fill = myBrush
rectangle_3.Fill = myBrush
' ...
rectangle_10.Fill = myBrush
Domyślnie WPF udostępnia program obsługi zdarzeń dla zdarzenia Changed obiektu SolidColorBrush w celu unieważnienia właściwości Fill obiektu Rectangle. W takim przypadku za każdym razem, gdy SolidColorBrush musi uruchomić swoje Changed zdarzenie, wymagane jest wywołanie funkcji wywołania zwrotnego dla każdej Rectangle— akumulacja tych wywołań funkcji wywołania zwrotnego nakłada znaczną karę za wydajność. Ponadto dodawanie i usuwanie procedur obsługi w tym momencie obciąża wydajność, ponieważ aplikacja musiałaby przechodzić przez całą listę, aby to zrobić. Jeśli scenariusz aplikacji nigdy nie zmienia SolidColorBrush, niepotrzebnie będziesz ponosić koszty utrzymania procedur obsługi zdarzeń Changed.
Zamrożenie Freezable może zwiększyć wydajność, ponieważ nie musi już poświęcać zasobów na utrzymywanie powiadomień o zmianach. Tabela poniżej pokazuje rozmiar prostego SolidColorBrush, gdy jego właściwość IsFrozen jest ustawiona na true
, w porównaniu do sytuacji, gdy nie jest. Przyjęto założenie, że stosuje się jeden pędzel do właściwości Fill u dziesięciu obiektów Rectangle.
stan | Rozmiar |
---|---|
Zamrożone SolidColorBrush | 212 Bajty |
Niemarznięte SolidColorBrush | 972 Bajty |
W poniższym przykładzie kodu przedstawiono tę koncepcję:
Brush frozenBrush = new SolidColorBrush(Colors.Blue);
frozenBrush.Freeze();
Brush nonFrozenBrush = new SolidColorBrush(Colors.Blue);
for (int i = 0; i < 10; i++)
{
// Create a Rectangle using a non-frozed Brush.
Rectangle rectangleNonFrozen = new Rectangle();
rectangleNonFrozen.Fill = nonFrozenBrush;
// Create a Rectangle using a frozed Brush.
Rectangle rectangleFrozen = new Rectangle();
rectangleFrozen.Fill = frozenBrush;
}
Dim frozenBrush As Brush = New SolidColorBrush(Colors.Blue)
frozenBrush.Freeze()
Dim nonFrozenBrush As Brush = New SolidColorBrush(Colors.Blue)
For i As Integer = 0 To 9
' Create a Rectangle using a non-frozed Brush.
Dim rectangleNonFrozen As New Rectangle()
rectangleNonFrozen.Fill = nonFrozenBrush
' Create a Rectangle using a frozed Brush.
Dim rectangleFrozen As New Rectangle()
rectangleFrozen.Fill = frozenBrush
Next i
Zmodyfikowane uchwyty w niezamarzniętych zamrażalnych mogą utrzymywać przy życiu obiekty.
Delegat, który obiekt przekazuje do zdarzenia Changed obiektu Freezable, jest w rzeczywistości odwołaniem do tego obiektu. W związku z tym programy obsługi zdarzeń Changed mogą utrzymywać obiekty aktywne dłużej niż oczekiwano. Podczas czyszczenia obiektu zarejestrowanego w celu nasłuchiwania zdarzenia Changed obiektu Freezable niezbędne jest usunięcie tego delegata przed zwolnieniem obiektu.
WPF również wewnętrznie obsługuje zdarzenia Changed. Na przykład wszystkie właściwości zależności, które przyjmują Freezable jako wartość, będą automatycznie nasłuchiwać zdarzeń Changed. Właściwość Fill, która przyjmuje Brush, ilustruje tę koncepcję.
Brush myBrush = new SolidColorBrush(Colors.Red);
Rectangle myRectangle = new Rectangle();
myRectangle.Fill = myBrush;
Dim myBrush As Brush = New SolidColorBrush(Colors.Red)
Dim myRectangle As New Rectangle()
myRectangle.Fill = myBrush
Podczas przypisania myBrush
do myRectangle.Fill
, delegat wskazujący z powrotem na obiekt Rectangle zostanie dodany do zdarzenia Changed obiektu SolidColorBrush. Oznacza to, że następujący kod w rzeczywistości nie czyni myRect
kwalifikującym się do zbierania śmieci:
myRectangle = null;
myRectangle = Nothing
W tym przypadku myBrush
nadal utrzymuje myRectangle
przy życiu i wywoła go ponownie, gdy uruchomi swoje zdarzenie Changed. Należy pamiętać, że przypisanie myBrush
do właściwości Fill nowego Rectangle spowoduje po prostu dodanie kolejnej procedury obsługi zdarzeń do myBrush
.
Zalecanym sposobem wyczyszczenia tych typów obiektów jest usunięcie Brush z właściwości Fill, która z kolei usunie program obsługi zdarzeń Changed.
myRectangle.Fill = null;
myRectangle = null;
myRectangle.Fill = Nothing
myRectangle = Nothing
Wirtualizacja interfejsu użytkownika
WPF udostępnia również odmianę elementu StackPanel, który automatycznie wirtualizuje związaną z danymi zawartość podrzędną. W tym kontekście słowo wirtualizowanie odnosi się do techniki, za pomocą której podzbiór obiektów jest generowany z większej liczby elementów danych na podstawie tych, które są widoczne na ekranie. Jest to intensywne, zarówno pod względem pamięci, jak i procesora, generowanie dużej liczby elementów interfejsu użytkownika, gdy tylko kilka może znajdować się na ekranie w danym momencie. VirtualizingStackPanel (za pomocą funkcji udostępnianych przez VirtualizingPanel) oblicza widoczne elementy i współpracuje z ItemContainerGenerator z ItemsControl (np. ListBox lub ListView), aby tworzyć tylko elementy dla widocznych elementów.
W ramach optymalizacji wydajności obiekty wizualne dla tych elementów są generowane lub przechowywane jako aktywne tylko wtedy, gdy są widoczne na ekranie. Gdy nie znajdują się już w widoku kontrolki, obiekty wizualne mogą zostać usunięte. Nie należy tego mylić z wirtualizacją danych, gdzie obiekty danych nie są obecne w lokalnej kolekcji— raczej przesyłane strumieniowo w razie potrzeby.
W poniższej tabeli przedstawiono upływ czasu dodawania i renderowania 5000 elementów TextBlock do StackPanel i VirtualizingStackPanel. W tym scenariuszu miary reprezentują czas między dołączeniem ciągu tekstowego do właściwości ItemsSource obiektu ItemsControl do czasu wyświetlania ciągu tekstowego przez elementy panelu.
panelu hosta |
czas renderowania (ms) |
---|---|
StackPanel | 3210 |
VirtualizingStackPanel | 46 |
Zobacz też
.NET Desktop feedback