Stromy ve WPF
V mnoha technologiích jsou prvky a komponenty uspořádány ve stromové struktuře, kde vývojáři přímo manipulují s uzly objektů ve stromu, aby ovlivnili vykreslování nebo chování aplikace. Windows Presentation Foundation (WPF) také používá několik metafor struktury stromové struktury k definování vztahů mezi prvky programu. Většina vývojářů WPF může vytvořit aplikaci v kódu nebo definovat části aplikace v XAML a přitom koncepčně přemýšlet o metaforou stromu objektů, ale budou používat konkrétní API nebo značky místo některých obecných API pro manipulaci se stromem objektů, podobně jako se používá v XML DOM. WPF zveřejňuje dvě pomocné třídy, které poskytují zobrazení metaforu stromu, LogicalTreeHelper a VisualTreeHelper. Termíny vizuální strom a logický strom se také používají v dokumentaci WPF, protože tyto stejné stromy jsou užitečné pro pochopení chování určitých klíčových funkcí WPF. Toto téma definuje, co vizuální strom a logický strom představují, popisuje, jak tyto stromy souvisejí s celkovým konceptem stromu objektů, a zavádí LogicalTreeHelper a VisualTreeHelpers.
Stromy ve WPF
Nejúplnější stromovou strukturou ve WPF je strom objektů. Pokud definujete stránku aplikace v jazyce XAML a pak načtete XAML, stromová struktura se vytvoří na základě vnořených vztahů prvků v značkovacím jazyce. Pokud definujete aplikaci nebo část aplikace v kódu, vytvoří se stromová struktura na základě toho, jak přiřadíte hodnoty vlastností, které implementují model obsahu pro daný objekt. Ve WPF existují dva způsoby, jak je kompletní strom objektů koncepční a lze ho hlásit do svého veřejného rozhraní API: jako logický strom a jako vizuální strom. Rozdíly mezi logickým stromem a vizuálním stromem nemusí být vždy důležité, ale někdy mohou způsobit problémy s určitými subsystémy WPF a ovlivnit volby, které uděláte ve značkování nebo kódu.
I když s logickým stromem nebo stromem vizuálu nemusíte vždy manipulovat přímo, pochopení konceptů interakce stromů je užitečné pro pochopení WPF jako technologie. Přemýšlení o WPF jako o metaforickém stromu je také klíčové pro pochopení toho, jak ve WPF funguje dědičnost vlastností a směrování událostí.
Poznámka
Protože strom objektů je spíše koncept než skutečné rozhraní API, dalším způsobem, jak si představit koncept, je jako objektový graf. V praxi existují vztahy mezi objekty při spuštění, kde metafora stromu přestane platit. Metafora stromu je však relevantní zejména ve uživatelském rozhraní definovaném pomocí XAML, takže většina dokumentace WPF při odkazování na tento obecný koncept použije termín strom objektů.
Logický strom
Ve WPF přidáte obsah do prvků uživatelského rozhraní nastavením vlastností objektů, které podporují tyto prvky. Například přidáte položky do ovládacího prvku ListBox manipulací s jeho vlastností Items. Tímto způsobem umístíte položky do ItemCollection, která je hodnotou vlastnosti Items. Podobně, pokud chcete přidat objekty do DockPanel, manipulujete hodnotou jeho vlastnosti Children. Tady přidáváte objekty do UIElementCollection. Uvedený příklad kódu naleznete v části Jak dynamicky přidat prvek.
Při umístění položek seznamu do ListBox nebo ovládacích prvků nebo jiných prvků uživatelského rozhraní v DockPanelpoužijete také Items a Children vlastnosti, a to explicitně nebo implicitně, jako v následujícím příkladu.
<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>
Pokud byste tento KÓD XAML zpracovávali jako XML v rámci modelu objektu dokumentu a pokud jste zahrnuli značky okomentované jako implicitní (což by bylo legální), výsledný strom XML DOM by zahrnoval elementy pro <ListBox.Items>
a další implicitní položky. Ale XAML nezpracovává tímto způsobem při čtení značek a zápisu do objektů, výsledný objektový graf neobsahuje doslova ListBox.Items
. Má nicméně vlastnost ListBox s názvem Items
, která obsahuje ItemCollection, a ItemCollection je inicializován, ale prázdný při zpracování XAML ListBox. Pak je každý podřízený prvek objektu, který existuje jako obsah pro ListBox, přidán do ItemCollection voláním analyzátoru ItemCollection.Add
. Tento příklad zpracování XAML do stromu objektů je zatím zdánlivě příkladem, ve kterém je vytvořený strom objektu v podstatě logickým stromem.
Logický strom však není celý graf objektů, který existuje pro uživatelské rozhraní vaší aplikace za běhu, a to ani při vyloučení implicitních syntaktických prvků XAML. Hlavním důvodem jsou zobrazení a šablony. Představte si například Button. Logický strom hlásí objekt Button a také jeho řetězec Content
. Ve stromu objektů za běhu má toto tlačítko ještě další funkce. Tlačítko se konkrétně zobrazuje jenom na obrazovce, protože byla použita určitá šablona ovládacího prvku Button. Vizuály, které pocházejí z použité šablony (například šablony definované Border tmavě šedé kolem tlačítka vizuálu), se v logickém stromu neoznamují, i když se během běhu díváte na logický strom (například zpracování vstupní události z viditelného uživatelského rozhraní a následné čtení logického stromu). Pokud chcete najít vizuální prvky šablony, museli byste místo toho prozkoumat vizuální strom.
Další informace o tom, jak syntaxe XAML mapuje na vytvořený graf objektů a implicitní syntaxi v XAML, viz Syntaxe XAML podrobně nebo XAML ve WPF.
Účel logického stromu
Logický strom existuje tak, aby modely obsahu mohly snadno iterovat nad jejich možnými podřízenými objekty a aby modely obsahu mohly být rozšiřitelné. Logický strom také poskytuje rozhraní pro určitá oznámení, například při načtení všech objektů v logickém stromu. Logický strom je v podstatě aproximace grafu objektů během běhu na úrovni frameworku, která vylučuje vizuály, ale je vhodná pro mnoho operací dotazování na složení vaší aplikace během běhu.
Kromě toho jsou statické i dynamické odkazy na prostředky vyřešeny tak, že se postupuje vzhůru logickým stromem a hledá se Resources kolekce na počátečním objektem žádající o zdroje, pak se pokračuje vzhůru stromem a kontroluje se každá FrameworkElement (nebo FrameworkContentElement) pro další Resources
hodnotu, která obsahuje ResourceDictionary, případně ten klíč. Logický strom se používá pro vyhledávání prostředků, když jsou k dispozici jak logický strom, tak vizuální strom. Další informace o slovníkech prostředků a vyhledávání najdete v tématu prostředky XAML.
Složení logického stromu
Logický strom je definován na úrovni architektury WPF, což znamená, že základní prvek WPF, který je pro operace logického stromu nejrelevantní, je FrameworkElement nebo FrameworkContentElement. Jak ale vidíte, jestli skutečně používáte rozhraní API LogicalTreeHelper, logický strom někdy obsahuje uzly, které nejsou FrameworkElement nebo FrameworkContentElement. Například logický strom hlásí hodnotu Text pro TextBlock, což je řetězec.
Přepsání logického stromu
Pokročilí autoři ovládacích prvků mohou upravit logický strom pomocí přepsání několika rozhraní API, která určují, jak obecný objekt nebo model obsahu přidává nebo odebírá objekty v rámci tohoto stromu. Pro příklad, jak přepsat logický strom, se podívejte na část Přepsání logického stromu.
Dědičnost hodnot vlastností
Dědičnost hodnot vlastností funguje prostřednictvím hybridního stromu. Skutečná metadata, která obsahují vlastnost Inherits, jež umožňuje dědičnost vlastností, jsou třídou úrovně WPF frameworkové FrameworkPropertyMetadata. Proto musí být nadřazený objekt, který obsahuje původní hodnotu, i podřízený objekt, který dědí danou hodnotu, FrameworkElement nebo FrameworkContentElementa oba musí být součástí některého logického stromu. U existujících vlastností WPF, které podporují dědičnost vlastností, může však dědičnost hodnoty vlastnosti přetrvávat prostřednictvím intervenujícího objektu, který není v logickém stromu. Hlavním cílem je zajistit, aby prvky šablony mohly využívat jakékoli zděděné hodnoty vlastností, které jsou nastaveny buď na instanci využívající šablonu, nebo na vyšších úrovních složení stránky, a tedy výše v logickém stromu. Aby dědičnost hodnot vlastností fungovala konzistentně napříč takovou hranicí, musí být děděná vlastnost registrována jako připojená vlastnost a pokud chcete definovat vlastní vlastnost závislosti s dědičným chováním, měli byste postupovat podle tohoto vzoru. Přesný strom použitý pro dědičnost vlastností nemůže být zcela předvídán utilitní metodou pomocné třídy, ani za běhu programu. Další informace naleznete v tématu Dědičnost hodnoty vlastnosti.
Vizuální strom
Kromě konceptu logického stromu existuje také koncept vizuálního stromu ve WPF. Vizuální strom popisuje strukturu vizuálních objektů, jak je reprezentováno Visual základní třídou. Při psaní šablony pro ovládací prvek definujete nebo znovu definujete strom vizuálu, který se vztahuje na tento ovládací prvek. Vizuální strom je také zajímavý pro vývojáře, kteří chtějí mít kontrolu nad kreslením na nižší úrovni z důvodů výkonu a optimalizace. Jednou z expozic vizuálního stromu v rámci konvenčního programování aplikací WPF je to, že trasy směrovaných událostí většinou cestují podél vizuálního stromu, nikoli podél logického stromu. Toto jemné chování směrovaných událostí nemusí být okamžitě zřejmé, pokud nejste autorem ovládacího prvku. Směrování událostí prostřednictvím vizuálního stromu umožňuje ovládacím prvkům, které provádějí kompozici na vizuální úrovni, aby zpracovávaly události nebo vytvářely nastavovače událostí.
Stromy, prvky obsahu a hostitelé obsahu
Prvky obsahu (třídy odvozené z ContentElement) nejsou součástí vizuálního stromu; nedědí z Visual a nemají vizuální reprezentaci. Aby se vůbec zobrazilo v uživatelském rozhraní, musí být ContentElement hostovaný v hostiteli obsahu, který je Visual i účastníkem logického stromu. Takový objekt je obvykle FrameworkElement. Můžete koncepčně určit, že hostitel obsahu je trochu jako prohlížeč obsahu, a zvolí, jak tento obsah zobrazit v oblasti obrazovky, kterou řídí hostitel. Když je obsah hostován, může se stát účastníkem určitých procesů stromu, které jsou obvykle přidruženy k vizuálnímu stromu. Obecně platí, že třída hostitelů FrameworkElement obsahuje kód implementace, který přidá všechny hostované ContentElement do trasy události přes podnode logického stromu obsahu, i když hostovaný obsah není součástí skutečného vizuálního stromu. Je to nezbytné, aby ContentElement mohl vyvolat směrovanou událost, která se směruje na libovolný jiný prvek než na něho samotného.
Procházení stromu
Třída LogicalTreeHelper poskytuje metody GetChildren, GetParenta FindLogicalNode pro procházení logického stromu. Ve většině případů byste neměli procházet logický strom existujících ovládacích prvků, protože tyto ovládací prvky téměř vždy zveřejňují jejich logické podřízené prvky jako vyhrazenou vlastnost kolekce, která podporuje přístup k kolekci, jako je Add
, indexer atd. Procházení stromem je především scénář, který využívají vývojáři ovládacích prvků, kteří se rozhodnou neodvozovat od zamýšlených vzorů, jako jsou ItemsControl nebo Panel, kde jsou již definovány vlastnosti sbírek, a chtějí poskytovat vlastní podporu pro tyto vlastnosti.
Vizuální strom také podporuje pomocnou třídu pro procházení vizuálního stromu VisualTreeHelper. Vizuální strom není přístupný tak pohodlně prostřednictvím vlastností specifických pro ovládací prvky, takže třída VisualTreeHelper je doporučený způsob, jak procházet vizuální strom, pokud je to nezbytné ve vašem programovacím scénáři. Další informace naleznete v přehledu grafického vykreslování WPF .
Poznámka
Někdy je nutné prozkoumat vizuální strom použité šablony. Při použití této techniky byste měli být opatrní. I když procházíte vizuální strom ovládacího prvku, ve kterém šablonu definujete, můžou uživatelé ovládacího prvku kdykoli změnit šablonu nastavením vlastnosti Template u instancí a dokonce i koncový uživatel může ovlivnit použitou šablonu změnou systémového motivu.
Trasy pro směrované události jako strom
Jak už bylo zmíněno dříve, trasa každé routované události se pohybuje po jediné předem určené cestě v rámci stromu, která je hybridem vizuální a logické reprezentace stromu. Trasa události může cestovat ve stromu nahoru nebo dolů v závislosti na tom, zda se jedná o směrovanou událost typu tunelování nebo bublání. Koncept trasy událostí nemá přímou podpůrnou třídu, kterou by bylo možné použít pro procházení trasy události nezávisle na vyvolání události, která skutečně trasuje. Existuje třída, která představuje trasu, EventRoute, ale metody této třídy jsou obecně určené pouze pro interní použití.
Slovníky a stromy zdrojů
Hledání ve slovníku prostředků pro všechny Resources
definované na stránce v podstatě prochází logickým stromem. Objekty, které nejsou v logickém stromu, mohou odkazovat na klíčové prostředky, ale pořadí vyhledávání prostředků začíná v okamžiku, kdy je tento objekt připojen k logickému stromu. Ve WPF můžou mít pouze uzly logického stromu vlastnost Resources
, která obsahuje ResourceDictionary, proto není výhodné procházet vizuální strom hledáním klíčových prostředků z ResourceDictionary.
Vyhledávání prostředků se ale může rozšířit i nad rámec bezprostředního logického stromu. U značek aplikací může vyhledávání prostředků pokračovat dál ve slovnících prostředků na úrovni aplikace, a poté pokračovat k podpoře motivů a systémovým hodnotám, které jsou odkazovány jako statické vlastnosti nebo klíče. Motivy mohou také odkazovat na systémové hodnoty mimo logický strom motivu, pokud jsou odkazy na prostředky dynamické. Další informace o slovnících prostředků a logice vyhledávání najdete v tématu Prostředky XAML.
Viz také
- Přehled Vstupu
- přehled vykreslování grafiky WPF
- Přehled směrovaných událostí
- inicializace prvků objektů, které nejsou ve stromu objektů
- Architektura WPF
.NET Desktop feedback