Panoramica delle proprietà di dipendenza

Windows Presentation Foundation (WPF) include un set di servizi che è possibile usare per estendere la funzionalità di una proprietà di un tipo. In genere, questi servizi vengono definiti collettivamente come 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. Viene anche spiegato come usare le proprietà di dipendenza esistenti in XAML e nel codice. In questa panoramica vengono anche illustrati aspetti specifici delle proprietà di dipendenza, ad esempio i metadati delle proprietà di dipendenza e la creazione di una proprietà di dipendenza in una classe personalizzata.

Prerequisiti

In questo argomento si presuppone che l'utente disponga di una conoscenza di base del sistema di tipi .NET e della programmazione orientata a oggetti. Per seguire gli esempi illustrati in questo argomento, è anche necessario conoscere XAML e saper scrivere applicazioni WPF. Per altre informazioni, vedere Procedura dettagliata: La prima applicazione desktop WPF.

Proprietà di dipendenza e proprietà CLR

In WPF le proprietà vengono generalmente esposte come proprietà .NET standard. A un livello di base, si potrebbe interagire direttamente con queste proprietà senza sapere che vengono implementate come una proprietà di dipendenza. Tuttavia, è necessario acquisire familiarità con alcune o tutte le funzionalità del sistema di proprietà WPF, in modo da poterle sfruttare.

Lo scopo delle proprietà di dipendenza consiste nel fornire un modo per calcolare il valore di una proprietà in base al valore di altri input. Questi potrebbero includere proprietà del sistema, quali temi e preferenze dell'utente, meccanismi di determinazione della proprietà JIT, ad esempio data binding dati e animazioni o storyboard, modelli multiuso, quali risorse e stili oppure valori noti tramite relazioni padre-figlio con altri elementi dell'albero degli elementi. Inoltre, una proprietà di dipendenza può essere implementata per fornire una convalida autonoma, valori predefiniti, callback per il monitoraggio delle modifiche di altre proprietà, nonché un sistema che può assegnare forzatamente valori della proprietà in base a potenziali informazioni di runtime. Le classi derivate possono anche modificare alcune caratteristiche specifiche di una proprietà esistente, eseguendo l'override dei metadati della proprietà di dipendenza invece dell'override dell'implementazione effettiva delle proprietà esistenti oppure della creazione di nuove proprietà.

Nel riferimento SDK, è possibile identificare la proprietà di dipendenza grazie alla sezione Informazioni sulle proprietà di dipendenza, presente nella pagina di riferimento gestita per quella proprietà. Nella sezione sono inclusi un collegamento al campo dell'identificatore DependencyProperty per quella proprietà di dipendenza e un elenco delle opzioni dei metadati impostati per quella proprietà, informazioni sull'override per classe e altri dettagli.

Proprietà di dipendenza che supportano le proprietà CLR

Le proprietà di dipendenza e il sistema di proprietà WPF estendono le funzionalità della proprietà fornendo un tipo che supporta una proprietà, come implementazione alternativa al modello standard di supporto della proprietà con un campo privato. Il nome del tipo è DependencyProperty. L'altro tipo importante che definisce il sistema di proprietà WPF è DependencyObject. DependencyObject definisce la classe di base che può essere registrata ed essere proprietaria di una proprietà di dipendenza.

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

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

  • Identificatore della proprietà di dipendenza: istanza di DependencyProperty, ottenuta come valore restituito quando si registra 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.

  • "Wrapper" CLR: le implementazioni effetti Get e Set per la proprietà. Queste implementazioni includono l'identificatore della proprietà di dipendenza usandolo nelle chiamate GetValue e SetValue, in modo da fornire il supporto per la proprietà tramite il sistema di proprietà WPF.

L'esempio seguente definisce la proprietà di dipendenza IsSpinning e mostra la relazione tra l'identificatore DependencyProperty e la proprietà supportata.

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 del relativo campo DependencyProperty di supporto è importante. Il nome del campo è sempre il nome della proprietà al quale viene aggiunto il suffisso Property. Per altre informazioni su questa convenzione e i relativi motivi, vedere Proprietà di dipendenza personalizzate.

Impostazione dei valori delle proprietà

È possibile impostare le proprietà nel codice o in XAML.

Impostazione dei valori delle proprietà in XAML

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

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

XAML supporta diverse sintassi per l'impostazione delle proprietà. La sintassi da usare per una proprietà particolare dipende dal tipo di valore usato da una proprietà e da altri fattori, quali la presenza di un convertitore dei tipi. Per altre informazioni sulla sintassi XAML per l'impostazione della proprietà, vedi XAML in WPF e sintassi XAML in dettaglio.

