WPF グラフィックス レンダリングの概要

ここでは、WPF のビジュアル層の概要について説明します。 特に、WPF モデルでのレンダリングをサポートする Visual クラスの役割に重点を置いて説明します。

ビジュアル オブジェクトの役割

Visual クラスは、すべての FrameworkElement オブジェクトの派生元となる基本抽象クラスです。 また、このクラスは、WPF で新しいコントロールを作成するためのエントリ ポイントとしても機能し、多くの点で Win32 アプリケーション モデルのウィンドウ ハンドル (HWND) と考えることができます。

Visual オブジェクトは、WPF の中心的なオブジェクトであり、その第一の役割はレンダリングをサポートすることです。 ButtonTextBox などのユーザー インターフェイス コントロールは、Visual クラスから派生しており、レンダリング データを保持するために使用されます。 Visual オブジェクトは、以下をサポートしています。

  • 出力表示: ビジュアルの、シリアル化された永続的な描画内容をレンダリングします。

  • 変換: ビジュアルで変換を実行します。

  • クリッピング: ビジュアルのクリッピング領域をサポートします。

  • ヒット テスト: 座標またはジオメトリがビジュアルの境界内に含まれているかどうかを判断します。

  • 境界ボックスの計算: ビジュアルの四角形領域を決定します。

ただし、Visual オブジェクトでは、次のような描画以外の機能はサポートされていません。

  • イベント処理

  • レイアウト

  • スタイル

  • データ バインディング

  • グローバリゼーション

Visual はパブリック抽象クラスとして公開され、子クラスはこのクラスから派生する必要があります。 WPF で公開されるビジュアル オブジェクトの階層構造を次の図に示します。

Diagram of classes derived from the Visual object

DrawingVisual クラス

DrawingVisual は、図形、イメージ、またはテキストのレンダリングに使用する軽量の描画クラスです。 このクラスが軽量と見なされる理由は、レイアウトやイベントの処理を行わないため、実行時のパフォーマンスが向上するからです。 そのため、背景やクリップ アートの描画に適しています。 DrawingVisual は、カスタム ビジュアル オブジェクトを作成するために使用できます。 詳しくは、「DrawingVisual オブジェクトの使用」をご覧ください。

Viewport3DVisual クラス

Viewport3DVisual は、2D VisualVisual3D オブジェクト間のブリッジを提供します。 Visual3D クラスは、すべての 3D ビジュアル要素の基底クラスです。 Viewport3DVisual では、Camera 値と Viewport 値を定義する必要があります。 カメラを使用するとシーンを表示できます。 ビューポートは、投影が 2D サーフェイス上にマップされる場所を設定します。 WPF の 3D について詳しくは、「3D グラフィックスの概要」をご覧ください。

ContainerVisual クラス

ContainerVisual クラスは、Visual オブジェクトのコレクションのコンテナーとして使用されます。 DrawingVisual クラスは、ContainerVisual クラスから派生するため、ビジュアル オブジェクトのコレクションを格納できます。

ビジュアル オブジェクトの描画コンテンツ

Visual オブジェクトは、レンダリング データをベクター グラフィックス命令リストとして格納します。 命令リスト内の各項目は、グラフィックス データと関連リソースの低レベル セットを、シリアル化された形式で表します。 描画コンテンツを格納できるレンダリング データには、次の 4 つの種類があります。

描画コンテンツの種類 説明
ベクター グラフィックス ベクター グラフィックス データと、関連する Brush および Pen の情報を表します。
Image Rect で定義された領域内のイメージを表します。
グリフ 指定したフォント リソースからの一連のグリフである、GlyphRun をレンダリングする描画を表します。 テキストはこれによって表示されます。
ビデオ ビデオをレンダリングする描画を表します。

DrawingContext を使用すると、Visual にビジュアル コンテンツを読み込むことができます。 DrawingContext オブジェクトの描画コマンドを使用した場合、画面上にリアルタイムに描画が行われるのではなく、実際には、グラフィックス システムによって後で使用される、一連のレンダリング データが格納されます。

