依存関係プロパティ値の優先順位 (WPF .NET)

Windows Presentation Foundation (WPF) プロパティ システムの動作は、依存関係プロパティの値に影響を与えます。 この記事では、WPF プロパティ システム内のさまざまなプロパティベースの入力の優先順位によって、依存関係プロパティの有効値がどのように決定されるかについて説明します。

重要

.NET 7 と .NET 6 用のデスクトップ ガイド ドキュメントは作成中です。

必須コンポーネント

この記事では、依存関係プロパティの基本的な知識と、依存関係プロパティの概要に関する記事を参照済みであることを前提としています。 この記事の例について理解するには、Extensible Application Markup Language (XAML) を使い慣れていて、WPF アプリケーションの記述方法を理解していると役に立ちます。

WPF プロパティ システム

WPF プロパティ システムでは、さまざまな要因を使用して依存関係プロパティの値が決定されます。たとえば、リアルタイムのプロパティ検証、遅延バインド、関連するプロパティのプロパティ変更通知などです。 依存関係プロパティ値の決定に使用される順序とロジックは複雑ですが、これを学習することによって不要なプロパティ設定を避けることができます。また、依存関係プロパティを設定しようとしたときに予期した値が得られなかった理由を把握することもできます。

複数の場所で設定される依存関係プロパティ

次の XAML の例は、ボタンの Background プロパティに対する 3 つの異なる "設定" 操作が、その値にどのような影響を与えるかを示しています。

<StackPanel>
    <StackPanel.Resources>
        <ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
            <Border Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" 
                    BorderBrush="{TemplateBinding BorderBrush}">
                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
            </Border>
        </ControlTemplate>
    </StackPanel.Resources>

    <Button Template="{StaticResource ButtonTemplate}" Background="Red">
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <Setter Property="Background" Value="Blue"/>
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="Yellow" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
        Which color do you expect?
    </Button>
</StackPanel>

この例では、Background プロパティはローカルで Red に設定されています。 しかし、ボタンのスコープ内で宣言されている暗黙的なスタイルでは、Background プロパティを Blue に設定しようとしています。 また、マウスがボタン上にあるときは、暗黙的なスタイルのトリガーによって Background プロパティを Yellow に設定しようとしています。 強制型変換とアニメーションを除き、ローカルに設定されたプロパティ値が最も優先順位が高いため、ボタンは (マウス ポイント時でも) 赤色になります。 ただし、ローカルに設定された値をボタンから削除した場合、その Background 値はスタイルから取得されます。 スタイル内ではトリガーが優先されるので、ボタンはマウス ポイント時には黄色、それ以外の場合は青になります。 この例ではボタンの既定の ControlTemplate が置き換えられています。既定のテンプレートではマウス ポイントの Background 値がハードコーディングされているためです。

依存関係プロパティの優先順位リスト

次の一覧は、プロパティ システムで依存関係プロパティにランタイム値を割り当てるときに使用される、優先順位の決定的な順序を示しています。 優先順位が高いものから順に示されています。

  1. プロパティ システムの強制型変換。 強制型変換の詳細については、「強制型変換とアニメーション」を参照してください。

  2. アクティブなアニメーション、または保留動作のアニメーション。 実際的な効果を得るためには、アニメーション値が、基本 (アニメーション化されていない) 値よりも高い優先順位を持つ必要があります (基本値がローカルに設定されていても)。 詳細については、「強制型変換とアニメーション」を参照してください。

  3. ローカル値。 ローカル値を設定するには、"ラッパー" プロパティを使用するか (これは XAML で属性またはプロパティ要素を設定することに相当します)、または特定のインスタンスのプロパティを使用して SetValue API を呼び出します。 バインドまたはリソースによって設定されたローカル値は、直接設定された値と同じ優先順位を持ちます。

  4. TemplatedParent テンプレート プロパティ値。 テンプレート (ControlTemplate または DataTemplate) によって作成された場合、要素には TemplatedParent があります。 詳細については、「TemplatedParent」を参照してください。 TemplatedParent によって指定されたテンプレート内では、優先順位は次のようになります。

    1. トリガー。

    2. プロパティ セット。通常は XAML 属性を使用します。

  5. 暗黙的なスタイルStyle プロパティのみに適用されます。 Style 値は、要素の型に一致する TargetType 値を持つ任意のスタイル リソースです。 スタイル リソースは、ページまたはアプリケーション内に存在する必要があります。 暗黙的なスタイル リソースの検索は、テーマ内のスタイル リソースにまでは及びません。

  6. スタイル トリガー。 スタイル トリガーは、明示的または暗黙的なスタイル内のトリガーです。 スタイルは、ページまたはアプリケーション内に存在する必要があります。 既定のスタイル内のトリガーは、優先順位が低くなります。

  7. テンプレートのトリガー。 テンプレートのトリガーは、直接適用されたテンプレート、またはスタイル内のテンプレートからのトリガーです。 スタイルは、ページまたはアプリケーション内に存在する必要があります。

  8. スタイルのセッター値。 スタイルのセッター値は、スタイル内の Setter によって適用される値です。 スタイルは、ページまたはアプリケーション内に存在する必要があります。

  9. 既定のスタイル (テーマ スタイルとも呼ばれます)。 詳細については、「既定 (テーマ) のスタイル」を参照してください。 既定のスタイル内では、優先順位は次のようになります。

    1. アクティブなトリガー。

    2. セッター。

  10. 継承。 子要素の依存関係プロパティの一部は、親要素からその値を継承します。 そのため、アプリケーション全体ですべての要素でプロパティ値を設定する必要がない場合があります。 詳細については、「プロパティ値の継承」を参照してください。

  11. 依存関係プロパティのメタデータの既定値 依存関係プロパティには、そのプロパティのプロパティ システム登録時に、既定値を設定できます。 依存関係プロパティを継承する派生クラスでは、型ごとに依存関係プロパティのメタデータ (既定値を含む) をオーバーライドできます。 詳細については、「依存関係プロパティのメタデータ」を参照してください。 継承されたプロパティの場合、親要素の既定値は、子要素の既定値よりも優先されます。 そのため、継承可能なプロパティが設定されていない場合は、ルートまたは親の既定値が、子要素の既定値の代わりに使用されます。

