Condividi tramite


Panoramica delle proprietà di dipendenza

Windows Presentation Foundation (WPF) fornisce un set di servizi che possono essere usati per estendere la funzionalità della proprietà di un tipo. Collettivamente, questi servizi vengono in genere definiti sistema di proprietà WPF. Una proprietà supportata dal sistema di proprietà WPF è nota come proprietà di dipendenza. Questa panoramica descrive il sistema di proprietà WPF e le funzionalità di una proprietà di dipendenza. Ciò include come usare le proprietà di dipendenza esistenti in XAML e nel codice. Questa panoramica presenta anche aspetti specializzati delle proprietà di dipendenza, ad esempio i metadati delle proprietà di dipendenza, e come creare una proprietà di dipendenza personalizzata in una classe personalizzata.

Prerequisiti

In questo argomento si presuppone che si disponga di alcune conoscenze di base del sistema dei tipi .NET e della programmazione orientata agli oggetti. Per seguire gli esempi in questo argomento, è necessario comprendere anche XAML e sapere come scrivere applicazioni WPF. Per altre informazioni, vedere Procedura dettagliata: La mia prima applicazione desktop WPF.

Proprietà di dipendenza e proprietà CLR

In WPF le proprietà vengono in genere esposte come proprietà standard di .NET . A livello di base, è possibile interagire direttamente con queste proprietà e non sapere mai che vengono implementate come proprietà di dipendenza. Tuttavia, è consigliabile acquisire familiarità con alcune o tutte le funzionalità del sistema di proprietà WPF, in modo da poter sfruttare queste funzionalità.

Lo scopo delle proprietà di dipendenza è quello di fornire un modo per calcolare il valore di una proprietà in base al valore di altri input. Questi altri input possono includere proprietà di sistema, come temi e preferenze dell'utente, meccanismi di determinazione delle proprietà just-in-time, come il data binding e le animazioni/storyboard, modelli riutilizzabili, come risorse e stili, o valori noti tramite relazioni padre-figlio con altri elementi nell'albero degli elementi. Inoltre, è possibile implementare una proprietà di dipendenza per fornire la convalida autonoma, i valori predefiniti, i callback che monitorano le modifiche ad altre proprietà e un sistema in grado di coercere i valori delle proprietà in base a informazioni potenzialmente in fase di esecuzione. Le classi derivate possono anche modificare alcune caratteristiche specifiche di una proprietà esistente eseguendo l'override dei metadati delle proprietà di dipendenza, anziché eseguire l'override dell'implementazione effettiva delle proprietà esistenti o creando nuove proprietà.

Nel riferimento all'SDK è possibile identificare quale proprietà è una proprietà di dipendenza in base alla presenza della sezione Informazioni sulle proprietà di dipendenza nella pagina di riferimento gestita per tale proprietà. La sezione Informazioni sulle proprietà di dipendenza include un collegamento al campo identificatore DependencyProperty per tale proprietà di dipendenza e include anche un elenco delle opzioni di metadati impostate per tale proprietà, informazioni sull'override per classe e altri dettagli.

Le proprietà di dipendenza supportano le proprietà CLR

Le proprietà di dipendenza e il sistema di proprietà WPF estendono la funzionalità delle proprietà fornendo un tipo che supporta una proprietà, come alternativa al modello standard di associazione della proprietà con un campo privato. Il nome di questo tipo è DependencyProperty. L'altro tipo importante che definisce il sistema di proprietà WPF è DependencyObject. DependencyObject definisce la classe di base in grado di registrare e possedere una proprietà di dipendenza.

Di seguito è elencata la terminologia usata con le proprietà di dipendenza:

  • Proprietà di dipendenza: Una proprietà supportata da un DependencyProperty.

  • identificatore della proprietà di dipendenza: un'istanza di DependencyProperty, ottenuta come valore restituito durante la registrazione di una proprietà di dipendenza e quindi archiviata come membro statico di una classe. Questo identificatore viene usato come parametro per molte API che interagiscono con il sistema di proprietà WPF.

  • CLR "wrapper": Le implementazioni effettive dei metodi get e set per la proprietà. Queste implementazioni incorporano l'identificatore della proprietà di dipendenza usandolo nelle chiamate GetValue e SetValue, fornendo così il backup per la proprietà usando il sistema di proprietà WPF.

