Sdílet prostřednictvím


Zpětné volání a ověření vlastností závislostí

Toto téma popisuje, jak vytvořit vlastnosti závislostí pomocí alternativních vlastních implementací pro funkce související s vlastnostmi, jako je určení ověření, zpětné volání, které se vyvolávají při každé změně efektivní hodnoty vlastnosti a přepsání možných vnějších vlivů na stanovení hodnoty. Toto téma také popisuje scénáře, kdy je vhodné rozšířit výchozí chování systému vlastností pomocí těchto technik.

Předpoklady

V tomto tématu se předpokládá, že rozumíte základním scénářům implementace vlastnosti závislosti a způsobu použití metadat na vlastní vlastnost závislosti. Kontext najdete v tématu Vlastní vlastnosti závislostí a metadata vlastností závislostí .

Zpětná volání ověřování

Zpětná volání ověřování je možné přiřadit k vlastnosti závislosti při první registraci. Zpětné volání ověřování není součástí metadat vlastností; je to přímý vstup Register metody. Proto po vytvoření zpětného volání ověření pro vlastnost závislosti ji nelze přepsat novou implementací.

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

Zpětná volání jsou implementována tak, aby byla poskytována hodnota objektu. true Vrátí, pokud je zadaná hodnota platná pro vlastnost; jinak vrátí false. Předpokládá se, že vlastnost je správného typu podle typu registrovaného v systému vlastností, takže kontrola typu v rámci zpětného volání není obvykle dokončena. Zpětné volání používá systém vlastností v různých operacích. To zahrnuje inicializaci počátečního typu ve výchozím nastavení, programovou změnu vyvoláním SetValuenebo pokusem o přepsání metadat zadanou novou výchozí hodnotou. Pokud je zpětné volání ověření vyvoláno některou z těchto operací a vrátí , falsebude vyvolána výjimka. Zapisovače aplikací musí být připravené na zpracování těchto výjimek. Běžné použití zpětného volání ověřování ověřuje hodnoty výčtu nebo omezující hodnoty celých čísel nebo zdvojnásobí, když vlastnost nastaví hodnoty, které musí být nula nebo větší.

Zpětná volání ověřování jsou konkrétně určena jako validátory tříd, nikoli validátory instancí. Parametry zpětného volání nesdělují konkrétní vlastnost DependencyObject , pro kterou jsou nastaveny vlastnosti k ověření. Proto zpětná volání ověřování nejsou užitečná pro vynucování možných "závislostí", které by mohly ovlivnit hodnotu vlastnosti, kde hodnota specifická pro instanci vlastnosti závisí na faktorech, jako jsou hodnoty specifické pro instanci jiných vlastností nebo stav za běhu.

Následuje příklad kódu pro velmi jednoduchý scénář zpětného volání ověření: ověření, že vlastnost, která je zadána jako Double primitiv není PositiveInfinity nebo 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

Zpětná volání hodnoty a změněné události vlastností

Zpětné volání zpětného volání hodnoty vyvolají konkrétní DependencyObject instanci vlastností, stejně jako PropertyChangedCallback implementace, které systém vlastností vyvolá pokaždé, když se změní hodnota vlastnosti závislosti. Pomocí těchto dvou zpětných volání v kombinaci můžete vytvořit řadu vlastností na prvcích, kde změny v jedné vlastnosti vynutí převod nebo znovuhodnocení jiné vlastnosti.

Typický scénář použití propojení vlastností závislostí je, když máte vlastnost řízenou uživatelským rozhraním, kde prvek obsahuje jednu vlastnost pro minimální a maximální hodnotu a třetí vlastnost pro skutečnou nebo aktuální hodnotu. Pokud by se toto maximum upravilo takovým způsobem, že aktuální hodnota překročila nové maximum, měli byste aktuální hodnotu převést tak, aby nebyla větší než nové maximum, a podobnou relaci pro minimum až aktuální hodnotu.

Následuje velmi stručný příklad kódu pouze pro jednu ze tří vlastností závislosti, které tuto relaci ilustrují. Příklad ukazuje, jak CurrentReading je zaregistrována vlastnost Min/Max/Current sady souvisejících *Vlastností čtení. Používá ověřování, jak je znázorněno v předchozí části.

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

