Drzewa w WPF

W wielu technologiach elementy i składniki są zorganizowane w strukturze drzewa, w której deweloperzy bezpośrednio manipulują węzłami obiektów w drzewie, aby wpłynąć na renderowanie lub zachowanie aplikacji. Program Windows Presentation Foundation (WPF) używa również kilku metafor struktury drzewa do definiowania relacji między elementami programu. W większości deweloperzy WPF mogą utworzyć aplikację w kodzie lub zdefiniować fragmenty aplikacji w języku XAML, myśląc koncepcyjnie o metaforze drzewa obiektów, ale będą wywoływać konkretny interfejs API lub używać określonego znaczników, aby to zrobić, zamiast ogólnego interfejsu API manipulowania drzewami obiektów, takiego jak można użyć w modelu DOM XML. WPF uwidacznia dwie klasy pomocnicze, które zapewniają widok metafory drzewa, LogicalTreeHelper i VisualTreeHelper. Terminy drzewa wizualnego i drzewa logicznego są również używane w dokumentacji WPF, ponieważ te same drzewa są przydatne do zrozumienia zachowania niektórych kluczowych funkcji WPF. W tym temacie opisano, co reprezentuje drzewo wizualne i drzewo logiczne, omawia sposób, w jaki takie drzewa odnoszą się do ogólnej koncepcji drzewa obiektów oraz wprowadza LogicalTreeHelper i VisualTreeHelpers.

Drzewa w WPF

Najbardziej kompletną strukturą drzewa w WPF jest drzewo obiektów. Jeśli zdefiniujesz stronę aplikacji w języku XAML, a następnie załadujesz kod XAML, struktura drzewa zostanie utworzona na podstawie relacji zagnieżdżania elementów w znacznikach. Jeśli zdefiniujesz aplikację lub część aplikacji w kodzie, struktura drzewa zostanie utworzona na podstawie sposobu przypisywania wartości właściwości dla właściwości implementujących con tryb namiotu l dla danego obiektu. W WPF istnieją dwa sposoby, że kompletne drzewo obiektów jest koncepcyjne i może być zgłaszane do publicznego interfejsu API: jako drzewo logiczne i jako drzewo wizualne. Różnice między drzewem logicznym a drzewem wizualnym nie zawsze są ważne, ale czasami mogą powodować problemy z niektórymi podsystemami WPF i wpływać na wybory dokonane w znaczników lub kodzie.

Mimo że nie zawsze manipulujesz drzewem logicznym lub drzewem wizualnym bezpośrednio, zrozumienie koncepcji interakcji drzew jest przydatne do zrozumienia platformy WPF jako technologii. Myślenie O WPF jako metafory drzewa jest również kluczowe dla zrozumienia, jak dziedziczenie właściwości i routing zdarzeń działa w WPF.

Uwaga

Ponieważ drzewo obiektów jest bardziej pojęciem niż rzeczywistym interfejsem API, innym sposobem myślenia o koncepcji jest graf obiektu. W praktyce istnieją relacje między obiektami w czasie wykonywania, w którym metafora drzewa się rozpada. Niemniej jednak, szczególnie w przypadku interfejsu użytkownika zdefiniowanego przez język XAML, metafora drzewa jest wystarczająco odpowiednia, że większość dokumentacji WPF będzie używać terminu drzewo obiektów podczas odwoływania się do tej ogólnej koncepcji.

Drzewo logiczne

W WPF dodajesz zawartość do elementów interfejsu użytkownika, ustawiając właściwości obiektów, które z powrotem te elementy. Możesz na przykład dodać elementy do kontrolki ListBox , manipulując jej Items właściwością. W ten sposób umieszczasz elementy w ItemCollectionItems wartości właściwości . Podobnie, aby dodać obiekty do DockPanelobiektu , manipulujesz jego Children wartością właściwości. W tym miejscu dodasz obiekty do obiektu UIElementCollection. Aby zapoznać się z przykładem kodu, zobacz Instrukcje: dynamiczne dodawanie elementu.

W rozszerzalnym języku znaczników aplikacji (XAML) podczas umieszczania elementów listy w kontrolkach ListBox lub lub innych elementach interfejsu użytkownika w elemecie DockPanel, należy również użyć Items właściwości i Children , jawnie lub niejawnie, jak w poniższym przykładzie.

