Partager via


Priorité de la valeur d’une propriété de dépendance (WPF .NET)

Les fonctionnements du système de propriétés Windows Presentation Foundation (WPF) affectent la valeur d’une propriété de dépendance. Cet article explique comment la priorité des différentes entrées basées sur des propriétés dans le système de propriétés WPF détermine la valeur effective d’une propriété de dépendance.

Conditions préalables

L’article suppose une connaissance de base des propriétés de dépendance et que vous avez lu vue d’ensemble des propriétés de dépendance. Pour suivre les exemples de cet article, il vous aide à connaître le langage XAML (Extensible Application Markup Language) et à savoir comment écrire des applications WPF.

Système de propriétés WPF

Le système de propriétés WPF utilise divers facteurs pour déterminer la valeur des propriétés de dépendance, telles que la validation des propriétés en temps réel, la liaison tardive et les notifications de modification de propriété pour les propriétés associées. Bien que l’ordre et la logique utilisés pour déterminer les valeurs de propriété de dépendance soient complexes, l’apprentissage peut vous aider à éviter les paramètres de propriété inutiles et à déterminer également pourquoi une tentative de définition d’une propriété de dépendance n’a pas entraîné la valeur attendue.

Propriétés de dépendance définies à plusieurs emplacements

L’exemple XAML suivant montre comment trois opérations « set » différentes sur la propriété Background du bouton peuvent influencer sa valeur.

<StackPanel>
    <StackPanel.Resources>
        <ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
            <Border Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" 
                    BorderBrush="{TemplateBinding BorderBrush}">
                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
            </Border>
        </ControlTemplate>
    </StackPanel.Resources>

    <Button Template="{StaticResource ButtonTemplate}" Background="Red">
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <Setter Property="Background" Value="Blue"/>
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="Yellow" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
        Which color do you expect?
    </Button>
</StackPanel>

Dans l’exemple, la propriété Background est définie localement sur Red. Toutefois, le style implicite déclaré dans l’étendue du bouton tente de définir la propriété Background sur Blue. Et lorsque la souris passe sur le bouton, le déclencheur dans le style implicite tente de définir la propriété Background à Yellow. À l’exception des contraintes et de l’animation, une valeur de propriété définie localement a la priorité la plus élevée, de sorte que le bouton sera rouge, même au survol de la souris. Toutefois, si vous supprimez la valeur définie localement du bouton, elle obtient sa valeur Background du style. Dans un style, les déclencheurs sont prioritaires, ainsi le bouton sera jaune au passage de la souris et bleu sinon. L’exemple remplace le ControlTemplate par défaut du bouton, car le modèle par défaut a une valeur Background de pointage de souris codée en dur.

Liste de priorité des propriétés de dépendance

La liste suivante est l’ordre de priorité définitif que le système de propriétés utilise lors de l’affectation de valeurs d’exécution aux propriétés de dépendance. La priorité la plus élevée est répertoriée en premier.

  1. Forçage du système de propriétés. Pour plus d'informations sur la coercition, consultez les sections Coercition et animations.

  2. Animations actives ou animations avec un comportement de blocage. Pour avoir un effet pratique, une valeur d’animation doit avoir la priorité sur la valeur de base (non animée), même si la valeur de base a été définie localement. Pour plus d’informations, consultez Forçage et animations.

  3. Valeurs locales. Vous pouvez définir une valeur locale via une propriété « wrapper », qui équivaut à définir un attribut ou un élément de propriété en XAML, ou par un appel à l’API SetValue à l’aide d’une propriété d’une instance spécifique. Une valeur locale définie par le biais d’une liaison ou d’une ressource aura la même priorité qu’une valeur définie directement.

  4. Valeurs des propriétés du modèle TemplatedParent. Un élément a un TemplatedParent s’il a été créé par un modèle (ControlTemplate ou DataTemplate). Pour plus d’informations, consultez TemplatedParent. Dans le modèle spécifié par l'TemplatedParent, l’ordre de priorité est :

    1. Déclencheurs

    2. Jeux de propriétés, généralement par le biais d’attributs XAML.

  5. Styles implicites. S’applique uniquement à la propriété Style. La valeur Style est n’importe quelle ressource de style avec une valeur TargetType qui correspond au type d’élément. La ressource de style doit exister dans la page ou l’application. La recherche d’une ressource de style implicite ne s’étend pas aux ressources de styles dans Thèmes.

  6. Déclencheurs de style. Un déclencheur de style est un déclencheur dans un style explicite ou implicite. Le style doit exister dans la page ou l’application. Les déclencheurs dans les styles par défaut ont une priorité inférieure.

  7. Déclencheurs de modèles. Un déclencheur de modèle correspond à un déclencheur provenant soit d'un modèle appliqué directement, soit d'un modèle inclus dans un style. Le style doit exister dans la page ou l’application.

  8. Valeur setter de style. Une valeur setter de style est une valeur appliquée par un Setter dans un style. Le style doit exister dans la page ou l’application.

  9. styles par défaut, également appelés styles de thème . Pour plus d’informations, consultez les styles par défaut (Thème). Dans un style par défaut, l’ordre de priorité est :

    1. Déclencheurs actifs.

    2. Setters.

  10. Héritage. Certaines propriétés de dépendance d’un élément enfant héritent de leur valeur de l’élément parent. Par conséquent, il n’est peut-être pas nécessaire de définir des valeurs de propriété sur chaque élément dans l’application. Pour plus d’informations, consultez Héritage de valeur de propriété.

  11. valeur par défaut des métadonnées de propriété de dépendance Une propriété de dépendance peut avoir une valeur par défaut définie pendant l’inscription du système de propriétés de cette propriété. Les classes dérivées qui héritent d’une propriété de dépendance peuvent remplacer les métadonnées de propriété de dépendance (y compris la valeur par défaut) par type. Pour plus d’informations, consultez Métadonnées de propriété de dépendance. Pour une propriété héritée, la valeur par défaut d’un élément parent est prioritaire sur la valeur par défaut d’un élément enfant. Par conséquent, si aucune propriété héritée n’est définie, la valeur par défaut de la racine ou du parent est utilisée au lieu de la valeur par défaut de l’élément enfant.