WPF コントロール (Button など) を作成すると、そのコントロールは、それ自身を描画するためのレンダリング データを暗黙的に生成します。 たとえば、ButtonContent プロパティを設定すると、グリフのレンダリング表現がコントロールに格納されます。

Visual のコンテンツは、DrawingGroup 内に含まれる 1 つ以上の Drawing オブジェクトとして表されます。 また DrawingGroup は、コンテンツに適用される不透明マスク、変換、ビットマップ効果などの操作も表します。 コンテンツをレンダリングする際、DrawingGroup 操作は、OpacityMaskOpacityBitmapEffectClipGeometryGuidelineSetTransform の順に適用されます。

次の図は、レンダリング シーケンス中に DrawingGroup 操作が適用される順序を示したものです。

DrawingGroup order of operations
DrawingGroup 操作の順序

詳しくは、「Drawing オブジェクトの概要」をご覧ください。

ビジュアル層での描画コンテンツ

DrawingContext を直接インスタンス化することはできません。ただし、描画コンテキストは、特定のメソッド (DrawingGroup.OpenDrawingVisual.RenderOpen など) から取得できます。 次の例では、DrawingVisual から DrawingContext を取得し、それを四角形の描画に使用しています。

// Create a DrawingVisual that contains a rectangle.
private DrawingVisual CreateDrawingVisualRectangle()
{
    DrawingVisual drawingVisual = new DrawingVisual();

    // Retrieve the DrawingContext in order to create new drawing content.
    DrawingContext drawingContext = drawingVisual.RenderOpen();

    // Create a rectangle and draw it in the DrawingContext.
    Rect rect = new Rect(new System.Windows.Point(160, 100), new System.Windows.Size(320, 80));
    drawingContext.DrawRectangle(System.Windows.Media.Brushes.LightBlue, (System.Windows.Media.Pen)null, rect);

    // Persist the drawing content.
    drawingContext.Close();

    return drawingVisual;
}
' Create a DrawingVisual that contains a rectangle.
Private Function CreateDrawingVisualRectangle() As DrawingVisual
    Dim drawingVisual As New DrawingVisual()

    ' Retrieve the DrawingContext in order to create new drawing content.
    Dim drawingContext As DrawingContext = drawingVisual.RenderOpen()

    ' Create a rectangle and draw it in the DrawingContext.
    Dim rect As New Rect(New Point(160, 100), New Size(320, 80))
    drawingContext.DrawRectangle(Brushes.LightBlue, CType(Nothing, Pen), rect)

    ' Persist the drawing content.
    drawingContext.Close()

    Return drawingVisual
End Function

ビジュアル層で描画コンテンツを列挙する

他の用途に加えて、Drawing オブジェクトでは、Visual の内容を列挙するためのオブジェクト モデルも提供されます。

注意

ビジュアルのコンテンツを列挙する場合は、レンダリング データの基になる表現をベクター グラフィックス命令リストとして取得するのではなく、Drawing オブジェクトを取得することになります。

次の例では、GetDrawing メソッドを使用して VisualDrawingGroup 値を取得し、それを列挙します。

public void RetrieveDrawing(Visual v)
{
    DrawingGroup drawingGroup = VisualTreeHelper.GetDrawing(v);
    EnumDrawingGroup(drawingGroup);
}

// Enumerate the drawings in the DrawingGroup.
public void EnumDrawingGroup(DrawingGroup drawingGroup)
{
    DrawingCollection dc = drawingGroup.Children;

    // Enumerate the drawings in the DrawingCollection.
    foreach (Drawing drawing in dc)
    {
        // If the drawing is a DrawingGroup, call the function recursively.
        if (drawing is DrawingGroup group)
        {
            EnumDrawingGroup(group);
        }
        else if (drawing is GeometryDrawing)
        {
            // Perform action based on drawing type.
        }
        else if (drawing is ImageDrawing)
        {
            // Perform action based on drawing type.
        }
        else if (drawing is GlyphRunDrawing)
        {
            // Perform action based on drawing type.
        }
        else if (drawing is VideoDrawing)
        {
            // Perform action based on drawing type.
        }
    }
}

