Propriétés de dépendance personnalisées

Cette rubrique décrit les raisons pour lesquelles les développeurs d’applications windows Presentation Foundation (WPF) et les auteurs de composants peuvent vouloir créer une propriété de dépendance personnalisée et décrire les étapes d’implémentation ainsi que certaines options d’implémentation qui peuvent améliorer les performances, la facilité d’utilisation ou la polyvalence de la propriété.

Prérequis

Cette rubrique suppose que vous comprenez les propriétés de dépendance du point de vue d’un consommateur de propriétés de dépendance existantes sur les classes WPF et que vous avez lu la rubrique Vue d’ensemble des propriétés de dépendance. Pour pouvoir suivre les exemples de cette rubrique, vous devez également comprendre le langage XAML et savoir comment écrire des applications WPF.

Qu’est ce qu’une propriété de dépendance ?

Vous pouvez activer ce qui serait autrement une propriété CLR (Common Language Runtime) pour prendre en charge le style, la liaison de données, l’héritage, les animations et les valeurs par défaut en l’implémentant en tant que propriété de dépendance. Les propriétés de dépendance sont des propriétés inscrites auprès du système de propriétés WPF en appelant la Register méthode (ou RegisterReadOnly) et qui sont sauvegardées par un champ d’identificateur DependencyProperty . Les propriétés de dépendance ne peuvent être utilisées que par DependencyObject les types, mais DependencyObject elles sont assez élevées dans la hiérarchie de classes WPF, de sorte que la majorité des classes disponibles dans WPF peuvent prendre en charge les propriétés de dépendance. Pour plus d’informations sur les propriétés de dépendance et certaines des terminologies et conventions utilisées pour les décrire dans ce Kit de développement logiciel (SDK), consultez Vue d’ensemble des propriétés de dépendance.

Exemples de propriétés de dépendance

Parmi les exemples de propriétés de dépendance implémentées sur les classes WPF, citons la Background propriété, la Width propriété et la Text propriété, entre autres. Chaque propriété de dépendance exposée par une classe a un champ statique public correspondant de type DependencyProperty exposé sur cette même classe. Il s’agit de l’identificateur de la propriété de dépendance. L’identificateur est nommé à l’aide d’une convention : le nom de la propriété de dépendance, auquel est ajoutée la chaîne Property. Par exemple, le champ d’identificateur correspondant DependencyProperty pour la Background propriété est BackgroundProperty. L’identificateur stocke les informations relatives à la propriété de dépendance telle qu’elle a été inscrite, et l’identificateur est ensuite utilisé ultérieurement pour d’autres opérations impliquant la propriété de dépendance, telles que l’appel SetValue.

Comme mentionné dans la vue d’ensemble des propriétés de dépendance, toutes les propriétés de dépendance dans WPF (à l’exception de la plupart des propriétés jointes) sont également des propriétés CLR en raison de l’implémentation « wrapper ». Par conséquent, à partir du code, vous pouvez obtenir ou définir des propriétés de dépendance en appelant des accesseurs CLR qui définissent les wrappers de la même façon que vous utiliseriez d’autres propriétés CLR. En tant que consommateur de propriétés de dépendance établies, vous n’utilisez généralement pas les DependencyObject méthodes GetValue et SetValue, qui sont le point de connexion au système de propriétés sous-jacent. Au lieu de cela, l’implémentation existante des propriétés CLR a déjà appelé GetValue et SetValueset dans les get implémentations wrapper de la propriété, en utilisant le champ d’identificateur de manière appropriée. Si vous implémentez une propriété de dépendance personnalisée vous-même, vous définirez le wrapper de la même façon.

Quand faut-il implémenter une propriété de dépendance ?