Come esempio di sintassi senza attributi, nell'esempio di XAML seguente viene illustrato lo sfondo di un altro pulsante. Questa volta, invece di impostare un semplice colore a tinta unita, lo sfondo viene impostato su un'immagine, con un elemento che rappresenta tale immagine e la relativa origine, specificate come un attributo dell'elemento annidato. Si tratta di un esempio di sintassi per gli elementi 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

Anche il recupero di un valore della proprietà prevede sostanzialmente una chiamata all'implementazione del "wrapper" Get:

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

È anche possibile chiamare le GetValue API del sistema di proprietà e SetValue direttamente. 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.

È anche possibile impostare le proprietà in XAML e successivamente accedervi nel codice tramite code-behind. Per informazioni dettagliate, vedere Code-behind e XAML in WPF.

Funzionalità offerte da una proprietà di dipendenza

Una proprietà di dipendenza fornisce funzionalità che consentono di estendere la funzionalità di una proprietà, rispetto a una proprietà supportata da un campo. Spesso tale funzionalità rappresenta o supporta 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 (questi percorsi consentono un accesso più semplice alla risorsa). L'esempio seguente illustra come definire una risorsa SolidColorBrush.

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

Una volta definita, è possibile fare riferimento alla risorsa e usarla per fornire un valore della proprietà:

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

A questa risorsa particolare viene fatto riferimento come estensione del markup DynamicResource. In XAML WPF è possibile usare un riferimento di risorsa statica o dinamica. Per usare un riferimento di risorsa dinamica, è necessario eseguire l'impostazione su una proprietà di dipendenza, in modo che il sistema di proprietà WPF attivi l'utilizzo specifico del riferimento di risorsa dinamica. Per altre informazioni, vedere Risorse XAML.

Nota

Le risorse vengono considerate come valore locale, per cui se si imposta un altro valore locale, il riferimento di risorsa sarà eliminato. Per altre informazioni, vedere Precedenza del valore della proprietà di dipendenza.

Data binding

Una proprietà di dipendenza può fare riferimento a un valore tramite il data binding, che funziona tramite una sintassi per estensione di markup specifica in XAML o l'oggetto Binding nel codice. Con il data binding, la determinazione del valore della proprietà finale viene rinviata fino alla fase di esecuzione, momento in cui il valore viene ottenuto da un'origine dati.

L'esempio seguente imposta la proprietà Content per un oggetto Button tramite un'associazione dichiarata in XAML. L'associazione usa un contesto dei dati ereditato e un'origine dati XmlDataProvider (non illustrata). L'associazione stessa specifica la proprietà di origine desiderata mediante l'oggetto XPath all'interno dell'origine dati.

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

Nota

I binding vengono considerati come valore locale, per cui se si imposta un altro valore locale, il binding verrà eliminato. Per altri dettagli, vedere Precedenza del valore della proprietà di dipendenza.

Le proprietà di dipendenza, o la classe DependencyObject, non supportano INotifyPropertyChanged in modo nativo per la generazione di notifiche delle modifiche nel valore della proprietà di origine DependencyObject per le operazioni di associazione di dati. Per altre informazioni su come creare proprietà da usare nel data binding, che consentano di segnalare modifiche di una destinazione di data binding, vedere Panoramica sul data binding.

Stili

Stili e modelli sono due degli scenari principali che giustificano l'uso delle proprietà di dipendenza. Gli stili sono particolarmente utili per impostare le proprietà che definiscono l'interfaccia utente dell'applicazione. In genere gli stili vengono definiti come risorse in XAML e interagiscono con il sistema di proprietà in quanto di solito contengono "setter" per proprietà particolari, nonché "trigger" che modificano un valore della proprietà in base al valore in tempo reale per un'altra proprietà.

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

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

Per altre informazioni, vedere Applicazione di stili e modelli.

Animazioni

Alle proprietà di dipendenza è possibile aggiungere un'animazione. Quando un'animazione viene applicata ed è in esecuzione, il valore a cui è stata aggiunta un'animazione opera a un livello di precedenza superiore rispetto a qualsiasi altro valore (ad esempio un valore locale) della proprietà.

L'esempio seguente aggiunge un'animazione alla proprietà Background di un oggetto Button. Tecnicamente, l'animazione viene aggiunta alla proprietà Background tramite la sintassi degli elementi proprietà per specificare un SolidColorBrush vuoto come Background e quindi la proprietà Color dell'oggetto SolidColorBrush è la proprietà a cui viene direttamente applicata l'animazione.

<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 di proprietà, vedere Cenni preliminari sull'animazione e Cenni preliminari sugli storyboard.

Override dei metadati