ビジュアル オブジェクトを使用してコントロールをビルドする方法

WPF のオブジェクトの多くは、他のビジュアル オブジェクトで構成されています。そのため、それらのオブジェクトには、子孫オブジェクトのさまざまな階層を格納することができます。 WPF のユーザー インターフェイス要素 (コントロールなど) の多くは、さまざまな種類のレンダリング要素を表す、複数のビジュアル オブジェクトで構成されています。 たとえば、Button コントロールは、ClassicBorderDecoratorContentPresenterTextBlock など、他のオブジェクトを多数格納できます。

次のコードは、マークアップで定義された Button コントロールを示しています。

<Button Click="OnClick">OK</Button>

既定の Button コントロールを構成するビジュアル オブジェクトを列挙した場合、ビジュアル オブジェクトの階層は次の図のようになります。

Diagram of visual tree hierarchy

Button コントロールには ClassicBorderDecorator 要素が含まれており、さらにそれには ContentPresenter 要素が含まれています。 ClassicBorderDecorator 要素は、Button の境界線と背景を描画します。 ContentPresenter 要素は、Button の内容を表示します。 この場合、テキストを表示しているため、ContentPresenter 要素には TextBlock 要素が格納されています。 Button コントロールで ContentPresenter が使用されるということは、そのコンテンツが、Image やジオメトリ (たとえば EllipseGeometry) など、他の要素によって表現可能だということを意味します。

コントロール テンプレート

コントロールをコントロールの階層に拡張する場合には、ControlTemplate が重要になります。 コントロール テンプレートは、コントロールの既定のビジュアル階層を指定します。 コントロールを明示的に参照すると、コントロールのビジュアル階層が暗黙的に参照されます。 コントロール テンプレートの既定値をオーバーライドして、コントロールの外観をカスタマイズすることもできます。 たとえば、Button コントロールの背景色の値を変更して、純色値の代わりに線形グラデーション値を使用することもできます。 詳しくは、「ボタンのスタイルとテンプレート」をご覧ください。

Button コントロールなどのユーザー インターフェイス要素には、コントロールのすべてのレンダリング定義を表す、複数のベクター グラフィックス命令リストが含まれています。 次のコードは、マークアップで定義された Button コントロールを示しています。

<Button Click="OnClick">
  <Image Source="images\greenlight.jpg"></Image>
</Button>

Button コントロールを構成するビジュアル オブジェクトとベクター グラフィックス命令リストを列挙した場合、オブジェクトの階層は次の図のようになります。

Diagram of visual tree and rendering data

Button コントロールには ClassicBorderDecorator 要素が含まれており、さらにそれには ContentPresenter 要素が含まれています。 ClassicBorderDecorator 要素は、ボタンの境界線と背景を構成する個々のグラフィックス要素をすべて描画します。 ContentPresenter 要素は、Button の内容を表示します。 この場合、イメージを表示しているため、ContentPresenter 要素には Image 要素が格納されています。

ビジュアル オブジェクトとベクター グラフィックス命令リストの階層については、次の点に注意する必要があります。

  • 階層内の順序は、描画情報のレンダリング順序を表します。 子要素は、ルート ビジュアル要素を起点として、左から右、上から下に走査されます。 要素に子ビジュアル要素がある場合、子ビジュアル要素は要素の兄弟よりも先に走査されます。

  • 階層内の非リーフ要素 (ContentPresenter など) は、子要素を格納するために使用されます。これらには、命令リストは格納されません。

  • ビジュアル要素にベクター グラフィックス命令リストとビジュアル子の両方が含まれている場合は、ビジュアル子オブジェクトが描画される前に、親ビジュアル要素の命令リストがレンダリングされます。

  • ベクター グラフィックス命令リスト内の項目は、左から右の順にレンダリングされます。

ビジュアル ツリー