Lorsque vous implémentez une propriété sur une classe, tant que votre classe dérive de DependencyObject, vous avez la possibilité de sauvegarder votre propriété avec un DependencyProperty identificateur et donc de la rendre une propriété de dépendance. Faire de votre propriété une propriété de dépendance n’est pas toujours nécessaire ni approprié. Cela dépend des besoins de votre scénario. Parfois, la technique classique qui consiste à stocker la propriété dans un champ privé est adéquate. Toutefois, vous devez implémenter votre propriété en tant que propriété de dépendance chaque fois que vous souhaitez que votre propriété prend en charge une ou plusieurs des fonctionnalités WPF suivantes :

  • Vous souhaitez que votre propriété puisse être définie dans un style. Pour plus d’informations, consultez Application d’un style et création de modèles.

  • Vous souhaitez que votre propriété prenne en charge la liaison de données. Pour plus d’informations sur les propriétés de dépendance de liaison de données, consultez Lier les propriétés de deux contrôles.

  • Vous souhaitez que votre propriété puisse être définie avec une référence à une ressource dynamique. Pour plus d’informations, consultez Ressources XAML.

  • Vous voulez hériter automatiquement une valeur de propriété d’un élément parent dans l’arborescence d’éléments. Dans ce cas, inscrivez-vous avec la RegisterAttached méthode, même si vous créez également un wrapper de propriétés pour l’accès CLR. Pour plus d’informations, consultez Héritage de valeur de propriété.

  • Vous souhaitez que votre propriété puisse être animée. Pour plus d’informations, consultez Vue d’ensemble de l’animation.

  • Vous souhaitez que le système de propriétés signale quand la valeur précédente de la propriété a été modifiée par des actions effectuées par le système de propriétés, l’environnement ou l’utilisateur, ou par la lecture et l’utilisation de styles. À l’aide des métadonnées de propriété, votre propriété peut spécifier une méthode de rappel qui sera appelée chaque fois que le système de propriétés détermine que la valeur de propriété a été modifiée de manière certaine. Le forçage de valeur de propriété est un concept connexe. Pour plus d’informations, consultez Validation et rappels de propriétés de dépendance.

  • Vous souhaitez utiliser des conventions de métadonnées établies qui sont également utilisées par les processus WPF, telles que la création de rapports indiquant si la modification d’une valeur de propriété doit exiger que le système de disposition récompose les visuels d’un élément. Ou vous souhaitez pouvoir utiliser des substitutions de métadonnées pour que les classes dérivées puissent changer des caractéristiques basées sur les métadonnées, telles que la valeur par défaut.

  • Vous souhaitez que les propriétés d’un contrôle personnalisé reçoivent la prise en charge du Concepteur WPF Visual Studio, comme la modification de fenêtre Propriétés . Pour plus d’informations, consultez Vue d’ensemble de la création de contrôles.

Quand vous examinez ces scénarios, vous devez également réfléchir si vous pouvez accomplir votre scénario en substituant les métadonnées d’une propriété de dépendance existante, plutôt qu’en implémentant une toute nouvelle propriété. Si un remplacement de métadonnées est pratique dépend de votre scénario et de la manière dont ce scénario ressemble à l’implémentation dans les propriétés et classes de dépendance WPF existantes. Pour plus d’informations sur la substitution de métadonnées dans les propriétés existantes, consultez Métadonnées de propriété de dépendance.

Liste de vérification pour la définition d’une propriété de dépendance

La définition d’une propriété de dépendance implique quatre concepts distincts. Ces concepts ne sont pas des étapes procédurales nécessairement strictes, car certaines d’entre elles finissent par être combinées sur des lignes de code uniques dans l’implémentation :

  • (Facultatif) Créez des métadonnées de propriété pour la propriété de dépendance.

  • Inscrivez le nom de propriété auprès du système de propriétés, en spécifiant un type de propriétaire et le type de la valeur de propriété. Spécifiez également les métadonnées de propriété, si elles sont utilisées.

  • Définissez un DependencyProperty identificateur en tant que publicstaticreadonly champ sur le type de propriétaire.

  • Définissez une propriété CLR « wrapper » dont le nom correspond au nom de la propriété de dépendance. Implémentez les accesseurs et set les propriétés get clR « wrapper » pour se connecter à la propriété de dépendance qui l’accompagne.

Inscription de la propriété auprès du système de propriétés

Pour que votre propriété soit une propriété de dépendance, vous devez l’inscrire dans une table gérée par le système de propriétés et lui donner un identificateur unique utilisé comme qualificateur pour les opérations de système de propriétés ultérieures. Ces opérations peuvent être des opérations internes ou votre propre code appelant des API de système de propriétés. Pour inscrire la propriété, vous appelez la Register méthode dans le corps de votre classe (à l’intérieur de la classe, mais en dehors de toutes les définitions de membres). Le champ d’identificateur est également fourni par l’appel Register de méthode, comme valeur de retour. La raison pour laquelle l’appel Register est effectué en dehors d’autres définitions de membres est que vous utilisez cette valeur de retour pour affecter et créer unreadonlypublicstaticchamp de type DependencyProperty dans le cadre de votre classe. Ce champ devient l’identificateur de votre propriété de dépendance.

