Rappels et validation des propriétés de dépendance
Cette rubrique explique comment créer des propriétés de dépendance à l’aide d’implémentations personnalisées alternatives pour les fonctionnalités liées aux propriétés, telles que la détermination de la validation, les rappels qui sont appelés chaque fois que la valeur effective de la propriété est modifiée et en substituant les influences externes possibles sur la détermination de la valeur. Cette rubrique décrit également les scénarios où l’expansion des comportements du système de propriétés par défaut à l’aide de ces techniques est appropriée.
Conditions préalables
Cette rubrique part du principe que vous comprenez les scénarios de base de l’implémentation d’une propriété de dépendance et la façon dont les métadonnées sont appliquées à une propriété de dépendance personnalisée. Consultez Propriétés de Dépendance Personnalisées et Métadonnées de Propriété de Dépendance pour le contexte.
Rappels de validation
Les rappels de validation peuvent être affectés à une propriété de dépendance lorsque vous l’inscrivez pour la première fois. Le rappel de validation ne fait pas partie des métadonnées de propriété ; il s’agit d’une entrée directe de la méthode Register. Par conséquent, une fois qu’un rappel de validation est créé pour une propriété de dépendance, il ne peut pas être substitué par une nouvelle implémentation.
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
Les rappels sont implémentés de manière à recevoir une valeur d'objet. Ils retournent true
si la valeur fournie est valide pour la propriété ; sinon, ils retournent false
. Il est supposé que la propriété est du type correct par rapport au type enregistré dans le système de propriétés, il est donc inhabituel de vérifier le type dans les fonctions de rappel. Les rappels sont utilisés par le système de propriétés dans diverses opérations. Cela inclut l'initialisation de type par valeur par défaut, la modification programmatique en appelant SetValue, ou les tentatives de remplacer les métadonnées par une nouvelle valeur par défaut fournie. Si le rappel de validation est appelé par l’une de ces opérations et retourne false
, une exception sera levée. Les développeurs d'applications doivent être prêts à gérer ces exceptions. Une utilisation courante des rappels de validation consiste à valider des valeurs d’énumération ou à limiter les valeurs d’entiers ou de doubles lorsque la propriété définit des mesures qui doivent être égales ou supérieures.
Les rappels de validation sont spécifiquement destinés à être des validateurs de classe, et non des validateurs d’instance. Les paramètres du rappel ne communiquent pas une DependencyObject spécifique sur laquelle les propriétés à valider sont définies. Par conséquent, les rappels de validation ne sont pas utiles pour appliquer les « dépendances » possibles qui peuvent influencer une valeur de propriété, où la valeur spécifique à l’instance d’une propriété dépend de facteurs tels que les valeurs spécifiques à l’instance d’autres propriétés ou l’état d’exécution.
Voici un exemple de code pour un scénario de rappel de validation très simple : validation qu’une propriété typée comme la primitive Double n’est pas PositiveInfinity ni 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
Rappels de coercion de valeur et événements de changement de propriété
Les rappels de coercition de valeur passent l’instance spécifique de DependencyObject pour les propriétés, ainsi que les implémentations PropertyChangedCallback qui sont appelées par le système de propriétés chaque fois que la valeur d’une propriété de dépendance change. À l’aide de ces deux rappels en combinaison, vous pouvez créer une série de propriétés sur des éléments où les modifications d’une propriété forceront une contrainte ou une réévaluation d’une autre propriété.
Un scénario classique d’utilisation d’une liaison de propriétés de dépendance est lorsque vous disposez d’une propriété pilotée par l’interface utilisateur où l’élément contient une propriété chacune pour la valeur minimale et maximale, et une troisième propriété pour la valeur réelle ou actuelle. Ici, si la valeur maximale a été ajustée de telle sorte que la valeur actuelle dépasse le nouveau maximum, vous souhaitez forcer la valeur actuelle à ne pas dépasser la nouvelle valeur maximale et une relation similaire pour le minimum à actuel.
Voici un exemple de code très bref pour l’une des trois propriétés de dépendance qui illustrent cette relation. L'exemple montre comment la propriété CurrentReading
d'un ensemble des propriétés Min/Max/Actuel liées à la lecture est enregistrée. Il utilise la validation comme indiqué dans la section précédente.
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
Le rappel de modification de propriété pour Current est utilisé pour transférer la modification vers d’autres propriétés dépendantes, en appelant explicitement les rappels de valeur de coerce inscrits pour ces autres propriétés :
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
Le rappel de valeur de force vérifie les valeurs des propriétés dont la propriété actuelle dépend potentiellement, et force la valeur actuelle si nécessaire :
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
Note
Les valeurs par défaut des propriétés ne sont pas forcées. Une valeur de propriété égale à la valeur par défaut peut se produire si une valeur de propriété a toujours sa valeur par défaut initiale ou en supprimant d’autres valeurs avec ClearValue.
Les rappels de modification de propriété et la valeur contrainte font partie des métadonnées de propriété. Par conséquent, vous pouvez modifier les rappels d’une propriété de dépendance particulière telle qu’elle existe sur un type que vous dérivez du type propriétaire de la propriété de dépendance, en remplaçant les métadonnées de cette propriété sur votre type.
Scénarios avancés de forçage et de rappel
Contraintes et valeurs souhaitées
Les callbacks CoerceValueCallback seront utilisés par le système de propriétés pour contraindre une valeur selon la logique que vous définissez, mais une valeur coercée d'une propriété définie localement conserve toujours une « valeur souhaitée » en interne. Si les contraintes sont basées sur d’autres valeurs de propriété qui peuvent changer dynamiquement pendant la durée de vie de l’application, les contraintes sont modifiées dynamiquement également, et la propriété contrainte peut modifier sa valeur pour qu’elle soit aussi proche de la valeur souhaitée que possible en fonction des nouvelles contraintes. La valeur devient la valeur souhaitée si toutes les contraintes sont levées. Vous pouvez éventuellement introduire des scénarios de dépendance assez compliqués si vous avez plusieurs propriétés dépendantes les unes des autres de manière circulaire. Par exemple, dans le scénario Min/Max/Current, vous pouvez choisir d’avoir minimum et Maximum être défini par l’utilisateur. Si c’est le cas, vous devrez peut-être forcer que Maximum soit toujours supérieur à Minimum et vice versa. Toutefois, si ce forçage est actif et que le Maximum est contraint au Minimum, cela laisse Current dans un état non définissable, car il dépend à la fois du Maximum et du Minimum et est limité à la plage entre les valeurs, qui est de zéro. Ensuite, si la valeur maximale ou minimale est ajustée, Current semble « suivre » l’une des valeurs, car la valeur souhaitée de Current est toujours stockée et tente d’atteindre la valeur souhaitée, car les contraintes sont relâchées.
Il n’y a rien de techniquement incorrect avec les dépendances complexes, mais ils peuvent être un léger détriment des performances s’ils nécessitent un grand nombre de réévaluations, et peuvent également être déroutants pour les utilisateurs s’ils affectent directement l’interface utilisateur. Soyez prudent avec les rappels de modification de propriété et de contrainte de valeur et assurez-vous que la coercition tentée peut être traitée aussi clairement que possible, et ne soit pas trop contraignante.
Utilisation de CoerceValue pour annuler les modifications de valeur
Le système de propriétés traite tout CoerceValueCallback qui retourne la valeur UnsetValue comme un cas spécial. Ce cas spécial signifie que le changement de propriété ayant entraîné l'appel de CoerceValueCallback doit être rejeté par le système de propriétés, et que ce dernier doit plutôt signaler la valeur antérieure de la propriété. Ce mécanisme peut être utile pour vérifier que les modifications apportées à une propriété initiées de manière asynchrone sont toujours valides pour l’état de l’objet actuel et suppriment les modifications si ce n’est pas le cas. Un autre scénario possible est que vous pouvez supprimer sélectivement une valeur en fonction du composant de détermination de la valeur de propriété qui est responsable de la valeur signalée. Pour ce faire, vous pouvez utiliser le DependencyProperty passé dans le callback et l’identificateur de propriété comme entrée pour GetValueSource, puis traiter le ValueSource.
Voir aussi
- Vue d’ensemble des propriétés de dépendance
- métadonnées de propriété de dépendance
- propriétés de dépendance personnalisées
.NET Desktop feedback