Деревья в WPFTrees in WPF

Во многих технологиях элементы и компоненты организованы в форме древовидной структуры, и разработчики могут напрямую управлять узлами объекта в дереве, чтобы повлиять на визуализацию или поведение приложения.In many technologies, elements and components are organized in a tree structure where developers directly manipulate the object nodes in the tree to affect the rendering or behavior of an application. В Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) часто используется несколько метафор древовидных структур, чтобы определить отношения между программными элементами.Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) also uses several tree structure metaphors to define relationships between program elements. Для большей части WPF разработчики могут создать приложение в коде или определить части приложения в XAML и при этом концептуально думать о метафоре дерева объектов, но для этого им потребуется вызвать определенный интерфейс API или использовать конкретную разметку, а не интерфейс API управления деревом некоторых общих объектов, какой можно использовать в XML DOM.For the most part WPF developers can create an application in code or define portions of the application in XAML while thinking conceptually about the object tree metaphor, but will be calling specific API or using specific markup to do so rather than some general object tree manipulation API such as you might use in XML DOM. WPF предоставляет два вспомогательных класса, которые обеспечивают представление метафоры дерева LogicalTreeHelper и VisualTreeHelper.WPF exposes two helper classes that provide a tree metaphor view, LogicalTreeHelper and VisualTreeHelper. Термины "логическое дерево" и "визуальное дерево" также используются в документации WPF, поскольку эти же деревья помогают понять поведение определенных ключевых функций WPF.The terms visual tree and logical tree are also used in the WPF documentation because these same trees are useful for understanding the behavior of certain key WPF features. В этом разделе определены понятия визуального дерева и логического дерева, обсуждается, как эти деревья связаны с общим понятием дерева объектов, и представлены классы LogicalTreeHelper и VisualTreeHelpers.This topic defines what the visual tree and logical tree represent, discusses how such trees relate to an overall object tree concept, and introduces LogicalTreeHelper and VisualTreeHelpers.

Деревья в WPFTrees in WPF

Самой полной древовидной структурой в WPFWPF является дерево объектов.The most complete tree structure in WPFWPF is the object tree. При определении страницы приложения в XAMLXAML и последующей загрузке XAMLXAML древовидная структура создается на основе отношений вложенности элементов в разметке.If you define an application page in XAMLXAML and then load the XAMLXAML, the tree structure is created based on the nesting relationships of the elements in the markup. При определении приложения или части приложения в коде древовидная структура создается в зависимости от того, как присваиваются значения свойствам, которые реализуют модель содержимого для данного объекта.If you define an application or a portion of the application in code, then the tree structure is created based on how you assign property values for properties that implement the content model for a given object. В Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) существует два способа концептуализации и передачи в открытый интерфейс API полного дерева объектов: в виде логического дерева и в виде визуального дерева.In Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF), there are two ways that the complete object tree is conceptualized and can be reported to its public API: as the logical tree and as the visual tree. Различия между логическими деревьями и визуальными деревьями не всегда важны, но иногда они могут вызвать проблемы с некоторыми подсистемами WPFWPF и повлиять на изменения, внесенные в разметку или код.The distinctions between logical tree and visual tree are not always necessarily important, but they can occasionally cause issues with certain WPFWPF subsystems and affect choices you make in markup or code.

Несмотря на то, что управление логическим деревом или визуальным деревом не всегда происходит напрямую, понимание концепций взаимодействия деревьев позволяет понять WPF как технологию.Even though you do not always manipulate either the logical tree or the visual tree directly, understanding the concepts of how the trees interact is useful for understanding WPF as a technology. Рассмотрение WPF как некоторой метафоры дерева также важно для понимания того, как в WPFWPF осуществляется наследование свойств и маршрутизация событий.Thinking of WPF as a tree metaphor of some kind is also crucial to understanding how property inheritance and event routing work in WPFWPF.

Примечание