TemplatedParent

TemplatedParent の優先順位は、標準的なアプリケーション マークアップで直接宣言されている要素のプロパティには適用されません。 TemplatedParent の概念は、テンプレートの適用によって作成されるビジュアル ツリー内の子項目に対してのみ存在します。 プロパティ システムで、ある要素のプロパティ値の TemplatedParent によって指定されたテンプレートが検索される場合は、その要素を作成したテンプレートが検索されます。 TemplatedParent テンプレートから設定されるプロパティ値は、通常、要素でローカルに設定された値のように処理されますが、テンプレートは共有される可能性があるため、実際のローカル値よりも優先順位が低くなります。 詳細については、 TemplatedParent を参照してください。

Style プロパティ

Style プロパティを除き、すべての依存関係プロパティに同じ優先順位が適用されます。 Style プロパティは、それ自体はスタイル設定できないという点で独特です。 Style プロパティに対して強制型変換またはアニメーション化を行うことは推奨されません (また、Style プロパティのアニメーション化にはカスタム アニメーション クラスが必要です)。 そのため、すべての優先順位項目が適用されるわけではありません。 Style プロパティを設定する方法は、次の 3 つだけです。

  • 明示的スタイル。 要素の Style プロパティが直接設定されます。 Style プロパティ値は、ローカル値であるかのように処理され、優先順位リストの項目 3 と同じ優先順位を持ちます。 ほとんどのシナリオでは、明示的スタイルはインラインで定義されず、代わりにリソースとして明示的に参照されます (例: Style="{StaticResource myResourceKey}")。

  • 暗黙的スタイル。 要素の Style プロパティが直接設定されません。 代わりに、スタイルは、ページまたはアプリケーション内のあるレベルに存在するときに適用され、スタイルが適用される要素の型に一致するリソース キーを持ちます (例: <Style TargetType="x:Type Button">)。 型は厳密に一致する必要があります。たとえば、<Style TargetType="x:Type Button">MyButton 型には (MyButtonButton から派生していても) 適用されません。 Style プロパティ値は、優先順位リストの項目 5 と同じ優先順位を持ちます。 暗黙的なスタイルの値を検出するには、DependencyPropertyHelper.GetValueSource メソッドを呼び出して Style プロパティを渡し、結果の ImplicitStyleReference をチェックします。

  • 既定のスタイル (テーマ スタイルとも呼ばれます)。 要素の Style プロパティが直接設定されません。 代わりに、WPF プレゼンテーション エンジンによる実行時のテーマの評価から取得されます。 実行前の Style プロパティ値は null です。 Style プロパティ値は、優先順位リストの項目 9 と同じ優先順位を持ちます。

既定 (テーマ) のスタイル

WPF に付属するすべてのコントロールは既定のスタイルを持ち、それはテーマによって異なる場合があります。そのため、既定のスタイルは "テーマ スタイル" と呼ばれることもあります。