public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register(
  "AquariumGraphic",
  typeof(Uri),
  typeof(AquariumObject),
  new FrameworkPropertyMetadata(null,
      FrameworkPropertyMetadataOptions.AffectsRender,
      new PropertyChangedCallback(OnUriChanged)
  )
);
Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty = DependencyProperty.Register("AquariumGraphic", GetType(Uri), GetType(AquariumObject), New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender, New PropertyChangedCallback(AddressOf OnUriChanged)))

Conventions pour les noms des propriétés de dépendance

Il existe des conventions d’affectation de noms établies pour les propriétés de dépendance. Vous devez les respecter dans toutes les circonstances, sauf les plus exceptionnelles.

La propriété de dépendance elle-même aura un nom de base, « AquariumGraphic » comme dans cet exemple, qui est donné comme premier paramètre de Register. Ce nom doit être unique dans chaque type d’inscription. Les propriétés de dépendance héritées par l’intermédiaire des types de base sont considérées comme faisant déjà partie du type d’inscription ; les noms des propriétés héritées ne peuvent pas être réinscrits. Il existe toutefois une technique pour ajouter une classe en tant que propriétaire d’une propriété de dépendance, même quand celle-ci n’est pas héritée. Pour plus d’informations, consultez Métadonnées de propriété de dépendance.

Quand vous créez le champ d’identificateur, affectez-lui le même nom que la propriété que vous avez inscrite, plus le suffixe Property. Ce champ est votre identificateur pour la propriété de dépendance. Il sera utilisé ultérieurement comme entrée pour les SetValue wrappers et GetValue les appels que vous effectuerez dans les wrappers, par tout autre accès de code à la propriété par votre propre code, par tout accès au code externe que vous autorisez, par le système de propriétés et potentiellement par les processeurs XAML.

Remarque

La définition de la propriété de dépendance dans le corps de la classe est l’implémentation type, mais vous pouvez aussi définir une propriété de dépendance dans le constructeur statique de classe. Cette approche peut être plus logique si vous avez besoin de plus d’une ligne de code pour initialiser la propriété de dépendance.

Implémentation du « Wrapper »

Votre implémentation wrapper doit appeler GetValue dans l’implémentation get et SetValue dans l’implémentation (l’appel d’inscription d’origine et le set champ sont affichés ici aussi pour plus de clarté).

Dans toutes les circonstances, mais exceptionnelles, vos implémentations wrapper doivent effectuer uniquement les actions et SetValue les GetValue actions, respectivement. La raison est expliquée dans la rubrique Propriétés de dépendance et chargement XAML.

Toutes les propriétés de dépendance publique existantes fournies sur les classes WPF utilisent ce modèle d’implémentation de wrapper simple ; la plupart de la complexité du fonctionnement des propriétés de dépendance est soit intrinsèquement un comportement du système de propriétés, soit est implémenté par le biais d’autres concepts tels que la contrainte ou les rappels de modification de propriété par le biais de métadonnées de propriété.


public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register(
  "AquariumGraphic",
  typeof(Uri),
  typeof(AquariumObject),
  new FrameworkPropertyMetadata(null,
      FrameworkPropertyMetadataOptions.AffectsRender,
      new PropertyChangedCallback(OnUriChanged)
  )
);
public Uri AquariumGraphic
{
  get { return (Uri)GetValue(AquariumGraphicProperty); }
  set { SetValue(AquariumGraphicProperty, value); }
}

Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty = DependencyProperty.Register("AquariumGraphic", GetType(Uri), GetType(AquariumObject), New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender, New PropertyChangedCallback(AddressOf OnUriChanged)))
Public Property AquariumGraphic() As Uri
    Get
        Return CType(GetValue(AquariumGraphicProperty), Uri)
    End Get
    Set(ByVal value As Uri)
        SetValue(AquariumGraphicProperty, value)
    End Set
End Property

Là encore, par convention, le nom de la propriété wrapper doit être identique au nom choisi et donné comme premier paramètre de l’appel Register qui a inscrit la propriété. Si votre propriété ne respecte pas la convention, toutes les utilisations possibles ne sont pas nécessairement désactivées, mais vous rencontrerez plusieurs problèmes :

  • Certains aspects des styles et modèles ne fonctionneront pas.

  • La plupart des outils et concepteurs doivent s’appuyer sur les conventions d’affectation de noms pour sérialiser correctement le code XAML ou fournir une assistance d’environnement de concepteur au niveau de la propriété.

  • L’implémentation actuelle du chargeur XAML WPF contourne entièrement les wrappers et s’appuie sur la convention d’affectation de noms lors du traitement des valeurs d’attribut. Pour plus d’informations, consultez Propriétés de dépendance et chargement XAML.