Поскольку дерево объектов — это более широкое понятие, чем фактический интерфейс API, еще одним способом представить себе концепцию является граф объекта.Because the object tree is more of a concept than an actual API, another way to think of the concept is as an object graph. На практике отношения между объектами можно наблюдать во время выполнения, когда метафора дерева разделяется.In practice, there are relationships between objects at run time where the tree metaphor will break down. Тем не менее, особенно в пользовательском интерфейсе, определенном XAML, метафора дерева достаточно релевантна, и в большей части документации WPF используется термин "дерево объекта" при ссылке на это общее понятие.Nevertheless, particularly with XAML-defined UI, the tree metaphor is relevant enough that most WPF documentation will use the term object tree when referencing this general concept.

Логическое деревоThe Logical Tree

В WPFWPF содержимое добавляется в элементы пользовательского интерфейса путем задания свойств объектов, которые поддерживают эти элементы.In WPFWPF, you add content to UI elements by setting properties of the objects that back those elements. Например, вы добавляете элементы в ListBox элемент управления, управляя его Items свойством.For example, you add items to a ListBox control by manipulating its Items property. Таким образом, вы помещаете в объект ItemCollection элементы, которые Items являются значением свойства.By doing this, you are placing items into the ItemCollection that is the Items property value. Аналогичным образом, чтобы добавить объекты DockPanelв, вы управляете его Children значением свойства.Similarly, to add objects to a DockPanel, you manipulate its Children property value. Здесь вы добавляете объекты UIElementCollectionв.Here, you are adding objects to the UIElementCollection. Пример кода см. в разделе как Добавьте элемент динамически.For a code example, see How to: Add an Element Dynamically.

В Язык XAMLExtensible Application Markup Language (XAML)при помещении элементов списка ListBox в элементы управления или или DockPanelдругие элементы пользовательского интерфейса в можно также использовать Items свойства и Children явно или неявно, как показано в следующем примере.In Язык XAMLExtensible Application Markup Language (XAML), when you place list items in a ListBox or controls or other UI elements in a DockPanel, you also use the Items and Children properties, either explicitly or implicitly, as in the following example.

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

Если бы этот XAML обрабатывался как XML в объектной модели документов, и если бы были включены теги, закомментированные как неявные (были бы допустимы), полученное дерево XML DOM включало бы элементы для <ListBox.Items> и другие неявные элементы.If you were to process this XAML as XML under a document object model, and if you had included the tags commented out as implicit (which would have been legal), then the resulting XML DOM tree would have included elements for <ListBox.Items> and the other implicit items. Но XAML не выполняет такую обработку при чтении разметки и записи в объекты, полученный граф объекта не включает ListBox.Items в буквальном смысле.But XAML does not process that way when you read the markup and write to objects, the resulting object graph does not literally include ListBox.Items. Однако у ListBox него есть свойство с именем Items , которое содержит объект ItemCollection, ListBox который ItemCollection инициализируется, но является пустым при обработке XAML.It does however have a ListBox property named Items that contains a ItemCollection, and that ItemCollection is initialized but empty when the ListBox XAML is processed. Затем каждый дочерний элемент объекта, существующий как содержимое для ListBox , добавляется ItemCollection в средство синтаксического анализа для ItemCollection.Addвызовов.Then, each child object element that exists as content for the ListBox is added to the ItemCollection by parser calls to ItemCollection.Add. Этот пример обработки XAML в дереве объектов на первый взгляд похож на пример, в котором созданное дерево объектов, по сути, является логическим деревом.This example of processing XAML into an object tree is so far seemingly an example where the created object tree is basically the logical tree.