È possibile modificare determinati comportamenti di una proprietà di dipendenza eseguendo l'override dei metadati per quella proprietà, quando si deriva dalla classe che registra originariamente la proprietà di dipendenza. L'override dei metadati si basa sull'identificatore DependencyProperty. L'override dei metadati non richiede la ripetizione della proprietà. La modifica dei metadati viene gestita in modo nativo dal sistema di proprietà. Ogni classe contiene, potenzialmente, i metadati specifici 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 dei metadati di questa particolare proprietà di dipendenza fa parte di un modello di implementazione che consente di creare controlli che possono usare stili predefiniti dai 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 altre informazioni sull'override dei metadati delle proprietà o su come ottenere i 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 di oggetti.

Nota

Il comportamento dell'ereditarietà del valore della proprietà non viene abilitato a livello globale per tutte le proprietà di dipendenza, poiché il tempo di calcolo per l'ereditarietà influisce negativamente sulle prestazioni. In genere, l'ereditarietà del valore della proprietà viene abilitata solo per le proprietà in cui uno scenario particolare suggerisce che tale ereditarietà è appropriata. La possibilità di ereditare di una proprietà di dipendenza può essere determinata consultando la sezione Informazioni proprietà di dipendenza per una determinata proprietà di dipendenza, nel riferimento SDK.

L'esempio seguente descrive un'associazione e imposta la proprietà DataContext che specifica l'origine dell'associazione, non illustrata nell'esempio di associazione precedente. Eventuali associazioni successive in oggetti figlio non devono necessariamente specificare l'origine, ma possono usare il valore ereditato da DataContext nell'oggetto StackPanel padre. In alternativa, un oggetto figlio può invece scegliere di specificare direttamente il proprio DataContext o un Source in Binding e non usare deliberatamente il valore ereditato per il contesto dei dati delle sue associazioni.

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

Per altre informazioni, vedere Ereditarietà del valore della proprietà.

Integrazione di WPF Designer

Un controllo personalizzato con proprietà implementate come proprietà di dipendenza riceverà il supporto appropriato di WPF Designer per Visual Studio. Un esempio è rappresentato dalla capacità di modificare proprietà di dipendenza dirette e associate tramite la finestra Proprietà. Per altre informazioni, vedere Cenni preliminari sulla modifica 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 per tale proprietà tramite uno qualsiasi degli altri input basati su proprietà che fanno parte del sistema di proprietà WPF. La precedenza del valore della proprietà di dipendenza consente interazioni prevedibili per un'ampia gamma di scenari relativi al modo in cui le proprietà ottengono i rispettivi valori.

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

Nota

Nella documentazione SDK i termini "valore locale" o "valore impostato localmente" vengono usati talvolta per la descrizione delle proprietà di dipendenza. Un valore impostato localmente è un valore di proprietà che viene impostato direttamente in un'istanza di oggetto nel codice o come attributo di un elemento in XAML.

In teoria, per il primo pulsante la proprietà viene impostata due volte, ma viene applicato un solo valore, quello con la precedenza più alta. Un valore impostato localmente ha la massima precedenza (eccetto per un'animazione in esecuzione; tuttavia, in questo esempio non viene applicata nessuna animazione), pertanto per lo sfondo del primo pulsante viene usato il valore impostato localmente invece del valore del setter di stile. Il secondo pulsante non ha un valore locale (e nessun altro valore con precedenza più alta rispetto a un setter di stile), pertanto lo sfondo di quel pulsante proviene dal setter 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>

Motivi dell'esistenza della precedenza delle 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). Per questo motivo, i valori che provengono dagli stili operano a un livello di precedenza inferiore rispetto a un valore impostato localmente. Per un elenco più completo delle proprietà di dipendenza e informazioni sulla possibile provenienza di un valore effettivo di una proprietà di dipendenza, vedere Precedenza del valore della proprietà di dipendenza.

Nota

Molte proprietà definite negli elementi WPF non sono proprietà di dipendenza. In generale, le proprietà vengono implementate come proprietà di dipendenza solo quando è necessario supportare almeno uno degli scenari abilitati dal sistema di proprietà: data binding, applicazione degli stili, animazione, supporto del valore predefinito, ereditarietà, proprietà associate o invalidamento.

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 nell'consentire agli elementi figlio di informare l'elemento padre come devono essere presentati nell'interfaccia utente; per un esempio, vedere Dock o Left. Per informazioni dettagliate, vedere Cenni preliminari sulle proprietà associate.

  • Gli sviluppatori di componenti o di applicazioni possono decidere di creare una proprietà di dipendenza personalizzata al fine di abilitare funzionalità quali il data binding o il supporto degli stili oppure per il supporto dell'invalidamento e della coercizione del valore. Per informazioni dettagliate, vedere Proprietà di dipendenza personalizzate.

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

Vedi anche