Wywołania zwrotne i walidacja właściwości zależności (WPF .NET)

W tym artykule opisano sposób definiowania właściwości zależności i implementowania wywołań zwrotnych właściwości zależności. Wywołania zwrotne obsługują walidację wartości, przymus wartości i inną logikę potrzebną w przypadku zmiany wartości właściwości.

Ważne

Dokumentacja przewodnika dla komputerów dla platform .NET 7 i .NET 6 jest w budowie.

Wymagania wstępne

W tym artykule przyjęto założenie, że masz podstawową wiedzę na temat właściwości zależności i zapoznasz się z omówieniem właściwości zależności. Aby postępować zgodnie z przykładami w tym artykule, warto zapoznać się z językiem Extensible Application Markup Language (XAML) i wiedzieć, jak pisać aplikacje WPF.

Wywołania zwrotne wartości weryfikacji

Wywołania zwrotne validate-value umożliwiają sprawdzenie, czy nowa wartość właściwości zależności jest prawidłowa przed zastosowaniem przez system właściwości. To wywołanie zwrotne zgłasza wyjątek, jeśli wartość nie spełnia kryteriów weryfikacji.

Wywołania zwrotne wartości weryfikacji można przypisać tylko do właściwości zależności raz podczas rejestracji właściwości. Podczas rejestrowania właściwości zależności możesz przekazać ValidateValueCallback odwołanie do Register(String, Type, Type, PropertyMetadata, ValidateValueCallback) metody . Wywołania zwrotne wartości weryfikacji nie są częścią metadanych właściwości i nie można ich zastąpić.

Efektywną wartością właściwości zależności jest jej zastosowana wartość. Wartość efektywna jest określana za pomocą pierwszeństwa wartości właściwości, gdy istnieje wiele danych wejściowych opartych na właściwościach. Jeśli wywołanie zwrotne wartości walidacji jest zarejestrowane dla właściwości zależności, system właściwości wywoła wywołanie zwrotne wartości weryfikacji wartości dla zmiany wartości, przekazując nową wartość jako obiekt. W wywołaniu zwrotnym można rzutować obiekt wartości z powrotem do typu zarejestrowanego w systemie właściwości, a następnie uruchomić logikę walidacji na nim. Wywołanie zwrotne zwraca true wartość , jeśli wartość jest prawidłowa dla właściwości , w przeciwnym razie false.

Jeśli wywołanie zwrotne validate-value zwraca falsewartość , zostanie zgłoszony wyjątek i nowa wartość nie zostanie zastosowana. Autorzy aplikacji muszą być przygotowani do obsługi tych wyjątków. Typowym zastosowaniem wywołań zwrotnych wartości weryfikacji jest weryfikowanie wartości wyliczenia lub ograniczanie wartości liczbowych, gdy reprezentują one miary, które mają limity. Wywołania zwrotne wartości weryfikacji są wywoływane przez system właściwości w różnych scenariuszach, w tym:

  • Inicjowanie obiektu, które stosuje wartość domyślną w czasie tworzenia.
  • Programowe wywołania do .SetValue
  • Przesłonięcia metadanych określające nową wartość domyślną.

Wywołania zwrotne validate-value nie mają parametru określającego DependencyObject wystąpienie, na którym jest ustawiona nowa wartość. Wszystkie wystąpienia DependencyObject udziału tego samego wywołania zwrotnego wartości validate-value, dzięki czemu nie mogą być używane do sprawdzania poprawności scenariuszy specyficznych dla wystąpienia. W celu uzyskania więcej informacji, zobacz następujący temat: ValidateValueCallback.

W poniższym przykładzie pokazano, jak zapobiec właściwości, typizowanej jako , ustawionej na DoublePositiveInfinity wartość lub NegativeInfinity.

public class Gauge1 : Control
{
    public Gauge1() : base() { }

