Panoramica delle proprietà di dipendenza (WPF .NET)

Windows Presentation Foundation (WPF) include un set di servizi che è possibile usare per estendere la funzionalità di una proprietà di un tipo. Collettivamente, questi servizi vengono 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, tra cui 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

Questo articolo presuppone una conoscenza di base del sistema dei tipi .NET e della programmazione orientata agli oggetti. Per seguire gli esempi in questo articolo, è utile comprendere XAML e sapere come scrivere applicazioni WPF. Per altre informazioni, vedere Esercitazione: Creare una nuova app WPF con .NET.

Proprietà di dipendenza e proprietà CLR

Le proprietà WPF vengono in genere esposte come proprietà .NET standard. È possibile interagire con queste proprietà a un livello di base e non sapere mai che vengono implementate come proprietà di dipendenza. Tuttavia, la familiarità con alcune o tutte le funzionalità del sistema di proprietà WPF consente di sfruttare tali 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, ad esempio:

  • Proprietà di sistema, ad esempio temi e preferenze utente.
  • Meccanismi di determinazione delle proprietà JIT, ad esempio data binding e animazioni/storyboard.
  • Modelli a uso multiplo, ad esempio risorse e stili.
  • Valori noti tramite relazioni padre-figlio con altri elementi nell'albero degli elementi.

Inoltre, una proprietà di dipendenza può fornire:

  • Convalida autonoma.
  • Valori predefiniti.
  • Callback che monitorano le modifiche ad altre proprietà.
  • Sistema in grado di coecere i valori delle proprietà in base alle informazioni di runtime.

Le classi derivate possono modificare alcune caratteristiche di una proprietà esistente eseguendo l'override dei metadati di una proprietà di dipendenza, anziché eseguire l'override dell'implementazione effettiva delle proprietà esistenti o creando nuove proprietà.

Nel riferimento all'SDK è possibile identificare una proprietà di dipendenza in base alla presenza di una 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 DependencyProperty campo identificatore per tale proprietà di dipendenza. Include anche l'elenco delle opzioni di metadati per tale proprietà, informazioni di 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 la funzionalità delle proprietà fornendo un tipo che restituisce una proprietà, in alternativa al modello standard di supporto di una proprietà con un campo privato. Il nome del tipo è DependencyProperty. L'altro tipo importante che definisce il sistema di proprietà WPF è DependencyObject, che definisce la classe base in grado di registrare e possedere una proprietà di dipendenza.

Ecco alcuni termini di uso comune:

  • Proprietà di dipendenza, ovvero una proprietà supportata da un oggetto DependencyProperty.

  • Identificatore di proprietà di dipendenza, ovvero un'istanza DependencyProperty ottenuta come valore restituito durante la registrazione di una proprietà di dipendenza e quindi archiviata come membro statico di una classe. Molte API che interagiscono con il sistema di proprietà WPF usano l'identificatore della proprietà di dipendenza come parametro.

  • CLR "wrapper", ovvero le get implementazioni e set per la proprietà . Queste implementazioni incorporano l'identificatore della proprietà di dipendenza usandolo nelle GetValue chiamate e SetValue . In questo modo, il sistema di proprietà WPF fornisce il backup per la proprietà .

Nell'esempio seguente viene definita la IsSpinning proprietà di dipendenza per visualizzare la relazione dell'identificatore DependencyProperty con la proprietà di cui viene eseguito il backup.

public static readonly DependencyProperty IsSpinningProperty = DependencyProperty.Register(
    "IsSpinning", typeof(bool),
    typeof(MainWindow)
    );

public bool IsSpinning
{
    get => (bool)GetValue(IsSpinningProperty);
    set => SetValue(IsSpinningProperty, value);
}
Public Shared ReadOnly IsSpinningProperty As DependencyProperty =
    DependencyProperty.Register("IsSpinning", GetType(Boolean), GetType(MainWindow))