Однако логическое дерево не является полным графом объекта, существующим в пользовательском интерфейсе приложения во время выполнения, даже когда неявные элементы синтаксиса XAML факторизованы. Основная причина этого — визуальные элементы и шаблоны.However, the logical tree is not the entire object graph that exists for your application UI at run time, even with the XAML implicit syntax items factored out. The main reason for this is visuals and templates. Например, рассмотрим Button.For example, consider the Button. Логическое дерево сообщает об Button объекте, а также его строке. ContentThe logical tree reports the Button object and also its string Content. Но в дереве объектов времени выполнения имеется больше сведений об этой кнопке.But there is more to this button in the run-time object tree. В частности, кнопка отображается на экране только в том виде, в котором был применен Button определенный шаблон элемента управления.In particular, the button only appears on screen the way it does because a specific Button control template was applied. Визуальные элементы, полученные из примененного шаблона (например, определенного Border шаблоном темно-серого вокруг визуальной кнопки), не передаются в логическое дерево, даже если вы просматриваете логическое дерево во время выполнения (например, обработку события ввода из видимый пользовательский интерфейс, а затем — чтение логического дерева).The visuals that come from an applied template (such as the template-defined Border of dark gray around the visual button) are not reported in the logical tree, even if you are looking at the logical tree during run time (such as handling an input event from the visible UI and then reading the logical tree). Чтобы найти визуальные элементы шаблона, необходимо проверить визуальное дерево.To find the template visuals, you would instead need to examine the visual tree.

Дополнительные сведения о сопоставлении синтаксиса XAMLXAML с созданным графом объекта и неявным синтаксисом в XAML см. в разделе Подробное описание синтаксиса XAML или Общие сведения о языке XAML (WPF).For more information about how XAMLXAML syntax maps to the created object graph, and implicit syntax in XAML, see XAML Syntax In Detail or XAML Overview (WPF).

Назначение логического дереваThe Purpose of the Logical Tree

Логическое дерево существует для того, чтобы модели содержимого имели возможность пройти по своим доступным дочерним объектам, а также для их расширяемости.The logical tree exists so that content models can readily iterate over their possible child objects, and so that content models can be extensible. Кроме того, логическое дерево предоставляет оболочку для некоторых уведомлений, например при загрузке всех объектов логического дерева.Also, the logical tree provides a framework for certain notifications, such as when all objects in the logical tree are loaded. По существу, логическое дерево является подобием графа объекта времени выполнения на уровне оболочки, исключающего визуальные элементы, но подходит для большинства операций запросов для собственной композиции приложения времени выполнения.Basically, the logical tree is an approximation of a run time object graph at the framework level, which excludes visuals, but is adequate for many querying operations against your own run time application's composition.

Кроме того, как статические, так и динамические ссылки на ресурсы разрешаются путем просмотра логического дерева для Resources коллекций исходного запрашивающего объекта, а затем продолжается логическое дерево и проверяется каждый FrameworkElement (или FrameworkContentElement) для другого Resources значения, ResourceDictionaryсодержащего, возможно, содержащего этот ключ.In addition, both static and dynamic resource references are resolved by looking upwards through the logical tree for Resources collections on the initial requesting object, and then continuing up the logical tree and checking each FrameworkElement (or FrameworkContentElement) for another Resources value that contains a ResourceDictionary, possibly containing that key. Логическое дерево используется для просмотра ресурсов при наличии логического дерева и визуального дерева.The logical tree is used for resource lookup when both the logical tree and the visual tree are present. Дополнительные сведения о словарях ресурсов и поиске см. в разделе Общие сведения о ресурсах.For more information on resource dictionaries and lookup, see XAML Resources.

Композиция логического дереваComposition of the Logical Tree

Логическое дерево определяется на уровне платформы WPF. Это означает, что базовый элемент WPF, наиболее подходящий для операций логического дерева, — это либо FrameworkElement. FrameworkContentElementThe logical tree is defined at the WPF framework-level, which means that the WPF base element that is most relevant for logical tree operations is either FrameworkElement or FrameworkContentElement. Однако, как можно увидеть, если вы фактически используете LogicalTreeHelper API, логическое дерево иногда содержит узлы, которые FrameworkElement не FrameworkContentElementявляются либо.However, as you can see if you actually use the LogicalTreeHelper API, the logical tree sometimes contains nodes that are not either FrameworkElement or FrameworkContentElement. Например, логическое дерево сообщает Text значение TextBlock, которое является строкой.For instance, the logical tree reports the Text value of a TextBlock, which is a string.

