Dela via


Återanrop och validering av beroendeegenskaper

Det här avsnittet beskriver hur du skapar beroendeegenskaper med hjälp av alternativa anpassade implementeringar för egenskapsrelaterade funktioner, till exempel valideringsbestämning, återanrop som anropas när egenskapens effektiva värde ändras och åsidosätter möjliga yttre påverkan på värdebestämning. I det här avsnittet beskrivs också scenarier där det är lämpligt att utöka standardbeteendena för egenskapssystemet med hjälp av dessa tekniker.

Förutsättningar

Det här avsnittet förutsätter att du förstår de grundläggande scenarierna för att implementera en beroendeegenskap och hur metadata tillämpas på en anpassad beroendeegenskap. Se Anpassade beroendeegenskaper och beroendeegenskapsmetadata för sammanhang.

Återanrop för validering

Valideringsåteranrop kan tilldelas till en beroendeegenskap när du först registrerar den. Valideringsåteranropet är inte en del av egenskapsmetadata. det är en direkt indata för metoden Register. När en valideringsåteranrop har skapats för en beroendeegenskap kan den därför inte åsidosättas av en ny implementering.

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

Återanropen implementeras så att de får ett objektvärde. De returnerar true om det angivna värdet är giltigt för egenskapen. annars returnerar de false. Det antas att egenskapen är av rätt typ enligt den typ som har registrerats i egenskapssystemet, så kontroll av typen görs normalt inte i återanropen. Återanropen används av egenskapssystemet i flera olika operationer. Detta inkluderar den inledande typinitieringen med standardvärde, programmatisk ändring genom att kalla på SetValueeller försök att åsidosätta metadata genom att ange ett nytt standardvärde. Om valideringsåteranropet anropas av någon av dessa åtgärder och returnerar falsegenereras ett undantag. Programskrivare måste vara beredda att hantera dessa undantag. En vanlig användning av valideringsåteranrop är att validera uppräkningsvärden eller begränsa värden för heltal eller dubblar när egenskapen anger mått som måste vara noll eller större.

Valideringsåteranrop är specifikt avsedda att vara klassverifierare, inte instansverifierare. Parametrarna för återanropet anger inte en specifik DependencyObject där egenskaperna som ska valideras fastställs. Därför är valideringsåteranropen inte användbara för att framtvinga möjliga "beroenden" som kan påverka ett egenskapsvärde, där det instansspecifika värdet för en egenskap är beroende av faktorer som instansspecifika värden för andra egenskaper eller körningstillstånd.

Följande är exempelkod för ett mycket enkelt scenario med en valideringsåterkallning: att kontrollera att en egenskap med datatypen Double inte är PositiveInfinity eller 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

Tvinga värdeåterkopplingar och egenskapsändringshändelser

Tvingade värdefunktioner skickar den specifika DependencyObject-instansen för egenskaper, precis som PropertyChangedCallback-implementationer som anropas av egenskapssystemet när värdet på en beroendeegenskap ändras. Med hjälp av dessa två återanrop i kombination kan du skapa en serie egenskaper på element där ändringar i en egenskap tvingar fram eller omvärderar en annan egenskap.

Ett typiskt scenario för att använda en länkning av beroendeegenskaper är när du har en användargränssnittsdriven egenskap där elementet innehåller en egenskap var för det lägsta och högsta värdet och en tredje egenskap för det faktiska eller aktuella värdet. Om det maximala värdet justerades på ett sådant sätt att det aktuella värdet överskred det nya maxvärdet, skulle du vilja tvinga det aktuella värdet att inte vara större än det nya maxvärdet och en liknande relation för minsta till aktuella.

Följande är mycket kort exempelkod för bara en av de tre beroendeegenskaperna som illustrerar den här relationen. Exemplet visar hur egenskapen CurrentReading för en Min/Max/Current-uppsättning med relaterade *Läsegenskaper registreras. Den använder valideringen enligt föregående avsnitt.

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

Återanropet för ändring av egenskapen Current används för att vidarebefordra ändringen till andra beroende egenskaper, genom att uttryckligen anropa de tvångsåteranrop för värde som är registrerade för dessa andra egenskaper.

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

Återanropsfunktionen för att tvinga värdet kontrollerar värdena för egenskaper som den aktuella egenskapen potentiellt är beroende av och tvingar det aktuella värdet om det behövs:

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

Not

Standardvärden för egenskaper är inte framtvingade. Ett egenskapsvärde som är lika med standardvärdet kan inträffa om ett egenskapsvärde fortfarande har sin ursprungliga standard eller genom att rensa andra värden med ClearValue.

Tvångsvärdet och ändrade återanrop för egenskaper är en del av egenskapsmetadata. Därför kan du ändra återanropen för en viss beroendeegenskap eftersom den finns på en typ som du härleder från den typ som äger beroendeegenskapen genom att åsidosätta metadata för den egenskapen för din typ.

Avancerade scenarier för tvångsmedel och återuppringning

Begränsningar och önskade värden

CoerceValueCallback-callbacks kommer att användas av egenskapssystemet för att ändra ett värde enligt den logik du deklarerar, och ett framtvingat värde för en lokalt angiven egenskap kommer fortfarande att behålla ett "önskat värde" internt. Om begränsningarna baseras på andra egenskapsvärden som kan ändras dynamiskt under programmets livslängd ändras även tvångsbegränsningarna dynamiskt och den begränsade egenskapen kan ändra dess värde så att den kommer så nära önskat värde som möjligt med tanke på de nya begränsningarna. Värdet blir det önskade värdet om alla begränsningar lyfts. Du kan potentiellt introducera några ganska komplicerade beroendescenarier om du har flera egenskaper som är beroende av varandra på ett cirkulärt sätt. I scenariot Min/Max/Current kan du till exempel välja att lägsta och högsta ska vara användarinställningsbara. I så fall kan du behöva framtvinga att Maximum alltid är större än Minimum och vice versa. Men om det tvånget är aktivt, och Maximum tvingar till Minimum, lämnar det Current i ett oetabelt tillstånd, eftersom det är beroende av båda och är begränsat till intervallet mellan värdena, som är noll. Om Maximum eller Minimum sedan justeras verkar Current "följa" ett av värdena, eftersom det önskade värdet för Current fortfarande lagras och försöker nå önskat värde när begränsningarna lossas.

Det är inget tekniskt fel med komplexa beroenden, men de kan vara en liten prestandafördel om de kräver ett stort antal omvärderingar, och kan också vara förvirrande för användarna om de påverkar användargränssnittet direkt. Var försiktig med ändringar i egenskaper och tvingande callback-funktioner och se till att försöket att tvinga kan behandlas så entydigt som möjligt och inte överbegränsar.

Använda CoerceValue för att avbryta värdeändringar

Egenskapssystemet behandlar alla CoerceValueCallback som returnerar värdet UnsetValue som ett specialfall. Det här specialfallet innebär att den egenskapsändring som resulterade i att CoerceValueCallback anropades ska avvisas av egenskapssystemet och att egenskapssystemet i stället ska rapportera det tidigare värde som egenskapen hade. Den här mekanismen kan vara användbar för att kontrollera att ändringar i en egenskap som initierades asynkront fortfarande är giltiga för det aktuella objekttillståndet och utelämnar ändringarna om inte. Ett annat möjligt scenario är att du selektivt kan utelämna ett värde beroende på vilken komponent i egenskapsvärdebestämningen som ansvarar för det värde som rapporteras. För att göra detta kan du använda DependencyProperty som skickas i återanropet och egenskapsidentifieraren som indata för GetValueSourceoch sedan bearbeta ValueSource.

Se även