依存関係プロパティのコールバックと検証Dependency Property Callbacks and Validation

このトピックでは、検証による判定、プロパティの有効値が変更されたときに呼び出されるコールバック、値の決定への外部的影響のオーバーライドなど、プロパティ関連機能の代替カスタム実装を使用して依存関係プロパティを作成する方法について説明します。This topic describes how to create dependency properties using alternative custom implementations for property-related features such as validation determination, callbacks that are invoked whenever the property's effective value is changed, and overriding possible outside influences on value determination. また、これらの手法を用いてプロパティ システムの既定の動作を拡張することが適切であるシナリオについても説明します。This topic also discusses scenarios where expanding on the default property system behaviors by using these techniques is appropriate.

必須コンポーネントPrerequisites

このトピックでは、依存関係プロパティの実装の基本シナリオとカスタム依存関係プロパティへのメタデータの適用方法を理解していることを前提とします。This topic assumes that you understand the basic scenarios of implementing a dependency property, and how metadata is applied to a custom dependency property. 詳細については、「カスタム依存関係プロパティ」および「依存関係プロパティのメタデータ」を参照してください。See Custom Dependency Properties and Dependency Property Metadata for context.

検証コールバックValidation Callbacks

検証コールバックは、最初の登録時に依存関係プロパティに割り当てることができます。Validation callbacks can be assigned to a dependency property when you first register it. 検証コールバックは、プロパティメタデータの一部ではありません。これは、 Registerメソッドを直接入力したものです。The validation callback is not part of property metadata; it is a direct input of the Register method. したがって、依存関係プロパティの検証コールバックを作成した後で、その検証コールバックを新しい実装でオーバーライドすることはできません。Therefore, once a validation callback is created for a dependency property, it cannot be overridden by a new implementation.

public static readonly DependencyProperty CurrentReadingProperty = DependencyProperty.Register(
    "CurrentReading",
    typeof(double),
    typeof(Gauge),
    new FrameworkPropertyMetadata(
        Double.NaN,
        FrameworkPropertyMetadataOptions.AffectsMeasure,
        new PropertyChangedCallback(OnCurrentReadingChanged),
        new CoerceValueCallback(CoerceCurrentReading)
    ),
    new ValidateValueCallback(IsValidReading)
);
public double CurrentReading
{
  get { return (double)GetValue(CurrentReadingProperty); }
  set { SetValue(CurrentReadingProperty, value); }
}
Public Shared ReadOnly CurrentReadingProperty As DependencyProperty =
    DependencyProperty.Register("CurrentReading",
        GetType(Double), GetType(Gauge),
        New FrameworkPropertyMetadata(Double.NaN,
            FrameworkPropertyMetadataOptions.AffectsMeasure,
            New PropertyChangedCallback(AddressOf OnCurrentReadingChanged),
            New CoerceValueCallback(AddressOf CoerceCurrentReading)),
        New ValidateValueCallback(AddressOf IsValidReading))

Public Property CurrentReading() As Double
    Get
        Return CDbl(GetValue(CurrentReadingProperty))
    End Get
    Set(ByVal value As Double)
        SetValue(CurrentReadingProperty, value)
    End Set
End Property

検証コールバックは、オブジェクト値を受け取るように実装されます。The callbacks are implemented such that they are provided an object value. 指定された値がプロパティに対して有効である場合は true を、有効でない場合は falseを返します。They return true if the provided value is valid for the property; otherwise, they return false. プロパティの型はプロパティ システムに登録済みの適切な型であると想定されます。したがって、通常の場合、コールバック内での型チェックは行われません。It is assumed that the property is of the correct type per the type registered with the property system, so checking type within the callbacks is not ordinarily done. 検証コールバックは、プロパティ システムが実行するさまざまな操作で使用されます。The callbacks are used by the property system in a variety of different operations. これには、既定値による初期型の初期化、呼び出しSetValueによるプログラムによる変更、または指定された新しい既定値を使用したメタデータのオーバーライドが含まれます。This includes the initial type initialization by default value, programmatic change by invoking SetValue, or attempts to override metadata with new default value provided. これらの操作で呼び出された検証コールバックが false を返す場合、例外が発生します。If the validation callback is invoked by any of these operations, and returns false, then an exception will be raised. アプリケーションの作成者はこれらの例外を処理する必要があります。Application writers must be prepared to handle these exceptions. 検証コールバックは、列挙値の検証や、プロパティの測定値がゼロ以上に設定されている場合に integer 型または double 型の値を制限するときによく使用されます。A common use of validation callbacks is validating enumeration values, or constraining values of integers or doubles when the property sets measurements that must be zero or greater.

