XAML 読み込みと依存関係プロパティ (WPF .NET)

Extensible Application Markup Language (XAML) プロセッサの Windows Presentation Foundation (WPF) 実装は、本質的に依存関係プロパティに対応しています。 そのため、XAML プロセッサでは、WPF プロパティ システム メソッドを使用して XAML を読み込み、依存関係プロパティ属性を処理し、GetValueSetValue などの WPF プロパティ システム メソッドを使用して依存関係プロパティ ラッパーを完全にバイパスします。 したがって、カスタム依存関係プロパティのプロパティ ラッパーにカスタム ロジックを追加した場合、XAML でプロパティ値が設定されたときに XAML プロセッサによって呼び出されることはありません。

前提条件

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

WPF XAML ローダーのパフォーマンス

WPF XAML プロセッサが依存関係プロパティのプロパティ ラッパーを使用するのではなく、SetValue を直接呼び出して依存関係プロパティの値を設定する方が計算コストが低くなります。

XAML プロセッサでプロパティ ラッパーを使用した場合は、マークアップで示されている型とメンバーのリレーションシップのみに基づいて、バッキング コードのオブジェクト モデル全体を推論する必要があります。 型は xmlns とアセンブリ属性の組み合わせを使用してマークアップから識別できますが、メンバーを識別し、属性として設定できるメンバーを決定し、サポートされているプロパティ値型を解決するには、PropertyInfo を使用する広範なリフレクションが必要になります。

WPF プロパティ システムでは、特定の DependencyObject の派生型に実装された依存関係プロパティのストレージ テーブルが保持されます。 XAML プロセッサは、そのテーブルを使用して、依存関係プロパティの依存関係プロパティ識別子を推論します。 たとえば、慣例では、ABC という名前の依存関係プロパティの依存関係プロパティ識別子は ABCProperty です。 XAML プロセッサは、依存関係プロパティ識別子を使用して、それを含んでいる型で SetValue メソッドを呼び出すことによって、依存関係プロパティの値を効率的に設定できます。

依存関係プロパティ ラッパーの詳細については、「カスタム依存関係プロパティ」を参照してください。

カスタム依存関係プロパティに対する影響

WPF XAML プロセッサは、プロパティ ラッパーをバイパスし、SetValue を直接呼び出して依存関係プロパティ値を設定します。 そのため、XAML でプロパティ値が設定される場合、そのロジックは実行されないため、カスタム依存関係プロパティの set アクセサーに追加のロジックを含めないでください。 set アクセサーには SetValue 呼び出しのみを含める必要があります。

同様に、WPF XAML プロセッサのプロパティ値を取得する側面では、プロパティ ラッパーをバイパスして GetValue を直接呼び出します。 そのため、XAML でプロパティ値が読み取られる場合、そのロジックは実行されないため、カスタム依存関係プロパティの get アクセサーにも追加のロジックを含めないでください。 get アクセサーには GetValue 呼び出しのみを含める必要があります。

ラッパーを使用した依存関係プロパティの例

次の例では、プロパティ ラッパーを使用した推奨される依存関係プロパティ定義を示しています。 依存関係プロパティ識別子は public static readonly フィールドとして格納され、get および set アクセサーには、依存関係プロパティ値をサポートする必須の WPF プロパティ システム メソッド以外のコードは含まれません。 依存関係プロパティの値が変更されたときに実行する必要があるコードがある場合は、そのコードを依存関係プロパティの PropertyChangedCallback に配置することを検討してください。 詳細については、「プロパティ変更コールバック」を参照してください。

// Register a dependency property with the specified property name,
// property type, owner type, and property metadata. Store the dependency
// property identifier as a public static readonly member of the class.
public static readonly DependencyProperty AquariumGraphicProperty =
    DependencyProperty.Register(
      name: "AquariumGraphic",
      propertyType: typeof(Uri),
      ownerType: typeof(Aquarium),
      typeMetadata: new FrameworkPropertyMetadata(
          defaultValue: new Uri("http://www.contoso.com/aquarium-graphic.jpg"),
          flags: FrameworkPropertyMetadataOptions.AffectsRender,
          propertyChangedCallback: new PropertyChangedCallback(OnUriChanged))
    );

// Property wrapper with get & set accessors.
public Uri AquariumGraphic
{
    get => (Uri)GetValue(AquariumGraphicProperty);
    set => SetValue(AquariumGraphicProperty, value);
}

// Property-changed callback.
private static void OnUriChanged(DependencyObject dependencyObject, 
    DependencyPropertyChangedEventArgs e)
{
    // Some custom logic that runs on effective property value change.
    Uri newValue = (Uri)dependencyObject.GetValue(AquariumGraphicProperty);
    Debug.WriteLine($"OnUriChanged: {newValue}");
}
' Register a dependency property with the specified property name,
' property type, owner type, and property metadata. Store the dependency
' property identifier as a public static readonly member of the class.
Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty =
    DependencyProperty.Register(
        name:="AquariumGraphic",
        propertyType:=GetType(Uri),
        ownerType:=GetType(Aquarium),
        typeMetadata:=New FrameworkPropertyMetadata(
            defaultValue:=New Uri("http://www.contoso.com/aquarium-graphic.jpg"),
            flags:=FrameworkPropertyMetadataOptions.AffectsRender,
            propertyChangedCallback:=New PropertyChangedCallback(AddressOf OnUriChanged)))

' Property wrapper with get & set accessors.
Public Property AquariumGraphic As Uri
    Get
        Return CType(GetValue(AquariumGraphicProperty), Uri)
    End Get
    Set
        SetValue(AquariumGraphicProperty, Value)
    End Set
End Property

' Property-changed callback.
Private Shared Sub OnUriChanged(dependencyObject As DependencyObject,
                                e As DependencyPropertyChangedEventArgs)
    ' Some custom logic that runs on effective property value change.
    Dim newValue As Uri = CType(dependencyObject.GetValue(AquariumGraphicProperty), Uri)
    Debug.WriteLine($"OnUriChanged: {newValue}")
End Sub

関連項目