    // Register a dependency property with the specified property name,
    // property type, owner type, property metadata, and callbacks.
    public static readonly DependencyProperty CurrentReadingProperty =
        DependencyProperty.Register(
            name: "CurrentReading",
            propertyType: typeof(double),
            ownerType: typeof(Gauge1),
            typeMetadata: new FrameworkPropertyMetadata(
                defaultValue: double.NaN,
                flags: FrameworkPropertyMetadataOptions.AffectsMeasure),
            validateValueCallback: new ValidateValueCallback(IsValidReading));

    // CLR wrapper with get/set accessors.
    public double CurrentReading
    {
        get => (double)GetValue(CurrentReadingProperty);
        set => SetValue(CurrentReadingProperty, value);
    }

    // Validate-value callback.
    public static bool IsValidReading(object value)
    {
        double val = (double)value;
        return !val.Equals(double.NegativeInfinity) && 
            !val.Equals(double.PositiveInfinity);
    }
}
Public Class Gauge1
    Inherits Control

    Public Sub New()
        MyBase.New()
    End Sub

    Public Shared ReadOnly CurrentReadingProperty As DependencyProperty =
        DependencyProperty.Register(
            name:="CurrentReading",
            propertyType:=GetType(Double),
            ownerType:=GetType(Gauge1),
            typeMetadata:=New FrameworkPropertyMetadata(
                defaultValue:=Double.NaN,
                flags:=FrameworkPropertyMetadataOptions.AffectsMeasure),
            validateValueCallback:=New ValidateValueCallback(AddressOf IsValidReading))

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

    Public Shared Function IsValidReading(value As Object) As Boolean
        Dim val As Double = value
        Return Not val.Equals(Double.NegativeInfinity) AndAlso
            Not val.Equals(Double.PositiveInfinity)
    End Function

End Class
public static void TestValidationBehavior()
{
    Gauge1 gauge = new();

    Debug.WriteLine($"Test value validation scenario:");

    // Set allowed value.
    gauge.CurrentReading = 5;
    Debug.WriteLine($"Current reading: {gauge.CurrentReading}");

    try
    {
        // Set disallowed value.
        gauge.CurrentReading = double.PositiveInfinity;
    }
    catch (ArgumentException e)
    {
        Debug.WriteLine($"Exception thrown by ValidateValueCallback: {e.Message}");
    }

    Debug.WriteLine($"Current reading: {gauge.CurrentReading}");

    // Current reading: 5
    // Exception thrown by ValidateValueCallback: '∞' is not a valid value for property 'CurrentReading'.
    // Current reading: 5
}
Public Shared Sub TestValidationBehavior()
    Dim gauge As New Gauge1()

    Debug.WriteLine($"Test value validation scenario:")

    ' Set allowed value.
    gauge.CurrentReading = 5
    Debug.WriteLine($"Current reading: {gauge.CurrentReading}")

    Try
        ' Set disallowed value.
        gauge.CurrentReading = Double.PositiveInfinity
    Catch e As ArgumentException
        Debug.WriteLine($"Exception thrown by ValidateValueCallback: {e.Message}")
    End Try

    Debug.WriteLine($"Current reading: {gauge.CurrentReading}")

    ' Current reading: 5
    ' Exception thrown by ValidateValueCallback: '∞' is not a valid value for property 'CurrentReading'.
    ' Current reading 5
End Sub

Wywołania zwrotne zmienione przez właściwość

Wywołania zwrotne zmienione przez właściwość powiadamiają o zmianie obowiązującej wartości właściwości zależności.

Wywołania zwrotne zmienione przez właściwość są częścią metadanych właściwości zależności. Jeśli pochodzisz z klasy definiującej właściwość zależności lub dodaj klasę jako właściciela właściwości zależności, możesz zastąpić metadane. Podczas zastępowania metadanych możesz podać nowe PropertyChangedCallback odwołanie. Użyj wywołania zwrotnego zmienionej właściwości, aby uruchomić logikę potrzebną w przypadku zmiany wartości właściwości.