ControlTemplate は、コントロールの既定のスタイル内の重要な項目です。 ControlTemplate は、スタイルの Template プロパティのセッター値です。 既定のスタイルにテンプレートが含まれていなかった場合、カスタム スタイルの一部としてカスタム テンプレートがないコントロールは、表示されません。 テンプレートによって、コントロールの外観が定義されるだけでなく、テンプレートのビジュアル ツリー内のプロパティと対応するコントロール クラスの間の接続も定義されます。 各コントロールでは、テンプレートを置き換えることなくコントロールの外観に影響を与えることができる、一連のプロパティが公開されています。 例として、ScrollBar のコンポーネントである、Thumb コントロールの既定の外観について考えてみましょう。

Thumb コントロールには、特定のカスタマイズ可能なプロパティがあります。 Thumb コントロールの既定のテンプレートでは、べベルの外観を作成する入れ子になった複数の Border コンポーネントを含む、基本的な構造 (またはビジュアル ツリー) が作成されます。 テンプレート内では、Thumb クラスによってカスタマイズできるように意図されたプロパティが TemplateBinding を介して公開されています。 Thumb コントロールの既定のテンプレートには、BackgroundBorderThickness などのプロパティとテンプレートのバインドを共有する、さまざまな罫線プロパティがあります。 ただし、プロパティや視覚的な配置の値がテンプレートでハードコーディングされていたり、テーマから直接取得される値にバインドされていたりする場所では、テンプレート全体を置き換えない限りそれらの値を変更できません。 一般に、プロパティがテンプレート化された親から取得され、TemplateBinding によって公開されていない場合は、スタイルによってそのプロパティ値を変更することはできません。それをターゲットにするための便利な方法がないためです。 ただし、適用されるテンプレートのプロパティ値の継承によって、または既定値によって、そのプロパティに影響を与えることはできます。

既定のスタイルでは、その定義で TargetType が指定されます。 実行時のテーマの評価では、既定のスタイルの TargetType とコントロールの DefaultStyleKey プロパティが照合されます。 これに対し、暗黙的スタイルの検索の動作では、コントロールの実際の型が使用されます。 DefaultStyleKey の値は派生クラスによって継承されるため、それ以外の場合は関連付けられたスタイルがない可能性のある派生要素は、既定の外観を取得します。 たとえば、Button から MyButton を派生させる場合、MyButtonButton の既定のテンプレートを継承します。 派生クラスでは、依存関係プロパティのメタデータで DefaultStyleKey の既定値をオーバーライドできます。 そのため、MyButton で別の視覚的表現を使用したい場合は、MyButtonDefaultStyleKey の依存関係プロパティのメタデータをオーバーライドしてから、テンプレートを含む関連する既定のスタイルを定義し、それを MyButton コントロールでパッケージ化することができます。 詳細については、「コントロールの作成の概要」を参照してください。

動的リソース

動的リソースの参照およびバインド操作には、それらが設定される場所での優先順位が適用されます。 たとえば、ローカル値に適用される動的リソースは、優先順位リストの項目 3 と同じ優先順位を持ちます。 別の例として、既定のスタイル内のプロパティ セッターに適用される動的リソースのバインドは、優先順位リストの項目 9 と同じ優先順位を持ちます。 動的リソースの参照とバインドではアプリケーションの実行時の状態から値を取得する必要があるため、特定のプロパティに関するプロパティ値の優先順位を決定するプロセスは、実行時にまで及びます。

動的リソースの参照は、技術的にはプロパティ システムの一部ではなく、優先順位リストと対話する独自の検索順序を持っています。 基本的に、動的リソース参照の優先順位は、ページ ルートの要素、アプリケーション、テーマ、システムという順になります。 詳細については、「XAML リソース」を参照してください。

動的リソースの参照とバインドにはそれらが設定される場所での優先順位が適用されますが、値は遅延されます。 その 1 つの帰結として、ローカル値に動的リソースまたはバインドを設定した場合、ローカル値を変更すると動的リソースまたはバインドが完全に置き換えられます。 ClearValue メソッドを呼び出してローカルに設定された値をクリアした場合であっても、動的リソースまたはバインドは復元されません。 実際、動的リソースまたはバインドが設定されている (リテラル ローカル値を持たない) プロパティに対して ClearValue を呼び出すと、動的リソースまたはバインドはクリアされます。

SetCurrentValue