Переопределение логического дереваOverriding the Logical Tree

Авторы расширенного элемента управления могут переопределить логическое дерево путем переопределения нескольких интерфейсов API, определяющих, как общий объект или модель содержимого добавляют или удаляют объекты в логическом дереве.Advanced control authors can override the logical tree by overriding several APIs that define how a general object or content model adds or removes objects within the logical tree. Пример переопределения логического дерева содержится в разделе Переопределение логического дерева.For an example of how to override the logical tree, see Override the Logical Tree.

Наследование значения свойстваProperty Value Inheritance

Наследование значения свойств действует через гибридное дерево.Property value inheritance operates through a hybrid tree. Фактические метаданные, содержащие Inherits свойство, которое позволяет наследование свойств, — это класс уровня FrameworkPropertyMetadata среды WPF.The actual metadata that contains the Inherits property that enables property inheritance is the WPF framework-level FrameworkPropertyMetadata class. Таким образом, как родитель, содержащий исходное значение, так и дочерний объект, который наследует это значение FrameworkElement , FrameworkContentElementдолжны быть или, и они должны быть частью некоторого логического дерева.Therefore, both the parent that holds the original value and the child object that inherits that value must both be FrameworkElement or FrameworkContentElement, and they must both be part of some logical tree. Однако для существующих свойств WPF, поддерживающих наследование свойств, наследование значений свойств способно принять промежуточный объект, которого нет в логическом дереве.However, for existing WPF properties that support property inheritance, property value inheritance is able to perpetuate through an intervening object that is not in the logical tree. Как правило, это распространяется на элементы шаблона, использующие все унаследованные значения свойств, заданные как в экземпляре, который является шаблоном, так и на более высоких уровнях композиции уровня страницы и, следовательно, выше в логическом дереве.Mainly this is relevant for having template elements use any inherited property values set either on the instance that is templated, or at still higher levels of page-level composition and therefore higher in the logical tree. Чтобы наследование значений свойств осуществлялось согласованно в таких пределах, наследуемое свойство должно быть зарегистрировано как вложенное свойство, кроме того, необходимо следовать этому шаблону, если требуется определить пользовательское свойство зависимостей с поведением наследования свойств.In order for property value inheritance to work consistently across such a boundary, the inheriting property must be registered as an attached property, and you should follow this pattern if you intend to define a custom dependency property with property inheritance behavior. Точное дерево, используемое для наследования свойств, не может быть полностью предсказано вспомогательным служебным методом класса даже во время выполнения.The exact tree used for property inheritance cannot be entirely anticipated by a helper class utility method, even at run time. Дополнительные сведения см. в разделе Наследование значения свойства.For more information, see Property Value Inheritance.

Визуальное деревоThe Visual Tree

В дополнение к концепции логического дерева в WPFWPF также существует концепция визуального дерева.In addition to the concept of the logical tree, there is also the concept of the visual tree in WPFWPF. Визуальное дерево описывает структуру визуальных объектов, представленных Visual базовым классом.The visual tree describes the structure of visual objects, as represented by the Visual base class. При написании шаблона для элемента управления следует определить или переопределить визуальное дерево, применяемое для данного элемента управления.When you write a template for a control, you are defining or redefining the visual tree that applies for that control. Визуальное дерево также представляет интерес для разработчиков, заинтересованных в контроле рисования на нижнем уровне по соображениям производительности и оптимизации.The visual tree is also of interest to developers who want lower-level control over drawing for performance and optimization reasons. Слабым местом визуального дерева как части программирования стандартных приложений WPFWPF является то, что маршруты событий для перенаправленного события в большинстве случаев проходят по визуальному дереву, а не по логическому.One exposure of the visual tree as part of conventional WPFWPF application programming is that event routes for a routed event mostly travel along the visual tree, not the logical tree. Эта тонкость поведения перенаправленного события может быть не очевидна, если вы не являетесь автором элемента управления.This subtlety of routed event behavior might not be immediately apparent unless you are a control author. Маршрутизация событий по визуальному дереву позволяет элементам управления, которые реализуют композицию на визуальном уровне, обрабатывать события или создавать установщики событий.Routing events through the visual tree enables controls that implement composition at the visual level to handle events or create event setters.

