Vue d’ensemble des propriétés de dépendance

WPF (Windows Presentation Foundation) fournit un ensemble de services qui permettent d’étendre la fonctionnalité de la propriété d’un type. Ces services sont généralement regroupés sous le nom « système de propriétés WPF ». Une propriété qui repose sur le système de propriété WPF s’appelle une propriété de dépendance. Cette vue d’ensemble décrit le système de propriétés WPF et les fonctionnalités d’une propriété de dépendance. notamment comment utiliser les propriétés de dépendance existantes en XAML et dans le code. Elle introduit également des aspects spécialisés des propriétés de dépendance, tels que les métadonnées de propriétés de dépendance et comment créer votre propre propriété de dépendance dans une classe personnalisée.

Prérequis

Cette rubrique part du principe que vous avez des connaissances de base relatives au système de types .NET et à la programmation orientée objet. Pour pouvoir suivre les exemples de cette rubrique, vous devez également comprendre le langage XAML et savoir comment écrire des applications WPF. Pour plus d’informations, consultez Procédure pas à pas : ma première application de bureau WPF.

Propriétés de dépendance et propriétés CLR

Dans WPF, les propriétés sont généralement exposées en tant que propriétés .NET standard. À un niveau basique, vous pourriez interagir directement avec ces propriétés et ne jamais savoir qu’elles sont implémentées en tant que propriétés de dépendance. Toutefois, vous devez vous familiariser avec une partie ou l’ensemble des fonctionnalités du système de propriétés WPF pour pouvoir tirer parti de ces fonctionnalités.

Les propriétés de dépendance ont pour but de fournir un moyen de calculer la valeur d’une propriété en fonction de la valeur d’autres entrées. Ces autres entrées peuvent inclure des propriétés système telles que des thèmes et des préférences de l’utilisateur, des mécanismes de détermination de propriété juste-à-temps tels que la liaison de données et les animations/plans conceptuels, des modèles à plusieurs usages comme les ressources et les styles, ou des valeurs connues par l’intermédiaire de relations parent-enfant avec d’autres éléments dans l’arborescence d’éléments. En outre, une propriété de dépendance peut être implémentée pour fournir une validation autonome, des valeurs par défaut, des rappels qui surveillent les modifications apportées à d’autres propriétés, et un système capable de forcer des valeurs de propriétés en fonction d’informations d’exécution potentielles. Les classes dérivées peuvent également changer certaines caractéristiques spécifiques d’une propriété existante en substituant les métadonnées de propriété de dépendance, plutôt qu’en substituant l’implémentation réelle des propriétés existantes ou en créant de nouvelles propriétés.

Dans la référence du SDK, vous pouvez identifier quelle propriété est une propriété de dépendance par la présence de la section Informations sur les propriétés de dépendance dans la page de référence managée de cette propriété. La section des informations sur les propriétés de dépendance comprend un lien vers l’identificateur de champ DependencyProperty pour cette propriété de dépendance, ainsi qu’une liste d’options de métadonnées définies pour cette propriété, des informations de substitution par classe et d’autres détails.

Les propriétés CLR reposent sur les propriétés de dépendance

Les propriétés de dépendance et le système de propriétés WPF étendent la fonctionnalité des propriétés en fournissant un type qui prend en charge une propriété, en tant qu’implémentation alternative du modèle standard de stockage de la propriété avec un champ privé. Le nom de ce type est DependencyProperty. L’autre type important qui définit le système de propriétés WPF est DependencyObject. DependencyObject définit la classe de base qui peut inscrire et détenir une propriété de dépendance.

Voici une liste qui décrit la terminologie utilisée avec les propriétés de dépendance :

  • Propriété de dépendance : propriété qui repose sur un DependencyProperty.

  • Identificateur de propriété de dépendance : instance de DependencyProperty obtenue en tant que valeur retournée durant l’inscription d’une propriété de dépendance, puis stockée en tant que membre statique d’une classe. Cet identificateur est utilisé en tant que paramètre pour la plupart des API qui interagissent avec le système de propriétés WPF.

  • « wrapper » CLR : implémentations get et set pour la propriété. Ces implémentations incorporent l’identificateur de propriété de dépendance en l’utilisant dans les appels de GetValue et SetValue, ce qui assure ainsi le stockage de la propriété à l’aide du système de propriétés WPF.

