Precedenza del valore della proprietà di dipendenza

In questo argomento viene illustrato in che modo il funzionamento del sistema di proprietà Windows Presentation Foundation (WPF) può influire sul valore di una proprietà di dipendenza e descrive la precedenza in base agli aspetti del sistema di proprietà applicati al valore effettivo di una proprietà.

Prerequisiti

In questo argomento si presuppone che si comprendano le proprietà di dipendenza dal punto di vista di un consumer di proprietà di dipendenza esistenti nelle classi WPF e si abbia letto Cenni preliminari sulle proprietà di dipendenza. Per seguire gli esempi in questo argomento, è necessario comprendere anche XAML e sapere come scrivere applicazioni WPF.

Il sistema di proprietà WPF

Il sistema di proprietà WPF offre un modo efficace per determinare il valore delle proprietà di dipendenza da diversi fattori, abilitando funzionalità come la convalida delle proprietà in tempo reale, l'associazione tardiva e notificando le proprietà correlate delle modifiche ai valori per altre proprietà. L'ordine e logica precisi usati per determinare i valori delle proprietà di dipendenza sono piuttosto complessi. La conoscenza di questo ordine consentirà di evitare l'impostazione dì proprietà non necessarie e potrebbe anche eliminare i dubbi sui motivi precisi per i quali alcuni tentativi di influenzare o anticipare un valore delle proprietà di dipendenza non hanno determinato il valore atteso.

Le proprietà di dipendenza possono essere impostate in più punti

Di seguito è riportato un esempio di XAML in cui la stessa proprietà (Background) ha tre diverse operazioni "set" che potrebbero influenzare il valore.

<StackPanel>
    <StackPanel.Resources>
        <ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
            <Border Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" 
                    BorderBrush="{TemplateBinding BorderBrush}">
                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
            </Border>
        </ControlTemplate>
    </StackPanel.Resources>

    <Button Template="{StaticResource ButtonTemplate}" Background="Red">
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <Setter Property="Background" Value="Blue"/>
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="Yellow" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
        Which color do you expect?
    </Button>
</StackPanel>

In questo caso, quale colore ci si aspetta che verrà applicato, rosso, verde o blu?

Ad eccezione dei valori animati e della coercizione, i set di proprietà locali vengono impostati con la precedenza più elevata. Se si imposta localmente un valore, è possibile prevedere che il valore venga accettato, anche al di là degli stili o dei modelli di controllo. Qui nell'esempio Background è impostato su Red localmente. Pertanto, lo stile definito in questo ambito, anche se si tratta di uno stile implicito che altrimenti si applica a tutti gli elementi di tale tipo in tale ambito, non è la precedenza più alta per assegnare alla proprietà il Background relativo valore. Se è stato rimosso il valore locale Red dall'istanza specifica del controllo Button, lo stile avrà la precedenza e il pulsante otterrà il valore di Background dallo stile. All'interno dello stile, i trigger hanno la precedenza, pertanto il pulsante sarà blu se il mouse è posizionato su di esso e verde in caso contrario.

Elenco delle precedenze per l'impostazione delle proprietà di dipendenza