W przeciwieństwie do wywołań zwrotnych validate-value, wywołania zwrotne zmienione właściwości mają parametr określający DependencyObject wystąpienie, na którym jest ustawiona nowa wartość. W następnym przykładzie pokazano, jak wywołanie zwrotne zmienione właściwości może używać DependencyObject odwołania do wystąpienia w celu wyzwalania wywołań zwrotnych coerce-value.

Wywołania zwrotne coerce-value

Wywołania zwrotne coerce-value umożliwiają otrzymywanie powiadomień, gdy efektywna wartość właściwości zależności zostanie zmieniona, aby można było dostosować nową wartość przed jej zastosowaniem. Oprócz wyzwalania przez system właściwości można wywołać wywołania zwrotne coerce-value z kodu.

Wywołania zwrotne coerce-value są częścią metadanych właściwości zależności. Jeśli pochodzisz z klasy definiującej właściwość zależności lub dodaj klasę jako właściciela właściwości zależności, możesz zastąpić metadane. Podczas zastępowania metadanych możesz podać odwołanie do nowego CoerceValueCallbackelementu . Użyj wywołania zwrotnego coerce-value, aby ocenić nowe wartości i w razie potrzeby je przekształcać. Wywołanie zwrotne zwraca wartość coerced w przypadku wystąpienia przymusu, w przeciwnym razie zwraca niezniszczone nowe wartości.

Podobnie jak wywołania zwrotne zmiany właściwości, wywołania zwrotne coerce-value mają parametr określający DependencyObject wystąpienie, na którym jest ustawiona nowa wartość. W następnym przykładzie pokazano, jak wywołanie zwrotne wartości coerce-value może używać DependencyObject odwołania wystąpienia do wartości właściwości coerce.

Uwaga

Wartości właściwości domyślnych nie mogą być konercowane. Właściwość zależności ma domyślną wartość ustawioną na inicjowanie obiektu lub w przypadku wyczyszczenia innych wartości przy użyciu polecenia ClearValue.

Coerce-value i właściwości zmienione wywołania zwrotne w kombinacji

Zależności między właściwościami elementu można tworzyć przy użyciu wywołań zwrotnych coerce-value i wywołań zwrotnych zmienionych właściwości w kombinacji. Na przykład zmiany w jednej właściwości wymuszają wymuszanie lub ponowne ocenianie w innej właściwości zależności. W następnym przykładzie przedstawiono typowy scenariusz: trzy właściwości zależności, które odpowiednio przechowują bieżącą wartość, wartość minimalną i maksymalną wartość elementu interfejsu użytkownika. Jeśli wartość maksymalna zmieni się tak, aby była mniejsza niż bieżąca wartość, bieżąca wartość jest następnie ustawiona na nową wartość maksymalną. A jeśli wartość minimalna zmieni się tak, aby była większa niż bieżąca wartość, bieżąca wartość jest następnie ustawiona na nową wartość minimalną. W tym przykładzie PropertyChangedCallback dla bieżącej wartości jawnie wywołuje CoerceValueCallback wartości minimalne i maksymalne.

public class Gauge2 : Control
{
    public Gauge2() : base() { }

    // Register a dependency property with the specified property name,
    // property type, owner type, property metadata, and callbacks.
    public static readonly DependencyProperty CurrentReadingProperty =
        DependencyProperty.Register(
            name: "CurrentReading",
            propertyType: typeof(double),
            ownerType: typeof(Gauge2),
            typeMetadata: new FrameworkPropertyMetadata(
                defaultValue: double.NaN,
                flags: FrameworkPropertyMetadataOptions.AffectsMeasure,
                propertyChangedCallback: new PropertyChangedCallback(OnCurrentReadingChanged),
                coerceValueCallback: new CoerceValueCallback(CoerceCurrentReading)
            ),
            validateValueCallback: new ValidateValueCallback(IsValidReading)
        );

