Callback e convalida delle proprietà di dipendenzaDependency Property Callbacks and Validation

Questo argomento descrive come creare proprietà di dipendenza usando le implementazioni personalizzate alternative per funzionalità correlate alle proprietà, ad esempio la determinazione della convalida, i callback richiamati ogni volta che il valore effettivo della proprietà viene modificato e l'override delle possibili influenze esterne sulla determinazione del valore.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. In questo argomento vengono anche presentati scenari in cui è possibile espandere i comportamenti predefiniti del sistema di proprietà usando queste tecniche.This topic also discusses scenarios where expanding on the default property system behaviors by using these techniques is appropriate.

PrerequisitiPrerequisites

Nell'argomento si presuppone la conoscenza degli scenari di base dell'implementazione di una proprietà di dipendenza e del modo in cui i metadati vengono applicati a una proprietà di dipendenza personalizzata.This topic assumes that you understand the basic scenarios of implementing a dependency property, and how metadata is applied to a custom dependency property. Per il contesto, vedere Proprietà di dipendenza personalizzate e Metadati delle proprietà di dipendenza.See Custom Dependency Properties and Dependency Property Metadata for context.

Callback di convalidaValidation Callbacks

I callback di convalida possono essere assegnati a una proprietà di dipendenza al momento della sua prima registrazione.Validation callbacks can be assigned to a dependency property when you first register it. Il callback di convalida non fa parte dei metadati della proprietà. si tratta di un input diretto del Register metodo.The validation callback is not part of property metadata; it is a direct input of the Register method. Di conseguenza, dopo averlo creato per una proprietà di dipendenza, un callback di convalida non può essere sottoposto a override da parte di una nuova implementazione.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

I callback vengono implementati in modo che venga loro fornito un valore di oggetto.The callbacks are implemented such that they are provided an object value. Restituiscono true se il valore fornito è valido per la proprietà e in caso contrario restituiscono false.They return true if the provided value is valid for the property; otherwise, they return false. Si presuppone che la proprietà sia del tipo corretto per il tipo registrato nel sistema di proprietà, quindi all'interno dei callback non viene generalmente eseguito il controllo del tipo.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. I callback vengono usati dal sistema di proprietà in varie operazioni diverse.The callbacks are used by the property system in a variety of different operations. Questa operazione include l'inizializzazione del tipo iniziale per valore predefinito, la modifica SetValuea livello di codice richiamando o il tentativo di eseguire l'override dei metadati con il nuovo valore predefinito fornito.This includes the initial type initialization by default value, programmatic change by invoking SetValue, or attempts to override metadata with new default value provided. Se il callback di convalida viene richiamato da una di queste operazioni e restituisce false, verrà generata un'eccezione.If the validation callback is invoked by any of these operations, and returns false, then an exception will be raised. Gli autori di applicazioni devono essere preparati a gestire queste eccezioni.Application writers must be prepared to handle these exceptions. Un uso comune dei callback di convalida è la convalida dei valori di enumerazione o il vincolo di valori di Integer o Double quando la proprietà imposta misure che devono essere uguali o maggiori di zero.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.

I callback di convalida sono specificamente concepiti come validator di classi, non di istanze.Validation callbacks specifically are intended to be class validators, not instance validators. I parametri del callback non comunicano un oggetto DependencyObject specifico sul quale sono impostate le proprietà da convalidare.The parameters of the callback do not communicate a specific DependencyObject on which the properties to validate are set. Di conseguenza, i callback di convalida non sono utili per applicare le possibili "dipendenze" che possono influenzare un valore di proprietà, in cui il valore specifico delle istanze di una proprietà è dipendente da fattori quali i valori specifici delle istanze di altre proprietà o lo stato di runtime.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.

Di seguito è riportato un esempio di codice per uno scenario di callback di convalida molto semplice: la convalida che una Double proprietà tipizzata PositiveInfinity come NegativeInfinityprimitiva non sia o.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

Callback di valori soggetti a coercizione ed eventi di proprietà modificateCoerce Value Callbacks and Property Changed Events

I callback di valori con coercizione passano l' DependencyObject istanza specifica per le proprietà, PropertyChangedCallback come le implementazioni richiamate dal sistema di proprietà ogni volta che il valore di una proprietà di dipendenza viene modificato.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. Usando questi due callback in combinazione, è possibile creare una serie di proprietà su elementi in cui le modifiche in una proprietà imporranno una coercizione o una rivalutazione di un'altra proprietà.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.

Un scenario tipico per l'uso di un collegamento di proprietà di dipendenza è il caso in cui si dispone di una proprietà basata su un'interfaccia utente in cui l'elemento contiene una proprietà per il valore minimo e una per il valore massimo, oltre a una terza proprietà per il valore effettivo o corrente.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. In questo caso, se il massimo è stato regolato in modo che il valore corrente superi il nuovo massimo, sarà opportuno assegnare il valore corrente in modo che non sia maggiore del nuovo massimo e impostare una relazione simile per il valore minimo rispetto al valore corrente.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.