Métadonnées de propriété pour une nouvelle propriété de dépendance

Quand vous inscrivez une propriété de dépendance, l’inscription dans le système de propriétés crée un objet de métadonnées qui stocke les caractéristiques de la propriété. La plupart de ces caractéristiques ont des valeurs par défaut définies si la propriété est inscrite avec les signatures simples de Register. D’autres signatures vous permettent de spécifier les métadonnées souhaitées lors de Register l’inscription de la propriété. Les métadonnées les plus courantes pour les propriétés de dépendance consistent à leur attribuer une valeur par défaut qui est appliquée sur les nouvelles instances qui utilisent la propriété.

Si vous créez une propriété de dépendance qui existe sur une classe dérivée, FrameworkElementvous pouvez utiliser la classe FrameworkPropertyMetadata de métadonnées plus spécialisée plutôt que la classe de base PropertyMetadata . Le constructeur de la FrameworkPropertyMetadata classe a plusieurs signatures dans lesquelles vous pouvez spécifier différentes caractéristiques de métadonnées en combinaison. Si vous souhaitez spécifier la valeur par défaut uniquement, utilisez la signature qui prend un seul paramètre de type Object. Transmettez ce paramètre d’objet comme valeur par défaut spécifique au type pour votre propriété (la valeur par défaut fournie doit être le type que vous avez fourni comme propertyType paramètre dans l’appel Register ).

Pour FrameworkPropertyMetadatacela, vous pouvez également spécifier des indicateurs d’option de métadonnées pour votre propriété. Ces indicateurs sont convertis en propriétés discrètes dans les métadonnées de propriété après l’inscription, et sont utilisés pour spécifier certaines conditions à d’autres processus, tels que le moteur de disposition.

Définition des indicateurs de métadonnées appropriés

  • Si votre propriété (ou modification de sa valeur) affecte l’interface utilisateur et affecte en particulier la façon dont le système de disposition doit dimensionner ou afficher votre élément dans une page, définissez un ou plusieurs des indicateurs suivants : AffectsMeasure, AffectsArrange, AffectsRender.

    • AffectsMeasure indique qu’une modification de cette propriété nécessite une modification du rendu de l’interface utilisateur où l’objet conteneur peut nécessiter plus ou moins d’espace dans le parent. Par exemple, cet indicateur doit être défini pour une propriété « Width ».

    • AffectsArrange indique qu’une modification de cette propriété nécessite une modification du rendu de l’interface utilisateur qui ne nécessite généralement pas de modification de l’espace dédié, mais indique que le positionnement dans l’espace a changé. Par exemple, cet indicateur doit être défini pour une propriété « Alignment ».

    • AffectsRender indique que d’autres modifications se sont produites qui n’affecteront pas la disposition et la mesure, mais nécessitent un autre rendu. Un exemple serait une propriété qui change une couleur d’un élément existant, telle que « Background ».

    • Ces indicateurs sont souvent utilisés comme protocole dans les métadonnées pour vos propres implémentations de substitution de rappels de disposition ou de système de propriétés. Par exemple, vous pouvez avoir un OnPropertyChanged rappel qui appellera InvalidateArrange si une propriété de l’instance signale une modification de valeur et a AffectsArrange comme true dans ses métadonnées.

  • Certaines propriétés peuvent affecter les caractéristiques de rendu de l’élément parent conteneur, au-delà des modifications de taille requise mentionnées ci-dessus. Par exemple, la MinOrphanLines propriété utilisée dans le modèle de document de flux, où les modifications apportées à cette propriété peuvent modifier le rendu global du document de flux qui contient le paragraphe. Utilisez ou AffectsParentMeasure identifiez AffectsParentArrange des cas similaires dans vos propres propriétés.

  • Par défaut, les propriétés de dépendance prennent en charge la liaison de données. Vous pouvez désactiver délibérément la liaison de données, dans les cas où il n’existe aucun scénario réaliste pour elle, ou quand les performances de liaison de données pour un objet volumineux sont reconnues comme étant un problème.

  • Par défaut, la liaison Mode de données pour les propriétés de dépendance est OneWaypar défaut . Vous pouvez toujours modifier la liaison par TwoWay instance de liaison. Pour plus d’informations, consultez Spécifier la direction de la liaison. Toutefois, comme auteur de la propriété de dépendance, vous pouvez choisir d’utiliser TwoWay le mode de liaison par défaut. Un exemple de propriété de dépendance existante est MenuItem.IsSubmenuOpen: le scénario de cette propriété est que la IsSubmenuOpen logique de définition et la composition d’interaction MenuItem avec le style de thème par défaut. La IsSubmenuOpen logique de propriété utilise la liaison de données en mode natif pour maintenir l’état de la propriété conformément à d’autres propriétés d’état et appels de méthode. Un autre exemple de propriété qui lie TwoWay par défaut est TextBox.Text.

  • Vous pouvez également activer l’héritage de propriété dans une propriété de dépendance personnalisée en définissant l’indicateur Inherits . L’héritage de propriété est utile pour un scénario où des éléments parents et des éléments enfants ont une propriété en commun, et où il est logique que les éléments enfants aient la même valeur de propriété que le parent. Un exemple de propriété pouvant hériter est , qui est DataContextutilisé pour les opérations de liaison afin d’activer le scénario principal-détail important pour la présentation de données. En faisant DataContext hériter tous les éléments enfants héritent également de ce contexte de données. En raison de l’héritage de valeur de propriété, vous pouvez spécifier un contexte de données à la racine de la page ou de l’application, et vous n’avez pas besoin de le respécifier pour les liaisons dans tous les éléments enfants possibles. DataContext est également un bon exemple pour illustrer que l’héritage remplace la valeur par défaut, mais qu’il peut toujours être défini localement sur n’importe quel élément enfant particulier ; pour plus d’informations, consultez Utiliser le modèle maître-détail avec des données hiérarchiques. L’héritage de valeur de propriété peut avoir un impact sur les performances, et doit donc être utilisé avec modération. Pour plus d’informations, consultez Héritage de la valeur de propriété.

  • Définissez l’indicateur Journal pour indiquer si votre propriété de dépendance doit être détectée ou utilisée par les services de journalisation de navigation. Par exemple, la propriété est la SelectedIndex propriété ; tout élément sélectionné dans un contrôle de sélection doit être conservé lorsque l’historique de journalisation est parcouru.

