リソースの概要
ここでは、一般的に定義されたオブジェクトおよび値を再利用するための簡単な方法として、WPF リソースの使用方法について説明します。 ここでは、XAML におけるリソースの使用方法について重点的に説明します。 リソースの作成とアクセスに、コード、またはコードと Extensible Application Markup Language (XAML) の組み合わせを使用することもできます。 詳細については、「リソースとコード」を参照してください。
このトピックは、次のセクションで構成されています。
- XAML におけるリソースの使用
- 静的リソースと動的リソース
- スタイル、DataTemplate、および暗黙のキー
- 関連トピック
XAML におけるリソースの使用
次の例では、ページのルート要素上のリソースとして SolidColorBrush を定義します。 この例では、次にこのリソースを参照し、これを使用して、Ellipse、TextBlock、Button などの子要素のプロパティを設定します。
<Page Name="root"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
>
<Page.Resources>
<SolidColorBrush x:Key="MyBrush" Color="Gold"/>
<Style TargetType="Border" x:Key="PageBackground">
<Setter Property="Background" Value="Blue"/>
</Style>
<Style TargetType="TextBlock" x:Key="TitleText">
<Setter Property="Background" Value="Blue"/>
<Setter Property="DockPanel.Dock" Value="Top"/>
<Setter Property="FontSize" Value="18"/>
<Setter Property="Foreground" Value="#4E87D4"/>
<Setter Property="FontFamily" Value="Trebuchet MS"/>
<Setter Property="Margin" Value="0,40,10,10"/>
</Style>
<Style TargetType="TextBlock" x:Key="Label">
<Setter Property="DockPanel.Dock" Value="Right"/>
<Setter Property="FontSize" Value="8"/>
<Setter Property="Foreground" Value="{StaticResource MyBrush}"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Margin" Value="0,3,10,0"/>
</Style>
</Page.Resources>
<StackPanel>
<Border Style="{StaticResource PageBackground}">
<DockPanel>
<TextBlock Style="{StaticResource TitleText}">Title</TextBlock>
<TextBlock Style="{StaticResource Label}">Label</TextBlock>
<TextBlock DockPanel.Dock="Top" HorizontalAlignment="Left" FontSize="36" Foreground="{StaticResource MyBrush}" Text="Text" Margin="20" />
<Button DockPanel.Dock="Top" HorizontalAlignment="Left" Height="30" Background="{StaticResource MyBrush}" Margin="40">Button</Button>
<Ellipse DockPanel.Dock="Top" HorizontalAlignment="Left" Width="100" Height="100" Fill="{StaticResource MyBrush}" Margin="40" />
</DockPanel>
</Border>
</StackPanel>
</Page>
フレームワーク レベルの各要素 (FrameworkElement または FrameworkContentElement) には、Resources プロパティがあります。このプロパティには、リソースで定義する複数のリソース (ResourceDictionary として) が含まれます。 任意の要素上にリソースを定義できます。 ただし、通常は、ルート要素上にリソースを定義します。この例では Page がルート要素です。
リソース ディクショナリの各リソースは、一意のキーを持つ必要があります。 リソースをマークアップで定義するときに、x:Key ディレクティブを使用して一意のキーを割り当てます。 通常、キーは文字列ですが、適切なマークアップ拡張機能を使用すると、その他のオブジェクト型を設定することもできます。 文字列以外のリソース キーは、WPF の一部の機能エリアによって、特にスタイル、コンポーネント リソース、およびデータ スタイルで使用されます。
リソースを定義したら、キー名を指定するリソース マークアップ拡張機能構文を使用することにより、リソースを参照してプロパティ値で使用されるようにすることができます。次に例を示します。
<Button Background="{StaticResource MyBrush}"/>
<Ellipse Fill="{StaticResource MyBrush}"/>
前の例では、Button の Background プロパティの値 {StaticResource MyBrush} を XAML ローダーが処理するとき、リソース検索ロジックは、まず Button 要素のリソース ディクショナリをチェックします。 Button にリソース キー MyBrush の定義がない場合 (実際、リソース コレクションが空であるため定義がありません)、次に Button の親要素 Page をチェックします。 したがって、Page ルート要素上に定義したリソースは、Page の論理ツリー内のすべての要素からアクセスでき、このリソースの Type を受け入れる任意のプロパティに値を設定する際に再利用できます。 前の例では、同じ MyBrush リソースで 2 つの異なるプロパティを設定しています。Button の Background と、Rectangle の Fill です。
静的リソースと動的リソース
リソースは、静的リソースとしても、動的リソースとしても参照できます。 これを行うには、StaticResource のマークアップ拡張機能または DynamicResource のマークアップ拡張機能を使用します。 マークアップ拡張機能は XAML の一機能です。マークアップ拡張機能で属性文字列を処理することによってオブジェクト参照を指定し、そのオブジェクトを XAML ローダーに返すことができます。 マークアップ拡張機能の動作の詳細については、「マークアップ拡張機能と WPF XAML」を参照してください。
マークアップ拡張機能を使用する場合、通常は 1 つ以上のパラメーターを文字列形式で指定します。このパラメーターは、その特定のマークアップ拡張機能によって処理され、設定されるプロパティのコンテキスト内では評価されません。 StaticResource のマークアップ拡張機能は、使用可能なすべてのリソース ディクショナリ内でそのキーの値を検索することにより、キーを処理します。 これは読み込み時に行われます。この時点で、読み込みプロセスは、静的リソース参照を受け取るプロパティ値を割り当てる必要があります。 一方、DynamicResource のマークアップ拡張機能は、式を作成することによってキーを処理しますが、この式は、アプリケーションが実際に実行されるまで評価されません。アプリケーションの実行時に式が評価され、値が指定されます。
リソースの参照時に、静的リソース参照と動的リソース参照のどちらを使用するかに影響する可能性がある考慮事項は、次のとおりです。
アプリケーションに対するリソース作成方法 (ページ単位、アプリケーション内、Loose XAML 内、リソース専用アセンブリ内) の全体的な設計。
アプリケーションの機能。アプリケーションの要件に、リアルタイムでのリソースの更新が含まれるかどうか。
リソース参照の種類に応じた検索動作。
プロパティ型またはリソース型、およびそれらの型におけるネイティブの動作。
静的リソース
静的リソース参照は、次のような状況で最も適切に機能します。
アプリケーションの設計上、リソースのほぼすべてが、ページ レベルまたはアプリケーション レベルのリソース ディクショナリに集中している場合。 静的リソース参照では、ページの再読み込みなどのランタイム動作に基づく再評価が行われません。このため、リソースおよびアプリケーションの設計ごとに必要でない動的リソース参照が多数回避され、パフォーマンス上の利点となる可能性があります。
DependencyObject 上にも Freezable 上にもないプロパティの値を設定する場合。
作成するリソース ディクショナリを後で DLL にコンパイルし、アプリケーションの一部としてパッケージ化するか、複数アプリケーション間で共有する場合。
カスタム コントロールのテーマの作成時に、テーマ内で使用されるリソースを定義する場合。 この場合は、通常、動的リソース参照の検索動作は不適切です。検索がテーマにとって予測可能かつ自己完結的であるためには、静的リソース参照の動作が必要です。 動的リソース参照を使用すると、テーマ内での参照も実行時まで評価されません。このため、テーマが適用されたときに、テーマで参照しようとするキーがいずれかのローカル要素によって再定義され、そのローカル要素がテーマ自体より先に検索される可能性があります。 そのような場合、テーマは予期したとおりには動作しません。
多数の依存関係プロパティを設定するためにリソースを使用する場合。 依存関係プロパティでは、プロパティ システムによって有効な値のキャッシュが有効化されています。そのため、読み込み時に評価可能な値を依存関係プロパティに指定すると、依存関係プロパティは再評価された式があるかどうかをチェックする必要がなく、最後の有効な値を返すことができます。 この方法はパフォーマンス上の利点となる可能性があります。
すべてのコンシューマー用の基になるリソースを変更するか、x:Shared 属性を使用して各コンシューマー用の個別の書き込み可能インスタンスを保持する場合。
静的リソースの検索動作
検索プロセスは、プロパティを設定する要素で定義されているリソース ディクショナリに、要求されたキーがないか確認します。
次に、検索プロセスは、論理ツリーを親要素とそのリソース ディクショナリまで上方向に処理します。 これは、ルート要素に到達するまで続行されます。
次に、アプリケーション リソースがチェックされます。 アプリケーション リソースは、WPF アプリケーションの Application オブジェクトで定義されているリソース ディクショナリ内のリソースです。
リソース ディクショナリ内からの静的リソース参照で参照するリソースは、リソース参照前に語彙的に定義されている必要があります。 静的リソース参照では前方参照を解決できません。 このため、静的リソース参照を使用する場合は、他のリソースによって使用される予定のリソースを、各リソース ディクショナリの冒頭または冒頭付近で定義するよう、リソース ディクショナリ構造を設計する必要があります。
静的リソース検索は、テーマまたはシステム リソースまで拡張できますが、これがサポートされるのは、XAML ローダーによって要求が遅延されるためです。 ページの読み込み時のランタイム テーマがアプリケーションに正しく適用されるためには、遅延が必要です。 ただし、テーマ内にのみ、またはシステム リソースとしてのみ存在することがわかっているキーに対しては、静的リソース参照の使用をお勧めしません。 ユーザーによってリアルタイムにテーマが変更されても、そのような参照は再評価されないためです。 テーマやシステム リソースの要求時には、動的リソース参照の方が信頼できます。 例外は、テーマ要素自体が別のリソースを要求する場合です。 このような参照は、前述した理由により、静的リソース参照とする必要があります。
静的リソース参照が見つからない場合の例外動作は、場合によって異なります。 リソースが遅延された場合は、実行時に例外が発生します。 リソースが遅延されなかった場合は、読み込み時に例外が発生します。
動的リソース
動的リソースは、次のような状況で最も適切に機能します。
リソースの値が、実行時まで判明しない条件に依存する場合。 これには、システム リソース、または他のユーザーが設定可能なリソースが含まれます。 たとえば、SystemColors、SystemFonts、または SystemParameters によって公開されるシステム プロパティを参照する setter 値を作成できます。 これらの値は、最終的にユーザーのランタイム環境とオペレーティング システムによって決まるため、完全に動的です。 また、変更可能なアプリケーション レベルのテーマがある場合に、ページ レベルのリソース アクセスでその変更を認識する必要が生じることもあります。
カスタム コントロールのテーマ スタイルを作成または参照する場合。
アプリケーションの有効期間中に ResourceDictionary のコンテンツを調整する予定がある場合。
依存関係が含まれる複雑なリソース構造で、前方参照の必要が生じる可能性がある場合。 静的リソース参照では前方参照がサポートされませんが、動的リソース参照ではサポートされます。実行時までリソースが評価される必要がないため、前方参照の概念には関連しないためです。
コンパイルまたは作業セットの観点からは非常に大規模であり、ページの読み込み時に直ちに使用されない可能性があるリソースを参照する場合。 静的リソース参照は、ページの読み込み時に常に XAML から読み込まれます。これに対して動的リソース参照は、実際に使用されるまで読み込まれません。
作成するスタイルの setter 値が、テーマやその他のユーザー設定の影響を受ける別の値によって決まる可能性がある場合。
リソースを適用する要素に対し、アプリケーションの有効期間中に論理ツリー内で親の再指定が行われる可能性がある場合。 親が変更されると、リソース検索スコープも変更される可能性があります。親が再指定された要素用のリソースを、新しいスコープに基づいて再評価するには、必ず動的リソース参照を使用してください。
動的リソースの検索動作
動的リソース参照のリソース検索動作は、FindResource または SetResourceReference を呼び出す場合のコードでの検索動作と同じです。
検索プロセスは、プロパティを設定する要素で定義されているリソース ディクショナリに、要求されたキーがないか確認します。
次に、検索プロセスは、論理ツリーを親要素とそのリソース ディクショナリまで上方向に処理します。 これは、ルート要素に到達するまで続行されます。
次に、アプリケーション リソースがチェックされます。 アプリケーション リソースは、WPF アプリケーションの Application オブジェクトで定義されているリソース ディクショナリ内のリソースです。
テーマ リソース ディクショナリで、現在アクティブなテーマがチェックされます。 実行時にテーマが変更されると、値が再評価されます。
システム リソースがチェックされます。
例外動作 (存在する場合) は、場合によって異なります。
FindResource 呼び出しによって要求されたリソースが見つからなかった場合は、例外が発生します。
TryFindResource 呼び出しによって要求されたリソースが見つからなかった場合は、例外が発生せずに、null 値が返されます。 設定されるプロパティが null を受け入れない場合、より深いレベルの例外が発生する可能性があります (設定されるプロパティによって異なります)。
XAML 内の動的リソース参照によって要求されたリソースが見つからなかった場合、動作はプロパティ システム全体に依存しますが、通常の動作は、リソースが存在するレベルでプロパティ設定操作がまったく行われなかった場合の動作です。 たとえば、評価できなかったリソースを使用して、独立したボタン要素に背景を設定しようとした場合、値の設定は行われませんが、プロパティ システム内のその他の構成要素と、値の優先順位から、有効値が決まる可能性があります。 たとえば、ローカルに定義されたボタン スタイルによって、またはテーマ スタイルによって、背景値が決まる可能性があります。 テーマ スタイルで定義されていないプロパティの場合、リソース評価に失敗した後の有効値が、プロパティ メタデータのデフォルト値によって決まる可能性があります。
制約
動的リソース参照には、注意すべき制限がいくつかあります。 少なくとも次のうちの 1 つが満たされている必要があります。
設定されるプロパティは、FrameworkElement 上または FrameworkContentElement 上のプロパティである必要があります。 そのプロパティは、DependencyProperty によってサポートされる必要があります。
設定されるプロパティは、FrameworkElement プロパティまたは FrameworkContentElement プロパティの値として指定されるか、Setter 値として指定される、Freezable 上のプロパティである必要があります。
設定されるプロパティが DependencyProperty プロパティか Freezable プロパティに限られるため、プロパティ変更 (変更された動的リソース値) がプロパティ システムによって確認され、ほとんどのプロパティ変更を UI に反映できます。 ほとんどのコントロールに含まれているロジックでは、DependencyProperty が変更され、そのプロパティがレイアウトに影響する可能性がある場合に、コントロールの別のレイアウトが強制されます。 ただし、DynamicResource のマークアップ拡張機能を値として持つプロパティは、必ずしも UI 内でリアルタイムに更新されるような方法で値を提供するとは限りません。 プロパティ、プロパティを所有する型、またはアプリケーションの論理構造によっても、その機能が異なる場合があります。
スタイル、DataTemplate、および暗黙のキー
前述したとおり、ResourceDictionary 内のすべての項目はキーを持つ必要があります。 しかし、すべてのリソースが明示的な x:Key を持つ必要があるという意味ではありません。 いくつかのオブジェクト型は、リソースとして定義された場合に暗黙のキーをサポートします。このキーの値は、別のプロパティの値に関連付けられます。 これが暗黙のキーと呼ばれるのに対して、x:Key 属性は明示的なキーです。 明示的なキーを指定することにより、任意の暗黙のキーを上書きできます。
リソースに関する重要なシナリオの 1 つとして、Style を定義する場合があります。 実際、Style は、リソース ディクショナリのエントリとしてほぼ必ず定義されます。スタイルは、本質的に再利用を目的としているためです。 スタイルの詳細については、「スタイルとテンプレート」を参照してください。
コントロールのスタイルは、暗黙のキーを使用して作成と参照の両方ができます。 コントロールの既定の外観を定義するテーマ スタイルは、この暗黙のキーに依存します。 要求する側から見ると、暗黙のキーはコントロール自体の Type です。 リソースを定義する側から見ると、暗黙のキーはスタイルの TargetType です。 したがって、既存のテーマ スタイルと対話するスタイルを作成して、カスタム コントロールのテーマを作成する場合、その Style に x:Key ディレクティブを指定する必要はありません。 そのテーマ スタイルを使用する場合、スタイルを指定する必要はまったくありません。 たとえば、次のスタイル定義では Style リソースがキーを持たないように見えますが、問題なく機能します。
<Style TargetType="Button">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush>
<GradientStop Offset="0.0" Color="AliceBlue"/>
<GradientStop Offset="1.0" Color="Salmon"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="FontSize" Value="18"/>
</Style>
このスタイルは実際にはキーを持っています。暗黙のキー typeof(Button) です。 マークアップで、TargetType を型名として直接指定できます (オプションで {x:Type...} を使用して Type を返すこともできます)。
Button 自体は、その Style プロパティや、スタイルへの具体的なリソース参照を指定しようとしませんが、WPF で使用される既定のテーマ スタイル機構を通じて、そのスタイルがページ上の Button のランタイム スタイルとして適用されます。 ページに定義されたスタイルは、テーマ ディクショナリのスタイルと同じキーを使用すると、検索順序の前の方で、テーマ ディクショナリのスタイルよりも先に見つかります。 ページの任意の場所に <Button>Hello</Button> を指定するだけで、Button の TargetType に定義したスタイルがそのボタンに適用されます。 マークアップをわかりやすくするため、TargetType と同じ型の値を持つスタイルを明示的に入力することもできますが、必須ではありません。
OverridesDefaultStyle が true の場合、スタイルの暗黙のキーがコントロールに適用されません (OverridesDefaultStyle は、コントロールのインスタンス上で明示的に設定されるのではなく、コントロール クラスのネイティブな動作の一部として設定される場合があります)。 また、派生クラスのシナリオで暗黙のキーをサポートするためには、コントロールで DefaultStyleKey をオーバーライドする必要があります (WPF の一部として提供されている既存のコントロールはすべてこれを行います)。 スタイル、テーマ、およびコントロール設計の詳細については、「スタイルの設定が可能なコントロールを設計するためのガイドライン」を参照してください。
DataTemplate も暗黙のキーを持ちます。 DataTemplate の暗黙のキーは DataType プロパティ値です。 {x:Type...} を明示的に使用するのではなく、DataType を型の名前として指定することもできます。 詳細については、「データ テンプレートの概要」を参照してください。