Dela via


Träd i WPF

I många tekniker organiseras element och komponenter i en trädstruktur där utvecklare direkt manipulerar objektnoderna i trädet för att påverka återgivningen eller beteendet för ett program. Windows Presentation Foundation (WPF) använder också flera trädstrukturmetaforer för att definiera relationer mellan programelement. WPF-utvecklare kan till största delen skapa ett program i kod eller definiera delar av programmet i XAML samtidigt som de tänker konceptuellt på objektträdsmetaforen, men anropar specifikt API eller använder specifik markering för att göra det i stället för något allmänt API för objektträdsmanipulering som du kan använda i XML DOM. WPF exponerar två hjälpklasser som ger en trädmetaforvy LogicalTreeHelper och VisualTreeHelper. Termen visuellt träd och logiskt träd används också i WPF-dokumentationen eftersom samma träd är användbara för att förstå beteendet för vissa viktiga WPF-funktioner. Det här avsnittet definierar vad det visuella trädet och det logiska trädet representerar, diskuterar hur sådana träd relaterar till ett övergripande objektträdskoncept och introducerar LogicalTreeHelper och VisualTreeHelpers.

Träd i WPF

Den mest kompletta trädstrukturen i WPF är objektträdet. Om du definierar en programsida i XAML och sedan läser in XAML skapas trädstrukturen baserat på kapslingsrelationerna för elementen i markeringen. Om du definierar ett program eller en del av programmet i kod skapas trädstrukturen baserat på hur du tilldelar egenskapsvärden för egenskaper som implementerar innehållsmodellen för ett visst objekt. I WPF finns det två sätt att konceptualisera hela objektträdet och kan rapporteras till dess offentliga API: som det logiska trädet och som det visuella trädet. Skillnaderna mellan logiskt träd och visuellt träd är inte alltid nödvändigtvis viktiga, men de kan ibland orsaka problem med vissa WPF-undersystem och påverka val som du gör i markering eller kod.

Även om du inte alltid manipulerar antingen det logiska trädet eller det visuella trädet direkt, är det användbart att förstå begreppen om hur träden interagerar för att förstå WPF som en teknik. Att tänka på WPF som en trädmetafor av något slag är också avgörande för att förstå hur egendomsarv och händelseroutning fungerar i WPF.

Anmärkning

Eftersom objektträdet är mer ett begrepp än ett faktiskt API är ett annat sätt att tänka på begreppet som ett objektdiagram. I praktiken finns det relationer mellan objekt vid körning där trädmetaforen bryter samman. Men särskilt med XAML-definierat användargränssnitt är trädmetaforen tillräckligt relevant för att de flesta WPF-dokumentationen ska använda termen objektträd när det refererar till det här allmänna konceptet.

Det logiska trädet

I WPF lägger du till innehåll i användargränssnittselement genom att ange egenskaper för de objekt som stöder dessa element. Du kan till exempel lägga till objekt i en ListBox kontroll genom att ändra dess Items egenskap. Genom att göra detta placerar du objekt i egenskapsvärdet ItemCollection som är Items. På samma sätt ändrar du dess DockPanel egenskapsvärde om du vill lägga till objekt i en Children. Här lägger du till objekt i UIElementCollection. Ett kodexempel finns i Så här lägger du till ett element dynamiskt.

I XAML (Extensible Application Markup Language), när du placerar listobjekt i ett ListBox eller kontroller eller andra gränssnittselement i ett DockPanel, använder du även egenskaperna Items och Children, antingen explicit eller implicit, som i följande exempel.

<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>

Om du skulle bearbeta XAML som XML under en dokumentobjektmodell, och om du hade inkluderat taggarna som kommenterats ut som implicita (vilket skulle ha varit lagligt), skulle det resulterande XML DOM-trädet ha inkluderat element för <ListBox.Items> och de andra implicita objekten. Men XAML fungerar inte på det sättet när du läser markeringen och skriver till objekt. Det resulterande objektdiagrammet innehåller inte bokstavligen ListBox.Items. Den har dock en ListBox egenskap med namnet Items som innehåller en ItemCollection, och att ItemCollection initieras men är tom när ListBox XAML bearbetas. Sedan läggs varje underordnat objektelement som finns som innehåll för ListBox till ItemCollection genom parser-anrop till ItemCollection.Add. Det här exemplet på att bearbeta XAML till ett objektträd är hittills till synes ett exempel där det skapade objektträdet i princip är det logiska trädet.

