Zależność wartości wywołania zwrotnego i walidacji

W tym temacie opisano sposób tworzenia właściwości zależności przy użyciu alternatywnych niestandardowych implementacji dla funkcji związanych z właściwościami, takich jak ustalanie poprawności, wywołania zwrotne wywoływane za każdym razem, gdy efektywna wartość właściwości zostanie zmieniona, i zastąpienie możliwych zewnętrznych wpływów na określanie wartości. W tym temacie omówiono również scenariusze, w których rozszerzenie domyślnego zachowania systemu właściwości przy użyciu tych technik jest odpowiednie.

Wymagania wstępne

W tym temacie założono, że rozumiesz podstawowe scenariusze implementowania właściwości zależności oraz sposób stosowania metadanych do niestandardowej właściwości zależności. Zobacz Niestandardowe właściwości zależności i metadane właściwości zależności, aby zapoznać się z kontekstem.

Wywołania zwrotne weryfikacji

Wywołania zwrotne weryfikacji można przypisać do właściwości zależności podczas jego pierwszej rejestracji. Wywołanie zwrotne weryfikacji nie jest częścią metadanych właściwości; jest to bezpośrednie dane wejściowe Register metody . W związku z tym po utworzeniu wywołania zwrotnego weryfikacji dla właściwości zależności nie można jej zastąpić przez nową implementację.

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

Wywołania zwrotne są implementowane w taki sposób, że są one dostarczane wartości obiektu. true Zwracają, jeśli podana wartość jest prawidłowa dla właściwości; w przeciwnym razie zwracają wartość false. Zakłada się, że właściwość jest poprawnym typem dla typu zarejestrowanego w systemie właściwości, więc sprawdzanie typu w wywołaniach zwrotnych nie jest zwykle wykonywane. Wywołania zwrotne są używane przez system właściwości w różnych operacjach. Obejmuje to inicjowanie typu początkowego domyślnie, programową zmianę przez wywołanie SetValue, lub próby zastąpienia metadanych nową wartością domyślną. Jeśli wywołanie zwrotne weryfikacji jest wywoływane przez dowolną z tych operacji i zwraca falsewartość , zostanie zgłoszony wyjątek. Autorzy aplikacji muszą być przygotowani do obsługi tych wyjątków. Typowym zastosowaniem wywołań zwrotnych weryfikacji jest walidacja wartości wyliczenia lub ograniczenie wartości liczb całkowitych lub podwajanych, gdy właściwość ustawia miary, które muszą być równe zero lub większe.

Wywołania zwrotne weryfikacji mają być modułami sprawdzania poprawności klas, a nie modułami sprawdzania poprawności wystąpień. Parametry wywołania zwrotnego nie komunikują się z określonymi DependencyObject właściwościami do zweryfikowania. W związku z tym wywołania zwrotne weryfikacji nie są przydatne do wymuszania możliwych "zależności", które mogą mieć wpływ na wartość właściwości, gdzie wartość specyficzna dla wystąpienia właściwości jest zależna od czynników, takich jak wartości specyficzne dla wystąpienia innych właściwości lub stan czasu wykonywania.

Poniżej przedstawiono przykładowy kod dla bardzo prostego scenariusza wywołania zwrotnego weryfikacji: weryfikowanie, czy właściwość, która jest typowana jako Double typ pierwotny, to nie PositiveInfinity lub 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

Wywołania zwrotne wartości coerce i zdarzenia zmiany właściwości

Wywołania zwrotne wartości coerce przekazują określone DependencyObject wystąpienie właściwości, podobnie jak PropertyChangedCallback implementacje wywoływane przez system właściwości za każdym razem, gdy wartość właściwości zależności się zmienia. Korzystając z tych dwóch wywołań zwrotnych w połączeniu, można utworzyć serię właściwości w elementach, w których zmiany w jednej właściwości wymusią wymuszanie lub ponowne wyliczanie innej właściwości.

Typowy scenariusz użycia powiązania właściwości zależności polega na tym, że istnieje właściwość sterowana interfejsem użytkownika, w której element przechowuje jedną właściwość dla wartości minimalnej i maksymalnej oraz trzecią właściwość dla rzeczywistej lub bieżącej wartości. W tym miejscu, jeśli wartość maksymalna została skorygowana w taki sposób, że bieżąca wartość przekroczyła nową wartość maksymalną, należy zmienić wartość bieżącą na wartość nie większą niż nowa maksymalna i podobną relację dla wartości minimalnej do bieżącej.

Poniżej przedstawiono bardzo krótki przykładowy kod tylko jednej z trzech właściwości zależności ilustrujących tę relację. W przykładzie pokazano, w jaki sposób CurrentReading właściwość właściwości Min/Max/Current powiązanych właściwości *Odczyt jest zarejestrowana. Używa walidacji, jak pokazano w poprzedniej sekcji.

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