Public Property IsSpinning As Boolean
    Get
        Return GetValue(IsSpinningProperty)
    End Get
    Set(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 sui 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 XAML seguente imposta il colore di sfondo di un pulsante su rosso. Il valore stringa per l'attributo XAML viene convertito dal parser XAML WPF in un tipo WPF. Nel codice generato, il tipo WPF è un Coloroggetto , tramite un oggetto SolidColorBrush.

<Button Content="I am red" Background="Red"/>

XAML supporta diversi 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à e da altri fattori, ad esempio la presenza di un convertitore di tipi. Per altre informazioni sulla sintassi XAML per l'impostazione delle proprietà, vedi XAML in WPF e sintassi XAML In dettaglio.

L'esempio XAML seguente mostra un altro sfondo del pulsante che usa la sintassi dell'elemento proprietà anziché la sintassi degli attributi. Anziché impostare un colore a tinta unita semplice, XAML imposta la proprietà button Background su un'immagine. Un elemento rappresenta l'immagine e un attributo dell'elemento annidato specifica l'origine dell'immagine.

<Button Content="I have an image background">
    <Button.Background>
        <ImageBrush ImageSource="stripes.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 set esposta dal clr "wrapper":

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

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

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

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

È 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

A differenza di una proprietà supportata da un campo, una proprietà di dipendenza estende la funzionalità di una proprietà. Spesso, la funzionalità aggiunta rappresenta o supporta una di queste funzionalità:

Risorse

È possibile impostare un valore della proprietà di dipendenza facendo riferimento a una risorsa. Le risorse vengono in genere specificate come Resources valore della proprietà di un elemento radice della pagina o dell'applicazione, poiché queste posizioni offrono un accesso pratico alla risorsa. In questo esempio viene definita una SolidColorBrush risorsa:

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

Ora che la risorsa è definita, è possibile fare riferimento alla risorsa per fornire un valore per la Background proprietà :

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

In XAML WPF è possibile usare un riferimento a risorse statiche o dinamiche. A questa particolare risorsa viene fatto riferimento come DynamicResource. Un riferimento di risorsa dinamica può essere usato solo per impostare una proprietà di dipendenza, pertanto è in particolare l'utilizzo di riferimento di risorse dinamiche abilitato dal sistema di proprietà WPF. Per altre informazioni, vedi 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 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 finale della proprietà viene posticipata fino alla fase di esecuzione, in cui il valore viene ottenuto da un'origine dati.

L'esempio seguente imposta la Content proprietà per un Buttonoggetto usando un'associazione dichiarata in XAML. L'associazione usa un contesto dei dati ereditato e un'origine dati XmlDataProvider (non illustrata). L'associazione specifica la proprietà di origine all'interno dell'origine dati da XPath.

<Button Content="{Binding Source={StaticResource TestData}, XPath=test[1]/@text}"/>

Nota

Le associazioni vengono considerate come un valore locale, ovvero 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 DependencyObject classe non supportano INotifyPropertyChanged in modo nativo la notifica delle modifiche apportate al DependencyObject valore della proprietà di origine per le operazioni di data binding. Per altre informazioni su come creare proprietà da usare nel data binding in grado di segnalare modifiche a una destinazione di data binding, vedere Panoramica del data binding.

Stili

Gli stili e i modelli sono motivi interessanti per usare le 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 Gli stili interagiscono con il sistema di proprietà perché in genere contengono "setter" per proprietà specifiche e "trigger" che modificano un valore della proprietà in base al valore di runtime per un'altra proprietà.

Nell'esempio seguente viene creato uno stile semplice, che verrebbe definito all'interno di un Resources dizionario (non visualizzato). Tale stile viene quindi 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}" Content="I am green"/>

Per altre informazioni, vedere Applicazione di stili e modelli.

Animazioni

Alle proprietà di dipendenza è possibile aggiungere un'animazione. Quando viene eseguita un'animazione applicata, il valore animato ha una precedenza maggiore rispetto a qualsiasi altro valore della proprietà, incluso un valore locale.

Nell'esempio seguente viene animata la Background proprietà di un oggetto Button. Tecnicamente, la sintassi dell'elemento proprietà imposta un valore vuoto SolidColorBrush come Backgrounde la Color proprietà di SolidColorBrush viene animata.

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

Per altre informazioni sull'animazione delle proprietà, vedi Panoramica dell'animazione e Panoramica degli storyboard.

Override dei metadati

È possibile modificare comportamenti specifici di una proprietà di dipendenza eseguendo l'override dei relativi metadati quando si deriva dalla classe che originariamente ha registrato la proprietà di dipendenza. L'override dei metadati si basa sull'identificatore DependencyProperty e non richiede la ripetizione 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, per ogni tipo.

L'esempio seguente esegue l'override dei metadati per una DefaultStyleKey proprietà di dipendenza. L'override dei metadati per questa particolare proprietà di dipendenza fa parte di un modello di implementazione per la creazione di controlli che possono usare 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 altre informazioni sull'override o sull'accesso ai metadati per le proprietà di dipendenza, vedere Eseguire l'override dei metadati per una 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 di ereditarietà dei valori di proprietà non è abilitato a livello globale per tutte le proprietà di dipendenza, perché il tempo di calcolo per l'ereditarietà influisce sulle prestazioni. L'ereditarietà del valore della proprietà è in genere abilitata solo negli scenari che suggeriscono l'applicabilità. È possibile verificare se una proprietà di dipendenza eredita esaminando la sezione Informazioni sulle proprietà di dipendenza per tale proprietà di dipendenza nel riferimento all'SDK.