Di seguito viene riportato l'ordine definitivo usato dal sistema di proprietà per l'assegnazione dei valori di runtime delle proprietà di dipendenza. La precedenza più elevata viene elencata per prima. Questo elenco si basa su alcuni dei concetti generali espressi in Panoramica sulle proprietà di dipendenza.

  1. Coercizione del sistema di proprietà. Per informazioni dettagliate sulla coercizione, vedere Coercizione, animazione e valore di base più avanti in questo argomento.

  2. Animazioni attive o animazioni con un comportamento di attesa. Per ottenere qualsiasi effetto pratico, l'animazione di una proprietà deve essere in grado di avere la precedenza sul valore di base (inanimato), anche se quel valore è stato impostato localmente. Per informazioni dettagliate, vedere Coercizione, animazione e valore di base più avanti in questo argomento.

  3. Valore locale. Un valore locale può essere impostato tramite la praticità della proprietà "wrapper", che equivale anche all'impostazione come attributo o elemento di proprietà in XAML o tramite una chiamata all'API usando una proprietà di un'istanza SetValue specifica. Se si imposta un valore locale usando un'associazione o una risorsa, ciascuno di questi elementi dispone della precedenza come se fosse stato impostato un valore diretto.

  4. Proprietà del modello TemplatedParent. Un elemento ha un TemplatedParent se è stato creato come parte di un modello (o ControlTemplateDataTemplate). Per informazioni dettagliate sulle situazioni in cui viene applicato, vedere TemplatedParent più avanti in questo argomento. All'interno del modello, viene applicata la seguente precedenza:

    1. Trigger dal TemplatedParent modello.

    2. Set di proprietà (in genere tramite attributi XAML) nel TemplatedParent modello.

  5. Stile implicito. Si applica solo alla proprietà Style. La proprietà Style viene riempita da qualsiasi risorsa di stile con una chiave che corrisponde al tipo di quell'elemento. Quella risorsa di stile deve essere presente nella pagina o nell'applicazione. La ricerca per una risorsa di stile implicita non viene eseguita nei temi.

  6. Trigger degli stili. I trigger all'interno di stili da una pagina o un'applicazione (questi stili potrebbero essere stili espliciti o impliciti, ma non derivati dagli stili predefiniti, che hanno una precedenza inferiore).

  7. Trigger dei modelli. Qualsiasi trigger da un modello all'interno di uno stile oppure un modello applicato direttamente.

  8. Setter di stili. Valori di un oggetto all'interno di Setter stili della pagina o dell'applicazione.

  9. Stile (tema) predefinito. Per informazioni dettagliate sui casi in cui viene applicato e sul modo in cui gli stili del tema si riferiscono ai modelli all'interno degli stili del tema, vedere Stili (tema) predefiniti più avanti in questo argomento. All'interno di uno stile predefinito, viene applicato il seguente ordine di precedenza:

    1. Trigger attivi nello stile del tema.

    2. Setter nello stile del tema.

  10. Ereditarietà. Alcune proprietà di dipendenza ereditano i propri valori dall'elemento padre agli elementi figlio, in modo che non sia necessario impostarli in modo specifico per ogni elemento in tutta l'applicazione. Per informazioni dettagliate, vedere Ereditarietà del valore della proprietà.

  11. Valore predefinito dai metadati delle proprietà di dipendenza. Qualsiasi proprietà di dipendenza specificata può avere un valore predefinito come stabilito dalla registrazione del sistema di proprietà di quella particolare proprietà. Inoltre, le classi derivate che ereditano una proprietà di dipendenza hanno la possibilità di eseguire l'override di tali metadati (incluso il valore predefinito) in base al tipo. Per altre informazioni, vedere Metadati delle proprietà di dipendenza. Dato che l'ereditarietà viene controllata prima del valore predefinito, per una proprietà ereditata un valore predefinito dell'elemento padre ha la precedenza su un elemento figlio. Di conseguenza, se una proprietà ereditabile non viene impostata ovunque, viene usato il valore predefinito come specificato nella radice o nell'elemento padre invece del valore predefinito dell'elemento figlio.

TemplatedParent

TemplatedParent come un elemento di precedenza non si applica a qualsiasi proprietà di un elemento che viene dichiarata direttamente nel markup dell'applicazione standard. Il concetto di TemplatedParent esiste solo per elementi figlio all'interno di una struttura ad albero visuale che vengono creati tramite l'applicazione del modello. Quando il sistema di proprietà cerca un valore nel TemplatedParent modello, esegue una ricerca nel modello che ha creato tale elemento. I valori delle proprietà del TemplatedParent modello funzionano in genere come se fossero impostati come valore locale nell'elemento figlio, ma questa precedenza minore rispetto al valore locale esiste perché i modelli sono potenzialmente condivisi. Per informazioni dettagliate, vedere TemplatedParent.

Proprietà Style

