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, incluso 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.

Importante

La documentazione di Desktop Guide per .NET 7 e .NET 6 è in fase di costruzione.

Prerequisiti

Questo articolo presuppone la conoscenza di base del sistema di tipi .NET e della programmazione orientata agli oggetti. Per seguire gli esempi in questo articolo, consente di 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 livello di base e non sapere mai che vengono implementate come proprietà di dipendenza. Tuttavia, familiarità con alcune o tutte le funzionalità del sistema di proprietà WPF, ti aiuterà a 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à just-in-time, ad esempio data binding e animazioni/storyboard.
  • Modelli di 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 monitora le modifiche ad altre proprietà.
  • Un sistema che può coercere 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é ignorare l'implementazione effettiva delle proprietà esistenti o la creazione di nuove proprietà.

Nel riferimento all'SDK è possibile identificare una proprietà di dipendenza in base alla presenza di una sezione Informazioni proprietà di dipendenza nella pagina di riferimento gestita per tale proprietà. La sezione Informazioni sulla proprietà dependency 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 esegue il backing di una proprietà, in alternativa al modello standard di backup 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 che può registrare e possedere una proprietà di dipendenza.

Ecco una terminologia comunemente usata:

  • Proprietà di dipendenza, che è una proprietà supportata da un DependencyPropertyoggetto .

  • Identificatore di proprietà di dipendenza, che è 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 delle 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 includono 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 alla 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à delle dipendenze 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 , in base a .SolidColorBrush

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

XAML supporta diversi moduli di sintassi per l'impostazione delle proprietà. Quale sintassi utilizzare per una determinata proprietà dipende dal tipo di valore usato da una proprietà e da altri fattori, ad esempio la presenza di un convertitore di tipi. Per altre informazioni sulla sintassi XAML per le proprietà di impostazione, vedere XAML in WPF e sintassi XAML In dettaglio.

L'esempio XAML seguente mostra un altro sfondo del pulsante che usa la sintassi degli elementi della proprietà anziché la sintassi degli attributi. Anziché impostare un semplice colore a tinta unita, il codice XAML imposta la proprietà del pulsante 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 da CLR "wrapper":

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

Ottenere un valore della 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 delle proprietà e SetValue direttamente. La chiamata diretta delle API è appropriata per alcuni scenari, ma in genere non quando si usano le 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 di 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>

Dopo aver definito la risorsa, è 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 di risorsa statica o dinamica. Questa particolare risorsa viene fatto riferimento a DynamicResource. Un riferimento di risorsa dinamica può essere usato solo per impostare una proprietà di dipendenza, pertanto è specificamente l'utilizzo di riferimento di risorse dinamiche abilitato dal sistema di proprietà WPF. Per altre informazioni, vedere Risorse XAML.

Nota

Le risorse vengono considerate come 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.

Associazione dati

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 all'ora di esecuzione, al momento in cui il valore viene ottenuto da un'origine dati.

Nell'esempio seguente viene impostata 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 stessa specifica la proprietà di origine all'interno dell'origine dati in XPathbase a .

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

Nota

Le associazioni vengono considerate come 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 per 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 le modifiche apportate 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 è 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 ha originariamente registrato la proprietà di dipendenza. L'override dei metadati si basa sull'identificatore DependencyProperty e non richiede la rimplementazione 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à del valore della 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 Dependency Property Information (Informazioni sulle proprietà di dipendenza) nella guida di riferimento dell'SDK.

Nell'esempio seguente viene illustrata un'associazione che include la DataContext proprietà per specificare l'origine dell'associazione. Pertanto, le associazioni negli oggetti figlio non devono 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 un Source oggetto in 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

I 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à ottengono i relativi valori interagiscono in modo prevedibile.

Nota

La documentazione dell'SDK talvolta usa il termine "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 di elemento in XAML.

Nell'esempio seguente è incluso uno stile che si applica alla Background proprietà di qualsiasi pulsante, ma specifica un pulsante con una proprietà impostata Background localmente. Tecnicamente, tale 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 a un setter di stile e quindi usa il valore 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 supporta 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é le proprietà di dipendenza sono in genere implementate solo quando è necessaria una funzionalità del sistema di proprietà WPF. Le funzionalità includono il data binding, lo stile, l'animazione, il supporto dei valori predefiniti, l'ereditarietà, le proprietà associate e l'invalidazione.

Altre informazioni sulle proprietà di dipendenza

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

  • Prendere in considerazione le proprietà di dipendenza come proprietà pubbliche, accessibili o individuabili da qualsiasi chiamante con accesso a un'istanza di . 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.

Vedere anche