    // CLR wrapper with get/set accessors.
    public double CurrentReading
    {
        get => (double)GetValue(CurrentReadingProperty);
        set => SetValue(CurrentReadingProperty, value);
    }

    // Validate-value callback.
    public static bool IsValidReading(object value)
    {
        double val = (double)value;
        return !val.Equals(double.NegativeInfinity) && !val.Equals(double.PositiveInfinity);
    }

    // Property-changed callback.
    private static void OnCurrentReadingChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        depObj.CoerceValue(MinReadingProperty);
        depObj.CoerceValue(MaxReadingProperty);
    }

    // Coerce-value callback.
    private static object CoerceCurrentReading(DependencyObject depObj, object value)
    {
        Gauge2 gauge = (Gauge2)depObj;
        double currentVal = (double)value;
        currentVal = currentVal < gauge.MinReading ? gauge.MinReading : currentVal;
        currentVal = currentVal > gauge.MaxReading ? gauge.MaxReading : currentVal;
        return currentVal;
    }

    // Register a dependency property with the specified property name,
    // property type, owner type, property metadata, and callbacks.
    public static readonly DependencyProperty MaxReadingProperty = DependencyProperty.Register(
        name: "MaxReading",
        propertyType: typeof(double),
        ownerType: typeof(Gauge2),
        typeMetadata: new FrameworkPropertyMetadata(
            defaultValue: double.NaN,
            flags: FrameworkPropertyMetadataOptions.AffectsMeasure,
            propertyChangedCallback: new PropertyChangedCallback(OnMaxReadingChanged),
            coerceValueCallback: new CoerceValueCallback(CoerceMaxReading)
        ),
        validateValueCallback: new ValidateValueCallback(IsValidReading)
    );

    // CLR wrapper with get/set accessors.
    public double MaxReading
    {
        get => (double)GetValue(MaxReadingProperty);
        set => SetValue(MaxReadingProperty, value);
    }

    // Property-changed callback.
    private static void OnMaxReadingChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        depObj.CoerceValue(MinReadingProperty);
        depObj.CoerceValue(CurrentReadingProperty);
    }

    // Coerce-value callback.
    private static object CoerceMaxReading(DependencyObject depObj, object value)
    {
        Gauge2 gauge = (Gauge2)depObj;
        double maxVal = (double)value;
        return maxVal < gauge.MinReading ? gauge.MinReading : maxVal;
    }

    // Register a dependency property with the specified property name,
    // property type, owner type, property metadata, and callbacks.
    public static readonly DependencyProperty MinReadingProperty = DependencyProperty.Register(
    name: "MinReading",
    propertyType: typeof(double),
    ownerType: typeof(Gauge2),
    typeMetadata: new FrameworkPropertyMetadata(
        defaultValue: double.NaN,
        flags: FrameworkPropertyMetadataOptions.AffectsMeasure,
        propertyChangedCallback: new PropertyChangedCallback(OnMinReadingChanged),
        coerceValueCallback: new CoerceValueCallback(CoerceMinReading)
    ),
    validateValueCallback: new ValidateValueCallback(IsValidReading));

    // CLR wrapper with get/set accessors.
    public double MinReading
    {
        get => (double)GetValue(MinReadingProperty);
        set => SetValue(MinReadingProperty, value);
    }

    // Property-changed callback.
    private static void OnMinReadingChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        depObj.CoerceValue(MaxReadingProperty);
        depObj.CoerceValue(CurrentReadingProperty);
    }

    // Coerce-value callback.
    private static object CoerceMinReading(DependencyObject depObj, object value)
    {
        Gauge2 gauge = (Gauge2)depObj;
        double minVal = (double)value;
        return minVal > gauge.MaxReading ? gauge.MaxReading : minVal;
    }
}
Public Class Gauge2
    Inherits Control

    Public Sub New()
        MyBase.New()
    End Sub

    ' Register a dependency property with the specified property name,
    ' property type, owner type, property metadata, And callbacks.
    Public Shared ReadOnly CurrentReadingProperty As DependencyProperty =
        DependencyProperty.Register(
            name:="CurrentReading",
            propertyType:=GetType(Double),
            ownerType:=GetType(Gauge2),
            typeMetadata:=New FrameworkPropertyMetadata(
                defaultValue:=Double.NaN,
                flags:=FrameworkPropertyMetadataOptions.AffectsMeasure,
                propertyChangedCallback:=New PropertyChangedCallback(AddressOf OnCurrentReadingChanged),
                coerceValueCallback:=New CoerceValueCallback(AddressOf CoerceCurrentReading)),
            validateValueCallback:=New ValidateValueCallback(AddressOf IsValidReading))

    ' CLR wrapper with get/set accessors.
    Public Property CurrentReading As Double
        Get
            Return GetValue(CurrentReadingProperty)
        End Get
        Set(value As Double)
            SetValue(CurrentReadingProperty, value)
        End Set
    End Property

    ' Validate-value callback.
    Public Shared Function IsValidReading(value As Object) As Boolean
        Dim val As Double = value
        Return Not val.Equals(Double.NegativeInfinity) AndAlso Not val.Equals(Double.PositiveInfinity)
    End Function

    ' Property-changed callback.
    Private Shared Sub OnCurrentReadingChanged(depObj As DependencyObject, e As DependencyPropertyChangedEventArgs)
        depObj.CoerceValue(MinReadingProperty)
        depObj.CoerceValue(MaxReadingProperty)
    End Sub

    ' Coerce-value callback.
    Private Shared Function CoerceCurrentReading(depObj As DependencyObject, value As Object) As Object
        Dim gauge As Gauge2 = CType(depObj, Gauge2)
        Dim currentVal As Double = value
        currentVal = If(currentVal < gauge.MinReading, gauge.MinReading, currentVal)
        currentVal = If(currentVal > gauge.MaxReading, gauge.MaxReading, currentVal)
        Return currentVal
    End Function

    Public Shared ReadOnly MaxReadingProperty As DependencyProperty =
        DependencyProperty.Register(
        name:="MaxReading",
        propertyType:=GetType(Double),
        ownerType:=GetType(Gauge2),
        typeMetadata:=New FrameworkPropertyMetadata(
            defaultValue:=Double.NaN,
            flags:=FrameworkPropertyMetadataOptions.AffectsMeasure,
            propertyChangedCallback:=New PropertyChangedCallback(AddressOf OnMaxReadingChanged),
            coerceValueCallback:=New CoerceValueCallback(AddressOf CoerceMaxReading)),
        validateValueCallback:=New ValidateValueCallback(AddressOf IsValidReading))

    ' CLR wrapper with get/set accessors.
    Public Property MaxReading As Double
        Get
            Return GetValue(MaxReadingProperty)
        End Get
        Set(value As Double)
            SetValue(MaxReadingProperty, value)
        End Set
    End Property

    ' Property-changed callback.
    Private Shared Sub OnMaxReadingChanged(depObj As DependencyObject, e As DependencyPropertyChangedEventArgs)
        depObj.CoerceValue(MinReadingProperty)
        depObj.CoerceValue(CurrentReadingProperty)
    End Sub

    ' Coerce-value callback.
    Private Shared Function CoerceMaxReading(depObj As DependencyObject, value As Object) As Object
        Dim gauge As Gauge2 = CType(depObj, Gauge2)
        Dim maxVal As Double = value
        Return If(maxVal < gauge.MinReading, gauge.MinReading, maxVal)
    End Function

    ' Register a dependency property with the specified property name,
    ' property type, owner type, property metadata, And callbacks.
    Public Shared ReadOnly MinReadingProperty As DependencyProperty =
        DependencyProperty.Register(
        name:="MinReading",
        propertyType:=GetType(Double),
        ownerType:=GetType(Gauge2),
        typeMetadata:=New FrameworkPropertyMetadata(
            defaultValue:=Double.NaN,
            flags:=FrameworkPropertyMetadataOptions.AffectsMeasure,
            propertyChangedCallback:=New PropertyChangedCallback(AddressOf OnMinReadingChanged),
            coerceValueCallback:=New CoerceValueCallback(AddressOf CoerceMinReading)),
        validateValueCallback:=New ValidateValueCallback(AddressOf IsValidReading))

    ' CLR wrapper with get/set accessors.
    Public Property MinReading As Double
        Get
            Return GetValue(MinReadingProperty)
        End Get
        Set(value As Double)
            SetValue(MinReadingProperty, value)
        End Set
    End Property

    ' Property-changed callback.
    Private Shared Sub OnMinReadingChanged(depObj As DependencyObject, e As DependencyPropertyChangedEventArgs)
        depObj.CoerceValue(MaxReadingProperty)
        depObj.CoerceValue(CurrentReadingProperty)
    End Sub

    ' Coerce-value callback.
    Private Shared Function CoerceMinReading(depObj As DependencyObject, value As Object) As Object
        Dim gauge As Gauge2 = CType(depObj, Gauge2)
        Dim minVal As Double = value
        Return If(minVal > gauge.MaxReading, gauge.MaxReading, minVal)
    End Function

