Partager via


Rappels et validation des propriétés de dépendance

Cet article explique comment définir une propriété de dépendance et implémenter des rappels de propriété de dépendance. Les rappels prennent en charge la validation de valeur, la contrainte de valeur et d’autres logiques nécessaires lorsqu’une valeur de propriété change.

Conditions préalables

L’article suppose une connaissance de base des propriétés de dépendance et que vous avez lu vue d’ensemble des propriétés de dépendance. Pour suivre les exemples de cet article, il vous aide à connaître le langage XAML (Extensible Application Markup Language) et à savoir comment écrire des applications WPF.

Fonctions de rappel de validation de valeur

Les rappels validate-value permettent de vérifier si une nouvelle valeur de propriété de dépendance est valide avant d’être appliquée par le système de propriétés. Ce rappel déclenche une exception si la valeur ne répond pas aux critères de validation.

Les rappels de validation de valeur ne peuvent être attribués qu'à une propriété de dépendance une seule fois, lors de l'enregistrement de la propriété. Lors de l’inscription d’une propriété de dépendance, vous avez la possibilité de transmettre une référence ValidateValueCallback à la méthode Register(String, Type, Type, PropertyMetadata, ValidateValueCallback). Les rappels validate-valeur ne font pas partie des métadonnées de propriété et ne peuvent pas être substitués.

La valeur effective d’une propriété de dépendance est sa valeur appliquée. La valeur effective est déterminée par la précédence de la valeur de propriété lorsque plusieurs entrées basées sur des propriétés existent. Si un rappel de valeur de validation est inscrit pour une propriété de dépendance, le système de propriétés appelle son rappel de valeur de validation lors de la modification de la valeur, en passant la nouvelle valeur en tant qu’objet. Dans la fonction de rappel, vous pouvez convertir l'objet valeur au type inscrit auprès du système de propriétés, puis exécuter votre logique de validation sur celui-ci. Le rappel retourne true si la valeur est valide pour la propriété, sinon false.

Si une fonction de rappel de validation de la valeur retourne false, une exception est levée et la nouvelle valeur n’est pas appliquée. Les développeurs d'applications doivent être prêts à gérer ces exceptions. Une utilisation courante des rappels validate-valeur consiste à valider des valeurs d’énumération ou à limiter les valeurs numériques lorsqu’elles représentent des mesures qui ont des limites. Les rappels de validation de valeur sont appelés par le système de propriétés dans différents scénarios, notamment :

  • Initialisation d’objet, qui applique une valeur par défaut au moment de la création.
  • Appels programmatiques vers SetValue.
  • Les remplacements de métadonnées qui définissent une nouvelle valeur par défaut.

Les rappels validate-value n’ont pas de paramètre qui spécifie l’instance DependencyObject sur laquelle la nouvelle valeur est définie. Toutes les instances d’un DependencyObject partagent le même rappel de valeur de validation, il ne peut donc pas être utilisé pour valider des scénarios spécifiques à chaque instance. Pour plus d’informations, consultez ValidateValueCallback.

L’exemple suivant montre comment empêcher qu’une propriété, typée comme Double, soit définie sur PositiveInfinity ou 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

Rappels à l'égard des changements de propriété

Les rappels de changement de propriété vous informent lorsque la valeur effective d'une propriété de dépendance a changé.

Les rappels de changement de propriété font partie des métadonnées des propriétés de dépendance. Si vous dérivez d’une classe qui définit une propriété de dépendance ou ajoutez votre classe en tant que propriétaire d’une propriété de dépendance, vous pouvez remplacer les métadonnées. En cas de substitution de métadonnées, vous avez la possibilité de fournir une référence nouvelle PropertyChangedCallback. Utilisez un rappel modifié par propriété pour exécuter une logique nécessaire lorsqu’une valeur de propriété change.

Contrairement aux rappels validate-value, les rappels modifiés par les propriétés ont un paramètre qui spécifie l’instance DependencyObject sur laquelle la nouvelle valeur est définie. L'exemple suivant montre comment un rappel de changement de propriété peut utiliser la DependencyObject référence d'instance pour déclencher des rappels de valeur contrainte.

Rappels de valeur de force

