Retornos de chamada da propriedade de dependência e validaçãoDependency Property Callbacks and Validation

Este tópico descreve como criar propriedades de dependência usando implementações alternativas personalizadas para recursos relacionados a propriedade como validação de determinação, callbacks que são chamadas sempre que o valor efetivo da propriedade é alterado e substituindo possíveis influências externas na determinação do 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. Este tópico também aborda os cenários em que é apropriado expandir os comportamentos padrões do sistema usando essas técnicas.This topic also discusses scenarios where expanding on the default property system behaviors by using these techniques is appropriate.

Pré-requisitosPrerequisites

Este tópico pressupõe que você compreenda os cenários básicos de implementar uma propriedade de dependência e como os metadados são aplicados a uma propriedade de dependência 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 Propriedades de dependência personalizadas e Metadados de propriedade de dependência para ver o contexto.See Custom Dependency Properties and Dependency Property Metadata for context.

Retornos de chamadas de validaçãoValidation Callbacks

Retornos de chamada de validação podem ser atribuídos a uma propriedade de dependência quando você registra-a primeiro.Validation callbacks can be assigned to a dependency property when you first register it. O retorno de chamada de validação não faz parte de metadados de propriedade; é uma entrada direta Register do método.The validation callback is not part of property metadata; it is a direct input of the Register method. Portanto, depois de criar um retorno de chamada de validação para uma propriedade de dependência, ela não pode ser substituída por uma nova implementação.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

Os retornos de chamada são implementados, de modo que eles recebem um valor de objeto.The callbacks are implemented such that they are provided an object value. Elas retornam true se o valor fornecido é válido para a propriedade; caso contrário, eles retornam false.They return true if the provided value is valid for the property; otherwise, they return false. Supõe-se que a propriedade é do tipo correto de acordo com o tipo registrado no sistema de propriedade, então a verificação de tipo dentro de retornos de chamada normalmente não é feita.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. Os retornos de chamada são usados pelo sistema de propriedades em uma variedade de operações diferentes.The callbacks are used by the property system in a variety of different operations. Isso inclui a inicialização do tipo inicial por valor SetValuepadrão, alteração programática invocando ou tentativas de substituir metadados com novo valor padrão fornecido.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 o retorno de chamada de validação é chamado por qualquer uma dessas operações e retorna false, em seguida, uma exceção será gerada.If the validation callback is invoked by any of these operations, and returns false, then an exception will be raised. Criadores de aplicativo devem estar preparados para tratar essas exceções.Application writers must be prepared to handle these exceptions. Um uso comum de retornos de chamada de validação é validar valores de enumeração ou restrição de valores de números inteiros ou duplos quando a propriedade define as medidas que devem ser zero ou maior.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.

Retornos de chamada de validação são projetados especificamente para ser validadores de classe, não validadores de instância.Validation callbacks specifically are intended to be class validators, not instance validators. Os parâmetros do retorno de DependencyObject chamada não comunicam uma específica sobre a qual as propriedades a serem validadas são definidas.The parameters of the callback do not communicate a specific DependencyObject on which the properties to validate are set. Portanto, os retornos de chamada de validação não são úteis para aplicar as possíveis "dependências" que podem influenciar a um valor da propriedade, no qual o valor específico de instância de uma propriedade é dependente de fatores como valores específicos de instância de outras propriedades ou estado de tempo de execução.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.

O seguinte é o código de exemplo para um cenário de chamada de Double validação muito PositiveInfinity NegativeInfinitysimples: validar que uma propriedade que é digitada como primitiva não é ou .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

Forçar retornos de chamada de valor e eventos alterados de propriedadeCoerce Value Callbacks and Property Changed Events

Os retornos de chamada de DependencyObject valor coerce PropertyChangedCallback passam a instância específica para propriedades, assim como implementações que são invocadas pelo sistema de propriedades sempre que o valor de uma propriedade de dependência muda.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. Ao usar esses dois retornos de chamada em combinação, você pode criar uma série de propriedades nos elementos em que alterações em uma propriedade forçará uma coerção ou reavaliação de outra propriedade.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.

Um cenário típico para usar uma ligação de propriedades de dependência é quando você tem uma propriedade controlada por interface do usuário no qual o elemento contém uma propriedade para o valor mínimo e máximo e uma terceira propriedade para o valor real ou atual.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. Aqui, se o máximo foi ajustado de tal forma que o valor atual excedeu o novo máximo, você pode querer forçar o valor atual a não ser maior do que o novo máximo e um relacionamento análogo para mínimo e atual.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.