<DockPanel
  Name="ParentElement"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  >
  <!--implicit: <DockPanel.Children>-->
  <ListBox DockPanel.Dock="Top">
    <!--implicit: <ListBox.Items>-->
    <ListBoxItem>
      <TextBlock>Dog</TextBlock>
    </ListBoxItem>
    <ListBoxItem>
      <TextBlock>Cat</TextBlock>
    </ListBoxItem>
    <ListBoxItem>
      <TextBlock>Fish</TextBlock>
    </ListBoxItem>
  <!--implicit: </ListBox.Items>-->
  </ListBox>
  <Button Height="20" Width="100" DockPanel.Dock="Top">Buy a Pet</Button>
  <!--implicit: </DockPanel.Children>-->
</DockPanel>

Jeśli chcesz przetworzyć ten kod XAML jako kod XML w modelu obiektu dokumentu, a jeśli tagi zostały uwzględnione jako niejawne (co byłoby legalne), wynikowe drzewo DOM XML zawierałoby elementy dla <ListBox.Items> i innych niejawnych elementów. Jednak kod XAML nie przetwarza w ten sposób podczas odczytywania znaczników i zapisywania w obiektach, wynikowy graf obiektu nie zawiera ListBox.Itemsdosłownie elementu . Ma jednak ListBox właściwość o nazwie Items , która zawiera ItemCollectionelement i jest ItemCollection inicjowana, ale pusta, gdy ListBox jest przetwarzany kod XAML. Następnie każdy element obiektu podrzędnego, który istnieje jako zawartość obiektu ListBox , jest dodawany do ItemCollection elementu przez wywołania analizatora do metody ItemCollection.Add. Ten przykład przetwarzania kodu XAML w drzewie obiektów jest do tej pory pozornie przykładem, w którym utworzone drzewo obiektów jest w zasadzie drzewem logicznym.

Jednak drzewo logiczne nie jest całym grafem obiektów, który istnieje dla interfejsu użytkownika aplikacji w czasie wykonywania, nawet w przypadku niejawnych elementów składni XAML uwzględnianych. Głównym powodem tego są wizualizacje i szablony. Rozważmy na przykład wartość Button. Drzewo logiczne zgłasza Button obiekt, a także jego ciąg Content. Jednak w drzewie obiektów czasu wykonywania jest więcej. W szczególności przycisk jest wyświetlany tylko na ekranie, ponieważ zastosowano określony Button szablon kontrolki. Wizualizacje pochodzące z zastosowanego szablonu (na przykład zdefiniowane Border przez szablon ciemnoszary wokół przycisku wizualizacji) nie są raportowane w drzewie logicznym, nawet jeśli patrzysz na drzewo logiczne w czasie wykonywania (np. obsługa zdarzenia wejściowego z widocznego interfejsu użytkownika, a następnie odczytywanie drzewa logicznego). Aby znaleźć wizualizacje szablonu, należy zamiast tego zbadać drzewo wizualne.

Aby uzyskać więcej informacji na temat mapowania składni XAML na utworzony graf obiektu i niejawnej składni w języku XAML, zobacz Składnia XAML w szczegółach lub XAML w WPF.

Przeznaczenie drzewa logicznego

Drzewo logiczne istnieje tak, aby listy con tryb namiotu ls mogły łatwo iterować nad ich możliwymi obiektami podrzędnymi, dzięki czemu można rozszerzać kon tryb namiotu ls. Ponadto drzewo logiczne zapewnia strukturę dla niektórych powiadomień, takich jak podczas ładowania wszystkich obiektów w drzewie logicznym. Zasadniczo drzewo logiczne jest przybliżeniem wykresu obiektu czasu wykonywania na poziomie struktury, który wyklucza wizualizacje, ale jest odpowiedni dla wielu operacji wykonywania względem kompozycji własnej aplikacji czasu wykonywania.

Ponadto zarówno odwołania do zasobów statycznych, jak i dynamicznych są rozwiązywane przez wyszukanie w górę drzewa logicznego kolekcji w Resources początkowym obiekcie żądającym, a następnie kontynuowanie drzewa logicznego i sprawdzanie każdej FrameworkElement wartości (lub FrameworkContentElement) dla innej Resources wartości zawierającej ResourceDictionary, prawdopodobnie zawierającej ten klucz. Drzewo logiczne jest używane do wyszukiwania zasobów, gdy jest obecne zarówno drzewo logiczne, jak i drzewo wizualne. Aby uzyskać więcej informacji na temat słowników zasobów i odnośników, zobacz Zasoby XAML.

Kompozycja drzewa logicznego