L'ordine di ricerca descritto in precedenza si applica a tutte le possibili proprietà di dipendenza, ad eccezione di una: la Style proprietà . La Style proprietà è univoca in quanto non può essere impostata come stile, pertanto gli elementi di precedenza da 5 a 8 non si applicano. Inoltre, l'animazione o la coercing Style non è consigliata (e l'animazione Style richiederebbe una classe di animazione personalizzata). In questo modo è possibile impostare la Style proprietà in tre modi:

  • Stile esplicito. La Style proprietà viene impostata direttamente. Nella maggior parte degli scenari, lo stile non viene definito inline, ma viene invece indicato come una risorsa, tramite una chiave esplicita. In questo caso la stessa proprietà Style agisce come se fosse un valore locale, elemento di precedenza 3.

  • Stile implicito. La Style proprietà non è impostata direttamente. Tuttavia, Style esiste a un certo livello nella sequenza di ricerca della risorsa (pagina, applicazione) e viene chiave usando una chiave di risorsa che corrisponde al tipo a cui applicare lo stile. In questo caso, la Style proprietà stessa agisce da una precedenza identificata nella sequenza come elemento 5. Questa condizione può essere rilevata usando DependencyPropertyHelper sulla Style proprietà e cercando ImplicitStyleReference nei risultati.

  • Stile predefinito, noto anche come stile del tema. La Style proprietà non viene impostata direttamente e in effetti verrà letta come null fino al runtime. In questo caso, lo stile deriva dalla valutazione del tema in fase di esecuzione che fa parte del motore di presentazione WPF.

Per gli stili impliciti non inclusi nei temi, il tipo deve corrispondere esattamente: una MyButtonButtonclasse derivata da non userà in modo implicito uno stile per Button.

Stili (tema) predefiniti

Ogni controllo fornito con WPF ha uno stile predefinito. Potenzialmente, lo stile predefinito varia in base al tema, motivo per il quale questo stile predefinito viene indicato in alcuni casi come uno stile del tema.

Le informazioni più importanti trovate all'interno di uno stile predefinito per un controllo sono il relativo modello di controllo, che esiste nello stile del tema come setter per la relativa Template proprietà. Se non esistesse un modello derivato dagli stili predefiniti, un controllo senza un modello personalizzato come parte di uno stile personalizzato non presenterebbe alcun aspetto visivo. Il modello derivato dallo stile predefinito fornisce all'aspetto visivo di ciascun controllo una struttura di base e definisce anche le connessioni tra proprietà definite nella struttura ad albero visuale del modello e la classe di controlli corrispondente. Ogni controllo espone un set di proprietà che può influenzare l'aspetto visivo del controllo senza sostituire completamente il modello. Si consideri ad esempio l'aspetto visivo predefinito di un Thumb controllo , che è un componente di un oggetto ScrollBar.

Un Thumb oggetto ha alcune proprietà personalizzabili. Il modello predefinito di un oggetto Thumb crea una struttura di base o una struttura ad albero visuale con diversi componenti annidati Border per creare un aspetto di rilievo. Se una proprietà che fa parte del modello deve essere esposta per la Thumb personalizzazione dalla classe , tale proprietà deve essere esposta da un Oggetto TemplateBinding all'interno del modello. Nel caso di Thumb, varie proprietà di questi bordi condividono un'associazione di modelli a proprietà come Background o BorderThickness. Ma certe altre proprietà o disposizioni visive sono hardcoded nel modello di controllo o sono associate a valori che derivano direttamente dal tema e non possono essere modificate senza sostituire l'intero modello. Generalmente, se una proprietà deriva da un elemento padre basato su modelli e non viene esposta da un'associazione di modelli, non può essere regolata dagli stili poiché non esiste un modo semplice per fare riferimento ad essa. Ma tale proprietà potrebbe essere ancora influenzata dall'ereditarietà dei valori della proprietà nel modello applicato oppure dal valore predefinito.

Gli stili del tema usano un tipo come chiave nelle definizioni. Tuttavia, quando i temi vengono applicati a una determinata istanza dell'elemento, la ricerca dei temi per questo tipo viene eseguita controllando la DefaultStyleKey proprietà in un controllo . Questa operazione è in contrasto con l'uso del tipo letterale, adottato dagli stili impliciti. Il valore di DefaultStyleKey erediterebbe alle classi derivate anche se l'implementatore non lo modificava (il modo previsto per modificare la proprietà non è eseguirne l'override a livello di proprietà, ma di modificarne il valore predefinito nei metadati della proprietà). Questo riferimento indiretto consente alle classi di base di definire gli stili del tema per gli elementi derivati che non dispongono di un altro stile oppure, in un caso più importante, che non dispongono di un modello all'interno di quello stile e che di conseguenza non avrebbero alcun aspetto visivo predefinito. Pertanto, è possibile derivare MyButton da Button e otterrà comunque il Button modello predefinito. Se si è l'autore del controllo di MyButton e si desidera un comportamento diverso, è possibile eseguire l'override dei metadati della proprietà di dipendenza per su MyButton per DefaultStyleKey restituire una chiave diversa e quindi definire gli stili del tema pertinenti, incluso il modello per MyButton il quale è necessario creare un pacchetto con il MyButton controllo. Per altri dettagli su temi, stili e creazione di controlli, vedere Cenni preliminari sulla modifica di controlli.

