Devoluciones de llamada y validación de las propiedades de dependenciaDependency Property Callbacks and Validation

En este tema se describe cómo crear propiedades de dependencia mediante implementaciones personalizadas alternativas de características relacionadas con las propiedades, como la determinación de la validación, las devoluciones de llamada que se invocan cuando cambia el valor efectivo de la propiedad y la invalidación de posibles influencias externas en la determinación del valor.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. En este tema también se describen los escenarios donde es apropiado expandir los comportamientos predeterminados del sistema de propiedades mediante estas técnicas.This topic also discusses scenarios where expanding on the default property system behaviors by using these techniques is appropriate.

Requisitos previosPrerequisites

En este tema se supone que entiende los escenarios básicos de la implementación de una propiedad de dependencia y cómo se aplican los metadatos a una propiedad de dependencia personalizada.This topic assumes that you understand the basic scenarios of implementing a dependency property, and how metadata is applied to a custom dependency property. Consulte Propiedades de dependencia personalizadas y Metadatos de las propiedades de dependencia para obtener contexto.See Custom Dependency Properties and Dependency Property Metadata for context.

Devoluciones de llamada de validaciónValidation Callbacks

Las devoluciones de llamada de validación pueden asignarse a una propiedad de dependencia cuando se registra por primera vez.Validation callbacks can be assigned to a dependency property when you first register it. La devolución de llamada de validación no forma parte de los metadatos de propiedad; es una entrada directa de la Register método.The validation callback is not part of property metadata; it is a direct input of the Register method. Por lo tanto, una vez creada una devolución de llamada de validación para una propiedad de dependencia, una nueva implementación no puede invalidarla.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

Las devoluciones de llamada se implementan de forma que obtienen un valor de objeto.The callbacks are implemented such that they are provided an object value. Devuelven true si el valor proporcionado es válido para la propiedad; en caso contrario, devuelven false.They return true if the provided value is valid for the property; otherwise, they return false. Se supone que la propiedad es del tipo correcto por el tipo registrado con el sistema de propiedades, por lo que la comprobación de tipo en las devoluciones de llamada no se suele llevar a cabo.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. El sistema de propiedades usa las devoluciones de llamada en distintas operaciones.The callbacks are used by the property system in a variety of different operations. Esto incluye la inicialización de tipos inicial por valor predeterminado, el cambio mediante programación invocando SetValue, o intenta reemplazar los metadatos con el nuevo valor predeterminado proporcionado.This includes the initial type initialization by default value, programmatic change by invoking SetValue, or attempts to override metadata with new default value provided. Si cualquiera de estas operaciones invoca la devolución de llamada de validación y devuelve false, se generará una excepción.If the validation callback is invoked by any of these operations, and returns false, then an exception will be raised. Los escritores de aplicaciones deben estar preparados para controlar estas excepciones.Application writers must be prepared to handle these exceptions. Un uso común de las devoluciones de llamada de validación es validar los valores de enumeración o restringir los valores de números enteros o dobles cuando la propiedad establece medidas que deben ser cero o un valor superior.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.

Las devoluciones de llamada de validación están diseñadas específicamente como validadores de clase, no como validadores de instancia.Validation callbacks specifically are intended to be class validators, not instance validators. Los parámetros de la devolución de llamada no comunican un determinado DependencyObject en que se establecen las propiedades que se va a validar.The parameters of the callback do not communicate a specific DependencyObject on which the properties to validate are set. Por lo tanto, las devoluciones de llamada de validación no resultan útiles para aplicar las posibles "dependencias" que podrían influir en un valor de propiedad, donde el valor específico de la instancia de una propiedad depende de factores, tales como los valores específicos de la instancia de otras propiedades o el estado de tiempo de ejecución.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.

El siguiente es un código de ejemplo para un escenario de devolución de llamada de validación muy simple: validar que una propiedad que se escribe como el Double primitivo no es PositiveInfinity o NegativeInfinity.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

Devoluciones de llamada de valor de coerción y eventos de propiedad cambiadaCoerce Value Callbacks and Property Changed Events

Valor de coerción devoluciones de llamada pasar específico del DependencyObject instancia para las propiedades, como hacen PropertyChangedCallback implementaciones que se invocan mediante el sistema de propiedades cada vez que cambia el valor de una propiedad de dependencia.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. Con estas dos devoluciones de llamada combinadas, puede crear una serie de propiedades en aquellos elementos donde los cambios en una propiedad forzarán una coerción o reevaluación de otra propiedad.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 escenario típico para usar la vinculación de propiedades de dependencia es cuando tiene una propiedad controlada por la interfaz de usuario donde el elemento contiene una propiedad para el valor mínimo y otra para el máximo, además de una tercera propiedad para el valor real o actual.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. Aquí, si el máximo está ajustado de manera que el valor actual supera el nuevo máximo, querrá forzar el valor actual para que no supere el nuevo máximo, además de una relación similar entre los valores mínimo y actual.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.