End Class

Zaawansowane scenariusze wywołania zwrotnego

Ograniczenia i żądane wartości

Jeśli lokalnie ustawiona wartość właściwości zależności zostanie zmieniona przez przymus, niezmieniona wartość lokalnie ustawiona zostanie zachowana jako żądana wartość. Jeśli przymus jest oparty na innych wartościach właściwości, system właściwości będzie dynamicznie ponownie wyceniany za każdym razem, gdy te inne wartości się zmienią. W ramach ograniczeń przymusu system właściwości zastosuje wartość znajdującą się najbliżej żądanej wartości. Jeśli warunek przymusu nie będzie już stosowany, system właściwości przywróci żądaną wartość — przy założeniu, że nie jest aktywna wyższa wartość pierwszeństwa . Poniższy przykład testuje przymus w bieżącym scenariuszu wartości, wartości minimalnej i maksymalnej wartości.

public static void TestCoercionBehavior()
{
    Gauge2 gauge = new()
    {
        // Set initial values.
        MinReading = 0,
        MaxReading = 10,
        CurrentReading = 5
    };

    Debug.WriteLine($"Test current/min/max values scenario:");

    // Current reading is not coerced.
    Debug.WriteLine($"Current reading: " +
        $"{gauge.CurrentReading} (min: {gauge.MinReading}, max: {gauge.MaxReading})");

    // Current reading is coerced to max value.
    gauge.MaxReading = 3;
    Debug.WriteLine($"Current reading: " +
        $"{gauge.CurrentReading} (min: {gauge.MinReading}, max: {gauge.MaxReading})");

    // Current reading is coerced, but tracking back to the desired value.
    gauge.MaxReading = 4;
    Debug.WriteLine($"Current reading: " +
        $"{gauge.CurrentReading} (min: {gauge.MinReading}, max: {gauge.MaxReading})");

    // Current reading reverts to the desired value.
    gauge.MaxReading = 10;
    Debug.WriteLine($"Current reading: " +
        $"{gauge.CurrentReading} (min: {gauge.MinReading}, max: {gauge.MaxReading})");

    // Current reading remains at the desired value.
    gauge.MinReading = 5;
    gauge.MaxReading = 5;
    Debug.WriteLine($"Current reading: " +
        $"{gauge.CurrentReading} (min: {gauge.MinReading}, max: {gauge.MaxReading})");

    // Current reading: 5 (min=0, max=10)
    // Current reading: 3 (min=0, max=3)
    // Current reading: 4 (min=0, max=4)
    // Current reading: 5 (min=0, max=10)
    // Current reading: 5 (min=5, max=5)
}
Public Shared Sub TestCoercionBehavior()

    ' Set initial values.
    Dim gauge As New Gauge2 With {
        .MinReading = 0,
        .MaxReading = 10,
        .CurrentReading = 5
    }

    Debug.WriteLine($"Test current/min/max values scenario:")

    ' Current reading is not coerced.
    Debug.WriteLine($"Current reading: " &
        $"{gauge.CurrentReading} (min={gauge.MinReading}, max={gauge.MaxReading})")

    ' Current reading is coerced to max value.
    gauge.MaxReading = 3
    Debug.WriteLine($"Current reading: " &
        $"{gauge.CurrentReading} (min={gauge.MinReading}, max={gauge.MaxReading})")

    ' Current reading is coerced, but tracking back to the desired value.
    gauge.MaxReading = 4
    Debug.WriteLine($"Current reading: " &
        $"{gauge.CurrentReading} (min={gauge.MinReading}, max={gauge.MaxReading})")

    ' Current reading reverts to the desired value.
    gauge.MaxReading = 10
    Debug.WriteLine($"Current reading: " &
        $"{gauge.CurrentReading} (min={gauge.MinReading}, max={gauge.MaxReading})")

    ' Current reading remains at the desired value.
    gauge.MinReading = 5
    gauge.MaxReading = 5
    Debug.WriteLine($"Current reading: " &
        $"{gauge.CurrentReading} (min={gauge.MinReading}, max={gauge.MaxReading})")

    ' Current reading: 5 (min=0, max=10)
    ' Current reading: 3 (min=0, max=3)
    ' Current reading: 4 (min=0, max=4)
    ' Current reading: 5 (min=0, max=10)
    ' Current reading: 5 (min=5, max=5)