Nell'esempio seguente viene definita la proprietà di dipendenza IsSpinning e viene mostrata la relazione dell'identificatore DependencyProperty con la proprietà che supporta.

public static readonly DependencyProperty IsSpinningProperty =
    DependencyProperty.Register(
    "IsSpinning", typeof(Boolean),
    typeof(MyCode)
    );
public bool IsSpinning
{
    get { return (bool)GetValue(IsSpinningProperty); }
    set { SetValue(IsSpinningProperty, value); }
}
Public Shared ReadOnly IsSpinningProperty As DependencyProperty =
    DependencyProperty.Register("IsSpinning",
                                GetType(Boolean),
                                GetType(MyCode))

Public Property IsSpinning() As Boolean
    Get
        Return CBool(GetValue(IsSpinningProperty))
    End Get
    Set(ByVal value As Boolean)
        SetValue(IsSpinningProperty, value)
    End Set
End Property

La convenzione di denominazione della proprietà e il relativo campo sottostante DependencyProperty è importante. Il nome del campo è sempre il nome della proprietà, con il suffisso Property accodato. Per ulteriori informazioni su questa convenzione e sui motivi di essa, consultare Proprietà di Dipendenza Personalizzate.

Impostazione dei valori delle proprietà

Puoi impostare le proprietà nel codice o in XAML.

Impostazione dei valori delle proprietà in XAML

L'esempio XAML seguente specifica il colore di sfondo di un pulsante come rosso. Questo esempio illustra un caso in cui un semplice valore stringa per un attributo XAML viene convertito dal parser XAML WPF in un tipo WPF (un Color, tramite un SolidColorBrush) nel codice generato.

<Button Background="Red" Content="Button!"/>

XAML supporta un'ampia gamma di moduli di sintassi per l'impostazione delle proprietà. La sintassi da utilizzare per una determinata proprietà dipende dal tipo di valore utilizzato da una proprietà, nonché da altri fattori, ad esempio la presenza di un convertitore di tipi. Per altre informazioni sulla sintassi XAML per l'impostazione delle proprietà, vedere XAML in WPF e "Sintassi XAML in Dettaglio".

Come esempio di sintassi senza attributi, l'esempio XAML seguente mostra un altro sfondo di un pulsante. Questa volta anziché impostare un colore a tinta unita semplice, lo sfondo viene impostato su un'immagine, con un elemento che rappresenta tale immagine e l'origine dell'immagine specificata come attributo dell'elemento annidato. Questo è un esempio di sintassi degli elementi di proprietà.

<Button Content="Button!">
  <Button.Background>
    <ImageBrush ImageSource="wavy.jpg"/>
  </Button.Background>
</Button>

Impostazione delle proprietà nel codice

L'impostazione dei valori delle proprietà di dipendenza nel codice è in genere solo una chiamata all'implementazione del set esposta dal clr "wrapper".

Button myButton = new Button();
myButton.Width = 200.0;
Dim myButton As New Button()
myButton.Width = 200.0

Ottenere un valore di proprietà è essenzialmente una chiamata all'implementazione di get "wrapper":

double whatWidth;
whatWidth = myButton.Width;
Dim whatWidth As Double
whatWidth = myButton.Width

È anche possibile chiamare direttamente le API del sistema di proprietà GetValue e SetValue. Questo non è in genere necessario se si usano proprietà esistenti (i wrapper sono più pratici e offrono una migliore esposizione della proprietà per gli strumenti di sviluppo), ma la chiamata diretta delle API è appropriata per determinati scenari.

Le proprietà possono essere impostate anche in XAML e quindi accessibili in un secondo momento nel codice tramite code-behind. Per informazioni dettagliate, vedere Code-Behind e XAML in WPF.

Funzionalità delle proprietà fornite da una proprietà di dipendenza

Una proprietà di dipendenza fornisce funzionalità che estendono la funzionalità di una proprietà anziché una proprietà supportata da un campo. Spesso, tali funzionalità rappresentano o supportano una delle funzionalità specifiche seguenti:

Risorse