Деревья, элементы содержимого и узлы содержимогоTrees, Content Elements, and Content Hosts

Элементы содержимого (классы, производные от ContentElement) не являются частью визуального дерева; они не Visual наследуются и не имеют визуального представления.Content elements (classes that derive from ContentElement) are not part of the visual tree; they do not inherit from Visual and do not have a visual representation. Для отображения в пользовательском интерфейсе элемент ContentElement должен размещаться на узле содержимого, который является Visual и, и участником логического дерева.In order to appear in a UI at all, a ContentElement must be hosted in a content host that is both a Visual and a logical tree participant. Обычно такой объект является FrameworkElement.Usually such an object is a FrameworkElement. Можно представить сайт содержимого в качестве "обозревателя" содержимого, который выбирает способ отображения содержимого в пределах области экрана, управляемой сайтом.You can conceptualize that the content host is somewhat like a "browser" for the content and chooses how to display that content within the screen region that the host controls. При размещении содержимого оно может стать участником некоторых процессов дерева, которые обычно связаны с визуальным деревом.When the content is hosted, the content can be made a participant in certain tree processes that are normally associated with the visual tree. Как правило, FrameworkElement класс Host включает код реализации, который добавляет все ContentElement , размещенные в маршруте события, через подузлы логического дерева содержимого, даже если размещенное содержимое не является частью истинного визуального дерева.Generally, the FrameworkElement host class includes implementation code that adds any hosted ContentElement to the event route through subnodes of the content logical tree, even though the hosted content is not part of the true visual tree. Это необходимо для того, чтобы ContentElement в качестве источника можно было направить перенаправленное событие, которое будет маршрутизироваться к любому элементу, отличному от самого себя.This is necessary so that a ContentElement can source a routed event that routes to any element other than itself.

Прохождение по деревуTree Traversal

Класс предоставляет методыGetParentFindLogicalNode для обхода логического дерева. GetChildren LogicalTreeHelperThe LogicalTreeHelper class provides the GetChildren, GetParent, and FindLogicalNode methods for logical tree traversal. В большинстве случаев не следует проходить по логическому дереву существующих элементов управления, так как эти элементы управления почти всегда предоставляют свои логические дочерние элементы в качестве выделенного свойства коллекции, которое поддерживает доступ к коллекции, например Add, индексатор и т. д.In most cases, you should not have to traverse the logical tree of existing controls, because these controls almost always expose their logical child elements as a dedicated collection property that supports collection access such as Add, an indexer, and so on. Обход дерева — главным образом сценарий, который используется авторами элементов управления, которые не являются производными от предполагаемых шаблонов элементов управления ItemsControl , Panel таких как или где уже определены свойства коллекции, и кто планирует предоставить собственную коллекцию. Поддержка свойств.Tree traversal is mainly a scenario that is used by control authors who choose not to derive from intended control patterns such as ItemsControl or Panel where collection properties are already defined, and who intend to provide their own collection property support.