ビジュアル ツリーには、アプリケーションのユーザー インターフェイスで使用されるすべてのビジュアル要素が含まれます。 ビジュアル要素には永続化された描画情報が含まれているので、ビジュアル ツリーは、ディスプレイ デバイスへの出力を構成するのに必要なレンダリング情報をすべて含んだ、シーン グラフであると考えることができます。 このツリーは、コードかマークアップかを問わず、アプリケーションで直接作成されたすべてのビジュアル要素を累積したものです。 ビジュアル ツリーには、コントロールやデータ オブジェクトなど、要素のテンプレート拡張によって作成されたビジュアル要素もすべて含まれます。

次のコードは、マークアップで定義された StackPanel 要素を示しています。

<StackPanel>
  <Label>User name:</Label>
  <TextBox />
  <Button Click="OnClick">OK</Button>
</StackPanel>

マークアップ例の StackPanel 要素を構成するビジュアル オブジェクトを列挙した場合、ビジュアル オブジェクトの階層は次の図のようになります。

Diagram of visual tree hierarchy of a StackPanel control.

レンダリング順序

ビジュアル ツリーは、WPF のビジュアル オブジェクトと描画オブジェクトの表示順序を決定します。 走査の順序は、ビジュアル ツリーの最上位ノードであるルート ビジュアルから始まります。 次に、ルート ビジュアルの子が左から右に走査されます。 ビジュアルの子が存在する場合、子はビジュアルの兄弟よりも先に走査されます。 つまり、子ビジュアルの内容は、そのビジュアル自体の内容よりも前面に表示されます。

Diagram of the visual tree rendering order

ルート ビジュアル

ルート ビジュアルは、ビジュアル ツリー階層内の最上位の要素です。 ほとんどのアプリケーションでは、ルート ビジュアルの基底クラスは Window または NavigationWindow のいずれかです。 ただし、ビジュアル オブジェクトを Win32 アプリケーションでホストする場合は、Win32 ウィンドウにホストする最上位のビジュアルがルート ビジュアルになります。 詳しくは、「チュートリアル: Win32 アプリケーションでのビジュアル オブジェクトのホスト」を参照してください。

論理ツリーとの関係

WPF の論理ツリーは、実行時のアプリケーションの要素を表します。 このツリーを直接操作することはありませんが、アプリケーションのこのビューは、プロパティの継承やイベントのルーティングを理解する上で役立ちます。 ビジュアル ツリーとは異なり、論理ツリーは、ビジュアル データ オブジェクト以外のオブジェクト (ListItem など) を表すことができます。 多くの場合、論理ツリーはアプリケーションのマークアップ定義にほぼ一致します。 次のコードは、マークアップで定義された DockPanel 要素を示しています。

<DockPanel>
  <ListBox>
    <ListBoxItem>Dog</ListBoxItem>
    <ListBoxItem>Cat</ListBoxItem>
    <ListBoxItem>Fish</ListBoxItem>
  </ListBox>
  <Button Click="OnClick">OK</Button>
</DockPanel>

マークアップ例の DockPanel 要素を構成する論理オブジェクトを列挙した場合、論理オブジェクトの階層は次の図のようになります。

Tree diagram
論理ツリーのダイアグラム

ビジュアル ツリーと論理ツリーはどちらも、現在のアプリケーション要素セットと同期し、要素の追加、削除、または変更をすべて反映します。 ただし、これらのツリーが表すアプリケーションのビューはそれぞれ異なるものです。 ビジュアル ツリーとは異なり、論理ツリーでは、コントロールの ContentPresenter 要素は展開されません。 つまり、同じオブジェクト セットについて見ても、論理ツリーとビジュアルツリーの間に 1 対 1 の対応関係はありません。 実際、同じ要素をパラメーターに使用して、LogicalTreeHelper オブジェクトの GetChildren メソッドと VisualTreeHelper オブジェクトの GetChild メソッドを呼び出すと、異なる結果が返されます。

論理ツリーについて詳しくは、「WPF のツリー」をご覧ください。

XamlPad を使用したビジュアル ツリーの表示

WPF ツールである XamlPad は、現在定義されている XAML コンテンツに対応するビジュアル ツリーを表示、探索するために使用できます。 ビジュアル ツリーを表示するには、メニュー バーの [Show Visual Tree] (ビジュアル ツリーを表示) ボタンをクリックします。 次の図は、XamlPad の [Visual Tree Explorer](ビジュアル ツリー エクスプローラー) パネルに表示されたビジュアル ツリー ノードで、XAML コンテンツを展開した様子を示したものです。