Un valore della proprietà di dipendenza può essere impostato facendo riferimento a una risorsa. Le risorse vengono in genere specificate come valore della proprietà Resources di un elemento radice della pagina o dell'applicazione (queste posizioni consentono l'accesso più pratico alla risorsa). Nell'esempio seguente viene illustrato come definire una risorsa SolidColorBrush.

<DockPanel.Resources>
  <SolidColorBrush x:Key="MyBrush" Color="Gold"/>
</DockPanel.Resources>

Dopo aver definito la risorsa, è possibile fare riferimento alla risorsa e usarla per fornire un valore della proprietà:

<Button Background="{DynamicResource MyBrush}" Content="I am gold" />

A questa particolare risorsa viene fatto riferimento come DynamicResource Markup Extension (in XAML WPF è possibile usare un riferimento a risorse statiche o dinamiche). Per usare un riferimento a una risorsa dinamica, è necessario impostarlo come una proprietà di dipendenza, pertanto è l'uso specifico del riferimento a risorse dinamiche che è abilitato dal sistema di proprietà WPF. Per altre informazioni, vedere risorse XAML.

Nota

Le risorse vengono considerate come un valore locale, ovvero se si imposta un altro valore locale, si eliminerà il riferimento alla risorsa. Per ulteriori informazioni, consultare Precedenza del Valore della Proprietà di Dipendenza.

Associazione dati

Una proprietà di dipendenza può fare riferimento a un valore tramite il data binding. L'associazione dati funziona tramite una sintassi di estensione di markup specifica in XAML oppure tramite l'oggetto Binding nel codice. Con il data binding, la determinazione finale del valore della proprietà viene posticipata fino al momento dell'esecuzione, in cui il valore viene ottenuto da una fonte di dati.

L'esempio seguente imposta la proprietà Content per un Button, usando un'associazione dichiarata in XAML. Il binding utilizza un contesto dati ereditato e un'origine dati XmlDataProvider (non mostrata). Il binding stesso specifica la proprietà di origine desiderata tramite XPath all'interno dell'origine dati.

<Button Content="{Binding XPath=Team/@TeamName}"/>

Nota

Le associazioni di legame vengono considerate come un valore locale, il che significa che, se si imposta un altro valore locale, si eliminerà l'associazione. Per informazioni dettagliate, vedere Precedenza del Valore della Proprietà di Dipendenza.

Le proprietà di dipendenza, o la classe DependencyObject, non supportano in modo nativo INotifyPropertyChanged ai fini della generazione di notifiche di modifiche nel valore della proprietà di origine DependencyObject per le operazioni di data binding. Per ulteriori informazioni su come creare proprietà da usare nell'associazione di dati capaci di segnalare le modifiche a un obiettivo di associazione di dati, vedere Panoramica del data binding.

Stili

Gli stili e i modelli sono due degli scenari principali motivanti per l'uso delle proprietà di dipendenza. Gli stili sono particolarmente utili per impostare le proprietà che definiscono l'interfaccia utente dell'applicazione. Gli stili vengono in genere definiti come risorse in XAML. Gli stili interagiscono con il sistema delle proprietà perché di solito contengono "setter" per proprietà specifiche e "trigger" che modificano il valore di una proprietà in base al valore attuale di un'altra proprietà.

Nell'esempio seguente viene creato uno stile semplice (che verrebbe definito all'interno di un dizionario Resources, non visualizzato), quindi lo stile viene applicato direttamente alla proprietà Style per un Button. Il setter all'interno dello stile imposta la proprietà Background per un Button in stile verde.

<Style x:Key="GreenButtonStyle">
  <Setter Property="Control.Background" Value="Green"/>
</Style>
<Button Style="{StaticResource GreenButtonStyle}">I am green!</Button>

Per ulteriori informazioni, consultare Styling e Templating.

Animazioni

Le proprietà di dipendenza possono essere animate. Quando viene applicata un'animazione ed è in esecuzione, il valore animato opera con una precedenza maggiore rispetto a qualsiasi valore (ad esempio un valore locale) che la proprietà ha in caso contrario.

Nell'esempio seguente, la Background viene animata sulla proprietà Button (tecnicamente, il Background viene animato usando la sintassi degli elementi di proprietà per specificare un SolidColorBrush vuoto come Background. Quindi, la proprietà Color di tale SolidColorBrush è la proprietà direttamente animata).

