プロパティ値の継承 (WPF .NET)

プロパティ値の継承は、Windows Presentation Foundation (WPF) プロパティ システムの機能であり、依存関係プロパティに適用されます。 プロパティ値の継承を使用すると、要素ツリー内の子要素で、最も近い親要素から特定のプロパティの値を取得できます。 親要素もプロパティ値の継承を通じてそのプロパティ値を取得している場合があるため、システムがページ ルートまで再帰する可能性があります。

WPF プロパティ システムでは、プロパティ値の継承は既定で有効になりません。また、依存関係プロパティのメタデータで明確に有効にされていない限り、値の継承は非アクティブになります。 プロパティ値の継承が有効になっている場合でも、子要素はより高い優先順位値がない場合にのみプロパティ値を継承します。

前提条件

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

要素ツリーを介した継承

プロパティ値の継承は、派生クラスが基底クラスのメンバーを継承するオブジェクト指向プログラミングのクラス継承と同じ概念ではありません。 その種類の継承は WPF でもアクティブですが、XAML では、継承された基底クラスのプロパティは、派生クラスを表す XAML 要素の属性として公開されます。

プロパティ値の継承は、依存関係プロパティを含む要素のツリー内の親要素から子要素に依存関係プロパティ値が伝達されるメカニズムです。 XAML マークアップでは、要素のツリーは入れ子になった要素として表示されます。

次の例は、XAML の入れ子になった要素を示しています。 WPF では、プロパティ値の継承を有効にし、既定値を false に設定するプロパティのメタデータを指定して、UIElement クラスに AllowDrop 依存関係プロパティを登録します。 AllowDrop 依存関係プロパティは、CanvasStackPanelLabel 要素に存在します。これらはすべて UIElement から派生するためです。 canvas1AllowDrop 依存関係プロパティは true に設定されているため、その子孫の stackPanel1 および label1 要素では AllowDrop 値として true が継承されます。

<Canvas x:Name="canvas1" Grid.Column="0" Margin="20" Background="Orange" AllowDrop="True">
    <StackPanel Name="stackPanel1" Margin="20" Background="Green">
        <Label Name="label1" Margin="20" Height="40" Width="40" Background="Blue"/>
    </StackPanel>
</Canvas>

別の要素オブジェクトの子要素のコレクションに要素オブジェクトを追加することで、プログラムによって要素のツリーを作成することもできます。 実行時に、結果のオブジェクト ツリーでプロパティ値の継承が行われます。 次の例では、canvas2子コレクションstackPanel2 が追加されます。 同様に、stackPanel2 の子コレクションに label2 が追加されます。 canvas2AllowDrop 依存関係プロパティは true に設定されているため、その子孫の stackPanel2 および label2 要素では AllowDrop 値として true が継承されます。

Canvas canvas2 = new()
{
    AllowDrop = true
};
StackPanel stackPanel2 = new();
Label label2 = new();
canvas2.Children.Add(stackPanel2);
stackPanel2.Children.Add(label2);
Dim canvas2 As New Canvas With {
    .AllowDrop = True
}
Dim stackPanel2 As New StackPanel()
Dim label2 As New Label()
canvas2.Children.Add(stackPanel2)
stackPanel2.Children.Add(label2)

プロパティ値の継承の実際の応用例

特定の WPF 依存関係プロパティでは、値の継承が既定で有効になります (AllowDropFlowDirection など)。 通常、値の継承が既定で有効になるプロパティは、基底 UI 要素クラスに実装されるため、派生クラスに存在します。 たとえば、AllowDropUIElement 基底クラスに実装されるため、その依存関係プロパティは UIElement から派生するすべてのコントロールにも存在します。 WPF では、ユーザーが親要素に対してプロパティ値を 1 回設定して、そのプロパティ値を要素ツリーの子孫要素に反映させる場合に便利な、依存関係プロパティに対する値の継承が可能です。

プロパティ値の継承モデルでは、依存関係プロパティ値の優先順位に従って、継承されるプロパティ値と継承されないプロパティ値の両方が割り当てられます。 そのため、親要素のプロパティ値は、子要素のプロパティの優先順位値 (ローカルに設定された値や、スタイル、テンプレート、データ バインディングを使用して取得された値など) がより高くない場合にのみ子要素に適用されます。

FlowDirection 依存関係プロパティでは、親要素内のテキスト要素と子 UI 要素のレイアウト方向が設定されます。 通常は、ページ内のテキスト要素と UI 要素のフロー方向が一貫していると想定されます。 FlowDirection のプロパティのメタデータで値の継承が有効になっているため、値はページの要素ツリーの最上位で 1 回設定する必要があるだけです。 まれに、フロー方向の混在がページで意図されている場合は、ローカルに設定された値を割り当てることによって、ツリー内の要素に異なるフロー方向を設定できます。 その後、新しいフロー方向が、そのレベルより下の子孫要素に伝達されます。

カスタム プロパティを継承可能にする

