Delen via


Afhankelijkheidseigenschappen: callbacks en validatie

In dit onderwerp wordt beschreven hoe u afhankelijkheidseigenschappen maakt met behulp van alternatieve aangepaste implementaties voor eigenschappengerelateerde functies, zoals validatiebepaling, callbacks die worden aangeroepen wanneer de effectieve waarde van de eigenschap wordt gewijzigd en mogelijke invloeden van buiten de waardebepaling overschrijven. In dit onderwerp worden ook scenario's besproken waarin het gedrag van het standaardeigenschapssysteem wordt uitgebreid met behulp van deze technieken.

Voorwaarden

In dit onderwerp wordt ervan uitgegaan dat u de basisscenario's voor het implementeren van een afhankelijkheidseigenschap begrijpt en hoe metagegevens worden toegepast op een aangepaste afhankelijkheidseigenschap. Zie aangepaste eigenschappen van afhankelijkheden en metagegevens van eigenschappen van afhankelijkheden voor context.

Callbacks voor validatie

Callbacks voor validatie kunnen worden toegewezen aan een afhankelijkheidseigenschap wanneer u deze voor het eerst registreert. De callback voor validatie maakt geen deel uit van metagegevens van eigenschappen; het is een directe invoer van de Register methode. Zodra een validatie-callback voor een afhankelijkheidseigenschap is gemaakt, kan deze daarom niet worden overschreven door een nieuwe implementatie.

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

De callbacks worden geïmplementeerd zodat ze een objectwaarde hebben. Ze retourneren true als de opgegeven waarde geldig is voor de eigenschap; anders retourneren ze false. Er wordt van uitgegaan dat de eigenschap van het juiste type is zoals geregistreerd bij het eigenschappensysteem, zodat het type binnen de callbacks meestal niet wordt gecontroleerd. De callbacks worden gebruikt door het eigenschappensysteem in verschillende bewerkingen. Dit omvat de initiële initialisatie van het type standaardwaarde, programmatische wijziging door SetValueaan te roepen of probeert metagegevens te overschrijven met de nieuwe standaardwaarde die is opgegeven. Als de validatieaanroep wordt aangeroepen door een van deze bewerkingen en falseretourneert, wordt er een uitzondering gegenereerd. Toepassingsschrijvers moeten worden voorbereid om deze uitzonderingen af te handelen. Een veelvoorkomend gebruik van callbacks voor validatie is het valideren van opsommingswaarden of het beperken van waarden van gehele getallen of doubles wanneer de eigenschap metingen instelt die nul of groter moeten zijn.

Validatie-callbacks zijn specifiek bedoeld als klassevalidatoren, niet exemplaarvalidators. De parameters van de callback communiceren geen specifieke DependencyObject waarop de te valideren eigenschappen zijn ingesteld. Daarom zijn de callbacks voor validatie niet handig voor het afdwingen van de mogelijke 'afhankelijkheden' die van invloed kunnen zijn op een eigenschapswaarde, waarbij de instantiespecifieke waarde van een eigenschap afhankelijk is van factoren zoals exemplaarspecifieke waarden van andere eigenschappen of runtimestatus.

Hier volgt een voorbeeldcode voor een zeer eenvoudig callbackscenario voor validatie: valideren dat een eigenschap die is getypt als de Double primitieve niet PositiveInfinity of NegativeInfinityis.

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

Callbacks voor het afdwingen van waarden en gebeurtenissen van gewijzigde eigenschappen

Callbacks voor coerce-waarden geven de specifieke DependencyObject instantie voor eigenschappen door, net zoals PropertyChangedCallback implementaties die door het eigenschapssysteem worden aangeroepen wanneer de waarde van een afhankelijkheidseigenschap verandert. Met deze twee callbacks in combinatie kunt u een reeks eigenschappen maken op elementen waarbij wijzigingen in de ene eigenschap een dwang of herwaardering van een andere eigenschap afdwingen.

Een typisch scenario voor het gebruik van een koppeling van afhankelijkheidseigenschappen is wanneer u een eigenschap op basis van de gebruikersinterface hebt waarin het element één eigenschap voor de minimum- en maximumwaarde bevat, en een derde eigenschap voor de werkelijke of huidige waarde. Hier, als het maximum zodanig is aangepast dat de huidige waarde het nieuwe maximum overschrijdt, wilt u de huidige waarde aanpassen zodat deze niet groter is dan het nieuwe maximum. En hetzelfde geldt voor het minimale en de huidige waarde.

Hier volgt een zeer korte voorbeeldcode voor slechts één van de drie afhankelijkheidseigenschappen die deze relatie illustreren. In het voorbeeld ziet u hoe de eigenschap CurrentReading van een set min/max/huidige gerelateerde *leeseigenschappen is geregistreerd. Validatie wordt gebruikt zoals weergegeven in de vorige sectie.

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