End Sub

Dość złożone scenariusze zależności mogą wystąpić, gdy istnieje wiele właściwości, które są zależne od siebie w sposób cykliczny. Technicznie nie ma nic złego w przypadku złożonych zależności — z tą różnicą, że duża liczba ponownych ocen może zmniejszyć wydajność. Ponadto złożone zależności, które są widoczne w interfejsie użytkownika, mogą mylić użytkowników. Traktuj PropertyChangedCallback i CoerceValueCallback tak jednoznacznie, jak to możliwe, i nie ograniczaj się.

Anulowanie zmian wartości

Wracając UnsetValue z CoerceValueCallbackklasy , można odrzucić zmianę wartości właściwości. Ten mechanizm jest przydatny, gdy zmiana wartości właściwości jest inicjowana asynchronicznie, ale zastosowanie tego mechanizmu nie jest już prawidłowe dla bieżącego stanu obiektu. Innym scenariuszem może być selektywne pomijanie zmiany wartości w zależności od tego, skąd pochodzi. W poniższym przykładzie metoda CoerceValueCallback wywołuje metodęValueSource, która zwraca strukturę z wyliczeniem BaseValueSource identyfikującym GetValueSource źródło nowej wartości.

// Coerce-value callback.
private static object CoerceCurrentReading(DependencyObject depObj, object value)
{
    // Get value source.
    ValueSource valueSource = 
        DependencyPropertyHelper.GetValueSource(depObj, CurrentReadingProperty);

    // Reject any property value change that's a locally set value.
    return valueSource.BaseValueSource == BaseValueSource.Local ? 
        DependencyProperty.UnsetValue : value;
}
' Coerce-value callback.
Private Shared Function CoerceCurrentReading(depObj As DependencyObject, value As Object) As Object
    ' Get value source.
    Dim valueSource As ValueSource =
        DependencyPropertyHelper.GetValueSource(depObj, CurrentReadingProperty)

    ' Reject any property value that's a locally set value.
    Return If(valueSource.BaseValueSource = BaseValueSource.Local, DependencyProperty.UnsetValue, value)
End Function

Zobacz też