検証コールバックは、インスタンスではなくクラスを検証するためのものです。Validation callbacks specifically are intended to be class validators, not instance validators. コールバックのパラメーターは、検証するプロパティがDependencyObject設定されている特定のとは通信しません。The parameters of the callback do not communicate a specific DependencyObject on which the properties to validate are set. したがって、検証コールバックを使用して、プロパティ値に影響を及ぼす可能性がある "依存関係" (インスタンス固有のプロパティ値が他のインスタンス固有のプロパティ値や実行時の状態などの要因に依存する関係) を強制することはできません。Therefore the validation callbacks are not useful for enforcing the possible "dependencies" that might influence a property value, where the instance-specific value of a property is dependent on factors such as instance-specific values of other properties, or run-time state.

非常に単純な検証コールバックシナリオのコード例を次に示します。 Doubleプリミティブとして型指定されたプロパティがまたはNegativeInfinityではないPositiveInfinityことを検証しています。The following is example code for a very simple validation callback scenario: validating that a property that is typed as the Double primitive is not PositiveInfinity or NegativeInfinity.

public static bool IsValidReading(object value)
{
    Double v = (Double)value;
    return (!v.Equals(Double.NegativeInfinity) && !v.Equals(Double.PositiveInfinity));
}
Public Shared Function IsValidReading(ByVal value As Object) As Boolean
    Dim v As Double = CType(value, Double)
    Return ((Not v.Equals(Double.NegativeInfinity)) AndAlso
            (Not v.Equals(Double.PositiveInfinity)))
End Function

強制値コールバックとプロパティ変更イベントCoerce Value Callbacks and Property Changed Events

強制値コールバックは、依存DependencyObject関係プロパティの値が変更PropertyChangedCallbackされるたびにプロパティシステムによって呼び出される実装と同様に、プロパティの特定のインスタンスを渡します。Coerce value callbacks do pass the specific DependencyObject instance for properties, as do PropertyChangedCallback implementations that are invoked by the property system whenever the value of a dependency property changes. これら 2 つのコールバックを組み合わせて使用すると、あるプロパティが変更されたときに他のプロパティに自動的に強制または再評価が適用される一連のプロパティを要素に作成することができます。Using these two callbacks in combination, you can create a series of properties on elements where changes in one property will force a coercion or reevaluation of another property.

依存関係プロパティ間の関連は、最小値、最大値、および実際の値 (または現在の値) を表す 3 つ目のプロパティが要素に含まれる場合において、ユーザー インターフェイスによって駆動されるプロパティがあるときによく使用されます。A typical scenario for using a linkage of dependency properties is when you have a user interface driven property where the element holds one property each for the minimum and maximum value, and a third property for the actual or current value. ここで、最大値が現在の値より小さい値に調整された場合、現在の値を新しい最大値以下の値に強制する必要があります。現在の値と最小値についても同様の関係が成り立ちます。Here, if the maximum was adjusted in such a way that the current value exceeded the new maximum, you would want to coerce the current value to be no greater than the new maximum, and a similar relationship for minimum to current.

次の、3 つの依存関係プロパティの 1 つを示す非常に簡単なコード例は、こうした関係を表しています。The following is very brief example code for just one of the three dependency properties that illustrate this relationship. この例は、関連する *Reading プロパティ (Min、Max、Current) のうちの 1 つである CurrentReading プロパティの登録方法を示しています。The example shows how the CurrentReading property of a Min/Max/Current set of related *Reading properties is registered. ここでは、前のセクションで説明した検証を使用しています。It uses the validation as shown in the previous section.

