종속성 속성 콜백 및 유효성 검사

이 항목에서는 유효성 검사 확인, 속성의 유효 값이 변경될 때마다 호출되는 콜백, 값 결정에 대한 가능한 외부 영향 재정의 등 속성 관련 기능에 대체 사용자 지정 구현을 사용하여 종속성 속성을 만드는 방법에 대해 설명합니다. 또한 이 항목에서는 이러한 기술을 사용한 기본 속성 시스템 동작 확장이 적절한 시나리오에 대해서도 설명합니다.

필수 구성 요소

이 항목에서는 종속성 속성을 구현하는 기본 시나리오와 메타데이터가 사용자 지정 종속성 속성에 적용되는 방법을 이해하고 있다고 가정합니다. 컨텍스트는 사용자 지정 종속성 속성종속성 속성 메타데이터를 참조하세요.

유효성 검사 콜백

유효성 검사 콜백은 종속성 속성을 처음 등록할 때 이 속성에 할당할 수 있습니다. 유효성 검사 콜백은 속성 메타데이터의 일부가 아닙니다. Register 메서드의 직접 입력입니다. 따라서 종속성 속성에 대해 유효성 검사 콜백을 만든 다음에는 새 구현으로 재정의할 수 없습니다.

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

콜백은 개체 값이 제공되도록 구현됩니다. 제공된 값이 속성에 유효하면 true를 반환하고, 그러지 않으면 false를 반환합니다. 속성은 속성 시스템에 등록된 형식에 따라 올바른 형식이 되므로 콜백 내에서 형식 검사가 일반적으로 수행되지 않는다고 간주합니다. 콜백은 다양한 작업에서 속성 시스템에 의해 사용됩니다. 여기에는 기본값에 의한 초기 형식 초기화, SetValue 호출을 통한 프로그래밍 방식 변경 또는 제공된 새 기본값으로 메타데이터 재정의 시도가 포함됩니다. 유효성 검사 콜백이 이러한 작업에 의해 호출되고 false를 반환하면 예외가 발생합니다. 애플리케이션 작성자는 이러한 예외를 처리할 수 있도록 준비해야 합니다. 유효성 검사 콜백의 일반적인 용도는 열거형 값의 유효성을 검사하거나 속성이 0 이상이어야 하는 측정값을 설정할 때 정수 또는 double 값을 제약하는 것입니다.

유효성 검사 콜백은 특별히 인스턴스 유효성 검사기가 아닌 클래스 유효성 검사기입니다. 콜백의 매개 변수는 유효성을 검사할 속성이 설정된 특정 DependencyObject를 전달하지 않습니다. 따라서 유효성 검사 콜백은 속성 값에 영향을 줄 수 있는 가능한 "종속성"을 적용하는 데 유용하지 않으며, 속성의 인스턴스 관련 값은 다른 속성의 인스턴스 관련 값이나 런타임 상태와 같은 요소에 따라 달라집니다.

다음은 매우 단순한 유효성 검사 콜백 시나리오의 예제 코드입니다. Double 기본이 PositiveInfinity 또는 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

강제 값 콜백 및 속성 변경 이벤트

종속성 속성의 값이 변경될 때마다 속성 시스템에서 호출되는 PropertyChangedCallback 구현을 전달하므로 강제 값 콜백은 속성에 대한 특정 DependencyObject 인스턴스를 전달하지 않습니다. 이 두 콜백을 함께 사용하여 한 속성이 변경되면 다른 속성의 강제 변환이나 재계산이 강제로 적용될 요소에 일련의 속성을 만들 수 있습니다.

종속성 속성의 연결을 사용하는 일반적인 시나리오는 요소가 최소값 및 최대값에 대해 각각 하나씩 속성을 보유하고 실제 또는 현재 값에 대해 세 번째 속성을 보유하는 사용자 인터페이스 기반 속성이 있는 경우입니다. 여기에서 현재 값이 새 최대값을 초과하는 방식으로 최대값을 조정한 경우 새 최대값보다 크지 않도록 현재 값을 강제 변환하고 최소값에 대한 유사한 관계를 현재값으로 강제 변환하려고 합니다.