Visual Tree Explorer panel in XamlPad

LabelTextBox、および Button の各コントロールによって、XamlPad の [Visual Tree Explorer](ビジュアル ツリー エクスプローラー) パネルに、個別のビジュアル オブジェクト階層がどのように表示されるかに注目してください。 これは、WPF コントロールに、そのコントロールのビジュアル ツリーを含む ControlTemplate があるためです。 コントロールを明示的に参照すると、コントロールのビジュアル階層が暗黙的に参照されます。

ビジュアル パフォーマンスのプロファイリング

WPF にはパフォーマンス プロファイリング ツールのセットがあります。アプリケーションの実行時動作を分析したり、適用できるパフォーマンス最適化の種類を決定したりできます。 Visual Profiler ツールでは、パフォーマンス データをアプリケーションのビジュアル ツリーに直接マップすることにより、それらのデータを多彩なグラフィカル ビューで表示できます。 このスクリーンショットでは、Visual Profiler の [CPU 使用率] セクションに、オブジェクトの WPF サービスの使用率が詳しく表示されています (レンダリングやレイアウトなど)。

Visual Profiler display output
Visual Profiler 表示出力

ビジュアル レンダリング動作

WPF では、ビジュアル オブジェクトのレンダリング動作に影響を及ぼす機能が導入されています。保持モード グラフィックス、ベクター グラフィックス、およびデバイス非依存グラフィックスです。

保持モード グラフィックス

ビジュアル オブジェクトの役割を理解するためには、イミディエイト モードのグラフィックス システムと保持モードのグラフィックス システムの違いについて理解することが重要です。 GDI または GDI+ に基づく標準的な Win32 アプリケーションでは、イミディエイト モードのグラフィックス システムが使用されます。 これは、ウィンドウのサイズ変更やオブジェクトの外観変更などといったアクションによって無効化されたクライアント領域の部分を、アプリケーションが再描画するということを意味します。

Diagram of Win32 rendering sequence

これに対し、WPF では、保持モード システムが使用されます。 これは、外観を持つアプリケーション オブジェクト側で、シリアル化された一連の描画データが定義されるということを意味します。 描画データが定義された後は、アプリケーション オブジェクトをレンダリングするためのすべての再描画要求に、システム側が対応します。 実行時であっても、アプリケーション オブジェクトを変更したり作成した場合の描画要求への対応は、システムに任せることができます。 保持モードのグラフィックス システムでは、描画情報が常にシリアル化された状態でアプリケーションに保持されますが、レンダリングはシステムによって実行されます。これが、保持モードのグラフィックス システムの長所です。 次の図は、アプリケーションでの描画要求が WPF によって処理される様子を示したものです。

Diagram of WPF rendering sequence

インテリジェントな再描画

保持モード グラフィックスを使用することの最も大きな利点の 1 つは、アプリケーション内のどの項目を再描画するのかを、WPF が効率的に最適化できることです。 さまざまな不透明度が適用された複雑なシーンがある場合でも、通常、再描画を最適化するために特殊な目的のコードを記述する必要はありません。 これに対し、Win32 プログラミングでは、更新領域内の再描画量を最小化してアプリケーションを最適化するために、多大な労力が費やされることもあります。 Win32 アプリケーションで再描画を最適化するための複雑な作業の例については、「Redrawing in the Update Region」(更新領域での再描画) をご覧ください。

ベクター グラフィックス

WPF では、レンダリング データ形式としてベクタ グラフィックスが使用されます。 スケーラブル ベクター グラフィックス (SVG)、Windows メタファイル (.wmf)、TrueType フォントなどのベクター グラフィックスは、レンダリング データを格納し、それを命令リストとして伝達します。命令リストには、グラフィックス プリミティブを使用してイメージを再作成するための方法が記述されます。 たとえば、TrueType フォントは、ピクセル配列ではなく、直線、曲線、およびコマンドのセットで表されるアウトライン フォントです。 ベクター グラフィックスの主な利点の 1 つは、任意のサイズや解像度に拡大縮小できることです。