L’exemple suivant définit la propriété de dépendance IsSpinning et montre la relation entre l’identificateur DependencyProperty et la propriété qu’il prend en charge.

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 convention de nommage de la propriété et de son champ DependencyProperty de stockage est importante. Le nom du champ est toujours le nom de la propriété, auquel est ajouté le suffixe Property. Pour plus d’informations sur cette convention et sur sa raison d’être, consultez Propriétés de dépendance personnalisées.

Définition de valeurs de propriété

Vous pouvez définir des propriétés dans le code ou en XAML.

Définition de valeurs de propriété en XAML

L’exemple XAML suivant spécifie le rouge comme couleur d’arrière-plan d’un bouton. Cet exemple illustre un cas où le type d’une valeur de chaîne simple d’un attribut XAML est converti par l’analyseur XAML WPF en type WPF (Color via SolidColorBrush) dans le code généré.

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

XAML prend en charge diverses formes de syntaxe pour définir les propriétés. La syntaxe à utiliser pour une propriété particulière varie en fonction du type de valeur utilisé par une propriété, ainsi que d’autres facteurs tels que la présence d’un convertisseur de type. Pour plus d’informations sur la syntaxe XAML pour le paramètre de propriété, consultez XAML dans WPF et syntaxe XAML en détail.

En guise d’exemple de syntaxe de non-attribut, l’exemple XAML suivant montre un autre arrière-plan de bouton. Cette fois-ci, au lieu de définir une couleur unie, l’arrière-plan est défini sur une image, avec un élément représentant cette image et la source de cette image spécifiée en tant qu’attribut de l’élément imbriqué. Il s’agit d’un exemple de syntaxe d’éléments de propriété.

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

Définition de propriétés dans le code

La définition des valeurs de propriété de dépendance dans le code n’est généralement qu’un appel à l’implémentation de jeu exposée par le CLR « wrapper ».

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

Pour obtenir une valeur de propriété, il suffit aussi généralement d’appeler l’implémentation « wrapper » Get :

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

Vous pouvez également appeler les API GetValue système de propriétés et SetValue directement. Cela n’est généralement pas nécessaire si vous utilisez des propriétés existantes (les wrappers sont plus pratiques et fournissent une meilleure exposition de la propriété pour les outils de développement), mais l’appel des API directement est approprié pour certains scénarios.

Vous pouvez aussi définir les propriétés en XAML et y accéder ultérieurement dans le code, par l’intermédiaire du code-behind. Pour plus d’informations, consultez Code-behind et XAML dans WPF.

Fonctionnalité de propriété fournie par une propriété de dépendance

Une propriété de dépendance fournit une fonctionnalité qui étend la fonctionnalité d’une propriété, par opposition à une propriété qui est stockée par un champ. Souvent, une telle fonctionnalité représente ou prend en charge l’une des fonctionnalités spécifiques suivantes :

Ressources

Une valeur de propriété de dépendance peut être définie en référençant une ressource. Les ressources sont généralement spécifiées en tant que valeur de propriété Resources d’un élément racine de page ou de l’application (ces emplacements autorisent l’accès le plus commode à la ressource). L’exemple suivant montre comment définir une ressource SolidColorBrush.

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

Une fois la ressource définie, vous pouvez la référencer et l’utiliser pour fournir une valeur de propriété :

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

Cette ressource particulière est référencée en tant qu’Extension de balisage DynamicResource (en XAML WPF, vous pouvez utiliser une référence de ressource statique ou dynamique). Pour utiliser une référence de ressource dynamique, vous devez définir une propriété de dépendance. Ainsi, l’utilisation de la référence de ressource dynamique est activée spécifiquement par le système de propriétés WPF. Pour plus d’informations, consultez Ressources XAML.

Remarque

Les ressources sont traitées comme une valeur locale, ce qui signifie que si vous définissez une autre valeur locale, vous supprimez la référence de ressource. Pour plus d’informations, consultez Priorité de la valeur de propriété de dépendance.

Liaison de données

Une propriété de dépendance peut référencer une valeur par l’intermédiaire d’une liaison de données. La liaison de données fonctionne via une syntaxe d’extension de balisage spécifique en XAML, ou via l’objet Binding dans du code. Avec la liaison de données, la détermination de valeur de propriété finale est différée jusqu’au moment de l’exécution, quand la valeur est obtenue à partir d’une source de données.

L’exemple suivant définit la propriété Content de Button à l’aide d’une liaison déclarée en XAML. La liaison utilise un contexte de données hérité et une source de données XmlDataProvider (non illustrée). La liaison proprement dite spécifie la propriété source souhaitée par XPath dans la source de données.

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