public static readonly DependencyProperty CurrentReadingProperty = DependencyProperty.Register(
    "CurrentReading",
    typeof(double),
    typeof(Gauge),
    new FrameworkPropertyMetadata(
        Double.NaN,
        FrameworkPropertyMetadataOptions.AffectsMeasure,
        new PropertyChangedCallback(OnCurrentReadingChanged),
        new CoerceValueCallback(CoerceCurrentReading)
    ),
    new ValidateValueCallback(IsValidReading)
);
public double CurrentReading
{
  get { return (double)GetValue(CurrentReadingProperty); }
  set { SetValue(CurrentReadingProperty, value); }
}
Public Shared ReadOnly CurrentReadingProperty As DependencyProperty =
    DependencyProperty.Register("CurrentReading",
        GetType(Double), GetType(Gauge),
        New FrameworkPropertyMetadata(Double.NaN,
            FrameworkPropertyMetadataOptions.AffectsMeasure,
            New PropertyChangedCallback(AddressOf OnCurrentReadingChanged),
            New CoerceValueCallback(AddressOf CoerceCurrentReading)),
        New ValidateValueCallback(AddressOf IsValidReading))

Public Property CurrentReading() As Double
    Get
        Return CDbl(GetValue(CurrentReadingProperty))
    End Get
    Set(ByVal value As Double)
        SetValue(CurrentReadingProperty, value)
    End Set
End Property

Current のプロパティ変更コールバックを使用して、他の依存関係プロパティに対して登録されている強制値コールバックを明示的に呼び出すことにより、変更を他のプロパティに伝播します。The property changed callback for Current is used to forward the change to other dependent properties, by explicitly invoking the coerce value callbacks that are registered for those other properties:

private static void OnCurrentReadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  d.CoerceValue(MinReadingProperty);
  d.CoerceValue(MaxReadingProperty);
}
Private Shared Sub OnCurrentReadingChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    d.CoerceValue(MinReadingProperty)
    d.CoerceValue(MaxReadingProperty)
End Sub

強制値コールバックは、Current プロパティが依存している可能性があるプロパティの値を検証し、必要に応じて現在の値を強制的に指定します。The coerce value callback checks the values of properties that the current property is potentially dependent upon, and coerces the current value if necessary:

private static object CoerceCurrentReading(DependencyObject d, object value)
{
  Gauge g = (Gauge)d;
  double current = (double)value;
  if (current < g.MinReading) current = g.MinReading;
  if (current > g.MaxReading) current = g.MaxReading;
  return current;
}
Private Shared Function CoerceCurrentReading(ByVal d As DependencyObject, ByVal value As Object) As Object
    Dim g As Gauge = CType(d, Gauge)
    Dim current As Double = CDbl(value)
    If current < g.MinReading Then
        current = g.MinReading
    End If
    If current > g.MaxReading Then
        current = g.MaxReading
    End If
    Return current
End Function

注意

プロパティの既定値は強制的に指定されません。Default values of properties are not coerced. 既定値に等しいプロパティ値は、プロパティ値が既定値のままである場合、またはその他の値をClearValueでクリアした場合に発生する可能性があります。A property value equal to the default value might occur if a property value still has its initial default, or through clearing other values with ClearValue.

強制値コールバックとプロパティ変更コールバックはプロパティ メタデータの一部です。The coerce value and property changed callbacks are part of property metadata. したがって、特定の依存関係プロパティを所有する型から派生させた型に存在するその特定の依存関係プロパティのコールバックは、派生させた型でプロパティのメタデータをオーバーライドすることで変更できます。Therefore, you can change the callbacks for a particular dependency property as it exists on a type that you derive from the type that owns the dependency property, by overriding the metadata for that property on your type.

高度な強制とコールバック シナリオAdvanced Coercion and Callback Scenarios

制約と目的の値Constraints and Desired Values