Riferimenti e associazione di risorse dinamiche

I riferimenti alle risorse dinamiche e le operazioni di associazione rispettano la precedenza della posizione su cui sono impostati. Ad esempio, una risorsa dinamica applicata a un valore locale agisce in base all'elemento di precedenza 3, a un'associazione per un setter di proprietà all'interno di un stile del tema viene applicato un elemento di precedenza 9 e così via. Dato che i riferimenti e l'associazione per le risorse dinamiche devono essere in grado di ottenere valori dallo stato della fase di esecuzione dell'applicazione, ciò comporta che anche il processo effettivo di determinazione della precedenza del valore di proprietà per qualsiasi proprietà specificata si estende nella fase di esecuzione.

I riferimenti alle risorse dinamiche non fanno parte in senso stretto del sistema di proprietà, ma hanno un proprio ordine di ricerca che interagisce con la sequenza elencata in precedenza. Tale precedenza è documentata più approfonditamente in Risorse XAML. La somma di base di tale precedenza è: elemento alla radice della pagina, applicazione, tema, sistema.

Le risorse dinamiche e le associazioni hanno la precedenza relativamente a dove sono state impostate, ma il valore viene rinviato. Una conseguenza di ciò è che, se si imposta una risorsa dinamica o un'associazione su un valore locale, qualsiasi modifica al valore locale sostituisce completamente la risorsa dinamica o l'associazione. Anche se si chiama il ClearValue metodo per cancellare il valore del set locale, la risorsa dinamica o l'associazione non verranno ripristinate. Infatti, se si chiama ClearValue su una proprietà con una risorsa dinamica o un'associazione sul posto (senza valore locale letterale), vengono cancellati anche dalla ClearValue chiamata.

SetCurrentValue

Il SetCurrentValue metodo è un altro modo per impostare una proprietà, ma non è nell'ordine di precedenza. Consente invece SetCurrentValue di modificare il valore di una proprietà senza sovrascrivere l'origine di un valore precedente. È possibile usare SetCurrentValue qualsiasi momento in cui si desidera impostare un valore senza assegnare tale valore alla precedenza di un valore locale. Ad esempio, se una proprietà viene impostata da un trigger e quindi assegnata un altro valore tramite SetCurrentValue, il sistema di proprietà rispetta comunque il trigger e la proprietà cambierà se si verifica l'azione del trigger. SetCurrentValue consente di modificare il valore della proprietà senza assegnargli un'origine con una precedenza maggiore. Analogamente, è possibile usare SetCurrentValue per modificare il valore di una proprietà senza sovrascrivere un'associazione.

Coercizione, animazioni e valore di base

La coercizione e l'animazione agiscono entrambi su un valore definito come "valore di base" in questo SDK. Di conseguenza, il valore di base è un qualsiasi valore determinato tramite la valutazione verso l'alto negli elementi fino a raggiungere l'elemento 2.

Per un'animazione, il valore di base può avere un effetto sul valore animato, se quell'animazione non specifica le impostazioni "From" e "To" per determinati comportamenti o se l'animazione ripristina intenzionalmente il valore di base quando viene completata. Per osservare questo comportamento nella pratica, eseguire l'Esempio valori di destinazione dell'animazione From/To/By. Tentare di impostare i valori locali dell'altezza del rettangolo nell'esempio, in modo che il valore locale iniziale sia diverso da qualsiasi impostazione "From" nell'animazione. Si noterà che le animazioni iniziano immediatamente usando i valori "From" valori e sostituiscono il valore di base una volta avviate. L'animazione potrebbe specificare di tornare al valore trovato prima dell'animazione al termine dell'animazione specificando stop FillBehavior. In seguito, verrà usata la precedenza normale per la determinazione del valore di base.