Remarque

Les liaisons sont traitées comme une valeur locale, ce qui signifie que si vous définissez une autre valeur locale, vous supprimez la liaison. Pour plus d’informations, consultez Priorité de la valeur de propriété de dépendance.

Les propriétés de dépendance, ou la classe DependencyObject, ne prennent pas en charge INotifyPropertyChanged de manière native afin de notifier les changements de valeurs de la propriété source DependencyObject pour les opérations de liaison de données. Pour plus d’informations sur la façon de créer (pour une utilisation dans la liaison de données)' des propriétés capables de signaler des changements de cible de liaison de données, consultez Vue d’ensemble de la liaison de données.

Styles

Les styles et modèles sont deux des principaux scénarios justifiant l’utilisation de propriétés de dépendance. Les styles sont particulièrement utiles pour définir des propriétés qui définissent l’interface utilisateur de l’application. Ils sont généralement définis en tant que ressources en XAML. Ils interagissent avec le système de propriétés car ils contiennent généralement des « méthodes setter » pour des propriétés particulières, ainsi que des « déclencheurs » qui changent une valeur de propriété d’après la valeur en temps réel d’une autre propriété.

L’exemple suivant crée un style simple (qui serait défini à l’intérieur d’un Resources dictionnaire, non affiché), puis applique ce style directement à la Style propriété pour un Button. La méthode setter contenue dans le style définit la propriété Background pour un Button mis en vert.

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

Pour plus d’informations, consultez Application d’un style et création de modèles.

Animations

Les propriétés de dépendance peuvent être animées. Quand une animation est appliquée et s’exécute, la valeur animée opère à une priorité plus élevée que toute autre valeur (par exemple une valeur locale) qu’aurait normalement la propriété.

L’exemple suivant anime Background sur une propriété Button (techniquement, il anime Background à l’aide de la syntaxe d’élément de propriété pour spécifier un SolidColorBrush vide en tant que Background, puis la propriété Color de SolidColorBrush est directement animée).

<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>

Pour plus d’informations sur l’animation de propriétés, consultez Vue d’ensemble des animations et Vue d’ensemble des plans conceptuels.

Substitutions de métadonnées

Vous pouvez changer certains comportements d’une propriété de dépendance en substituant les métadonnées de cette propriété quand vous dérivez de la classe qui inscrit initialement la propriété de dépendance. La substitution de métadonnées repose sur l’identificateur DependencyProperty. La substitution de métadonnées ne nécessite pas la réémise en œuvre de la propriété. Le changement de métadonnées est géré de manière native par le système de propriétés. Chaque classe contient potentiellement des métadonnées individuelles pour toutes les propriétés qui sont héritées des classes de base, sur la base de chaque type.

L’exemple suivant remplace les métadonnées d’une propriété de dépendance DefaultStyleKey. La substitution de ces métadonnées de propriété de dépendance particulières fait partie d’un modèle d’implémentation qui crée des contrôles capables d’utiliser les styles par défaut de thèmes.

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

Pour plus d’informations sur la substitution ou l’obtention de métadonnées de propriété, consultez Métadonnées de propriété de dépendance.

Héritage d’une valeur de propriété

Un élément peut hériter de la valeur d’une propriété de dépendance de son parent dans l’arborescence d’objets.

Remarque

Le comportement d’héritage de valeur de propriété n’est pas activé globalement pour toutes les propriétés de dépendance, car le temps de calcul pour l’héritage a un certain impact sur les performances. L’héritage de valeur de propriété est en général activé uniquement pour les propriétés où un scénario particulier suggère que l’héritage de valeur de propriété est appropriée. Vous pouvez déterminer si une propriété de dépendance hérite en consultant la section Informations sur les propriétés de dépendance de cette propriété de dépendance dans la référence du SDK.

L’exemple suivant illustre une liaison et définit la propriété DataContext qui spécifie la source de la liaison, ce qui n’était pas montré dans l’exemple de liaison précédent. Les liaisons suivantes dans les objets enfants n’ont pas besoin de spécifier la source. Elles peuvent utiliser la valeur héritée de DataContext dans l’objet StackPanel parent. (En guise d’alternative, un objet enfant peut choisir de spécifier directement son propre DataContext ou un Source dans Binding, et d’éviter délibérément d’utiliser la valeur héritée pour le contexte de données de ses liaisons.)

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