다음은 이 관계를 보여 주는 세 가지 종속성 속성 중 하나에 대한 매우 간단한 예제 코드입니다. 이 예제에서는 관련 *Reading 속성의 Min/Max/Current 집합 중 CurrentReading 속성이 등록되는 방법을 보여 주며, 이전 섹션에 표시된 유효성 검사를 사용합니다.

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에 대한 속성 변경 콜백은 다른 속성에 대해 등록된 강제 값 콜백을 명시적으로 호출하여 다른 종속 속성에 변경 내용을 전달하는 데 사용됩니다.

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

강제 값 콜백은 현재 속성이 잠재적으로 종속되어 있는 속성의 값을 확인하고 필요한 경우 현재 값을 강제 변환합니다.

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

참고

속성의 기본값은 강제 변환되지 않습니다. 속성 값에 여전히 초기 기본값이 있거나 ClearValue로 다른 값을 지우면 기본값과 같은 속성 값이 발생할 수 있습니다.

강제 값 및 속성 변경 콜백은 속성 메타데이터의 일부입니다. 따라서 특정 형식의 속성에 대한 메타데이터를 재정의하여 종속성 속성을 소유하는 형식에서 파생된 형식에 있는 특정 종속성 속성에 대한 콜백을 변경할 수 있습니다.

고급 강제 변환 및 콜백 시나리오

제약 조건 및 원하는 값

CoerceValueCallback 콜백은 선언된 논리에 따라 속성 시스템에서 값을 강제 변환하는 데 사용되지만, 로컬로 설정된 속성의 강제 변환된 값은 "원하는 값"을 내부적으로 계속 유지합니다. 제약 조건이 애플리케이션 수명 동안 동적으로 변경될 수 있는 다른 속성 값을 기반으로 하는 경우 강제 변환 제약 조건도 동적으로 변경되며 제한된 속성은 원하는 값에 최대한 가깝게 값을 변경하여 새 제약 조건을 제공할 수 있습니다. 모든 제약 조건이 적용되면 해당 값은 원하는 값이 됩니다. 여러 속성이 순환 방식으로 서로 종속되어 있는 경우 다소 복잡한 종속성 시나리오를 도입할 수 있습니다. 예를 들어 Min/Max/Current 시나리오에서 최소값과 최대값을 사용자가 설정할 수 있도록 선택할 수 있습니다. 그렇다면 최대값이 항상 최소값보다 크거나 그 반대가 되도록 강제 변환해야 할 수 있습니다. 그러나 해당 강제 변환이 활성화되고 최대값이 최소값으로 강제 변환되면 현재 값은 두 값에 종속되어 있고 두 값 사이의 범위(0)로 제한되기 때문에 설정할 수 없는 상태로 유지됩니다. 그런 다음 최대값이나 최소값을 조정하면 현재 값의 원하는 값이 계속 저장되고 제약 조건이 완화되면 원하는 값에 도달하려고 시도하기 때문에 현재 값은 두 값 중 하나를 "따르는" 것처럼 보입니다.

복잡한 종속성에서 기술적으로 잘못된 것은 없지만 많은 재계산이 필요한 경우 성능이 약간 저하될 수 있으며 UI에 직접 영향을 주는 경우 사용자에게 혼동을 줄 수도 있습니다. 속성 변경 및 강제 값 콜백을 사용할 때는 주의해야 하며 시도하려는 강제 변환이 최대한 명확하게 처리되고 "지나치게 제약"하지 않도록 해야 합니다.

CoerceValue를 사용하여 값 변경 내용 취소

속성 시스템은 UnsetValue 값을 반환하는 모든 CoerceValueCallback을 특수 문자로 취급합니다. 이 특별한 경우는 CoerceValueCallback이 호출되도록 하는 속성 변경을 속성 시스템에서 거부하고 대신 속성 시스템에서 속성의 이전 값을 모두 보고해야 함을 의미합니다. 이 메커니즘은 비동기적으로 시작된 속성의 변경 내용이 현재 개체 상태에 여전히 유효한지 확인하고 유효하지 않을 경우 변경 내용을 무시하는 데 유용할 수 있습니다. 다른 가능한 시나리오에서는 보고할 값을 담당하는 속성 값 결정의 구성 요소에 따라 선택적으로 값을 무시할 수 있습니다. 이를 위해서는 콜백에서 전달되는 DependencyProperty와 속성 식별자를 GetValueSource에 대한 입력으로 사용하고 ValueSource를 처리하면 됩니다.

참고 항목