WPF のツリーTrees 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) でも、プログラム要素間の関係を定義するために、いくつかのツリー構造のメタファーが使用されます。also uses several tree structure metaphors to define relationships between program elements. ほとんどの場合、WPF の開発者は、概念的にオブジェクト ツリーのメタファーについて検討しながら、アプリケーションをコードで作成したり、XAML でアプリケーションの部分を定義したりできますが、それを行うには特定の API を呼び出したり、特定のマークアップを使用したりするのであり、XML DOM で使用するような一般的なオブジェクト ツリー操作 API ではありません。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 では、ツリー メタファー ビューを提供する LogicalTreeHelperVisualTreeHelper の 2 つのヘルパー クラスが公開されています。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. このトピックでは、ビジュアル ツリーと論理ツリーが表す内容を定義し、そのようなツリーが全体的なオブジェクト ツリーの概念とどのように関連しているかについて説明し、LogicalTreeHelperVisualTreeHelper を紹介します。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.

WPF のツリーTrees 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 に報告する方法として、論理ツリーとビジュアル ツリーの 2 つがあります。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 より概念的であるため、概念を考えるもう 1 つの方法はオブジェクト グラフです。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 で定義された UI の場合、ツリー メタファーは、この一般的な概念を参照するときにほとんどの 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 では、UI 要素の基になっているオブジェクトのプロパティを設定することにより、要素にコンテンツを追加します。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. これにより、Items プロパティ値である ItemCollection に項目が配置されます。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.

Extensible Application Markup Language (XAML)Extensible Application Markup Language (XAML) では、ListBox にリスト項目を配置したり、DockPanel にコントロールや他の UI 要素を配置するときは、次の例のように、明示的または暗黙的に、Items および Children プロパティも使用します。In Extensible Application Markup Language (XAML)Extensible 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. ただし、ItemCollection を含む Items という名前の ListBox プロパティが作成され、その ItemCollection は初期化されますが、ListBox 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.Add のパーサー呼び出しによって ItemCollection に追加されます。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 の暗黙的な構文項目が考慮されていない場合でも、論理ツリーは実行時にアプリケーション UI に存在するオブジェクト グラフ全体ではありません。その主な理由は、ビジュアルとテンプレートです。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 オブジェクトとその文字列の Content が報告されます。The 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. 実行時に論理ツリーを見ている場合でも (表示されている UI からの入力イベントを処理してから、論理ツリーを読み取る場合など)、適用されたテンプレートによるビジュアルは (テンプレートで定義されている、ビジュアル ボタンの周りのダーク グレーの 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) で、そのキーを含む可能性のある ResourceDictionary が含まれる別の Resources の値を調べます。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. リソース ディクショナリと検索の詳細については、XAML リソースに関する記事を参照してください。For more information on resource dictionaries and lookup, see XAML Resources.

論理ツリーの構成Composition of the Logical Tree

論理ツリーは WPF フレームワーク レベルで定義されます。これは、論理ツリーの操作に最も関係する WPF の基本要素が、FrameworkElement または FrameworkContentElement であることを意味します。The 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. たとえば、論理ツリーでは TextBlockText の値が報告されますが、これは文字列です。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 プロパティが含まれる実際のメタデータは、WPF フレームワーク レベルの FrameworkPropertyMetadata クラスです。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 アプリケーション プログラミングの一部としてビジュアル ツリーが使用される場合の 1 つとして、ルーティング イベントのイベント ルートは、論理ツリーではなく、ほとんどの場合、ビジュアル ツリーに沿って移動します。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. UI に表示されるようにするには、Visual であり論理ツリーに含まれるコンテンツ ホストで、ContentElement をホストする必要があります。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 ホスト クラスには、ホストされているコンテンツが真のビジュアル ツリーの一部ではない場合でも、ホストされている 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

LogicalTreeHelper クラスでは、論理ツリーのトラバーサル用に GetChildrenGetParentFindLogicalNode の各メソッドが提供されています。The 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. ツリー トラバーサルは、主に、コレクション プロパティが既に定義されていて ItemsControlPanel などの目的のコントロール パターンからの派生を行わず、独自のコレクション プロパティのサポートを提供することを、コントロールの作成者が選択した場合に使用されるシナリオです。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 では、論理ツリーのノードだけが ResourceDictionary を含む Resources プロパティを持つことができるため、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