Pour plus d’informations, consultez Héritage de valeur de propriété.

Intégration du concepteur WPF

Un contrôle personnalisé avec des propriétés implémentées en tant que propriétés de dépendance reçoit le concepteur WPF approprié pour la prise en charge de Visual Studio. La possibilité de modifier des propriétés de dépendance directes et jointes avec la fenêtre Propriétés constitue un bon exemple. Pour plus d’informations, consultez Vue d’ensemble de la création de contrôles.

Priorité de la valeur d’une propriété de dépendance

Quand vous obtenez la valeur d’une propriété de dépendance, vous obtenez potentiellement une valeur définie pour cette propriété via l’une des autres entrées basées sur des propriétés qui participent au système de propriétés WPF. Il existe une priorité de valeur de propriété de dépendance, afin que différents scénarios d’obtention des valeurs par les propriétés puissent interagir de manière prévisible.

Considérez l'exemple suivant. L’exemple inclut un style qui s’applique à tous les boutons et à leurs propriétés Background. Toutefois, il spécifie également un bouton avec une valeur Background définie localement.

Remarque

La documentation du SDK utilise les termes « valeur locale » ou « valeur localement définie » occasionnellement lors de la discussion des propriétés de dépendance. Une valeur localement définie est une valeur de propriété qui est définie directement sur une instance d’objet dans le code, ou en tant qu’attribut d’élément en XAML.

En principe, pour le premier bouton, la propriété est définie à deux reprises, mais une seule valeur est applicable : celle ayant la priorité la plus élevée. Une valeur localement définie a la priorité la plus élevée (sauf une animation en cours d’exécution, mais aucune animation n’est applicable dans cet exemple). La valeur localement définie est donc utilisée au lieu de la valeur du Style Setter pour l’arrière-plan du premier bouton. Le deuxième bouton n’a aucune valeur locale (et aucune autre valeur ayant une priorité supérieure à celle d’un Style Setter). Par conséquent, l’arrière-plan de ce bouton provient du Style Setter.

<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>

Pourquoi la priorité des propriétés de dépendance existe-t-elle ?

En règle générale, vous ne souhaitez pas que les styles s’appliquent toujours et masquent même une valeur définie localement d’un élément individuel (sinon, il serait difficile d’utiliser des styles ou des éléments en général). Par conséquent, les valeurs qui proviennent de styles opèrent à une priorité plus faible que les valeurs définies localement. Pour obtenir une liste plus complète de propriétés de dépendance et pour savoir d’où la valeur effective d’une propriété de dépendance peut provenir, consultez Priorité de la valeur de propriété de dépendance.

Remarque

Un certain nombre de propriétés définies sur des éléments WPF ne sont pas des propriétés de dépendance. De manière générale, les propriétés ont été implémentées en tant que propriétés de dépendance uniquement quand il fallait prendre en charge au moins l’un des scénarios activés par le système de propriétés : liaison de données, styles, animation, prise en charge de valeur par défaut, héritage, propriétés jointes ou invalidation.

En savoir plus sur les propriétés de dépendance

  • Une propriété jointe est un type de propriété qui prend en charge une syntaxe spécialisée en XAML. Une propriété jointe n’a souvent pas de correspondance 1 :1 avec une propriété CLR (Common Language Runtime) et n’est pas nécessairement une propriété de dépendance. L’objectif classique d’une propriété jointe est d’autoriser les éléments enfants à signaler des valeurs de propriété à un élément parent, même si l’élément parent et l’élément enfant ne possèdent pas cette propriété dans le cadre des listes des membres de classe. Un scénario principal consiste à permettre aux éléments enfants d’informer le parent de la façon dont ils doivent être présentés dans l’interface utilisateur ; pour obtenir un exemple, voir Dock ou Left. Pour plus d’informations, consultez Vue d’ensemble des propriétés jointes.

  • Les développeurs de composants ou d’applications peuvent souhaiter créer leur propre propriété de dépendance, afin d’activer des fonctionnalités telles que la liaison de données ou la prise en charge des styles, ou pour prendre en charge l’invalidation et le forçage de valeur. Pour plus d’informations, consultez Propriétés de dépendance personnalisées.

  • Considérez les propriétés de dépendance comme des propriétés publiques, accessibles ou au moins détectables par n’importe quel appelant qui a accès à une instance. Pour plus d’informations, consultez Sécurité des propriétés de dépendance.

Voir aussi