Właściwość zmieniła wywołanie zwrotne dla bieżącej służy do przekazywania zmiany do innych właściwości zależnych, jawnie wywołując wywołania zwrotne wartości coerce, które są zarejestrowane dla tych innych właściwości:

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

Wywołanie zwrotne wartości coerce sprawdza wartości właściwości, od których jest potencjalnie zależna bieżąca właściwość, a w razie potrzeby przekształca bieżącą wartość:

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

Uwaga

Wartości domyślne właściwości nie są konerced. Wartość właściwości równej wartości domyślnej może wystąpić, jeśli wartość właściwości nadal ma początkową wartość domyślną lub przez wyczyszczenie innych wartości za pomocą ClearValuepolecenia .

Wartość coerce i właściwości zmienione wywołania zwrotne są częścią metadanych właściwości. W związku z tym można zmienić wywołania zwrotne dla określonej właściwości zależności, ponieważ istnieje na typie pochodzącym z typu będącego właścicielem właściwości zależności, przesłaniając metadane dla tej właściwości typu.

Zaawansowane scenariusze przymusu i wywołania zwrotnego

Ograniczenia i żądane wartości

Wywołania CoerceValueCallback zwrotne będą używane przez system właściwości do stosowania wartości zgodnie z zadeklarowaną logiką, ale wartość współużytkowana lokalnie ustawiona będzie nadal zachowywać "żądaną wartość" wewnętrznie. Jeśli ograniczenia są oparte na innych wartościach właściwości, które mogą zmieniać się dynamicznie w okresie istnienia aplikacji, ograniczenia przymusu są również zmieniane dynamicznie, a właściwość ograniczona może zmienić jego wartość, aby uzyskać jak najbliżej żądanej wartości, jak to możliwe, biorąc pod uwagę nowe ograniczenia. Wartość stanie się żądaną wartością, jeśli wszystkie ograniczenia zostaną zniesione. Potencjalnie można wprowadzić kilka dość skomplikowanych scenariuszy zależności, jeśli masz wiele właściwości, które są zależne od siebie w sposób okrągły. Na przykład w scenariuszu Minimalna/Maksymalna/Bieżąca można wybrać opcję minimalną i maksymalną dla ustawienia użytkownika. Jeśli tak, może być konieczne ograniczenie, że wartość Maksimum jest zawsze większa niż Minimum i odwrotnie. Jeśli jednak ten przymus jest aktywny, a wartość Maksymalna wartość coerces do minimum, pozostawia wartość Current w stanie nieskonfigurowanym, ponieważ jest zależna od obu i jest ograniczona do zakresu między wartościami, czyli zero. Następnie, jeśli wartość Maksymalna lub Minimalna zostanie skorygowana, wartość Current wydaje się być "obserwowana" jedną z wartości, ponieważ żądana wartość Current jest nadal przechowywana i próbuje osiągnąć żądaną wartość, ponieważ ograniczenia zostały poluzowane.

Nie ma nic technicznie złego w przypadku złożonych zależności, ale może to być niewielkie szkody w wydajności, jeśli wymagają dużej liczby ponownych oceny, a także mogą być mylące dla użytkowników, jeśli mają bezpośredni wpływ na interfejs użytkownika. Należy zachować ostrożność przy zmianie właściwości i wywołaniu zwrotnym wartości i upewnić się, że próba przymusu może być traktowana tak jednoznacznie, jak to możliwe, i nie "nadmiernego ograniczenia".

Używanie wartości CoerceValue do anulowania zmian wartości

System właściwości będzie traktować dowolną CoerceValueCallback , która zwraca wartość UnsetValue jako specjalny przypadek. Ten szczególny przypadek oznacza, że zmiana właściwości, która spowodowała CoerceValueCallback wywołanie, powinna zostać odrzucona przez system właściwości i że system właściwości powinien zamiast tego zgłosić niezależnie od poprzedniej wartości właściwości. Ten mechanizm może być przydatny do sprawdzania, czy zmiany właściwości zainicjowane asynchronicznie są nadal prawidłowe dla bieżącego stanu obiektu i pomijają zmiany, jeśli nie. Innym możliwym scenariuszem jest to, że można selektywnie pominąć wartość w zależności od tego, który składnik określania wartości właściwości jest odpowiedzialny za zgłaszaną wartość. W tym celu można użyć przekazanego DependencyProperty wywołania zwrotnego i identyfikatora właściwości jako danych wejściowych dla GetValueSourceelementu , a następnie przetworzyć ValueSourceelement .

Zobacz też