Di seguito viene riportato un brevissimo esempio di codice per una delle tre proprietà di dipendenza che illustrano questa relazione.The following is very brief example code for just one of the three dependency properties that illustrate this relationship. Nell'esempio viene illustrata la modalità di registrazione della proprietà CurrentReading di un set Min/Max/Current di proprietà *Reading.The example shows how the CurrentReading property of a Min/Max/Current set of related *Reading properties is registered. Viene usata la convalida come illustrato nella sezione precedente.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

Il callback per proprietà modificate per il valore Current viene usato per inoltrare la modifica ad altre proprietà dipendenti, richiamando in modo esplicito i callback di valori soggetti a coercizione registrati per tali proprietà: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

Il callback di valori soggetti a coercizione verifica i valori delle proprietà da cui la proprietà corrente è potenzialmente dipendente e assegna il valore corrente, se necessario: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

Nota

I valori predefiniti delle proprietà non vengono assegnati.Default values of properties are not coerced. Un valore di proprietà uguale al valore predefinito potrebbe verificarsi se il valore di una proprietà ha ancora l'impostazione predefinita iniziale o la cancellazione di ClearValuealtri valori con.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.

I callback di valori soggetti a coercizione e per proprietà modificate fanno parte dei metadati delle proprietà.The coerce value and property changed callbacks are part of property metadata. Pertanto, è possibile modificare i callback per una determinata proprietà di dipendenza quando quest'ultima è presente su un tipo derivato dal tipo proprietario della proprietà di dipendenza, eseguendo l'override dei metadati per tale proprietà sul tipo.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.

Coercizione avanzata e scenari di callbackAdvanced Coercion and Callback Scenarios

Vincoli e valori desideratiConstraints and Desired Values

I CoerceValueCallback callback verranno usati dal sistema di proprietà per assegnare un valore in base alla logica dichiarata, ma un valore forzato di una proprietà impostata localmente manterrà il "valore desiderato" internamente.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. Se i vincoli sono basati su altri valori di proprietà che possono essere modificati in modo dinamico nel corso della durata dell'applicazione, anche i vincoli di coercizione vengono modificati in modo dinamico e la proprietà vincolata può modificare il proprio valore per avvicinarsi il più possibile al valore desiderato in base ai nuovi vincoli.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. Il valore diventerà il valore desiderato se tutti i vincoli verranno rimossi.The value will become the desired value if all constraints are lifted. È potenzialmente possibile introdurre alcuni scenari di dipendenze piuttosto complicati se si hanno più proprietà dipendenti l'una dall'altra in modo circolare.You can potentially introduce some fairly complicated dependency scenarios if you have multiple properties that are dependent on one another in a circular manner. Ad esempio, nello scenario Min/Max/Current, è possibile scegliere che i valori Minimum e Maximum siano impostabili dall'utente.For instance, in the Min/Max/Current scenario, you could choose to have Minimum and Maximum be user settable. In questo caso, potrebbe essere necessario imporre che il valore Maximum sia sempre superiore al valore Minimum e viceversa.If so, you might need to coerce that Maximum is always greater than Minimum and vice versa. Ma se tale coercizione è attiva e il valore Maximum viene assegnato a Minimum, il valore Current viene lasciato in uno stato non impostabile, poiché è dipendente da entrambi ed è vincolato all'intervallo compreso tra i valori, che è pari a zero.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. Se i valori Maximum o Minimum vengono modificati, il valore Current sembrerà seguire uno dei valori, perché il valore desiderato di Current è ancora archiviato e tenta di raggiungere il valore desiderato con l'allentarsi dei vincoli.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.

Le dipendenze complesse non sono errate dal punto di vista tecnico, ma possono causare una leggera riduzione delle prestazioni se richiedono numerose rivalutazioni, oltre a generare confusione negli utenti se influiscono direttamente sull'interfaccia utente.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. È necessario prestare attenzione con i callback di valori soggetti a coercizione e per proprietà modificate e accertarsi che la coercizione tentata possa essere considerata nel modo meno ambiguo possibile e non imponga vincoli eccessivi.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".

Uso di CoerceValue per annullare le modifiche dei valoriUsing CoerceValue to Cancel Value Changes

Il sistema di proprietà considererà CoerceValueCallback tutti i che restituiscono il valore UnsetValue come caso speciale.The property system will treat any CoerceValueCallback that returns the value UnsetValue as a special case. Questo caso speciale indica che la modifica della proprietà che ha generato la CoerceValueCallback chiamata deve essere rifiutata dal sistema di proprietà e che il sistema di proprietà deve segnalare qualsiasi valore precedente della proprietà.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. Questo meccanismo può essere utile per controllare che le modifiche a una proprietà avviate in modo asincrono siano ancora valide per lo stato dell'oggetto corrente e per eliminarle in caso non lo siano.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. Un altro possibile scenario è costituito dalla possibilità di eliminare in modo selettivo un valore a seconda di quale componente della determinazione dei valori di proprietà sia responsabile del valore segnalato.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. A tale scopo, è possibile usare l' DependencyProperty oggetto passato nel callback e l'identificatore di proprietà come input GetValueSourceper ValueSource, quindi elaborare.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.

Vedere ancheSee also