Det logiska trädet är dock inte hela objektdiagrammet som finns för ditt programgränssnitt vid körning, även med XAML-implicita syntaxobjekt uträknade. Den främsta orsaken till detta är visuella objekt och mallar. Tänk till exempel på Button. Det logiska trädet rapporterar objektet Button och även dess sträng Content. Men det finns mer i den här knappen i körningsobjektträdet. I synnerhet visas knappen bara på skärmen som den gör eftersom en specifik Button kontrollmall tillämpades. De visuella effekterna som kommer från en tillämpad mall (till exempel den malldefinierade Border mörkgrå färg runt den visuella knappen) rapporteras inte i det logiska trädet, även om du granskar det logiska trädet under körning (till exempel hantering av en inmatningshändelse från det synliga användargränssnittet; sedan läser det logiska trädet). Om du vill hitta de visuella mallobjekten måste du i stället undersöka det visuella trädet.

Mer information om hur XAML-syntax mappar till det skapade objektdiagrammet och implicit syntax i XAML finns i XAML-syntax i detalj eller XAML i WPF.

Syftet med det logiska trädet

Det logiska trädet finns så att innehållsmodeller enkelt kan iterera över sina möjliga underordnade objekt och så att innehållsmodeller kan vara utökningsbara. Dessutom tillhandahåller det logiska trädet ett ramverk för vissa meddelanden, till exempel när alla objekt i det logiska trädet läses in. I grund och botten är det logiska trädet en uppskattning av ett körningsobjektdiagram på ramverksnivå, vilket exkluderar visuella objekt, men är tillräckligt för många frågeåtgärder mot ditt eget körningstidsprograms sammansättning.

Dessutom löses både statiska och dynamiska resursreferenser genom att titta uppåt genom det logiska trädet för Resources samlingar på det första begärande objektet och sedan fortsätta upp det logiska trädet och kontrollera varje FrameworkElement (eller FrameworkContentElement) för ett annat Resources värde som innehåller en ResourceDictionary, eventuellt innehållande nyckeln. Det logiska trädet används för resurssökning när både det logiska trädet och det visuella trädet finns. Mer information om resursordlistor och uppslag finns i XAML-resurser.

Sammansättning av det logiska trädet

Det logiska trädet definieras på WPF-ramverksnivå, vilket innebär att WPF-baselementet som är mest relevant för logiska trädåtgärder är antingen FrameworkElement eller FrameworkContentElement. Men som du kan se om du faktiskt använder API:et LogicalTreeHelper innehåller det logiska trädet ibland noder som inte är antingen FrameworkElement eller FrameworkContentElement. Till exempel rapporterar det logiska trädet Text värdet för en TextBlock, som är en sträng.

Åsidosätta det logiska trädet

Avancerade kontrollförfattare kan åsidosätta det logiska trädet genom att åsidosätta flera API:er som definierar hur ett allmänt objekt eller en innehållsmodell lägger till eller tar bort objekt i det logiska trädet. Ett exempel på hur du åsidosätter det logiska trädet finns i Åsidosätt det logiska trädet.

Egenskapsvärdesarv

Arv av egenskapsvärde fungerar via ett hybridträd. De faktiska metadata som innehåller egenskapen Inherits som aktiverar egenskapsarv är klassen WPF-ramverksnivå FrameworkPropertyMetadata . Därför måste både det överordnade som innehåller det ursprungliga värdet och det underordnade objekt som ärver det värdet både vara FrameworkElement eller FrameworkContentElement, och båda måste ingå i något logiskt träd. För befintliga WPF-egenskaper som stöder egenskapsarv kan arv av egenskapsvärde dock fortsätta genom ett mellanliggande objekt som inte finns i det logiska trädet. Detta är främst relevant för att mallelement ska använda alla ärvda egenskapsvärden som anges antingen på den mallade instansen eller på ännu högre nivåer av sidnivåsammansättning och därmed högre i det logiska trädet. För att arv av egenskapsvärde ska fungera konsekvent över en sådan gräns måste den ärvande egenskapen registreras som en bifogad egenskap, och du bör följa det här mönstret om du tänker definiera en anpassad beroendeegenskap med egenskapsarvbeteende. Det exakta träd som används för egenskapsarv i programmering kan inte helt förutses av en hjälpmetod i en klass, inte ens vid körningstid. Mer information finns i Arv av egenskapsvärde.

Det visuella trädet

Förutom konceptet med det logiska trädet finns det också begreppet visuellt träd i WPF. Det visuella trädet beskriver strukturen för visuella objekt, som representeras av basklassen Visual . När du skriver en mall för en kontroll definierar eller omdefinierar du det visuella träd som gäller för den kontrollen. Det visuella trädet är också av intresse för utvecklare som vill ha lägre kontroll över ritning av prestanda- och optimeringsskäl. En exponering av det visuella trädet som en del av konventionell WPF-programprogramprogrammering är att händelsevägar för en dirigerad händelse mestadels färdas längs det visuella trädet, inte det logiska trädet. Den här subtiliteten i beteendet för dirigerade händelser kanske inte är omedelbart uppenbar om du inte är en kontrollutvecklare. Routning av händelser via det visuella trädet möjliggör kontroller som implementerar komposition på visuell nivå för att hantera händelser eller skapa händelsesättare.