Propriétés de dépendance en lecture seule

Vous pouvez définir une propriété de dépendance en lecture seule. Toutefois, les scénarios pour lesquels vous pourriez définir la propriété en lecture seule sont quelque peu différents, tout comme la procédure pour les inscrire auprès du système de propriétés et exposer l’identificateur. Pour plus d’informations, consultez Propriétés de dépendance en lecture seule.

Propriétés de dépendance de type collection

Pour les propriétés de dépendance de type collection, vous devrez prendre en compte certains autres aspects relatifs à l’implémentation. Pour plus d’informations, consultez Propriétés de dépendance de type collection.

Considérations relatives à la sécurité des propriétés de dépendance

Les propriétés de dépendance doivent être déclarées en tant que propriétés publiques. Les champs d’identificateur de propriété de dépendance doivent être déclarés en tant que champs statiques publics. Même si vous tentez de déclarer d’autres niveaux d’accès (tels que protégés), une propriété de dépendance est toujours accessible via l’identificateur en combinaison avec les API du système de propriétés. Même un champ d’identificateur protégé est potentiellement accessible en raison des API de création de rapports de métadonnées ou de détermination de valeur qui font partie du système de propriétés, telles que LocalValueEnumerator. Pour plus d’informations, consultez Sécurité des propriétés de dépendance.

Propriétés de dépendance et constructeurs de classe

Il existe un principe fondamental dans la programmation de code managé (souvent appliqué par des outils d’analyse de code tels que FxCop) selon lequel les constructeurs de classe ne doivent pas appeler de méthodes virtuelles. Cela est dû au fait que les constructeurs peuvent être appelés en tant qu’initialisation de base d’un constructeur de classe dérivée, et l’entrée dans la méthode virtuelle par l’intermédiaire du constructeur peut se produire à un état d’initialisation incomplet de l’instance d’objet en cours de construction. Lorsque vous dérivez de n’importe quelle classe qui dérive déjà de DependencyObject, vous devez savoir que le système de propriétés lui-même appelle et expose les méthodes virtuelles en interne. Ces méthodes virtuelles font partie des services de système de propriétés WPF. La substitution des méthodes permet aux classes dérivées de participer à la détermination de valeur. Pour éviter les problèmes potentiels liés à l’initialisation au moment de l’exécution, vous ne devez pas définir de valeur de propriété de dépendance dans des constructeurs de classes, sauf si vous respectez un modèle de constructeur très spécifique. Pour plus d’informations, consultez Modèles de constructeur sécurisé pour DependencyObjects.

Voir aussi