종속성 속성 콜백 및 유효성 검사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. 유효성 검사 콜백은 속성 메타데이터의 일부가 아닙니다. 메서드의 직접 입력입니다. RegisterThe 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. 유효성 검사 콜백의 일반적인 용도는 열거형 값의 유효성을 검사하거나 속성이 0 이상이어야 하는 측정값을 설정할 때 정수 또는 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 기본으로 입력 된 PositiveInfinity NegativeInfinity속성이 아닌 지 또는 있는지 유효성을 검사 합니다.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. 이 두 콜백을 함께 사용하여 한 속성이 변경되면 다른 속성의 강제 변환이나 재계산이 강제로 적용될 요소에 일련의 속성을 만들 수 있습니다.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.

종속성 속성의 연결을 사용하는 일반적인 시나리오는 요소가 최소값 및 최대값에 대해 각각 하나씩 속성을 보유하고 실제 또는 현재 값에 대해 세 번째 속성을 보유하는 사용자 인터페이스 기반 속성이 있는 경우입니다.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.

다음은 이 관계를 보여 주는 세 가지 종속성 속성 중 하나에 대한 매우 간단한 예제 코드입니다.The following is very brief example code for just one of the three dependency properties that illustrate this relationship. 이 예제에서는 관련 *Reading 속성의 Min/Max/Current 집합 중 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

강제 값 콜백은 현재 속성이 잠재적으로 종속되어 있는 속성의 값을 확인하고 필요한 경우 현재 값을 강제 변환합니다.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 시나리오에서 최소값과 최대값을 사용자가 설정할 수 있도록 선택할 수 있습니다.For instance, in the Min/Max/Current scenario, you could choose to have Minimum and Maximum be user settable. 그렇다면 최대값이 항상 최소값보다 크거나 그 반대가 되도록 강제 변환해야 할 수 있습니다.If so, you might need to coerce that Maximum is always greater than Minimum and vice versa. 그러나 해당 강제 변환이 활성화되고 최대값이 최소값으로 강제 변환되면 현재 값은 두 값에 종속되어 있고 두 값 사이의 범위(0)로 제한되기 때문에 설정할 수 없는 상태로 유지됩니다.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. 그런 다음 최대값이나 최소값을 조정하면 현재 값의 원하는 값이 계속 저장되고 제약 조건이 완화되면 원하는 값에 도달하려고 시도하기 때문에 현재 값은 두 값 중 하나를 "따르는" 것처럼 보입니다.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. 다른 가능한 시나리오에서는 보고할 값을 담당하는 속성 값 결정의 구성 요소에 따라 선택적으로 값을 무시할 수 있습니다.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