SetCurrentValue メソッドはプロパティを設定するもう 1 つの方法ですが、優先順位リストには含まれていません。 SetCurrentValue を使用すると、前の値のソースを上書きすることなく、プロパティの値を変更することができます。 たとえば、トリガーによってプロパティが設定された後、SetCurrentValue を使用して別の値を割り当てると、次のトリガー アクションによってプロパティはトリガー値に戻されます。 ローカル値の優先順位を値に与えることなくプロパティ値を設定したい場合は、いつでも SetCurrentValue を使用することができます。 同様に、SetCurrentValue を使用すると、バインドを上書きすることなく、プロパティの値を変更できます。

強制型変換とアニメーション

強制型変換とアニメーションは、どちらも "基本値" に対して動作します。 "基本値" は、優先順位が最も高い依存関係プロパティの値です。優先順位リストを項目 2 に到達するまで上に評価していくことによって決定されます。

アニメーションで特定の動作に対して FromTo の両方のプロパティ値が指定されていない場合、またはアニメーションが完了すると基本値に意図的に戻る場合は、基本値がアニメーション化される値に影響を及ぼす可能性があります。 その実際の例を確認するには、Target Values サンプル アプリケーションを実行してください。 このサンプルで、四角形の高さに対して、From 値とは異なる初期のローカル値を設定してみます。 サンプル アニメーションは、基本値ではなく From 値を使用してすぐに開始されます。 FillBehavior として Stop を指定すると、完了時にアニメーションによってプロパティ値がその基本値にリセットされます。 アニメーションが終了した後の基本値の決定に、通常の優先順位が使用されます。

複数のアニメーションを 1 つのプロパティに適用し、それぞれのアニメーションに異なる優先順位を設定することができます。 最も高い優先順位でアニメーションを適用する代わりに、WPF プレゼンテーション エンジンよって、アニメーションの定義方法やアニメーション化される値の型に応じて、アニメーション値が合成されることがあります。 詳しくは、「アニメーションの概要」をご覧ください。

強制型変換は、優先順位リストの先頭にあります。 実行中のアニメーションであっても値の強制型変換が適用されます。 WPF の既存の依存関係プロパティの一部には、組み込みの強制型変換があります。 カスタム依存関係プロパティの場合は、プロパティの作成時にメタデータの一部として渡す CoerceValueCallback を記述することによって、強制型変換の動作を定義できます。 派生クラスでプロパティのメタデータをオーバーライドすることにより、既存のプロパティの強制型変換の動作をオーバーライドすることもできます。 強制型変換は、強制型変換に対する制約がその時点で存在するものとして適用されるように基本値と対話しますが、基本値は引き続き保持されます。 そのため、強制型変換の制約が後で無効になった場合は、強制型変換によってその基本値に可能な限り最も近い値が返され、プロパティに対する強制型変換の影響はすべての制約が無効になるとすぐになくなる可能性があります。 強制型変換の動作について詳しくは、「依存関係プロパティのコールバックと検証」を参照してください。

トリガー動作

コントロールでは、既定のスタイルの一部としてトリガー動作が定義されていることがよくあります。 コントロールにローカル プロパティを設定すると、それらのトリガーと競合が発生し、トリガーがユーザー駆動イベントに (視覚的または動作的に) 応答しなくなる可能性があります。 プロパティ トリガーの一般的な用途は、IsSelectedIsEnabled などの状態プロパティを制御することです。 たとえば、既定では、Button が無効の場合は、テーマ スタイルのトリガー (IsEnabledfalse) によって Button が灰色表示されるように Foreground 値が設定されます。ローカルの Foreground 値を設定した場合は、より優先順位の高いローカル プロパティ値がテーマ スタイルの Foreground 値よりも優先されます (Button が無効になっていても)。 コントロールについてテーマレベルのトリガー動作をオーバーライドするプロパティ値を設定する場合は、そのコントロールの意図されているユーザー エクスペリエンスを必要以上に妨げないように注意してください。

ClearValue

ClearValue メソッドを使用すると、要素の依存関係プロパティのローカルに適用された値がクリアされます。 ただし、ClearValue の呼び出しでは、プロパティの登録時にメタデータで設定された既定値が新しい有効な値になることは保証されません。 優先順位リスト内の他のすべての要素はアクティブなままであり、ローカルに設定された値のみが削除されます。 たとえば、テーマ スタイルを持つプロパティに対して ClearValue を呼び出した場合、メタデータに基づく既定値ではなく、テーマ スタイルの値が新しい値として適用されます。 プロパティ値を登録済みメタデータの既定値に設定する場合は、依存関係プロパティのメタデータを照会して既定のメタデータ値を取得し、SetValue を呼び出してプロパティ値をローカルに設定します。

関連項目