De veranderde eigenschapscallback voor Current wordt gebruikt om de wijziging door te sturen naar andere afhankelijke eigenschappen door expliciet de dwangwaarde-callbacks aan te roepen die zijn geregistreerd voor deze andere eigenschappen.

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

De callback voor de coerce-waarde controleert de waarden van de eigenschappen waarvan de huidige eigenschap mogelijk afhankelijk is, en corrigeert de huidige waarde indien nodig.

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

Notitie

Standaardwaarden van eigenschappen worden niet gedwongen. Een eigenschapswaarde die gelijk is aan de standaardwaarde kan optreden als een eigenschapswaarde nog steeds de oorspronkelijke standaardwaarde heeft of door andere waarden met ClearValuete wissen.

De coerce-waarde van eigenschappen en callbacks bij wijziging van eigenschappen maken deel uit van de metagegevens van eigenschappen. Daarom kunt u de callbacks voor een bepaalde afhankelijkheidseigenschap wijzigen, omdat deze bestaat voor een type dat u hebt afgeleid van het type dat eigenaar is van de afhankelijkheidseigenschap, door de metagegevens voor die eigenschap in uw type te overschrijven.

Geavanceerde scenario's voor dwang en callback

Beperkingen en gewenste waarden

De CoerceValueCallback callbacks worden gebruikt door het eigenschapssysteem om een waarde te dwingen overeenkomstig de logica die u declareert, maar een gecoerceerde waarde van een lokaal ingestelde eigenschap behoudt nog steeds intern een 'gewenste waarde'. Als de beperkingen zijn gebaseerd op andere eigenschapswaarden die dynamisch kunnen worden gewijzigd tijdens de levensduur van de toepassing, worden de beperkingen voor dwang ook dynamisch gewijzigd en kan de beperkte eigenschap de waarde ervan wijzigen om zo dicht mogelijk bij de gewenste waarde te komen, gezien de nieuwe beperkingen. De waarde wordt de gewenste waarde als alle beperkingen worden opgeheven. U kunt mogelijk een aantal redelijk gecompliceerde afhankelijkheidsscenario's introduceren als u meerdere eigenschappen hebt die afhankelijk zijn van elkaar op een kringvormige manier. In het scenario Min/Max/Current kunt u er bijvoorbeeld voor kiezen om minimum en maximum in te stellen voor gebruikers. Zo ja, dan moet u mogelijk dwingen dat Maximum altijd groter is dan Minimum en omgekeerd. Maar als die dwang actief is en Maximum dwingt naar Minimum, blijft Current in een niet-instelbare toestand, omdat deze afhankelijk is van beide en beperkt is tot het bereik tussen de waarden, die nul is. Als Maximum of Minimum wordt aangepast, lijkt Current een van de waarden te volgen, omdat de gewenste waarde van Current nog steeds wordt opgeslagen en probeert de gewenste waarde te bereiken wanneer de beperkingen worden versoepeld.

Er is technisch niets mis met complexe afhankelijkheden, maar ze kunnen een lichte prestatieschade zijn als ze grote aantallen herwaarderingen vereisen, en kunnen ook verwarrend zijn voor gebruikers als ze rechtstreeks van invloed zijn op de gebruikersinterface. Wees voorzichtig met property changed callbacks en dwangwaarde callbacks en zorg ervoor dat de poging tot dwang zo ondubbelzinnig mogelijk kan worden uitgevoerd en niet te restrictief is.

CoerceValue gebruiken om waardewijzigingen te annuleren

Het eigenschappensysteem zal elke CoerceValueCallback die de waarde UnsetValue retourneert als een speciaal geval behandelen. Dit speciale geval betekent dat de eigenschapswijziging die heeft geresulteerd in de CoerceValueCallback die wordt aangeroepen, moet worden geweigerd door het eigenschappensysteem en dat het eigenschappensysteem in plaats daarvan de vorige waarde van de eigenschap moet rapporteren. Dit mechanisme kan handig zijn om te controleren of wijzigingen in een eigenschap die asynchroon zijn geïnitieerd, nog steeds geldig zijn voor de huidige objectstatus en de wijzigingen onderdrukken als dat niet het juiste is. Een ander mogelijk scenario is dat u selectief een waarde kunt onderdrukken, afhankelijk van welk onderdeel van eigenschapswaardebepaling verantwoordelijk is voor de gerapporteerde waarde. Hiervoor kunt u de DependencyProperty die is doorgegeven in de callback en de eigenschaps-id als invoer voor GetValueSourcegebruiken en vervolgens de ValueSourceverwerken.

Zie ook