Udostępnij za pośrednictwem


Optymalizowanie wydajności: wiązanie danych

Powiązanie danych programu Windows Presentation Foundation (WPF) zapewnia prosty i spójny sposób prezentowania i interakcji z danymi przez aplikacje. Elementy mogą być powiązane z danymi z różnych źródeł danych w postaci obiektów CLR i XML.

Ten temat zawiera zalecenia dotyczące wydajności powiązania danych.

Jak rozwiązywane są odwołania w powiązaniach danych

Przed omówieniem problemów z wydajnością powiązania danych warto zapoznać się z tym, jak aparat powiązania danych programu Windows Presentation Foundation (WPF) rozwiązuje odwołania do obiektów w celu powiązania.

Źródłem powiązania danych programu Windows Presentation Foundation (WPF) może być dowolny obiekt CLR. Można powiązać z właściwościami, podwłaściwościami lub indeksatorami obiektu CLR. Referencje powiązań są rozwiązywane przy użyciu refleksji .NET Framework lub ICustomTypeDescriptor. Poniżej przedstawiono trzy metody rozpoznawania odwołań do obiektów dla powiązania.

Pierwsza metoda obejmuje użycie odbicia. W tym przypadku obiekt PropertyInfo służy do odnajdywania atrybutów właściwości i zapewnia dostęp do metadanych właściwości. W przypadku korzystania z interfejsu ICustomTypeDescriptor aparat powiązania danych używa tego interfejsu do uzyskiwania dostępu do wartości właściwości. Interfejs ICustomTypeDescriptor jest szczególnie przydatny w przypadkach, gdy obiekt nie ma statycznego zestawu właściwości.

Powiadomienia o zmianie właściwości mogą być udostępniane przez zaimplementowanie interfejsu INotifyPropertyChanged lub przy użyciu powiadomień o zmianie skojarzonych z TypeDescriptor. Jednak preferowaną strategią implementacji powiadomień o zmianie właściwości jest użycie INotifyPropertyChanged.

Jeśli obiekt źródłowy jest obiektem CLR, a właściwość źródłowa jest właściwością CLR, aparat powiązania danych programu Windows Presentation Foundation (WPF) musi najpierw użyć odbicia względem obiektu źródłowego, aby uzyskać TypeDescriptor, a następnie wykonać zapytanie o PropertyDescriptor. Ta sekwencja operacji odbicia jest potencjalnie bardzo czasochłonna z perspektywy wydajności.

Druga metoda rozwiązywania odwołań do obiektów obejmuje CLR obiekt źródłowy implementujący interfejs INotifyPropertyChanged oraz właściwość źródłową, która jest właściwością CLR. W tym przypadku silnik powiązywania danych bezpośrednio używa odbicia na typie źródłowym i pozyskuje wymaganą właściwość. Nadal nie jest to optymalna metoda, ale będzie kosztować mniej wymagań zestawu roboczego niż pierwsza metoda.

Trzecia metoda rozpoznawania odwołań do obiektów obejmuje obiekt źródłowy, który jest DependencyObject i właściwością źródłową, która jest DependencyProperty. W takim przypadku aparat powiązania danych nie musi używać odbicia. Zamiast tego aparat właściwości i aparat powiązania danych wspólnie rozpoznają odwołanie do właściwości niezależnie. Jest to optymalna metoda rozpoznawania odwołań do obiektów używanych do powiązania danych.

Tabela poniżej porównuje szybkość powiązania danych z właściwością Text dla tysiąca elementów TextBlock przy użyciu tych trzech metod.

powiązanie właściwości Text TextBlock czas powiązania (ms) Czas renderowania — w tym wiązanie (ms)
Do właściwości obiektu CLR 115 314
Do właściwości obiektu CLR, który implementuje INotifyPropertyChanged 115 305
Do DependencyPropertyDependencyObject. 90 263

Wiązanie z obiektami CLR dużych rozmiarów