Nell'esempio seguente viene illustrata un'associazione che include la DataContext proprietà per specificare l'origine dell'associazione. Le associazioni negli oggetti figlio non devono quindi specificare l'origine e possono usare il valore ereditato da DataContext nell'oggetto padre StackPanel . In alternativa, un oggetto figlio può specificare direttamente il proprio DataContext oggetto o in Source Bindinge non usare il valore ereditato.

<StackPanel Canvas.Top="50" DataContext="{Binding Source={StaticResource TestData}}">
    <Button Content="{Binding XPath=test[2]/@text}"/>
</StackPanel>

Per altre informazioni, vedere Ereditarietà dei valori delle proprietà.

Integrazione di WPF Designer

Controlli personalizzati con proprietà implementate come proprietà di dipendenza si integrano bene con WPF Designer per Visual Studio. Un esempio è la possibilità di modificare le proprietà di dipendenza dirette e associate nella finestra Proprietà . Per altre informazioni, vedere Cenni preliminari sulla creazione di controlli.

Precedenza del valore della proprietà di dipendenza

Qualsiasi input basato su proprietà all'interno del sistema di proprietà WPF può impostare il valore di una proprietà di dipendenza. La precedenza del valore della proprietà di dipendenza esiste in modo che i vari scenari per il modo in cui le proprietà ottengano i valori interagiscono in modo prevedibile.

Nota

La documentazione dell'SDK a volte usa il termine "valore locale" o "valore impostato localmente" per discutere delle proprietà di dipendenza. Un valore impostato localmente è un valore della proprietà impostato direttamente su un'istanza dell'oggetto nel codice o come attributo di elemento in XAML.

L'esempio successivo include uno stile che si applica alla Background proprietà di qualsiasi pulsante, ma specifica un pulsante con una proprietà impostata Background localmente. Tecnicamente, il pulsante ha la proprietà Background impostata due volte, anche se viene applicato un solo valore, ovvero il valore con la precedenza più alta. Un valore impostato localmente ha la precedenza più alta, ad eccezione di un'animazione in esecuzione, che non esiste qui. Il secondo pulsante usa quindi il valore impostato localmente per la Background proprietà, anziché il valore del setter di stile. Il primo pulsante non ha alcun valore locale o altro valore con precedenza superiore rispetto a un setter di stile e quindi usa il valore del setter di stile per la Background proprietà.

<StackPanel>
    <StackPanel.Resources>
        <Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
            <Setter Property="Background" Value="Orange"/>
        </Style>
    </StackPanel.Resources>
    <Button>I am styled orange</Button>
    <Button Background="Pink">I am locally set to pink (not styled orange)</Button>
</StackPanel>

Motivi dell'esistenza della precedenza delle proprietà di dipendenza

I valori impostati localmente hanno la precedenza sui valori del setter di stile, che supportano il controllo locale delle proprietà degli elementi. Per informazioni dettagliate, vedere Precedenza del valore della proprietà di dipendenza.

Nota

Alcune proprietà definite negli elementi WPF non sono proprietà di dipendenza, perché in genere sono state implementate proprietà di dipendenza solo quando era necessaria una funzionalità del sistema di proprietà WPF. Le funzionalità includono data binding, stili, animazione, supporto dei valori predefiniti, ereditarietà, proprietà associate e invalidazione.

Altre informazioni sulle proprietà di dipendenza

  • Gli sviluppatori di componenti o gli sviluppatori di applicazioni potrebbero voler creare la propria proprietà di dipendenza per aggiungere funzionalità, ad esempio il data binding o il supporto degli stili, oppure il supporto della coercizione di valori e invalidazione. Per altre informazioni, vedere Proprietà di dipendenza personalizzate.

  • Considerare le proprietà di dipendenza come proprietà pubbliche, accessibili o individuabili da qualsiasi chiamante con accesso a un'istanza. Per altre informazioni, vedere Sicurezza delle 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à Common Language Runtime e non è necessariamente una proprietà di dipendenza. Lo scopo principale 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 includono tale proprietà come parte degli elenchi dei membri della classe. Uno scenario principale consiste nell'abilitare un elemento figlio per informare gli elementi padre su come presentarli nell'interfaccia utente. Per esempi, vedere Dock e Left. Per altre informazioni, vedere Panoramica delle proprietà associate.

Vedi anche