Визуальное дерево также поддерживает вспомогательный класс для обхода визуального дерева, VisualTreeHelper.The visual tree also supports a helper class for visual tree traversal, VisualTreeHelper. Визуальное дерево недоступно для свойств конкретного элемента управления, поэтому VisualTreeHelper класс является рекомендуемым способом прохода по визуальному дереву, если это необходимо для сценария программирования.The visual tree is not exposed as conveniently through control-specific properties, so the VisualTreeHelper class is the recommended way to traverse the visual tree if that is necessary for your programming scenario. Дополнительные сведения см. в разделе Общие сведения об отрисовке графики в WPF.For more information, see WPF Graphics Rendering Overview.

Примечание

Иногда необходимо проверять визуальное дерево применяемого шаблона.Sometimes it is necessary to examine the visual tree of an applied template. Необходимо соблюдать осторожность при использовании этого метода.You should be careful when using this technique. Даже если вы просматриваете визуальное дерево для элемента управления, в котором вы определили шаблон, потребители элемента управления всегда могут изменить шаблон, задав Template свойство для экземпляров, и даже конечный пользователь может повлиять на примененный шаблон, изменив Тема системы.Even if you are traversing a visual tree for a control where you define the template, consumers of your control can always change the template by setting the Template property on instances, and even the end user can influence the applied template by changing the system theme.

Маршруты для маршрутизируемых событий как "дерево"Routes for Routed Events as a "Tree"

Как уже отмечалось ранее, маршрут любого заданного перенаправленного события проходит по одному предопределенному пути дерева, представляющего собой гибрид представлений визуального и логического деревьев.As mentioned before, the route of any given routed event travels along a single and predetermined path of a tree that is a hybrid of the visual and logical tree representations. Маршрут события может проходить как по восходящей, так и по нисходящий в пределах дерева в зависимости от того, имеет ли перенаправленное событие нисходящую или восходящую маршрутизацию.The event route can travel either in the up or down directions within the tree depending on whether it is a tunneling or bubbling routed event. Концепция маршрута события не имеет непосредственного вспомогательного класса, который может быть использован для "прохода" маршрута события независимо от вызова фактически перенаправленного события.The event route concept does not have a directly supporting helper class that could be used to "walk" the event route independently of raising an event that actually routes. Существует класс, представляющий маршрут, EventRouteно методы этого класса обычно предназначены только для внутреннего использования.There is a class that represents the route, EventRoute, but the methods of that class are generally for internal use only.

Словари и деревья ресурсовResource Dictionaries and Trees

Поиск по словарю ресурсов для всех Resources, определенных на странице, обычно проходит по логическому дереву.Resource dictionary lookup for all Resources defined in a page traverses basically the logical tree. Объекты, которые не входят в логическое дерево, могут ссылаться на ресурсы с ключом, но последовательность поиска ресурса начинается с той точки, где объект подключен к логическому дереву.Objects that are not in the logical tree can reference keyed resources, but the resource lookup sequence begins at the point where that object is connected to the logical tree. В WPF только логические узлы дерева могут иметь Resources свойство, которое ResourceDictionaryсодержит, поэтому нет смысла просматривать визуальное дерево ResourceDictionaryдля поиска ресурсов с ключом из.In WPF, only logical tree nodes can have a Resources property that contains a ResourceDictionary, therefore there is no benefit in traversing the visual tree looking for keyed resources from a ResourceDictionary.

В то же время поиск ресурсов также можно расширить за пределы логического дерева.However, resource lookup can also extend beyond the immediate logical tree. Для разметки приложения поиск ресурсов можно затем продолжить в словарях ресурсов на уровне приложений, поддержке тем и значениях системы, на которые ссылаются как на статические свойства или ключи.For application markup, the resource lookup can then continue onward to application-level resource dictionaries and then to theme support and system values that are referenced as static properties or keys. Сами темы также могут ссылаться на системные значения вне логического дерева тем, если ссылки на ресурсы являются динамическими.Themes themselves can also reference system values outside of the theme logical tree if the resource references are dynamic. Дополнительные сведения о словарях ресурсов и логике поиска см. в разделе Ресурсы XAML.For more information on resource dictionaries and the lookup logic, see XAML Resources.

См. такжеSee also