ビットマップ グラフィックスは、ベクター グラフィックスとは異なり、特定の解像度で事前にレンダリングされた、ピクセル単位のイメージ表現としてレンダリング データを格納します。 ビットマップ グラフィックス形式とベクター グラフィックス形式の主な違いの 1 つは、元のソース イメージへの忠実性です。 たとえば、ソース イメージのサイズを変更した場合、ビットマップ グラフィックス システムではイメージが伸縮されますが、ベクタ グラフィックス システムでは、イメージが拡大縮小されるため、イメージの忠実性が維持されます。

次の図は、ソース イメージが 300% に拡大された場合の例です。 ソース イメージをビットマップ グラフィックス イメージとして拡大したイメージには、ゆがみが生じています。ベクター グラフィックス イメージとして拡大縮小した場合、こうしたゆがみは生じません。

Differences between raster and vector graphics

次のマークアップは、定義された 2 つの Path 要素を示しています。 2 つ目の要素では、ScaleTransform を使用して、最初の要素の描画命令を 300% に拡大しています。 なお、Path 要素の描画命令は変更されていません。

<Path
  Data="M10,100 C 60,0 100,200 150,100 z"
  Fill="{StaticResource linearGradientBackground}"
  Stroke="Black"
  StrokeThickness="2" />

<Path
  Data="M10,100 C 60,0 100,200 150,100 z"
  Fill="{StaticResource linearGradientBackground}"
  Stroke="Black"
  StrokeThickness="2" >
  <Path.RenderTransform>
    <ScaleTransform ScaleX="3.0" ScaleY="3.0" />
  </Path.RenderTransform>
</Path>

解像度とデバイス非依存グラフィックスについて

画面上のテキストやグラフィックスのサイズを決定するシステム要素は、2 つあります。解像度と DPI です。 解像度は、画面に表示されるピクセルの数を表します。 解像度が高くなると、ピクセルが小さくなり、グラフィックスとテキストがより滑らかに表示されます。 モニターの解像度が 1024 x 768 に設定されている場合、解像度を 1600 x 1200 に変更すると、表示されるグラフィックスがより小さくなります。

もう 1 つのシステム設定である DPI は、画面上の 1 インチのサイズをピクセル単位で表したものです。 ほとんどの Windows システムは 96 DPI であり、これは、画面上の 1 インチが 96 ピクセルであることを意味します。 DPI の設定を上げると、画面上の 1 インチが長くなり、設定を下げると、画面上の 1 インチが短くなります。 そのため、ほとんどのシステムでは、画面上の 1 インチと実際の 1 インチが同じサイズにはなりません。 DPI を大きくすると、画面上の 1 インチのサイズが大きくなるため、DPI 対応のグラフィックスとテキストは大きくなります。 DPI を大きくすると、特に高解像度の場合にはテキストが読みやすくなります。

ただし、すべてのアプリケーションが DPI に対応しているわけではありません。一部のアプリケーションでは、主要な測定単位としてハードウェア ピクセルが使用されているため、システム DPI を変更しても影響がありません。 また、フォント サイズに DPI 対応の単位を使用している場合でも、それ以外の項目にはすべてピクセルを使用するアプリケーションが数多く存在します。 これらのアプリケーションで DPI を過度に小さくしたり、大きくしたりした場合、アプリケーションのテキストはシステムの DPI 設定に従って拡大縮小されますが、アプリケーションの UI は拡大縮小されないため、レイアウトの問題が発生する可能性があります。 この問題は、WPF を使用して開発されたアプリケーションでは発生しません。

WPF では、ハードウェア ピクセルの代わりに、デバイス非依存のピクセルを主要測定単位として用いた、自動拡大縮小機能がサポートされています。つまり、アプリケーション開発者が手を加えなくても、グラフィックスとテキストが適切に拡大縮小されます。 次の図は、WPF のテキストとグラフィックスが、さまざまな DPI でどのように表示されるかを示したものです。