TemplatedParent

TemplatedParent priorité ne s’applique pas aux propriétés des éléments déclarés directement dans le balisage d’application standard. Le concept TemplatedParent existe uniquement pour les éléments enfants d’une arborescence visuelle qui entrent en existence via l’application d’un modèle. Lorsque le système de propriétés recherche le modèle spécifié par l'TemplatedParent pour les valeurs de propriété d’un élément, il recherche le modèle qui a créé l’élément. Les valeurs de propriété du modèle TemplatedParent agissent généralement comme si elles étaient définies localement sur l’élément, mais avec une priorité moindre que les valeurs locales réelles, car les modèles sont potentiellement partagés. Pour plus d’informations, consultez TemplatedParent.

Propriété Style

Le même ordre de priorité s’applique à toutes les propriétés de dépendance, à l’exception de la propriété Style. La propriété Style est unique car elle ne peut pas être stylisée elle-même. Il n'est pas recommandé de forcer ou d'animer la propriété Style (et l'animation de la propriété Style nécessite une classe d'animation personnalisée). Par conséquent, tous les éléments de précédence ne s’appliquent pas. Il existe seulement trois façons de définir la propriété Style :

  • Style explicite. La propriété Style d’un élément est définie directement. La valeur de propriété Style agit comme s’il s’agissait d’une valeur locale et a la même priorité que l’élément 3 dans la liste de priorité . Dans la plupart des scénarios, les styles explicites ne sont pas définis inline et sont plutôt référencés explicitement en tant que ressource, par exemple Style="{StaticResource myResourceKey}".

  • Style implicite. La propriété Style d’un élément n’est pas définie directement. Au lieu de cela, un style est appliqué lorsqu’il existe à un niveau dans la page ou l’application, et a une clé de ressource qui correspond au type d’élément auquel le style s’applique, par exemple <Style TargetType="x:Type Button">. Le type doit correspondre exactement, par exemple <Style TargetType="x:Type Button"> n’est pas appliqué au type MyButton même si MyButton est dérivé de Button. La valeur de propriété Style a la même priorité que l’élément 5 dans la liste de priorité . Une valeur de style implicite peut être détectée en appelant la méthode DependencyPropertyHelper.GetValueSource, en passant la propriété Style et en vérifiant ImplicitStyleReference dans les résultats.

  • style par défaut, également appelé style de thème. La propriété Style d’un élément n’est pas définie directement. Au lieu de cela, il provient de l'évaluation en temps d'exécution du thème par le moteur de présentation WPF. Avant l’exécution, la valeur de la propriété Style est null. La valeur de propriété Style a la même priorité que l’élément 9 dans la liste de précédence .

Styles par défaut (thème)

Chaque contrôle fourni avec WPF a un style par défaut qui peut varier selon le thème, c’est pourquoi le style par défaut est parfois appelé style de thème.