Possono essere applicate più animazioni a una sola proprietà, ognuna delle quali definita da punti diversi nella precedenza dei valori. Tuttavia, queste animazioni comporranno i relativi valori, piuttosto che applicare semplicemente l'animazione dalla precedenza più elevata. Ciò dipende dal modo preciso in cui sono definite le animazioni e dal tipo del valore che viene animato. Per altre informazioni sull'animazione di proprietà, vedere Cenni preliminari sull'animazione.

La coercizione si applica al livello più elevato in assoluto. Anche un'animazione già in esecuzione è soggetta alla coercizione del valore. Alcune proprietà di dipendenza esistenti in WPF dispongono di coercizione predefinita. Per una proprietà di dipendenza personalizzata, si definisce il comportamento di coercizione per una proprietà di dipendenza personalizzata scrivendo un CoerceValueCallback oggetto e passando il callback come parte dei metadati quando si crea la proprietà. È anche possibile eseguire l'override del comportamento di coercizione di proprietà esistenti eseguendo l'override dei metadati di quella proprietà in una classe derivata. La coercizione interagisce con il valore di base in modo tale che i vincoli di coercizione vengano applicati come i vincoli esistenti in quel momento, mantenendo tuttavia il valore di base. Pertanto, se i vincoli nella coercizione vengono rimossi in un secondo momento, la coercizione restituirà il valore più vicino possibile a quel valore di base e l'influenza della coercizione su una proprietà cesserà potenzialmente appena vengono rimossi tutti i vincoli. Per altre informazioni sul comportamento di coercizione, vedere Callback e convalida delle proprietà di dipendenza.

Comportamenti dei trigger

I controlli spesso definiscono i comportamenti dei trigger come parte dello stile predefinito nei temi. L'impostazione di proprietà locali sui controlli può impedire ai trigger di essere in grado di rispondere agli eventi generati dagli utenti sia da un punto di vista visivo sia da un punto di vista di comportamento. L'uso più comune di un trigger di proprietà è per le proprietà di controllo o di stato, ad IsSelectedesempio . Ad esempio, per impostazione predefinita, quando un Button oggetto è disabilitato (trigger per IsEnabled è false) il Foreground valore nello stile del tema è ciò che fa sì che il controllo venga visualizzato "disattivato". Tuttavia, se è stato impostato un valore locale Foreground , tale normale colore grigio-out verrà sovrarugliato dalla proprietà locale impostata, anche in questo scenario attivato da proprietà. Prestare attenzione nell'impostazione di valori per proprietà che presentano comportamenti di trigger a livello di tema e accertarsi di non interferire impropriamente con l'esperienza utente prevista per quel controllo.

ClearValue e precedenza dei valori

Il ClearValue metodo fornisce un metodo utile per cancellare qualsiasi valore applicato localmente da una proprietà di dipendenza impostata su un elemento. Tuttavia, la chiamata ClearValue non garantisce che il valore predefinito stabilito nei metadati durante la registrazione delle proprietà sia il nuovo valore effettivo. Tutti gli altri partecipanti nella precedenza dei valori sono ancora attivi. Solo il valore impostato localmente è stato rimosso dalla sequenza di precedenza. Ad esempio, se si chiama ClearValue su una proprietà in cui tale proprietà viene impostata anche da uno stile del tema, il valore del tema viene applicato come nuovo valore anziché come valore predefinito basato sui metadati. Se si desidera escludere tutti i partecipanti al valore della proprietà dal processo e impostare il valore sul valore predefinito dei metadati registrati, è possibile ottenere il valore predefinito in modo definitivo eseguendo una query sui metadati della proprietà di dipendenza e quindi è possibile usare il valore predefinito per impostare localmente la proprietà con una chiamata a SetValue.

Vedi anche