Vlastnost změněná zpětné volání pro Current se používá k přesměrování změny na jiné závislé vlastnosti explicitně vyvoláním zpětného volání hodnoty coerce, které jsou registrovány pro tyto další vlastnosti:

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

Zpětné volání hodnoty převodu zkontroluje hodnoty vlastností, na které je aktuální vlastnost potenciálně závislá, a v případě potřeby převede aktuální hodnotu:

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

Poznámka:

Výchozí hodnoty vlastností nejsou přetěžovány. Hodnota vlastnosti, která se rovná výchozí hodnotě, může dojít, pokud hodnota vlastnosti má stále počáteční výchozí hodnotu, nebo vymazáním jiných hodnot pomocí ClearValue.

Hodnota převodu a změněná zpětná volání vlastnosti jsou součástí metadat vlastností. Proto můžete změnit zpětná volání konkrétní vlastnosti závislosti, protože existuje u typu, který je odvozen od typu, který vlastní vlastnost závislosti přepsáním metadat pro tuto vlastnost ve vašem typu.

Pokročilé scénáře převodu a zpětného volání

Omezení a požadované hodnoty

Zpětné CoerceValueCallback volání bude systém vlastností používat k vyřazování hodnoty v souladu s logikou, kterou deklarujete, ale vyměněná hodnota vlastnosti místně nastavené bude i nadále uchovávat "požadovanou hodnotu" interně. Pokud jsou omezení založená na jiných hodnotách vlastností, které se můžou dynamicky měnit během životnosti aplikace, mění se omezení vynucení dynamicky také a omezená vlastnost může změnit jeho hodnotu tak, aby se co nejvíce blížila požadované hodnotě vzhledem k novým omezením. Hodnota se stane požadovanou hodnotou, pokud jsou všechna omezení zrušena. Pokud máte více vlastností závislých na sobě cyklickém způsobem, můžete potenciálně zavést některé poměrně složité scénáře závislostí. Například ve scénáři Min/Max/Current můžete zvolit, že má být nastavená hodnota Minimum a Maximum uživatelem. Pokud ano, možná budete muset vymyslit, že maximum je vždy větší než Minimum a naopak. Pokud je ale tento převod aktivní a maximální převod na minimum, ponechá hodnotu Current v nenastavitelném stavu, protože je závislý na obou a je omezen na rozsah mezi hodnotami, což je nula. Pokud se pak upraví maximální nebo minimální hodnota, zdá se, že aktuální hodnota následuje jedna z hodnot, protože požadovaná hodnota Aktuální je stále uložená a snaží se dosáhnout požadované hodnoty, protože omezení jsou uvolněna.

U složitých závislostí není technicky nic špatného, ale může to být mírné snížení výkonu, pokud vyžadují velký počet reevaluací, a může být také matoucí pro uživatele, pokud mají přímý vliv na uživatelské rozhraní. Buďte opatrní při změně vlastnosti a zpětném volání hodnoty a ujistěte se, že převod, o který se pokoušíte, může být považován za jednoznačný, a ne "overconstrain".

Zrušení změn hodnot pomocí funkce CoerceValue

Systém vlastností bude považovat za všechny CoerceValueCallback , které vrátí hodnotu UnsetValue jako zvláštní případ. Tento zvláštní případ znamená, že změna vlastnosti, která způsobila CoerceValueCallback zavolání, by měla být odmítnuta systémem vlastností a že systém vlastností by měl místo toho hlásit jakoukoli předchozí hodnotu vlastnosti. Tento mechanismus může být užitečný ke kontrole, že změny vlastnosti, které byly inicializovány asynchronně jsou stále platné pro aktuální stav objektu, a pokud ne, potlačit změny. Dalším možným scénářem je, že můžete selektivně potlačit hodnotu v závislosti na tom, která součást určení hodnoty vlastnosti je zodpovědná za ohlášenou hodnotu. K tomu můžete použít DependencyProperty předaný v zpětném volání a identifikátor vlastnosti jako vstup pro GetValueSourcea pak zpracovat ValueSource.

Viz také