Drzewo logiczne jest definiowane na poziomie struktury WPF, co oznacza, że element podstawowy WPF, który jest najbardziej odpowiedni dla operacji drzewa logicznego, jest FrameworkElement albo .FrameworkContentElement Jednak, jak widać, jeśli faktycznie używasz interfejsu LogicalTreeHelper API, drzewo logiczne czasami zawiera węzły, które nie FrameworkElement są albo .FrameworkContentElement Na przykład drzewo logiczne zgłasza Text wartość TextBlock, która jest ciągiem.

Zastępowanie drzewa logicznego

Zaawansowani autorzy kontrolek mogą zastąpić drzewo logiczne, przesłaniając kilka interfejsów API definiujących sposób dodawania lub usuwania obiektów w drzewie logicznym przez obiekt ogólny lub con tryb namiotu l. Aby zapoznać się z przykładem przesłonięcia drzewa logicznego, zobacz Zastępowanie drzewa logicznego.

Przejęcie wartości właściwości

Dziedziczenie wartości właściwości działa za pośrednictwem drzewa hybrydowego. Rzeczywiste metadane zawierające właściwość, która Inherits umożliwia dziedziczenie właściwości, to klasa na poziomie FrameworkPropertyMetadata platformy WPF. W związku z tym zarówno element nadrzędny, który przechowuje oryginalną wartość, jak i obiekt podrzędny, który dziedziczy wartość, musi być FrameworkElement zarówno wartością , jak i FrameworkContentElement, i musi być częścią pewnego drzewa logicznego. Jednak w przypadku istniejących właściwości WPF, które obsługują dziedziczenie właściwości, dziedziczenie wartości właściwości jest w stanie utrwalać przez pośredniczącego obiektu, który nie znajduje się w drzewie logicznym. Jest to istotne głównie w przypadku używania elementów szablonu wszystkich dziedziczonych wartości właściwości ustawionych w wystąpieniu, które jest szablon, lub na jeszcze wyższych poziomach kompozycji na poziomie strony, a tym samym wyższych w drzewie logicznym. Aby dziedziczenie wartości właściwości działało spójnie przez taką granicę, właściwość dziedzicząca musi być zarejestrowana jako dołączona właściwość i należy postępować zgodnie z tym wzorcem, jeśli zamierzasz zdefiniować niestandardową właściwość zależności z zachowaniem dziedziczenia właściwości. Dokładne drzewo używane do dziedziczenia właściwości nie może być całkowicie przewidywane przez metodę narzędzia klasy pomocniczej, nawet w czasie wykonywania. Aby uzyskać więcej informacji, zobacz Dziedziczenie wartości właściwości.

Drzewo wizualne

Oprócz koncepcji drzewa logicznego istnieje również koncepcja drzewa wizualnego w WPF. Drzewo wizualizacji opisuje strukturę obiektów wizualnych reprezentowanych przez klasę bazową Visual . Podczas pisania szablonu dla kontrolki definiujesz lub ponownie definiujesz drzewo wizualne, które ma zastosowanie do tej kontrolki. Drzewo wizualne jest również interesujące dla deweloperów, którzy chcą kontrolować rysunek niższego poziomu ze względu na wydajność i optymalizację. Jedną z ekspozycji drzewa wizualnego w ramach konwencjonalnego programowania aplikacji WPF jest to, że trasy zdarzeń dla zdarzenia kierowanego głównie podróżują wzdłuż drzewa wizualnego, a nie drzewa logicznego. Ta subtelność zachowania zdarzeń kierowanych może nie być natychmiast widoczna, chyba że jesteś autorem kontrolki. Kierowanie zdarzeń za pomocą drzewa wizualnego umożliwia kontrolkom implementujące kompozycję na poziomie wizualizacji w celu obsługi zdarzeń lub tworzenia zestawów zdarzeń.

Drzewa, elementy zawartości i hosty zawartości

Elementy zawartości (klasy pochodzące z ContentElement) nie są częścią drzewa wizualnego; nie dziedziczą Visual i nie mają reprezentacji wizualnej. Aby w ogóle pojawiać się w interfejsie użytkownika, ContentElement musi być hostowany na hoście zawartości, który jest zarówno uczestnikiem drzewa logicznego, jak Visual i . Zazwyczaj taki obiekt jest obiektem FrameworkElement. Można koncepcyjnie określić, że host zawartości jest nieco jak "przeglądarka" dla zawartości i wybiera sposób wyświetlania tej zawartości w regionie ekranu, który kontroluje host. Gdy zawartość jest hostowana, zawartość może być uczestnikiem niektórych procesów drzewa, które są zwykle skojarzone z drzewem wizualnym. Ogólnie rzecz biorąc, klasa hosta zawiera kod implementacji, FrameworkElement który dodaje dowolne hostowane ContentElement do trasy zdarzeń przez podwęźle drzewa logicznego zawartości, mimo że hostowana zawartość nie jest częścią prawdziwego drzewa wizualnego. Jest to konieczne, aby możliwe ContentElement było źródło zdarzenia kierowanego, które kieruje do dowolnego elementu innego niż sam.