A seguir está um breve exemplo de código para apenas uma das três propriedades de dependência que ilustram esse relacionamento.The following is very brief example code for just one of the three dependency properties that illustrate this relationship. O exemplo mostra como a propriedade CurrentReading de um conjunto Mín/Máx/atual das *propriedades de leitura relacionadas está registrado.The example shows how the CurrentReading property of a Min/Max/Current set of related *Reading properties is registered. Ele usa a validação como mostrado na seção 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

O retorno de chamada de propriedade alterada para Atual é usado para encaminhar a alteração para outras propriedades dependentes, invocando explicitamente os retornos de chamada de valores que estão registrados para as outras propriedades: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

Os retornos de chamada de valor forçados verificam os valores das propriedades das quais a propriedade atual está potencialmente dependente e converte o valor atual se necessário: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

Observação

Valores padrão das propriedades não são forçados.Default values of properties are not coerced. Um valor de propriedade igual ao valor padrão pode ocorrer se um valor ClearValuede propriedade ainda tiver sua inadimplência inicial, ou através da compensação de outros valores com .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.

Os retornos de chamada de propriedade alterada e forçados fazem parte dos metadados da propriedade.The coerce value and property changed callbacks are part of property metadata. Portanto, você pode alterar os retornos de chamada para uma determinada propriedade de dependência, pois ela existe em um tipo que você deriva do tipo que possui a propriedade de dependência, substituindo os metadados por essa propriedade em seu 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.

Coerção avançada e cenários de retorno de chamadaAdvanced Coercion and Callback Scenarios

Restrições e valores desejadosConstraints and Desired Values

Os CoerceValueCallback retornos de chamada serão usados pelo sistema de propriedades para coagir um valor de acordo com a lógica que você declara, mas um valor coagido de uma propriedade definida localmente ainda manterá um "valor desejado" 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 as restrições são baseadas em outros valores de propriedade isso pode ser alterado dinamicamente durante o tempo de vida do aplicativo, as restrições de coerção são alteradas dinamicamente também e a propriedade restrita poderá alterar seu valor para chegar o mais perto possível do valor desejado dadas as novas restrições.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. O valor se tornará o valor desejado se todas as restrições forem respeitadas.The value will become the desired value if all constraints are lifted. Você poderá potencialmente criar algumas situações de dependência bastante complicadas se você tiver várias propriedades que são dependentes entre si, de maneira 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 exemplo, no cenário de Min/máx/atual, você pode optar por mínimo e máximo ser configurável pelo usuário.For instance, in the Min/Max/Current scenario, you could choose to have Minimum and Maximum be user settable. Nesse caso, você precisará forçar que o máximo seja sempre maior que o mínimo e vice-versa.If so, you might need to coerce that Maximum is always greater than Minimum and vice versa. Mas se essa coerção estiver ativa e o máximo é forçado para o mínimo, isso deixará Atual em um estado instável, porque ele é dependente de ambos e é restrito ao intervalo entre os valores, que é 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. Em seguida, se o valor máximo ou mínimo for ajustado, atual parecerá "seguir" um dos valores, porque o valor desejado de atual ainda está armazenado e está tentando atingir o valor desejado conforme as restrições são relaxadas.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.

Não há nada tecnicamente errado com dependências complexas, mas podem ser um ponto prejudicial no desempenho se elas exigem um grande número de reavaliações e também podem ser confusas para os usuários se afetam a interface do usuário diretamente.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. Tenha cuidado com a propriedade alterada e os retornos de chamada de valor e certifique-se de que a coerção que está sendo tentada pode ser tratada como sem ambiguidade possível e sem "restrições em excesso".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".

Usando CoerceValue para cancelar as alterações de valorUsing CoerceValue to Cancel Value Changes

O sistema de CoerceValueCallback propriedade tratará UnsetValue qualquer um que devolva o valor como um caso especial.The property system will treat any CoerceValueCallback that returns the value UnsetValue as a special case. Este caso especial significa que a mudança CoerceValueCallback de propriedade que resultou no chamado deve ser rejeitada pelo sistema imobiliário, e que o sistema imobiliário deve, em vez disso, informar qualquer valor anterior que o imóvel tinha.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. Esse mecanismo poderá ser útil para verificar se as alterações a uma propriedade que foram iniciadas de forma assíncrona ainda são válidas para o estado atual do objeto e para eliminar as alterações se não forem válidas.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. Outro cenário possível é que você pode seletivamente suprimir um valor dependendo de qual componente de determinação do valor da propriedade é responsável pelo valor que está sendo relatado.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 fazer isso, você DependencyProperty pode usar o passe no retorno GetValueSourcede chamada e ValueSourceo identificador de propriedade como entrada para , e, em seguida, processar o .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.

Confira tambémSee also