El siguiente es un código de ejemplo muy breve para una sola de las tres propiedades de dependencia que ilustran esta relación.The following is very brief example code for just one of the three dependency properties that illustrate this relationship. En el ejemplo se muestra cómo está registrada la propiedad CurrentReading de un conjunto Min/Max/Current de propiedades *Reading relacionadas.The example shows how the CurrentReading property of a Min/Max/Current set of related *Reading properties is registered. Usa la validación como se muestra en la sección anterior.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

La devolución de llamada de la propiedad cambiada Current se usa para reenviar el cambio a otras propiedades dependientes, mediante la invocación explícita de las devoluciones de llamada del valor de coerción registradas para esas otras propiedades: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

La devolución de llamada de valor de coerción comprueba los valores de propiedades de las que depende potencialmente la propiedad actual y fuerza el valor actual si es necesario: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

Los valores predeterminados de las propiedades no se fuerzan.Default values of properties are not coerced. Un valor de propiedad igual al valor predeterminado podría producirse si un valor de propiedad sigue teniendo su valor predeterminado inicial, o si se borran otros valores con ClearValue.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.

Las devoluciones de llamada de valor de coerción y propiedad cambiada forman parte de los metadatos de las propiedades.The coerce value and property changed callbacks are part of property metadata. Por lo tanto, puede cambiar las devoluciones de llamada de una propiedad de dependencia determinada, tal como existe en un tipo que se deriva del tipo que posee la propiedad de dependencia, mediante la invalidación de los metadatos de esa propiedad en el 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.

Escenarios de coerción y llamada de devolución avanzadosAdvanced Coercion and Callback Scenarios

Restricciones y valores deseadosConstraints and Desired Values

El CoerceValueCallback las devoluciones de llamada se utilizará por el sistema de propiedades para un valor de coerción de acuerdo con la lógica que se declara, pero un valor forzado de establecida localmente propiedad conservará un "valor deseado" 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. Si las restricciones se basan en otros valores de propiedad que pueden cambiar dinámicamente durante el ciclo de vida de la aplicación, las restricciones de coerción también cambian de forma dinámica y la propiedad restringida puede cambiar su valor para que se aproxime al valor deseado lo máximo posible según las nuevas restricciones.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. El valor se convertirá en el valor deseado si se levantan todas las restricciones.The value will become the desired value if all constraints are lifted. Se pueden introducir algunos escenarios de dependencia complejos en el caso de tener varias propiedades que sean dependientes entre sí de manera circular.You can potentially introduce some fairly complicated dependency scenarios if you have multiple properties that are dependent on one another in a circular manner. Por ejemplo, en el escenario Min/Max/Current, puede elegir que las propiedades Minimum y Maximum las pueda establecer el usuario.For instance, in the Min/Max/Current scenario, you could choose to have Minimum and Maximum be user settable. En este caso, es posible que deba forzar que Maximum sea siempre mayor que Minimum y viceversa.If so, you might need to coerce that Maximum is always greater than Minimum and vice versa. No obstante, si esa coerción está activa y la propiedad Maximum fuerza la propiedad Minimum, Current no se puede establecer, ya que depende de ambas y está restringida al intervalo entre los valores, que es cero.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. A continuación, si se ajustan las propiedades Maximum o Minimum, parecerá que Current "sigue" a uno de los valores, ya que el valor deseado de Current sigue estando almacenado e intenta alcanzar el valor deseado a medida que se suavizan las restricciones.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.

Técnicamente, no hay nada incorrecto en las dependencias complejas, pero pueden suponer un ligero deterioro del rendimiento si requieren grandes cantidades de reevaluaciones y también pueden ser confusas para los usuarios si afectan a la interfaz de usuario directamente.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. Tenga cuidado con las devoluciones de llamada de propiedad cambiada y valor de coerción, y asegúrese de que la coerción que se está intentando pueda tratarse de la manera más ambigua posible y no sea "sobrerrestrictiva".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 de CoerceValue para cancelar cambios de valorUsing CoerceValue to Cancel Value Changes

El sistema de propiedades tratará cualquier CoerceValueCallback que devuelve el valor UnsetValue como un caso especial.The property system will treat any CoerceValueCallback that returns the value UnsetValue as a special case. En este caso especial significa que el cambio de propiedad que dan como resultado la CoerceValueCallback se debe rechazar que se llama por el sistema de propiedades, y que el sistema de propiedades en su lugar debe notificar el valor anterior que tenía la propiedad.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. Este mecanismo puede ser útil para comprobar que los cambios en una propiedad iniciados de manera asincrónica siguen siendo válidos para el estado actual del objeto y suprimirlos si no lo son.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. Otro escenario posible es la posibilidad de suprimir de manera selectiva un valor según el componente de determinación del valor propiedad que sea responsable del valor comunicado.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. Para ello, puede usar el DependencyProperty pasa la devolución de llamada y el identificador de propiedad como entrada para GetValueSourcey, a continuación, procesar el ValueSource.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.

Vea tambiénSee also