カスタム依存関係プロパティを継承可能にするには、FrameworkPropertyMetadata のインスタンスで Inherits プロパティを有効にしてから、そのメタデータ インスタンスにカスタム依存関係プロパティを登録します。 既定では、FrameworkPropertyMetadataInheritsfalse に設定されています。 プロパティ値を継承可能にするとパフォーマンスに影響するため、その機能が必要な場合にのみ Inheritstrue に設定します。

メタデータで Inherits が有効になっている依存関係プロパティを登録する場合は、添付プロパティの登録に関する記事の説明に従って RegisterAttached メソッドを使用します。 また、継承可能な値が存在するように、プロパティに既定値を割り当てます。 添付されていない依存関係プロパティの場合と同様に、所有者の種類に対して get および set アクセサーを持つプロパティ ラッパーを作成することもできます。 このようにして、所有者の種類または派生した種類に対してプロパティ ラッパーを使用して、プロパティ値を設定できます。 次の例では、Inherits を有効にして既定値に false を設定し、IsTransparent という名前の依存関係プロパティを作成しています。 この例には、get および set アクセサーを持つプロパティ ラッパーも含まれています。

public class Canvas_IsTransparentInheritEnabled : Canvas
{
    // Register an attached dependency property with the specified
    // property name, property type, owner type, and property metadata
    // (default value is 'false' and property value inheritance is enabled).
    public static readonly DependencyProperty IsTransparentProperty =
        DependencyProperty.RegisterAttached(
            name: "IsTransparent",
            propertyType: typeof(bool),
            ownerType: typeof(Canvas_IsTransparentInheritEnabled),
            defaultMetadata: new FrameworkPropertyMetadata(
                defaultValue: false,
                flags: FrameworkPropertyMetadataOptions.Inherits));

    // Declare a get accessor method.
    public static bool GetIsTransparent(Canvas element)
    {
        return (bool)element.GetValue(IsTransparentProperty);
    }

    // Declare a set accessor method.
    public static void SetIsTransparent(Canvas element, bool value)
    {
        element.SetValue(IsTransparentProperty, value);
    }

    // For convenience, declare a property wrapper with get/set accessors.
    public bool IsTransparent
    {
        get => (bool)GetValue(IsTransparentProperty);
        set => SetValue(IsTransparentProperty, value);
    }
}
Public Class Canvas_IsTransparentInheritEnabled
    Inherits Canvas

    ' Register an attached dependency property with the specified
    ' property name, property type, owner type, and property metadata
    ' (default value is 'false' and property value inheritance is enabled).
    Public Shared ReadOnly IsTransparentProperty As DependencyProperty =
        DependencyProperty.RegisterAttached(
            name:="IsTransparent",
            propertyType:=GetType(Boolean),
            ownerType:=GetType(Canvas_IsTransparentInheritEnabled),
            defaultMetadata:=New FrameworkPropertyMetadata(
                defaultValue:=False,
                flags:=FrameworkPropertyMetadataOptions.[Inherits]))

    ' Declare a get accessor method.
    Public Shared Function GetIsTransparent(element As Canvas) As Boolean
        Return element.GetValue(IsTransparentProperty)
    End Function

    ' Declare a set accessor method.
    Public Shared Sub SetIsTransparent(element As Canvas, value As Boolean)
        element.SetValue(IsTransparentProperty, value)
    End Sub

    ' For convenience, declare a property wrapper with get/set accessors.
    Public Property IsTransparent As Boolean
        Get
            Return GetValue(IsTransparentProperty)
        End Get
        Set(value As Boolean)
            SetValue(IsTransparentProperty, value)
        End Set
    End Property
End Class

添付プロパティは、概念的にはグローバル プロパティに似ています。 任意の DependencyObject で値を確認し、有効な結果を取得できます。 添付プロパティの典型的なシナリオとして、子要素のプロパティ値を設定する場合があります。このシナリオは、該当のプロパティがツリー内の各 DependencyObject 要素の添付プロパティとして暗黙的に存在する場合に、さらに効果的です。

ツリー境界を越えてプロパティ値を継承する

プロパティの継承は、要素のツリーを走査して行われます。 多くの場合、このツリーは論理ツリーに対応します。 ただし、要素ツリーを定義するマークアップ内に WPF コアレベルのオブジェクト (Brush など) を含めると、連続しない論理ツリーが作成されます。 論理ツリーは WPF フレームワークレベルの概念であるため、本当の論理ツリーは Brush を通じて概念的に拡張されません。 LogicalTreeHelper のヘルパー メソッドを使用すると、論理ツリーの範囲を分析および表示できます。 プロパティ値の継承では、連続しない論理ツリーを介して継承された値を渡すことができますが、継承可能なプロパティが添付プロパティとして登録され、意図的に継承をブロックする境界 (Frame など) がない場合に限られます。

注意

プロパティ値の継承は、非添付依存関係プロパティについても機能しているように見えることがありますが、ランタイム ツリーでの一部の要素の境界を介する非添付プロパティの継承動作は定義されていません。 プロパティのメタデータに Inherits を指定するときは、RegisterAttached を使用してプロパティを登録します。

関連項目