La ControlTemplate est un élément important dans le style par défaut d’un contrôle. ControlTemplate est une valeur de définition pour la propriété Template du style. Si les styles par défaut ne contenaient pas de modèle, un contrôle sans modèle personnalisé dans le cadre d’un style personnalisé n’aurait aucune apparence visuelle. Non seulement un modèle définit l’apparence visuelle d’un contrôle, il définit également les connexions entre les propriétés de l’arborescence visuelle du modèle et la classe de contrôle correspondante. Chaque contrôle expose un ensemble de propriétés qui peuvent influencer l’apparence visuelle du contrôle sans remplacer le modèle. Par exemple, considérez l’apparence visuelle par défaut d’un contrôle Thumb, qui est un composant ScrollBar.

Un contrôle Thumb a certaines propriétés personnalisables. Le modèle par défaut d’un contrôle Thumb crée une structure de base ou une arborescence visuelle, avec plusieurs composants Border imbriqués pour créer une apparence biseautée. Dans le modèle, les propriétés destinées à être personnalisables par la classe Thumb sont exposées via TemplateBinding. Le modèle par défaut du contrôle Thumb a différentes propriétés de bordure qui partagent une liaison de modèle avec des propriétés telles que Background ou BorderThickness. Toutefois, lorsque les valeurs des propriétés ou des dispositions visuelles sont codées en dur dans le modèle ou qui sont liées à des valeurs provenant directement du thème, vous ne pouvez modifier ces valeurs qu’en remplaçant l’intégralité du modèle. En règle générale, si une propriété provient d'un parent basé sur un modèle et n'est pas exposée par un TemplateBinding, la valeur de cette propriété ne peut pas être modifiée par les styles, car il n'existe aucun moyen pratique d'y accéder. Toutefois, cette propriété peut toujours être influencée par l’héritage de valeur de propriété dans le modèle appliqué, ou par une valeur par défaut.

Les styles par défaut spécifient une TargetType dans leurs définitions. L’évaluation du thème au moment de l'exécution associe la TargetType d’un style par défaut à la propriété DefaultStyleKey d’un contrôle. En revanche, le comportement de recherche pour les styles implicites utilise le type réel du contrôle. La valeur de DefaultStyleKey est héritée par des classes dérivées, de sorte que les éléments dérivés qui n’ont pas de style associé obtiennent une apparence visuelle par défaut. Par exemple, si vous dérivez MyButton de Button, MyButton héritera du modèle par défaut de Button. Les classes dérivées peuvent remplacer la valeur par défaut de DefaultStyleKey dans les métadonnées de propriété de dépendance. Par conséquent, si vous souhaitez une représentation visuelle différente pour MyButton, vous pouvez remplacer les métadonnées de propriété de dépendance pour DefaultStyleKey sur MyButton, puis définir le style par défaut approprié, y compris un modèle, que vous allez empaqueter avec votre contrôle MyButton. Pour plus d’informations, consultez Vue d’ensemble de la création de contrôles.

Ressource dynamique

Les opérations de liaison et les références de ressources dynamiques ont la priorité de l’emplacement auquel elles sont définies. Par exemple, une ressource dynamique appliquée à une valeur locale a la même priorité que l’élément 3 de la liste de précédence . Comme autre exemple, une liaison de ressource dynamique appliquée à un setter de propriétés dans un style par défaut a la même priorité que l’élément 9 dans la liste de précédence . Étant donné que les références et la liaison de ressources dynamiques doivent obtenir des valeurs à partir de l’état d’exécution de l’application, le processus de détermination de la priorité des valeurs de propriété pour toute propriété donnée s’étend au runtime.

Les références de ressources dynamiques ne font pas partie techniquement du système de propriétés et ont leur propre ordre de recherche qui interagit avec la liste de priorité . Essentiellement, la priorité des références de ressources dynamiques est l’élément à la racine de page, à l’application, au thème, puis au système. Pour plus d'informations, consultez XAML Resources.

Même si les opérations de liaison et les références de ressources dynamiques ont la priorité de l’emplacement auquel elles sont définies, la valeur est reportée. L’une des conséquences est que si vous définissez une ressource dynamique ou une liaison à une valeur locale, toute modification apportée à la valeur locale remplace entièrement la ressource ou la liaison dynamique. Même si vous appelez la méthode ClearValue pour effacer la valeur définie localement, la ressource dynamique ou la liaison ne sera pas restaurée. En fait, si vous appelez ClearValue sur une propriété qui a une ressource dynamique ou une liaison (sans valeur locale littérale), la ressource dynamique ou la liaison sera effacée.

DéfinirValeurActuelle

La méthode SetCurrentValue est une autre façon de définir une propriété, mais elle n’est pas dans la liste de précédence . SetCurrentValue vous permet de modifier la valeur d’une propriété sans remplacer la source d’une valeur précédente. Par exemple, si une propriété est définie par un déclencheur, puis que vous affectez une autre valeur à l’aide de SetCurrentValue, l’action de déclencheur suivante rétablit la propriété sur la valeur du déclencheur. Vous pouvez utiliser SetCurrentValue chaque fois que vous souhaitez définir une valeur de propriété sans donner à cette valeur le niveau de précédence d’une valeur locale. De même, vous pouvez utiliser SetCurrentValue pour modifier la valeur d’une propriété sans remplacer une liaison.