コールCoerceValueCallbackバックは、宣言したロジックに従って値を強制するために、プロパティシステムによって使用されますが、ローカルに設定されたプロパティの強制値は、内部的に "必要な値" を保持します。The CoerceValueCallback callbacks will be used by the property system to coerce a value in accordance to the logic you declare, but a coerced value of a locally set property will still retain a "desired value" internally. 制約がアプリケーションの有効期間内に動的に変更される可能性があるプロパティ値に基づいている場合、強制の制約も動的に変更されます。そして、制約されているプロパティは、指定された新しい制約の下で、その値を目的の値にできる限り近づけることができます。If the constraints are based on other property values that may change dynamically during the application lifetime, the coercion constraints are changed dynamically also, and the constrained property can change its value to get as close to the desired value as possible given the new constraints. 制約がすべて解除された場合、値は目的の値になります。The value will become the desired value if all constraints are lifted. 相互に循環的に依存する複数のプロパティを使用している場合、非常に複雑な依存関係シナリオを導入することができます。You can potentially introduce some fairly complicated dependency scenarios if you have multiple properties that are dependent on one another in a circular manner. たとえば、Min、Max、Current のシナリオでは、Minimum と Maximum をユーザー設定可能なプロパティにすることができます。For instance, in the Min/Max/Current scenario, you could choose to have Minimum and Maximum be user settable. その場合、Maximum が常に Minimum より大きくなり、Minimum が常に Maximum より小さくなるように強制する必要があります。If so, you might need to coerce that Maximum is always greater than Minimum and vice versa. ただし、この強制を有効にし、Maximum が Minimum に強制された場合、Current が未設定の状態になります。なぜなら、Currenct は Maximum と Minimum の両方に依存し、両者の値の間の範囲内 (この場合はゼロ) に制約されるからです。But if that coercion is active, and Maximum coerces to Minimum, it leaves Current in an unsettable state, because it is dependent on both and is constrained to the range between the values, which is zero. その後、Maximum または Minimum が調整された場合、Current はいずれかの値を "追跡" すると考えられます。なぜなら、Current の目的の値が依然として保持されており、制約が緩和されると Current が目的の値に到達しようとするからです。Then, if Maximum or Minimum are adjusted, Current will seem to "follow" one of the values, because the desired value of Current is still stored and is attempting to reach the desired value as the constraints are loosened.

依存関係が複雑でも技術的には問題はありません。ただし、多数の再評価が必要になる場合、パフォーマンスが若干低下することがあります。また、UI に直接影響する場合は、ユーザーの混乱につながることもあります。There is nothing technically wrong with complex dependencies, but they can be a slight performance detriment if they require large numbers of reevaluations, and can also be confusing to users if they affect the UI directly. プロパティ変更コールバックおよび強制値コールバックについては注意が必要です。試行中の強制ができる限り明確に処理されること、およびその制約が "過剰制約" ではないことを確認してください。Be careful with property changed and coerce value callbacks and make sure that the coercion being attempted can be treated as unambiguously as possible, and does not "overconstrain".

CoerceValue を使用した値変更の取り消しUsing CoerceValue to Cancel Value Changes

プロパティシステムは、値CoerceValueCallback UnsetValueを返す任意のを特殊なケースとして扱います。The property system will treat any CoerceValueCallback that returns the value UnsetValue as a special case. この特別なケースは、呼び出されるがCoerceValueCallback呼び出されたプロパティの変更がプロパティシステムによって拒否され、プロパティシステムがそのプロパティに保持されていた以前の値を報告する必要があることを意味します。This special case means that the property change that resulted in the CoerceValueCallback being called should be rejected by the property system, and that the property system should instead report whatever previous value the property had. このメカニズムは、非同期に開始されたプロパティの変更が現在のオブジェクトの状態に対して依然として有効であるかどうかをチェックし、有効でない場合はその変更を抑制する場合に役立ちます。This mechanism can be useful to check that changes to a property that were initiated asynchronously are still valid for the current object state, and suppress the changes if not. 考えられるもう 1 つのシナリオとして、プロパティ値の決定におけるどの構成要素が報告されるプロパティ値を決定するかに応じて値を選択的に抑制することもできます。Another possible scenario is that you can selectively suppress a value depending on which component of property value determination is responsible for the value being reported. これを行うには、コールバックDependencyPropertyで渡されたとプロパティ識別子をのGetValueSource入力として使用し、 ValueSourceを処理します。To do this, you can use the DependencyProperty passed in the callback and the property identifier as input for GetValueSource, and then process the ValueSource.

関連項目See also