XAML 名前スコープは、XAML で定義されたオブジェクトの名前とそのインスタンスの同等の名前の間のリレーションシップを格納します。 この概念は、他のプログラミング言語やテクノロジの 名前スコープ というより広い意味に似ています。
XAML 名前スコープの定義方法
XAML 名前スコープ内の名前を使用すると、ユーザー コードは、XAML で最初に宣言されたオブジェクトを参照できます。 XAML の解析の内部的な結果は、ランタイムが、XAML 宣言でこれらのオブジェクトが持っていたリレーションシップの一部またはすべてを保持するオブジェクトのセットを作成することです。 これらのリレーションシップは、作成されたオブジェクトの特定のオブジェクト プロパティとして保持されるか、プログラミング モデル API のユーティリティ メソッドに公開されます。
XAML 名前スコープでの名前の最も一般的な使用方法は、オブジェクト インスタンスへの直接参照です。これは、部分クラス テンプレートで生成された InitializeComponent メソッドと組み合わせて、プロジェクト ビルド アクションとしてマークアップ コンパイル パスによって有効になります。
実行時にユーティリティ メソッド FindName を使用して、XAML マークアップの名前で定義されたオブジェクトへの参照を返すこともできます。
ビルド アクションと XAML の詳細
技術的には、XAML 自体がマークアップ コンパイラ パスを受け取ると同時に、XAML とコード ビハインド用に定義された部分クラスが一緒にコンパイルされるということです。 マークアップで 定義されている Name 属性または x:Name 属性 を持つ各オブジェクト要素は、XAML 名と一致する名前を持つ内部フィールドを生成します。 このフィールドは最初は空です。 その後、クラスは、すべての XAML が読み込まれた後にのみ呼び出される InitializeComponent メソッドを生成します。 InitializeComponent ロジック内では、各内部フィールドに、同等の名前文字列の FindName 戻り値が設定されます。 コンパイル後に Windows ランタイム アプリ プロジェクトの /obj サブフォルダーにある各 XAML ページに対して作成された ".g" (生成された) ファイルを調べることで、このインフラストラクチャを自分で観察できます。 フィールドと InitializeComponent メソッドを、結果のアセンブリのメンバーとして表示することもできます。その場合は、それらのアセンブリを反映したり、インターフェイス言語の内容を調べたりすることもできます。
注
具体的には、Visual C++ コンポーネント拡張 (C++/CX) アプリの場合、XAML ファイルのルート要素に対して x:Name 参照のバッキング フィールドは作成されません。 C++/CX 分離コードからルート オブジェクトを参照する必要がある場合は、他の API またはツリー トラバーサルを使用します。 たとえば、既知の名前付き子要素に対して FindName を呼び出し、 Parent を呼び出すことができます。
XamlReader.Load を使用して実行時にオブジェクトを作成する
XAML は、 XamlReader.Load メソッドの文字列入力としても使用できます。これは、最初の XAML ソース解析操作と同様に機能します。 XamlReader.Load では、実行時にオブジェクトの新しい切断ツリーが作成されます。 切断されたツリーは、メイン オブジェクト ツリー上のポイントにアタッチできます。 作成したオブジェクト ツリーは、Children などのコンテンツ プロパティ コレクションに追加するか、オブジェクト値を受け取る他のプロパティを設定して明示的に接続する必要があります (たとえば、Fill プロパティ値に対して新しい ImageBrush を読み込む)。
XamlReader.Load の XAML 名前スコープへの影響
XamlReader.Load によって作成された新しいオブジェクト ツリーによって定義された暫定的な XAML 名前スコープは、指定された XAML 内の定義された名前を一意性で評価します。 指定された XAML 内の名前がこの時点で内部的に一意でない場合、 XamlReader.Load は例外をスローします。 切断されたオブジェクト ツリーは、メイン アプリケーション オブジェクト ツリーに接続された場合でも、その XAML 名前スコープをメイン アプリケーションの XAML 名前スコープとマージしようとしません。 ツリーを接続すると、アプリには統一されたオブジェクト ツリーがありますが、そのツリーには個別の XAML 名前スコープがあります。 分割はオブジェクト間の接続ポイントで行われ、 XamlReader.Load 呼び出しから返される値にプロパティを設定します。
個別で分離された XAML 名前スコープの問題は、FindName メソッドや直接のマネージド オブジェクト参照の呼び出しが、統一された XAML 名前スコープに対して動作しなくなったことです。 代わりに、 FindName が呼び出される特定のオブジェクトはスコープを意味し、スコープは呼び出し元のオブジェクトが含まれる XAML 名前スコープになります。 直接マネージド オブジェクト参照の場合、スコープはコードが存在するクラスによって暗黙的に指定されます。 通常、アプリ コンテンツの "ページ" の実行時の相互作用の分離コードは、ルート "ページ" をバックする部分クラスに存在するため、XAML 名前スコープはルート XAML 名前スコープです。
ルート XAML 名前スコープで FindName を呼び出して名前付きオブジェクトを取得すると、 XamlReader.Load によって作成された個別の XAML 名前スコープからオブジェクトが見つかりません。 逆に、個別の XAML 名前スコープから取得したオブジェクトから FindName を呼び出した場合、メソッドはルート XAML 名前スコープ内の名前付きオブジェクトを見つけることができません。
この個別の XAML 名前スコープの問題は、 FindName 呼び出しを使用するときの XAML 名前スコープ内の名前によるオブジェクトの検索にのみ影響します。
別の XAML 名前スコープで定義されているオブジェクトへの参照を取得するには、いくつかの手法を使用できます。
- オブジェクト ツリー構造に存在することがわかっている 親 プロパティやコレクション プロパティ ( Panel.Children によって返されるコレクションなど) を使用して、ツリー全体を個別の手順で説明します。
- 個別の XAML 名前スコープから呼び出していて、ルート XAML 名前スコープが必要な場合は、現在表示されているメイン ウィンドウへの参照を常に簡単に取得できます。 呼び出し
Window.Current.Contentを使用して、現在のアプリケーション ウィンドウからビジュアル ルート (ルート XAML 要素、コンテンツ ソースとも呼ばれます) を 1 行のコードで取得できます。 その後、 FrameworkElement にキャストし、このスコープから FindName を 呼び出すことができます。 - ルート XAML 名前スコープから呼び出し、個別の XAML 名前スコープ内のオブジェクトが必要な場合は、コード内で事前に計画し、 XamlReader.Load によって返されたオブジェクトへの参照を保持し、メイン オブジェクト ツリーに追加することをお勧めします。 このオブジェクトは、個別の XAML 名前スコープ内で FindName を呼び出すための有効なオブジェクトになりました。 このオブジェクトは、グローバル変数として使用できるようにしておくか、メソッド パラメーターを使用して渡すことができます。
- ビジュアル ツリーを調べることで、名前と XAML 名前スコープに関する考慮事項を完全に回避できます。 VisualTreeHelper API を使用すると、位置とインデックスに基づいて、親オブジェクトと子コレクションの観点からビジュアル ツリーを走査できます。
テンプレート内の XAML 名前スコープ
XAML のテンプレートでは、簡単な方法でコンテンツを再利用および再適用できますが、テンプレート にはテンプレート レベルで定義された名前を持つ要素も含まれる場合があります。 同じテンプレートが 1 つのページで複数回使用される場合があります。 このため、テンプレートは、スタイルまたはテンプレートが適用される包含ページとは無関係に、独自の XAML 名前スコープを定義します。 次の例を考えてみましょう。
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Page.Resources>
<ControlTemplate x:Key="MyTemplate">
....
<TextBlock x:Name="MyTextBlock" />
</ControlTemplate>
</Page.Resources>
<StackPanel>
<SomeControl Template="{StaticResource MyTemplate}" />
<SomeControl Template="{StaticResource MyTemplate}" />
</StackPanel>
</Page>
ここでは、同じテンプレートが 2 つの異なるコントロールに適用されます。 テンプレートに個別の XAML 名前スコープがない場合、テンプレートで使用される "MyTextBlock" 名によって名前の競合が発生します。 テンプレートの各インスタンス化には独自の XAML 名前スコープがあるため、この例では、インスタンス化された各テンプレートの XAML 名前スコープには正確に 1 つの名前が含まれます。 ただし、ルート XAML 名前スコープには、どちらのテンプレートからの名前も含まれません。
XAML 名前スコープが異なるため、テンプレートが適用されているページのスコープからテンプレート内の名前付き要素を検索するには、別の手法が必要です。 オブジェクト ツリー内のオブジェクトで FindName を呼び出すのではなく、まずテンプレートが適用されているオブジェクトを取得してから、 GetTemplateChild を呼び出します。 コントロール作成者で、適用されたテンプレート内の特定の名前付き要素がコントロール自体で定義されている動作のターゲットである規則を生成する場合は、コントロール実装コードから GetTemplateChild メソッドを使用できます。 GetTemplateChild メソッドは保護されているため、コントロール作成者のみがアクセスできます。 また、パーツとテンプレート パーツに名前を付け、コントロール クラスに適用される属性値としてレポートするために、コントロール作成者が従う必要がある規則もあります。 この手法を使用すると、重要なパーツの名前を検出して、別のテンプレートを適用するユーザーを制御できます。これは、コントロール機能を維持するために名前付きパーツを置き換える必要があります。
関連トピック
Windows developer