Contrainte et animation

Le forçage et l’animation agissent tous deux sur une valeur de base. La valeur de base est la valeur de propriété de dépendance avec la priorité la plus élevée, déterminée par l’évaluation vers le haut par le biais de la liste de priorité jusqu’à ce que l’élément 2 soit atteint.

Si une animation ne spécifie pas à la fois From et To valeurs de propriété pour certains comportements, ou si l’animation revient délibérément à la valeur de base une fois terminée, la valeur de base peut affecter la valeur animée. Pour voir cela en pratique, exécutez l’exemple d'application des Valeurs cibles. Dans l’exemple, pour la hauteur du rectangle, essayez de définir les valeurs locales initiales qui diffèrent de n’importe quelle valeur From. Les exemples d’animations commencent immédiatement à l’aide de la valeur From au lieu de la valeur de base. En spécifiant Stop comme FillBehavior, une fois l'animation terminée, une valeur de propriété sera réinitialisée à sa valeur de base. La précédence normale est utilisée pour la détermination de la valeur de base après la fin d’une animation.

Plusieurs animations peuvent être appliquées à une propriété unique, chaque animation ayant une priorité différente. Au lieu d’appliquer l’animation avec la priorité la plus élevée, le moteur de présentation WPF peut compositer les valeurs d’animation, selon la façon dont les animations ont été définies et le type de valeurs animées. Pour plus d’informations, consultez Vue d’ensemble de l’animation.

La coercition est en tête de la liste de précédence . Même une animation en cours d’exécution est soumise à la contrainte de valeur. Certaines propriétés de dépendance existantes dans WPF ont un forçage intégré. Pour les propriétés de dépendance personnalisées, vous pouvez définir un comportement de contrainte en écrivant un CoerceValueCallback que vous transmettez dans le cadre des métadonnées lorsque vous créez une propriété. Vous pouvez également remplacer le comportement de contrainte des propriétés existantes en remplaçant les métadonnées de cette propriété dans une classe dérivée. Le forçage interagit avec la valeur de base de telle sorte que les contraintes sur la contrainte soient appliquées au moment où elles existent, mais que la valeur de base est toujours conservée. Par conséquent, si les contraintes sont ultérieurement levées, la coercition retournera la valeur la plus proche possible de la valeur de base, et potentiellement l’influence de la coercition sur une propriété cessera dès que toutes les contraintes sont levées. Pour plus d’informations sur le comportement de forçage, consultez Validation et rappels de propriétés de dépendance.

Comportements de déclencheur

Les contrôles définissent souvent des comportements de déclencheur dans leur style par défaut . La définition des propriétés locales sur les contrôles peut potentiellement entrer en conflit avec ces déclencheurs, ce qui empêche les déclencheurs de répondre (visuellement ou comportementalement) aux événements pilotés par l’utilisateur. Une utilisation courante d’un déclencheur de propriété consiste à contrôler les propriétés d’état, telles que IsSelected ou IsEnabled. Par exemple, par défaut, lorsqu’un Button est désactivé, un déclencheur de style de thème (IsEnabled est false) définit la valeur Foreground pour que le Button apparaisse grisé. Si vous avez défini une valeur de Foreground locale, la valeur de propriété locale de priorité la plus élevée remplace le style de thème Foreground valeur, même lorsque la Button est désactivée. Lorsque vous définissez des valeurs de propriété qui remplacent les comportements de déclencheur au niveau du thème pour un contrôle, veillez à ne pas interférer avec l’expérience utilisateur prévue pour ce contrôle.

ClearValue

La méthode ClearValue efface toute valeur appliquée localement d’une propriété de dépendance pour un élément. Toutefois, l’appel de ClearValue ne garantit pas que la valeur par défaut établie dans les métadonnées pendant l’inscription de propriétés est la nouvelle valeur effective. Tous les autres participants de la liste de priorité sont toujours actifs, et seule la valeur définie localement est supprimée. Par exemple, si vous appelez ClearValue sur une propriété qui a un style de thème, la valeur de style de thème est appliquée comme nouvelle valeur, plutôt que la valeur par défaut basée sur les métadonnées. Si vous souhaitez définir une valeur de propriété sur la valeur par défaut des métadonnées inscrites, obtenez la valeur de métadonnées par défaut en interrogeant les métadonnées de propriété de dépendance et définissez localement la valeur de la propriété avec un appel à SetValue.

Voir aussi