<Button>I am animated
  <Button.Background>
    <SolidColorBrush x:Name="AnimBrush"/>
  </Button.Background>
  <Button.Triggers>
    <EventTrigger RoutedEvent="Button.Loaded">
      <BeginStoryboard>
        <Storyboard>
          <ColorAnimation
            Storyboard.TargetName="AnimBrush" 
            Storyboard.TargetProperty="(SolidColorBrush.Color)"
            From="Red" To="Green" Duration="0:0:5" 
            AutoReverse="True" RepeatBehavior="Forever" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Button.Triggers>
</Button>

Per altre informazioni sull'animazione delle proprietà, vedere Cenni preliminari sull'animazione e panoramica Storyboard.

Override dei metadati

È possibile modificare determinati comportamenti di una proprietà di dipendenza eseguendo l'override dei metadati per tale proprietà quando si deriva dalla classe che registra originariamente la proprietà di dipendenza. La sovrascrittura dei metadati si basa sull'identificatore DependencyProperty. La sovrascrittura dei metadati non richiede la riimplementazione della proprietà. La modifica dei metadati viene gestita in modo nativo dal sistema di proprietà; ogni classe contiene potenzialmente singoli metadati per tutte le proprietà ereditate dalle classi di base, in base al tipo.

L'esempio seguente esegue l'override dei metadati per una proprietà di dipendenza DefaultStyleKey. L'override di questi particolari metadati delle proprietà di dipendenza fa parte di un modello di implementazione per creare controlli in grado di utilizzare gli stili predefiniti dei temi.

public class SpinnerControl : ItemsControl
{
    static SpinnerControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(SpinnerControl),
            new FrameworkPropertyMetadata(typeof(SpinnerControl))
        );
    }
}
Public Class SpinnerControl
    Inherits ItemsControl
    Shared Sub New()
        DefaultStyleKeyProperty.OverrideMetadata(GetType(SpinnerControl), New FrameworkPropertyMetadata(GetType(SpinnerControl)))
    End Sub
End Class

Per ulteriori informazioni sull'override o sul recupero dei metadati delle proprietà, vedere Metadati delle proprietà di dipendenza.

Ereditarietà del valore della proprietà

Un elemento può ereditare il valore di una proprietà di dipendenza dal relativo elemento padre nell'albero degli oggetti.

Nota

Il comportamento di ereditarietà del valore della proprietà non è abilitato a livello globale per tutte le proprietà di dipendenza, perché il tempo di calcolo per l'ereditarietà ha un impatto sulle prestazioni. L'ereditarietà del valore della proprietà è in genere abilitata solo per le proprietà in cui uno scenario specifico suggerisce che l'ereditarietà del valore della proprietà è appropriata. È possibile determinare se una proprietà di dipendenza eredita esaminando la sezione Informazioni sulle proprietà di dipendenza per quella proprietà di dipendenza nel riferimento dell'SDK.

Nell'esempio seguente viene illustrata un'associazione e viene impostata la proprietà DataContext che specifica l'origine dell'associazione, che non è stata illustrata nell'esempio di associazione precedente. Le associazioni di dati successive negli oggetti figlio non devono specificare la sorgente, ma possono usare il valore ereditato da DataContext nell'oggetto padre StackPanel. In alternativa, un oggetto figlio potrebbe scegliere di specificare direttamente il proprio DataContext o un Source nella Bindinge di non usare deliberatamente il valore ereditato per il contesto dati dei suoi vincoli.

<StackPanel Canvas.Top="50" DataContext="{Binding Source={StaticResource XmlTeamsSource}}">
  <Button Content="{Binding XPath=Team/@TeamName}"/>
</StackPanel>

Per ulteriori informazioni, vedere Ereditarietà del Valore della Proprietà.

Integrazione della finestra di progettazione WPF

Un controllo personalizzato con proprietà implementate come proprietà di dipendenza riceverà il supporto appropriato di WPF Designer per Visual Studio. Un esempio è la possibilità di modificare proprietà di dipendenza dirette e associate con la finestra di Proprietà. Per altre informazioni, vedere Cenni preliminari sulla creazione di controlli.