Les rappels de valeur coerce vous permettent d’être averti lorsque la valeur effective d’une propriété de dépendance est sur le point de changer, afin que vous puissiez ajuster la nouvelle valeur avant son application. En plus d’être déclenché par le système de propriétés, vous pouvez appeler des rappels de valeur de force à partir de votre code.

Les rappels de valeur de coerce font partie des métadonnées de propriété de dépendance. Si vous dérivez d’une classe qui définit une propriété de dépendance ou ajoutez votre classe en tant que propriétaire d’une propriété de dépendance, vous pouvez remplacer les métadonnées. Lorsque vous substituez les métadonnées, vous avez la possibilité de fournir une référence à un nouveau CoerceValueCallback. Utilisez un rappel de valeur de force pour évaluer de nouvelles valeurs et les forcer si nécessaire. Le rappel retourne la valeur contrainte si une contrainte s’est produite ; sinon elle retourne la nouvelle valeur inchangée.

Comme pour les rappels modifiés par les propriétés, les rappels de valeur de contrainte ont un paramètre qui spécifie l’instance DependencyObject sur laquelle la nouvelle valeur est définie. L’exemple suivant montre comment un callback de contrainte de valeur peut utiliser une référence d’instance DependencyObject pour contraindre les valeurs de propriété.

Remarque

Les valeurs de propriété par défaut ne peuvent pas être coédées. Une propriété de dépendance a sa valeur par défaut définie sur l’initialisation d’objet ou lorsque vous effacez d’autres valeurs à l’aide ClearValuede .

Rappels de coercition de valeur et de changement de propriété en combinaison

Vous pouvez créer des dépendances entre les propriétés d’un élément en combinant des rappels de contrainte de valeur et des rappels de propriété modifiée. Par exemple, les modifications apportées à une propriété entraînent une coercition ou une réévaluation dans une autre propriété de dépendance. L’exemple suivant montre un scénario courant : trois propriétés de dépendance qui stockent respectivement la valeur actuelle, la valeur minimale et la valeur maximale d’un élément d’interface utilisateur. Si la valeur maximale change afin qu’elle soit inférieure à la valeur actuelle, la valeur actuelle est alors définie sur la nouvelle valeur maximale. Et, si la valeur minimale change afin qu’elle soit supérieure à la valeur actuelle, la valeur actuelle est ensuite définie sur la nouvelle valeur minimale. Dans l’exemple, la PropertyChangedCallback valeur actuelle appelle explicitement les CoerceValueCallback valeurs minimales et maximales.

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

Scénarios de rappel avancés

Contraintes et valeurs souhaitées

Si une valeur définie localement d’une propriété de dépendance est modifiée par contrainte, la valeur définie localement inchangée est conservée comme valeur souhaitée. Si le forçage est basé sur d’autres valeurs de propriété, le système de propriétés réévalue dynamiquement le forçage chaque fois que ces autres valeurs changent. Dans les contraintes du forçage, le système de propriétés applique une valeur la plus proche de la valeur souhaitée. Si la condition de contrainte ne s’applique plus, le système de propriétés restaure la valeur souhaitée, en supposant qu’aucune valeur de précédence plus élevée n’est active. L’exemple suivant teste la contrainte dans la valeur actuelle, la valeur minimale et le scénario de valeur maximale.

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

Des scénarios de dépendance assez complexes peuvent se produire lorsque vous avez plusieurs propriétés dépendantes les unes des autres de manière circulaire. Techniquement, il n’existe aucun problème avec les dépendances complexes, sauf qu’un grand nombre de réévaluations peuvent réduire les performances. En outre, les dépendances complexes exposées dans l’interface utilisateur peuvent confondre les utilisateurs. Traitez PropertyChangedCallback et CoerceValueCallback aussi sans ambiguïté que possible, et ne limitez pas trop.

Annuler les modifications de valeur

En retournant UnsetValue à partir d’un CoerceValueCallback, vous pouvez rejeter une modification de valeur de propriété. Ce mécanisme est utile lorsqu’une modification de valeur de propriété est lancée de manière asynchrone, mais lorsqu’elle est appliquée n’est plus valide pour l’état actuel de l’objet. Un autre scénario peut être de supprimer sélectivement une modification de valeur en fonction de son origine. Dans l’exemple suivant, CoerceValueCallback appelle la méthode GetValueSource, qui retourne une structure ValueSource avec une énumération BaseValueSource identifiant la source de la nouvelle valeur.

// 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

Voir aussi