Graphics and text at different DPI settings
異なる DPI 設定のグラフィックスとテキスト

VisualTreeHelper クラス

VisualTreeHelper クラスは、ビジュアル オブジェクト レベルでのプログラミング用に低レベルの機能を提供する、静的なヘルパー クラスです。これは、高性能のカスタム コントロールの開発など、非常に限定されたシナリオで役立ちます。 多くの場合は、より高レベルの WPF フレームワーク オブジェクト (CanvasTextBlock など) を使用したほうが、柔軟性と使いやすさが向上します。

ヒット テスト

VisualTreeHelper クラスでは、ビジュアル オブジェクトに対するヒット テストのためのメソッドが提供されています。これらは、既定のヒット テスト サポートが要件を満たさない場合に使用できます。 VisualTreeHelper クラスの HitTest メソッドを使用すると、ジオメトリやポイントの座標値が特定のオブジェクト (コントロールやグラフィック要素など) の境界内にあるかどうかを確認できます。 たとえば、ヒット テストを使用して、オブジェクトの四角形領域内でのマウス クリックが円のジオメトリ内にあるかどうかを確認することもできます。また、ヒット テストの既定の実装をオーバーライドして、独自のカスタム ヒット テスト計算を実行することもできます。

ヒット テストについて詳しくは、「ビジュアル層でのヒット テスト」をご覧ください。

ビジュアル ツリーを列挙する

VisualTreeHelper クラスでは、ビジュアル ツリーのメンバーを列挙するための機能が提供されています。 親を取得するには、GetParent メソッドを呼び出します。 ビジュアル オブジェクトの子 (直接の子孫) を取得するには、GetChild メソッドを呼び出します。 このメソッドは、指定したインデックス位置にある親の子 Visual を返します。

次の例に示すのは、ビジュアル オブジェクトのすべての子孫を列挙する方法です。これは、ビジュアル オブジェクト階層のすべての描画情報をシリアル化する必要がある場合に使用できる手法です。

// Enumerate all the descendants of the visual object.
static public void EnumVisual(Visual myVisual)
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(myVisual); i++)
    {
        // Retrieve child visual at specified index value.
        Visual childVisual = (Visual)VisualTreeHelper.GetChild(myVisual, i);

        // Do processing of the child visual object.

        // Enumerate children of the child visual object.
        EnumVisual(childVisual);
    }
}
' Enumerate all the descendants of the visual object.
Public Shared Sub EnumVisual(ByVal myVisual As Visual)
    For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(myVisual) - 1
        ' Retrieve child visual at specified index value.
        Dim childVisual As Visual = CType(VisualTreeHelper.GetChild(myVisual, i), Visual)

        ' Do processing of the child visual object.

        ' Enumerate children of the child visual object.
        EnumVisual(childVisual)
    Next i
End Sub

ほとんどの場合、WPF アプリケーションの要素を表すのには、論理ツリーの方が役立ちます。 論理ツリーを直接変更することはありませんが、アプリケーションのこのビューは、プロパティの継承やイベントのルーティングを理解する上で役立ちます。 ビジュアル ツリーとは異なり、論理ツリーは、ビジュアル データ オブジェクト以外のオブジェクト (ListItem など) を表すことができます。 論理ツリーについて詳しくは、「WPF のツリー」をご覧ください。

VisualTreeHelper クラスでは、ビジュアル オブジェクトの四角形領域を返すメソッドが提供されています。 GetContentBounds を呼び出すと、ビジュアル オブジェクトの四角形領域が返されます。 GetDescendantBounds を呼び出すと、ビジュアル オブジェクトのすべての子孫 (ビジュアル オブジェクト自体も含む) の四角形領域が返されます。 次のコードは、ビジュアル オブジェクトとそのすべての子孫の四角形領域を計算する方法を示したものです。

// Return the bounding rectangle of the parent visual object and all of its descendants.
Rect rectBounds = VisualTreeHelper.GetDescendantBounds(parentVisual);
' Return the bounding rectangle of the parent visual object and all of its descendants.
Dim rectBounds As Rect = VisualTreeHelper.GetDescendantBounds(parentVisual)

関連項目