Przechodzenie drzewa

Klasa LogicalTreeHelper udostępnia GetChildrenmetody , GetParenti FindLogicalNode dla przechodzenia drzewa logicznego. W większości przypadków nie należy przechodzić przez logiczne drzewo istniejących kontrolek, ponieważ te kontrolki prawie zawsze uwidaczniają ich logiczne elementy podrzędne jako dedykowaną właściwość kolekcji, która obsługuje dostęp do kolekcji, taki jak Add, indeksator itd. Przechodzenie drzewa jest głównie scenariuszem, który jest używany przez autorów kontrolek, którzy zdecydują się nie pochodzić z zamierzonych wzorców kontrolek, takich jak ItemsControl lub Panel gdzie właściwości kolekcji są już zdefiniowane, i którzy zamierzają zapewnić własną obsługę właściwości kolekcji.

Drzewo wizualne obsługuje również klasę pomocnika dla przechodzenia drzewa wizualnego, VisualTreeHelper. Drzewo wizualizacji nie jest uwidocznione jako wygodne za pomocą właściwości specyficznych dla kontrolki, dlatego VisualTreeHelper klasa jest zalecanym sposobem przechodzenia przez drzewo wizualne, jeśli jest to konieczne w scenariuszu programowania. Aby uzyskać więcej informacji, zobacz Omówienie renderowania grafiki na platformie WPF.

Uwaga

Czasami konieczne jest sprawdzenie drzewa wizualnego zastosowanego szablonu. Podczas korzystania z tej techniki należy zachować ostrożność. Nawet jeśli przechodzisz przez drzewo wizualne dla kontrolki, w której definiujesz szablon, użytkownicy kontrolki zawsze mogą zmienić szablon, ustawiając Template właściwość w wystąpieniach, a nawet użytkownik końcowy może wpłynąć na zastosowany szablon, zmieniając motyw systemowy.

Trasy dla zdarzeń kierowanych jako "drzewo"

Jak wspomniano wcześniej, trasa dowolnego kierowanego zdarzenia jest przenoszona wzdłuż pojedynczej i wstępnie określonej ścieżki drzewa, który jest hybrydą reprezentacji wizualizacji i drzewa logicznego. Trasa zdarzenia może podróżować w kierunku w górę lub w dół w drzewie w zależności od tego, czy jest to tunelowanie, czy bubbling kierowane zdarzenie. Koncepcja trasy zdarzeń nie ma bezpośrednio pomocniczej klasy pomocniczej, która może służyć do "chodzenia" trasy zdarzenia niezależnie od zgłaszania zdarzenia, które faktycznie kieruje. Istnieje klasa reprezentująca trasę , EventRouteale metody tej klasy są zwykle przeznaczone tylko do użytku wewnętrznego.

Słowniki zasobów i drzewa

Wyszukiwanie słownika zasobów dla wszystkich Resources zdefiniowanych na stronie przechodzi w zasadzie drzewo logiczne. Obiekty, które nie znajdują się w drzewie logicznym, mogą odwoływać się do zasobów kluczy, ale sekwencja wyszukiwania zasobów rozpoczyna się w momencie, w którym ten obiekt jest połączony z drzewem logicznym. W WPF tylko węzły drzewa logicznego mogą mieć Resources właściwość zawierającą ResourceDictionaryelement , dlatego nie ma korzyści podczas przechodzenia drzewa wizualnego w poszukiwaniu kluczowych zasobów z elementu ResourceDictionary.

Jednak wyszukiwanie zasobów może również wykraczać poza bezpośrednie drzewo logiczne. W przypadku znaczników aplikacji wyszukiwanie zasobów może następnie przejść do słowników zasobów na poziomie aplikacji, a następnie do obsługi motywu i wartości systemowych, które są przywoływały jako właściwości statyczne lub klucze. Motywy mogą również odwoływać się do wartości systemowych poza drzewem logicznym motywu, jeśli odwołania do zasobów są dynamiczne. Aby uzyskać więcej informacji na temat słowników zasobów i logiki odnośników, zobacz Zasoby XAML.

Zobacz też