Träd, innehållselement och värdar för innehåll

Innehållselement (klasser som härleds från ContentElement) är inte en del av det visuella trädet. De ärver inte från Visual och har ingen visuell representation. För att visas i ett användargränssnitt överhuvudtaget måste en ContentElement finnas i en innehållsvärd som är både en Visual och en logisk träddeltagare. Vanligtvis är ett sådant objekt en FrameworkElement. Du kan föreställa dig att innehållsvärden är som en "webbläsare" för innehållet och väljer hur innehållet ska visas i den skärmregion som värden kontrollerar. När innehållet är värdbaserat kan det bli en deltagare i vissa trädprocesser som normalt är förknippade med det visuella trädet. I allmänhet innehåller värdklassen FrameworkElement implementeringskod som lägger till alla värdbaserade ContentElement i händelserutten genom innehållets logiska träd, även om det värdbaserade innehållet inte är en del av det sanna visuella trädet. Detta är nödvändigt så att en ContentElement kan hämta en dirigerad händelse som dirigerar till något annat element än sig självt.

Trädbläddning

Klassen LogicalTreeHelper innehåller GetChildrenmetoderna , GetParentoch FindLogicalNode för logisk trädbläddering. I de flesta fall bör du inte behöva gå igenom det logiska trädet för befintliga kontroller, eftersom dessa kontroller nästan alltid exponerar sina logiska underordnade element som en dedikerad samlingsegenskap som stöder samlingsåtkomst, Addtill exempel , en indexerare och så vidare. Trädbläddering är främst ett scenario som används av kontrollförfattare som väljer att inte härleda från avsedda kontrollmönster, till exempel ItemsControl eller Panel där samlingsegenskaper redan har definierats, och som har för avsikt att ge stöd för sin egen samlingsegenskap.

Det visuella trädet har också stöd för en hjälpklass för visuell trädbläddering, VisualTreeHelper. Det visuella trädet exponeras inte lika bekvämt genom kontrollspecifika egenskaper, så klassen VisualTreeHelper är det rekommenderade sättet att navigera genom det visuella trädet om det behövs för ditt programmeringsscenario. För mer information, se Översikt över WPF-grafikrendering.

Anmärkning

Ibland är det nödvändigt att undersöka det visuella trädet i en tillämpad mall. Du bör vara försiktig när du använder den här tekniken. Även om du går igenom ett visuellt träd för en kontroll där du definierar mallen kan användarna av din kontroll alltid ändra mallen genom att ange Template egenskapen på instanser, och även slutanvändaren kan påverka den tillämpade mallen genom att ändra systemtemat.

Vägar för routade händelser som ett "träd"

Som tidigare nämnts färdas rutten för en viss routerad händelse längs en enda och fördefinierad sökväg för ett träd som är en hybrid av de visuella och logiska trädrepresentationerna. Händelsevägen kan färdas antingen uppåt eller nedåt i trädet beroende på om det är en tunnlande eller bubblande händelse. Event Route-konceptet har inte en direkt stödjande hjälpklass som kan användas för att "gå" händelsevägen oberoende av att skapa en händelse som faktiskt dirigeras. Det finns en klass som representerar vägen, EventRoute, men metoderna för den klassen är vanligtvis endast för internt bruk.

Resursordlistor och träd

Resursordlistesökning för alla Resources som definierats på en sida passerar i princip det logiska trädet. Objekt som inte finns i det logiska trädet kan referera till nyckelade resurser, men resurssökningssekvensen börjar vid den punkt där objektet är anslutet till det logiska trädet. I WPF kan endast logiska trädnoder ha en Resources egenskap som innehåller en ResourceDictionary. Därför finns det ingen fördel med att gå igenom det visuella trädet och leta efter nyckelade resurser från en ResourceDictionary.

Resurssökning kan dock också sträcka sig bortom det omedelbara logiska trädet. För programmarkering kan resurssökningen fortsätta vidare till resursordlistor på programnivå och sedan använda temastöd och systemvärden som refereras till som statiska egenskaper eller nycklar. Själva teman kan också referera till systemvärden utanför temats logiska träd om resursreferenserna är dynamiska. Mer information om resursordlistor och uppslagslogik finns i XAML-resurser.

Se även