Istnieje znaczący wpływ na wydajność, gdy dane są powiązane z pojedynczym obiektem CLR z tysiącami właściwości. Ten wpływ można zminimalizować, dzieląc pojedynczy obiekt na wiele obiektów CLR z mniejszą liczbą właściwości. W tabeli przedstawiono czasy powiązania i renderowania danych dla pojedynczego dużego obiektu CLR w porównaniu z wieloma mniejszymi obiektami.

Wiązanie danych dla 1000 obiektów TextBlock czas powiązania (ms) Czas renderowania — w tym wiązanie (ms)
Do obiektu CLR z 1000 właściwościami 950 1200
Do 1000 obiektów CLR z jedną właściwością 115 314

Wiązanie z elementem ItemsSource

Rozważ scenariusz, w którym masz obiekt CLRList<T> zawierający listę pracowników, które mają być wyświetlane w ListBox. Aby utworzyć korespondencję między tymi dwoma obiektami, należy przypisać listę pracowników do właściwości ItemsSource obiektu ListBox. Załóżmy jednak, że masz nowego pracownika dołączanego do grupy. Możesz pomyśleć, że aby wstawić tę nową osobę do powiązanych wartości ListBox, wystarczy dodać ją do listy pracowników i oczekiwać, że ta zmiana zostanie automatycznie rozpoznana przez system powiązań danych. To założenie okaże się fałszywe; w rzeczywistości zmiana nie zostanie odzwierciedlona w ListBox automatycznie. Dzieje się tak, ponieważ obiekt CLRList<T> nie zgłasza automatycznie zdarzenia zmiany kolekcji. Aby ListBox przetworzył zmiany, należy ponownie utworzyć listę pracowników i ponownie dołączyć ją do właściwości ItemsSourceListBox. Chociaż to rozwiązanie działa, wprowadza ogromny wpływ na wydajność. Za każdym razem, gdy ponownie przypiszesz ItemsSourceListBox do nowego obiektu, ListBox najpierw wyrzuca jego poprzednie elementy i ponownie generuje całą listę. Wpływ na wydajność staje się większy, jeśli twoje ListBox odnosi się do złożonego DataTemplate.

Bardzo wydajnym rozwiązaniem tego problemu jest utworzenie listy pracowników jako ObservableCollection<T>. Obiekt ObservableCollection<T> zgłasza powiadomienie o zmianie, które może odbierać aparat powiązania danych. Zdarzenie dodaje lub usuwa element z ItemsControl bez konieczności ponownego generowania całej listy.

W poniższej tabeli przedstawiono czas potrzebny na zaktualizowanie ListBox (z wyłączoną wirtualizacją interfejsu użytkownika) po dodaniu jednego elementu. Liczba w pierwszym wierszu reprezentuje czas, który upłynął, gdy obiekt CLRList<T> jest powiązany z elementem ListBoxItemsSource. Liczba w drugim wierszu reprezentuje czas, który upłynął, gdy ObservableCollection<T> jest powiązany z elementem ListBoxItemsSource. Zwróć uwagę na znaczne oszczędności czasu przy użyciu strategii powiązania danych ObservableCollection<T>.

powiązanie danych z ItemsSource czas aktualizacji dla 1 elementu (ms)
Do obiektu CLRList<T> 1656
Do ObservableCollection<T> 20

Wiąż IList z ItemsControl, a nie z IEnumerable.

Jeśli istnieje wybór między powiązaniem IList<T> lub IEnumerable z obiektem ItemsControl, wybierz obiekt IList<T>. Powiązanie IEnumerable z ItemsControl wymusza utworzenie obiektu otoki IList<T>, co oznacza, że wydajność jest obniżona przez niepotrzebne obciążenie drugiego obiektu.

Nie konwertuj obiektów CLR na XML tylko dla powiązania danych.

WPF umożliwia powiązanie danych z zawartością XML; jednak powiązanie danych z zawartością XML jest wolniejsze niż powiązanie danych z obiektami CLR. Nie konwertuj CLR danych obiektów na XML, jeśli jedynym celem jest powiązanie danych.

Zobacz też