Precedenza del valore della proprietà di dipendenza

Quando si ottiene il valore di una proprietà di dipendenza, si ottiene potenzialmente un valore impostato su tale proprietà tramite uno degli altri input basati su proprietà che partecipano al sistema di proprietà WPF. La precedenza del valore della proprietà di dipendenza esiste in modo che un'ampia gamma di scenari per il modo in cui le proprietà ottengono i valori possano interagire in modo prevedibile.

Si consideri l'esempio seguente. L'esempio include uno stile che si applica a tutti i pulsanti e alle relative proprietà Background, ma specifica anche un pulsante con un valore Background impostato localmente.

Nota

La documentazione dell'SDK usa occasionalmente i termini "valore locale" o "valore impostato localmente" durante la discussione delle proprietà di dipendenza. Un valore impostato localmente è un valore della proprietà impostato direttamente su un'istanza dell'oggetto nel codice o come attributo su un elemento in XAML.

In linea di principio, per il primo pulsante, la proprietà viene impostata due volte, ma viene applicato un solo valore: il valore con la precedenza più alta. Un valore impostato localmente ha la precedenza più alta (ad eccezione di un'animazione in esecuzione, ma in questo esempio non viene applicata alcuna animazione) e pertanto viene usato il valore impostato localmente anziché il valore del setter di stile per lo sfondo sul primo pulsante. Il secondo pulsante non ha alcun valore locale (e nessun altro valore con precedenza superiore a un impostatore di stile) e quindi lo sfondo su tale pulsante proviene dall'impostatore di stile.

<StackPanel>
  <StackPanel.Resources>
    <Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
     <Setter Property="Background" Value="Red"/>
    </Style>
  </StackPanel.Resources>
  <Button Background="Green">I am NOT red!</Button>
  <Button>I am styled red</Button>
</StackPanel>

Perché esiste la precedenza della proprietà di dipendenza?

In genere, non si desidera che gli stili vengano sempre applicati e per nascondere anche un valore impostato localmente di un singolo elemento (in caso contrario, sarebbe difficile usare stili o elementi in generale). Pertanto, i valori provenienti da stili operano con un precedente inferiore rispetto a un valore impostato localmente. Per un elenco più completo delle proprietà dipendenti e da dove potrebbe provenire un valore effettivo di una proprietà dipendente, consultare Precedenza del valore della proprietà di dipendenza.

Nota

Esistono diverse proprietà definite per gli elementi WPF che non sono proprietà di dipendenza. In generale, le proprietà sono state implementate come proprietà di dipendenza solo quando si è reso necessario supportare almeno uno degli scenari abilitati dal sistema di proprietà: data binding, formattazione, animazione, supporto dei valori predefiniti, ereditarietà, proprietà associate o invalidazione.

Altre informazioni sulle proprietà di dipendenza

  • Una proprietà associata è un tipo di proprietà che supporta una sintassi specializzata in XAML. Una proprietà associata spesso non ha una corrispondenza 1:1 con una proprietà CLR (Common Language Runtime) e non è necessariamente una proprietà di dipendenza. Lo scopo tipico di una proprietà associata è consentire agli elementi figlio di segnalare i valori delle proprietà a un elemento padre, anche se l'elemento padre e l'elemento figlio non possiedono entrambe tale proprietà come parte degli elenchi dei membri della classe. Uno scenario principale consiste nel consentire agli elementi figli di informare l'elemento padre su come debbano essere presentati nell'interfaccia utente; ad esempio, vedere Dock o Left. Per informazioni dettagliate, vedere Panoramica delle proprietà associate.

  • Gli sviluppatori di componenti o gli sviluppatori di applicazioni possono voler creare la propria proprietà di dipendenza, allo scopo di abilitare funzionalità quali il data binding o il supporto degli stili, o per supportare l'invalidazione e la coercizione dei valori. Per informazioni dettagliate, vedere le Proprietà di Dipendenza Personalizzate .

  • Considerare le proprietà di dipendenza come proprietà pubbliche, accessibili o almeno individuabili da qualsiasi chiamante che abbia accesso a un'istanza. Per ulteriori informazioni, vedere Sicurezza delle proprietà di dipendenza.

Vedere anche