Partager via


Vue d’ensemble de la liaison de données

La liaison de données dans Windows Presentation Foundation (WPF) offre un moyen simple et cohérent pour les applications de présenter et d’interagir avec les données. Les éléments peuvent être liés à des données provenant de différents types de sources de données sous la forme d’objets .NET et XML. Tout ContentControl tel que Button et tout ItemsControl, tels que ListBox et ListView, ont des fonctionnalités intégrées permettant un style flexible des éléments de données individuels ou des ensembles d'éléments de données. Les vues de tri, de filtre et de groupe peuvent être générées en plus des données.

La liaison de données dans WPF présente plusieurs avantages par rapport aux modèles traditionnels, notamment la prise en charge inhérente de la liaison de données par un large éventail de propriétés, une représentation flexible de l’interface utilisateur des données et une séparation propre de la logique métier de l’interface utilisateur.

Cet article décrit d’abord les concepts fondamentaux de la liaison de données WPF, puis traite de l’utilisation de la classe et d’autres fonctionnalités de la Binding liaison de données.

Qu’est-ce que la liaison de données ?

La liaison de données est le processus qui établit une connexion entre l’interface utilisateur de l’application et les données qu’elle affiche. Si la liaison a les paramètres corrects et que les données fournissent les notifications appropriées, lorsque les données changent de valeur, les éléments liés aux données reflètent automatiquement les modifications. La liaison de données peut également signifier que si une représentation externe des données dans un élément change, les données sous-jacentes peuvent être automatiquement mises à jour pour refléter la modification. Par exemple, si l’utilisateur modifie la valeur dans un TextBox élément, la valeur de données sous-jacente est automatiquement mise à jour pour refléter cette modification.

Une utilisation classique de la liaison de données consiste à placer des données de configuration de serveur ou locales dans des formulaires ou d’autres contrôles d’interface utilisateur. Dans WPF, ce concept est développé pour inclure la liaison d’un large éventail de propriétés à différents types de sources de données. Dans WPF, les propriétés de dépendance d’éléments peuvent être liées à des objets .NET (y compris des objets ou des objets ADO.NET associés aux services Web et aux propriétés web) et aux données XML.

Concepts de liaison de données de base

Quel que soit l’élément que vous liez et la nature de votre source de données, chaque liaison suit toujours le modèle illustré par la figure suivante.

Diagramme montrant le modèle de liaison de données de base.

Comme le montre la figure, la liaison de données est essentiellement le pont entre votre cible de liaison et votre source de liaison. La figure illustre les concepts de liaison de données WPF fondamentaux suivants :

  • En règle générale, chaque liaison comporte quatre composants :

    • Objet cible de liaison.
    • Propriété cible.
    • Source de liaison.
    • Chemin d’accès à la valeur dans la source de liaison à utiliser.

    Par exemple, si vous liez le contenu d'un TextBox à la propriété Employee.Name, vous devez configurer votre liaison comme dans le tableau suivant :

    Réglage Valeur
    Cible TextBox
    Propriété cible Text
    Objet Source Employee
    Chemin de la valeur de l’objet source Name
  • La propriété cible doit être une propriété de dépendance.

    La plupart des propriétés sont des propriétés de dépendance et la plupart UIElement des propriétés de dépendance, à l’exception des propriétés en lecture seule, prennent en charge la liaison de données par défaut. Seuls les types dérivés de DependencyObject peuvent définir des propriétés de dépendance. Tous les UIElement types dérivent de DependencyObject.

  • Les sources de liaison ne sont pas limitées aux objets .NET personnalisés.

    Bien qu’il ne soit pas indiqué dans la figure, il convient de noter que l’objet source de liaison n’est pas limité à être un objet .NET personnalisé. La liaison de données WPF prend en charge les données sous la forme d’objets .NET, XML et même d’objets d’élément XAML. Pour fournir des exemples, votre source de liaison peut être un UIElement, un objet de liste, un objet ADO.NET ou des services web, ou un xmlNode qui contient vos données XML. Pour plus d’informations, consultez Vue d’ensemble des sources de liaison.

Il est important de se rappeler que lorsque vous établissez une liaison, vous liez une cible de liaison à une source de liaison. Par exemple, si vous affichez des données XML sous-jacentes dans un ListBox en utilisant la liaison de données, vous liez votre ListBox aux données XML.

Pour établir une liaison, vous utilisez l’objet Binding . Le reste de cet article traite de nombreux concepts associés à et certaines des propriétés et de l’utilisation de l’objet Binding .

Contexte de données

Lorsque la liaison de données est déclarée sur des éléments XAML, ceux-ci résolvent la liaison de données en examinant leur propriété immédiate DataContext. Le contexte de données est généralement l’objet source de liaison pour l’évaluation du chemin de la valeur source de liaison. Vous pouvez remplacer ce comportement dans la liaison et définir une valeur d’objet source de liaison spécifique. Si la DataContext propriété de l’objet hébergeant la liaison n’est pas définie, la propriété de DataContext l’élément parent est vérifiée, et ainsi de suite, jusqu’à la racine de l’arborescence d’objets XAML. En bref, le contexte de données utilisé pour résoudre la liaison est hérité du parent, sauf s’il est défini explicitement sur l’objet.

Les liaisons peuvent être configurées pour se résoudre avec un objet spécifique, au lieu d'utiliser le contexte de données pour la résolution de la liaison. La spécification d’un objet source directement est utilisée lorsque, par exemple, vous liez la couleur de premier plan d’un objet à la couleur d’arrière-plan d’un autre objet. Le contexte de données n’est pas nécessaire, car la liaison est résolue entre ces deux objets. À l’inverse, les liaisons qui ne sont pas liées à des objets sources spécifiques utilisent la résolution de contexte de données.

Lorsque la DataContext propriété change, toutes les liaisons susceptibles d’être affectées par le contexte de données sont réévaluées.

Direction du flux de données

Comme indiqué par la flèche de la figure précédente, le flux de données d’une liaison peut passer de la cible de liaison à la source de liaison (par exemple, la valeur source change lorsqu’un utilisateur modifie la valeur d’un TextBox) et/ou de la source de liaison à la cible de liaison (par exemple, votre TextBox contenu est mis à jour avec des modifications dans la source de liaison) si la source de liaison fournit les notifications appropriées.

Vous souhaiterez peut-être que votre application permet aux utilisateurs de modifier les données et de les propager à l’objet source. Vous ne souhaiterez peut-être pas autoriser les utilisateurs à mettre à jour les données sources. Vous pouvez contrôler le flux de données en définissant le Binding.Mode.

Cette figure illustre les différents types de flux de données :

Flux de données de liaison de données

  • OneWay la liaison entraîne la propagation automatique des modifications apportées à la propriété source, mais les modifications apportées à la propriété cible ne sont pas propagées à la propriété source. Ce type de liaison est approprié si le contrôle lié est implicitement en lecture seule. Par exemple, vous pouvez lier à une source telle qu’un ticker de stock, ou peut-être que votre propriété cible n’a aucune interface de contrôle fournie pour apporter des modifications, telles qu’une couleur d’arrière-plan liée aux données d’une table. S’il n’est pas nécessaire de surveiller les modifications de la propriété cible, l’utilisation du OneWay mode de liaison évite la surcharge du TwoWay mode de liaison.

  • TwoWay la liaison entraîne la modification de la propriété source ou de la propriété cible pour mettre automatiquement à jour l’autre. Ce type de liaison est approprié pour les formulaires modifiables ou d’autres scénarios d’interface utilisateur entièrement interactifs. La plupart des propriétés utilisent par défaut la liaisonOneWay, mais certaines propriétés de dépendance - généralement celles des contrôles modifiables par l'utilisateur, telles que la TextBox.Text et CheckBox.IsChecked - utilisent par défaut la liaison TwoWay.

    Une méthode programmatique pour déterminer si une propriété de dépendance lie unidirectionnel ou bidirectionnel par défaut consiste à obtenir les métadonnées de propriété avec DependencyProperty.GetMetadata. Le type de retour de cette méthode est PropertyMetadata, qui ne contient aucune métadonnées sur la liaison. Toutefois, si ce type peut être converti en dérivé FrameworkPropertyMetadata, la valeur booléenne de la FrameworkPropertyMetadata.BindsTwoWayByDefault propriété peut être vérifiée. L’exemple de code suivant illustre l’obtention des métadonnées pour la TextBox.Text propriété :

    public static void PrintMetadata()
    {
        // Get the metadata for the property
        PropertyMetadata metadata = TextBox.TextProperty.GetMetadata(typeof(TextBox));
    
        // Check if metadata type is FrameworkPropertyMetadata
        if (metadata is FrameworkPropertyMetadata frameworkMetadata)
        {
            System.Diagnostics.Debug.WriteLine($"TextBox.Text property metadata:");
            System.Diagnostics.Debug.WriteLine($"  BindsTwoWayByDefault: {frameworkMetadata.BindsTwoWayByDefault}");
            System.Diagnostics.Debug.WriteLine($"  IsDataBindingAllowed: {frameworkMetadata.IsDataBindingAllowed}");
            System.Diagnostics.Debug.WriteLine($"        AffectsArrange: {frameworkMetadata.AffectsArrange}");
            System.Diagnostics.Debug.WriteLine($"        AffectsMeasure: {frameworkMetadata.AffectsMeasure}");
            System.Diagnostics.Debug.WriteLine($"         AffectsRender: {frameworkMetadata.AffectsRender}");
            System.Diagnostics.Debug.WriteLine($"              Inherits: {frameworkMetadata.Inherits}");
        }
    
        /*  Displays:
         *  
         *  TextBox.Text property metadata:
         *    BindsTwoWayByDefault: True
         *    IsDataBindingAllowed: True
         *          AffectsArrange: False
         *          AffectsMeasure: False
         *           AffectsRender: False
         *                Inherits: False
        */
    }
    
    Public Shared Sub PrintMetadata()
    
        Dim metadata As PropertyMetadata = TextBox.TextProperty.GetMetadata(GetType(TextBox))
        Dim frameworkMetadata As FrameworkPropertyMetadata = TryCast(metadata, FrameworkPropertyMetadata)
    
        If frameworkMetadata IsNot Nothing Then
    
            System.Diagnostics.Debug.WriteLine($"TextBox.Text property metadata:")
            System.Diagnostics.Debug.WriteLine($"  BindsTwoWayByDefault: {frameworkMetadata.BindsTwoWayByDefault}")
            System.Diagnostics.Debug.WriteLine($"  IsDataBindingAllowed: {frameworkMetadata.IsDataBindingAllowed}")
            System.Diagnostics.Debug.WriteLine($"        AffectsArrange: {frameworkMetadata.AffectsArrange}")
            System.Diagnostics.Debug.WriteLine($"        AffectsMeasure: {frameworkMetadata.AffectsMeasure}")
            System.Diagnostics.Debug.WriteLine($"         AffectsRender: {frameworkMetadata.AffectsRender}")
            System.Diagnostics.Debug.WriteLine($"              Inherits: {frameworkMetadata.Inherits}")
    
            '  Displays:
            '
            '  TextBox.Text property metadata:
            '    BindsTwoWayByDefault: True
            '    IsDataBindingAllowed: True
            '          AffectsArrange: False
            '          AffectsMeasure: False
            '           AffectsRender: False
            '                Inherits: False
        End If
    
    
    End Sub
    
  • OneWayToSource est l’inverse de OneWay la liaison ; il met à jour la propriété source lorsque la propriété cible change. Un exemple de scénario est si vous devez uniquement réévaluer la valeur source de l’interface utilisateur.

  • La liaison OneTime n'est pas illustrée dans la figure, ce qui provoque l'initialisation de la propriété cible par la propriété source, mais ne propage pas les modifications suivantes. Si le contexte de données change ou si l’objet dans le contexte de données change, la modification n’est pas reflétée dans la propriété cible. Ce type de liaison est approprié si un instantané de l’état actuel est approprié ou si les données sont vraiment statiques. Ce type de liaison est également utile si vous souhaitez initialiser votre propriété cible avec une valeur à partir d’une propriété source et que le contexte de données n’est pas connu à l’avance. Ce mode est essentiellement une forme OneWay de liaison plus simple qui offre de meilleures performances dans les cas où la valeur source ne change pas.

Pour détecter les modifications de source (applicables aux liaisons OneWay et TwoWay), la source doit implémenter un mécanisme de notification de changement de propriété approprié tel que INotifyPropertyChanged. Découvrez comment : implémenter une notification de modification de propriété (.NET Framework) pour obtenir un exemple d’implémentation INotifyPropertyChanged .

La Binding.Mode propriété fournit plus d’informations sur les modes de liaison et un exemple de spécification de la direction d’une liaison.

Ce qui déclenche les mises à jour de la source

Les liaisons qui sont TwoWay ou OneWayToSource écoutent les changements de la propriété cible et les propagent à la source, ce qui est appelé mise à jour de la source. Par exemple, vous pouvez modifier le texte d’une zone de texte pour modifier la valeur source sous-jacente.

Toutefois, votre valeur source est-elle mise à jour pendant que vous modifiez le texte ou une fois que vous avez fini de modifier le texte et que le contrôle perd le focus ? La Binding.UpdateSourceTrigger propriété détermine ce qui déclenche la mise à jour de la source. Les points des flèches pointant vers la droite dans la figure suivante illustrent le rôle de la Binding.UpdateSourceTrigger propriété.

Diagramme montrant le rôle de la propriété UpdateSourceTrigger.

Si la UpdateSourceTrigger valeur est UpdateSourceTrigger.PropertyChanged, alors la valeur pointée par la flèche droite de TwoWay ou les liaisons OneWayToSource est mise à jour dès que la propriété cible change. Toutefois, si la UpdateSourceTrigger valeur est LostFocus, cette valeur est mise à jour uniquement avec la nouvelle valeur lorsque la propriété cible perd le focus.

Comme pour la Mode propriété, différentes propriétés de dépendance ont des valeurs par défaut UpdateSourceTrigger différentes. La valeur par défaut de la plupart des propriétés de dépendance est PropertyChanged, ce qui entraîne la modification instantanée de la valeur de la propriété source lorsque la valeur de la propriété cible est modifiée. Les modifications instantanées sont correctes pour CheckBox et d'autres contrôles simples. Toutefois, pour les champs de texte, la mise à jour après chaque séquence de touches peut diminuer les performances et refuse à l’utilisateur l’occasion habituelle d’effectuer un retour arrière et de corriger les erreurs de saisie avant de valider la nouvelle valeur. Par exemple, la TextBox.Text propriété est définie par défaut sur la UpdateSourceTrigger valeur de , ce qui entraîne la modification de LostFocusla valeur source uniquement lorsque l’élément de contrôle perd le focus, et non lorsque la TextBox.Text propriété est modifiée. Pour plus d’informations sur la recherche de la valeur par défaut d’une propriété de dépendance, consultez la UpdateSourceTrigger page de propriétés.

Le tableau suivant fournit un exemple de scénario pour chaque UpdateSourceTrigger valeur à l’aide de l’exemple TextBox .

Valeur de déclenchement de mise à jour source Lorsque la valeur source est mise à jour Exemple de scénario pour TextBox
LostFocus (valeur par défaut pour TextBox.Text) Lorsque le contrôle TextBox perd le focus. Zone de texte associée à la logique de validation (voir Validation des données ci-dessous).
PropertyChanged Au fur et à mesure que vous tapez dans le TextBox. Contrôles TextBox dans une fenêtre de salle de conversation.
Explicit Quand l’application appelle UpdateSource. Contrôles TextBox dans un formulaire modifiable (met à jour les valeurs sources uniquement lorsque l’utilisateur appuie sur le bouton Envoyer).

Pour obtenir un exemple, consultez Comment : Contrôler quand le texte de la TextBox met à jour la source (.NET Framework).

Exemple de liaison de données

Pour obtenir un exemple de liaison des données, regardez l'interface utilisateur suivante de l'application de démonstration , qui affiche une liste d'articles aux enchères.

Capture d’écran de l’exemple de liaison de données

L’application illustre les fonctionnalités suivantes de la liaison de données :

  • Le contenu de ListBox est lié à une collection d’objets AuctionItem . Un objet AuctionItem a des propriétés telles que Description, StartPrice, StartDate, Category et SpecialFeatures.

  • Les données (objets AuctionItem ) affichées dans le ListBox modèle sont mises en forme afin que la description et le prix actuel soient affichés pour chaque élément. Le modèle est créé à l’aide d’un DataTemplate. En outre, l’apparence de chaque élément dépend de la valeur SpecialFeatures de l’objet AuctionItem affichée. Si la valeur SpecialFeatures de l’objet AuctionItem est Color, l’élément a une bordure bleue. Si la valeur est Mise en surbrillance, l’élément a une bordure orange et une étoile. La section Création de modèles de données fournit des informations sur la création de modèles de données.

  • L’utilisateur peut regrouper, filtrer ou trier les données à l’aide de l’élément CheckBoxes fourni. Dans l’image ci-dessus, le groupe par catégorie , le tri par catégorie et la dateCheckBoxes sont sélectionnés. Vous avez peut-être remarqué que les données sont regroupées en fonction de la catégorie du produit et que le nom de la catégorie est dans l’ordre alphabétique. Il est difficile de remarquer à partir de l’image, mais les éléments sont également triés par date de début dans chaque catégorie. Le tri est effectué à l’aide d’un affichage collection. La section Liaison aux collections traite des vues de collection.

  • Lorsque l’utilisateur sélectionne un élément, il ContentControl affiche les détails de l’élément sélectionné. Cette expérience est appelée scénario maître-détail. La section scénario maître-détail fournit des informations sur ce type de liaison.

  • Le type de la propriété StartDate est DateTime, qui retourne une date qui inclut l’heure à la milliseconde. Dans cette application, un convertisseur personnalisé a été utilisé afin qu’une chaîne de date plus courte soit affichée. La section Conversion de données fournit des informations sur les convertisseurs.

Lorsque l’utilisateur sélectionne le bouton Ajouter un produit , le formulaire suivant s’affiche.

Page Ajouter une liste de produits

L’utilisateur peut modifier les champs dans le formulaire, afficher un aperçu de la description du produit à l’aide des volets courts ou détaillés de l’aperçu, puis sélectionner Submit pour ajouter la nouvelle description du produit. Tous les paramètres de regroupement, de filtrage et de tri existants s’appliquent à la nouvelle entrée. Dans ce cas particulier, l’élément entré dans l’image ci-dessus s’affiche en tant que deuxième élément de la catégorie Ordinateur .

Non indiquée dans cette image est la logique de validation fournie dans la Date de débutTextBox. Si l’utilisateur entre une date non valide (mise en forme non valide ou une date passée), l’utilisateur est averti avec un ToolTip point d’exclamation rouge en regard du TextBox. La section Validation des données explique comment créer une logique de validation.

Avant de passer aux différentes fonctionnalités de la liaison de données décrites ci-dessus, nous allons d’abord aborder les concepts fondamentaux qui sont essentiels pour comprendre la liaison de données WPF.

Créer une liaison

Pour rétablir certains des concepts abordés dans les sections précédentes, vous établissez une liaison à l’aide de l’objet Binding , et chaque liaison comporte généralement quatre composants : une cible de liaison, une propriété cible, une source de liaison et un chemin d’accès à la valeur source à utiliser. Cette section aborde comment configurer une liaison.

Les sources de liaison sont liées à l’élément actif DataContext . Les éléments héritent automatiquement de leur DataContext s’ils n’en ont pas défini explicitement un.

Prenons l’exemple suivant, dans lequel l’objet source de liaison est une classe nommée MyData définie dans l’espace de noms SDKSample . À des fins de démonstration, MyData a une propriété de chaîne nommée ColorName dont la valeur est définie sur « Red ». Ainsi, cet exemple génère un bouton avec un arrière-plan rouge.

<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:c="clr-namespace:SDKSample">
    <DockPanel.Resources>
        <c:MyData x:Key="myDataSource"/>
    </DockPanel.Resources>
    <DockPanel.DataContext>
        <Binding Source="{StaticResource myDataSource}"/>
    </DockPanel.DataContext>
    <Button Background="{Binding Path=ColorName}"
            Width="150" Height="30">
        I am bound to be RED!
    </Button>
</DockPanel>

Pour plus d’informations sur la syntaxe de déclaration de liaison et des exemples de configuration d’une liaison dans le code, consultez vue d’ensemble des déclarations de liaison.

Si nous appliquons cet exemple à notre diagramme de base, la figure résultante ressemble à ce qui suit. Cette figure décrit une OneWay liaison, car la propriété Background prend en charge OneWay la liaison par défaut.

Schéma montrant la propriété de liaison de données Background.

Vous pouvez vous demander pourquoi cette liaison fonctionne même si la propriété ColorName est de type chaîne tandis que la Background propriété est de type Brush. Cette liaison utilise la conversion de type par défaut, qui est abordée dans la section Conversion de données .

Spécification de la source de liaison

Notez que dans l’exemple précédent, la source de liaison est spécifiée en définissant la propriété DockPanel.DataContext . L’Button hérite ensuite de la valeur DataContext de l’élément DockPanel, qui est son parent. Pour répéter, l’objet source de liaison est l’un des quatre composants nécessaires d’une liaison. Par conséquent, sans que l’objet source de liaison ne soit spécifié, la liaison ne ferait rien.

Il existe plusieurs façons de spécifier l’objet source de liaison. L’utilisation de la DataContext propriété sur un élément parent est utile lorsque vous liez plusieurs propriétés à la même source. Toutefois, il peut parfois être plus approprié de spécifier la source de liaison sur des déclarations de liaison individuelles. Pour l’exemple précédent, au lieu d’utiliser la DataContext propriété, vous pouvez spécifier la source de liaison en définissant la Binding.Source propriété directement sur la déclaration de liaison du bouton, comme dans l’exemple suivant.

<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:c="clr-namespace:SDKSample">
    <DockPanel.Resources>
        <c:MyData x:Key="myDataSource"/>
    </DockPanel.Resources>
    <Button Background="{Binding Source={StaticResource myDataSource}, Path=ColorName}"
            Width="150" Height="30">
        I am bound to be RED!
    </Button>
</DockPanel>

En dehors de définir la DataContext propriété directement sur un élément, d’hériter de la DataContext valeur d’un ancêtre (par exemple, le bouton dans le premier exemple) et de spécifier explicitement la source de liaison en définissant la Binding.Source propriété sur la liaison (par exemple, le bouton le dernier exemple), vous pouvez également utiliser la Binding.ElementName propriété ou la Binding.RelativeSource propriété pour spécifier la source de liaison. La ElementName propriété est utile lorsque vous effectuez une liaison à d’autres éléments de votre application, par exemple lorsque vous utilisez un curseur pour ajuster la largeur d’un bouton. La RelativeSource propriété est utile lorsque la liaison est spécifiée dans un ControlTemplate ou un Style. Pour plus d’informations, consultez Vue d’ensemble des sources de liaison.

Spécification du chemin d’accès à la valeur

Si votre source de liaison est un objet, vous utilisez la Binding.Path propriété pour spécifier la valeur à utiliser pour votre liaison. Si vous effectuez une liaison à des données XML, vous utilisez la Binding.XPath propriété pour spécifier la valeur. Dans certains cas, il peut être applicable d’utiliser la Path propriété même lorsque vos données sont XML. Par exemple, si vous souhaitez accéder à la propriété Name d’un XmlNode retourné (à la suite d’une requête XPath), vous devez utiliser la Path propriété en plus de la XPath propriété.

Pour plus d'informations, consultez les propriétés Path et XPath.

Bien que nous ayons souligné que la Path valeur à utiliser est l’un des quatre composants nécessaires d’une liaison, dans les scénarios que vous souhaitez lier à un objet entier, la valeur à utiliser serait la même que l’objet source de liaison. Dans ces cas, il est applicable de ne pas spécifier un Path. Considérez l'exemple suivant.

<ListBox ItemsSource="{Binding}"
         IsSynchronizedWithCurrentItem="true"/>

L’exemple ci-dessus utilise la syntaxe de liaison vide : {Binding}. Dans ce cas, le ListBox DataContext hérite d’un élément DockPanel parent (non illustré dans cet exemple). Lorsque le chemin d’accès n’est pas spécifié, la valeur par défaut est de lier à l’objet entier. En d’autres termes, dans cet exemple, le chemin d’accès a été omis parce que nous lions la propriété ItemsSource à l’objet entier. (Consultez la section Liaison aux collections pour une discussion approfondie.)

Outre la liaison à une collection, ce scénario est également utile lorsque vous souhaitez lier à un objet entier au lieu d’une seule propriété d’un objet. Par exemple, si votre objet source est de type String, vous pouvez simplement vouloir lier à la chaîne elle-même. Un autre scénario courant consiste à lier un élément à un objet avec plusieurs propriétés.

Vous devrez peut-être appliquer une logique personnalisée afin que les données soient significatives pour votre propriété cible liée. La logique personnalisée peut être sous la forme d’un convertisseur personnalisé si la conversion de type par défaut n’existe pas. Consultez la conversion de données pour plus d’informations sur les convertisseurs.

Lien et ExpressionLien

Avant d’entrer dans d’autres fonctionnalités et utilisations de la liaison de données, il est utile d’introduire la BindingExpression classe. Comme vous l’avez vu dans les sections précédentes, la Binding classe est la classe de haut niveau pour la déclaration d’une liaison ; elle fournit de nombreuses propriétés qui vous permettent de spécifier les caractéristiques d’une liaison. Une classe associée, BindingExpressionest l’objet sous-jacent qui gère la connexion entre la source et la cible. Une liaison contient toutes les informations qui peuvent être partagées entre plusieurs expressions de liaison. A BindingExpression est une expression d’instance qui ne peut pas être partagée et contient toutes les informations d’instance du Binding.

Prenons l’exemple suivant, où myDataObject est une instance de la MyData classe, myBinding est l’objet source Binding et MyData est une classe définie qui contient une propriété de chaîne nommée ColorName. Cet exemple lie le contenu texte de myText, une instance de TextBlock, à ColorName.

// Make a new source
var myDataObject = new MyData();
var myBinding = new Binding("ColorName")
{
    Source = myDataObject
};

// Bind the data source to the TextBox control's Text dependency property
myText.SetBinding(TextBlock.TextProperty, myBinding);
' Make a New source
Dim myDataObject As New MyData
Dim myBinding As New Binding("ColorName")
myBinding.Source = myDataObject

' Bind the data source to the TextBox control's Text dependency property
myText.SetBinding(TextBlock.TextProperty, myBinding)

Vous pouvez utiliser le même objet myBinding pour créer d’autres liaisons. Par exemple, vous pouvez utiliser l’objet myBinding pour lier le contenu du texte d’une case à cocher à ColorName. Dans ce scénario, il y aura deux instances de partage de BindingExpression l’objet myBinding .

Un objet BindingExpression est retourné lorsqu'on appelle GetBindingExpression sur un objet lié aux données. Les articles suivants illustrent certaines des utilisations de la BindingExpression classe :

Conversion de données

Dans la section Créer une liaison , le bouton est rouge, car sa Background propriété est liée à une propriété de chaîne avec la valeur « Red ». Cette valeur de chaîne fonctionne, car un convertisseur de type est présent sur le Brush type pour convertir la valeur de chaîne en un Brush.

L’ajout de ces informations à la figure de la section Créer une liaison ressemble à ceci.

Diagramme montrant la propriété de liaison de données par défaut.

Toutefois, que se passe-t-il si au lieu d’avoir une propriété de type chaîne, votre objet source de liaison a une propriété Color de type Color? Dans ce cas, pour que la liaison fonctionne, vous devez d’abord transformer la valeur de la propriété Color en quelque chose que la Background propriété accepte. Vous devez créer un convertisseur personnalisé en implémentant l’interface IValueConverter , comme dans l’exemple suivant.

[ValueConversion(typeof(Color), typeof(SolidColorBrush))]
public class ColorBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Color color = (Color)value;
        return new SolidColorBrush(color);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}
<ValueConversion(GetType(Color), GetType(SolidColorBrush))>
Public Class ColorBrushConverter
    Implements IValueConverter
    Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
        Dim color As Color = CType(value, Color)
        Return New SolidColorBrush(color)
    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
        Return Nothing
    End Function
End Class

Pour plus d’informations, consultez IValueConverter.

Maintenant, le convertisseur personnalisé est utilisé au lieu de la conversion par défaut, et notre diagramme ressemble à ceci.

Diagramme montrant le convertisseur personnalisé de liaison de données.

Pour répéter, les conversions par défaut peuvent être disponibles en raison de convertisseurs de type présents dans le type lié. Ce comportement dépend des convertisseurs de type disponibles dans la cible. En cas de doute, créez votre propre convertisseur.

Voici quelques scénarios classiques où il est judicieux d’implémenter un convertisseur de données :

  • Vos données doivent être affichées différemment, en fonction de la culture. Par exemple, vous pouvez implémenter un convertisseur monétaire ou un convertisseur de date/heure de calendrier en fonction des conventions utilisées dans une culture particulière.

  • Les données utilisées ne sont pas nécessairement destinées à modifier la valeur de texte d’une propriété, mais elles sont plutôt destinées à modifier une autre valeur, telle que la source d’une image, ou la couleur ou le style du texte d’affichage. Les convertisseurs peuvent être utilisés dans cette instance en convertissant la liaison d’une propriété qui peut ne pas sembler appropriée, par exemple la liaison d’un champ de texte à la propriété Arrière-plan d’une cellule de tableau.

  • Plusieurs contrôles ou plusieurs propriétés de contrôles sont liées aux mêmes données. Dans ce cas, la liaison primaire peut simplement afficher le texte, tandis que d’autres liaisons gèrent des problèmes d’affichage spécifiques, mais utilisent toujours la même liaison que les informations sources.

  • Une propriété cible a une collection de liaisons, appelée MultiBinding. Pour MultiBinding, vous utilisez une valeur personnalisée IMultiValueConverter pour produire une valeur finale à partir des valeurs des liaisons. Par exemple, la couleur peut être calculée à partir de valeurs rouges, bleues et vertes, qui peuvent être des valeurs provenant des mêmes objets sources de liaison ou différents. Consultez MultiBinding pour des exemples et des informations.

Liaison à des collections

Un objet source de liaison peut être traité comme un objet unique dont les propriétés contiennent des données ou comme une collection de données d’objets polymorphes qui sont souvent regroupés (comme le résultat d’une requête vers une base de données). Jusqu’à présent, nous n’avons abordé que la liaison à des objets uniques. Toutefois, la liaison à une collection de données est un scénario courant. Par exemple, un scénario courant consiste à utiliser un ItemsControl tel qu’un ListBox, ListView, ou TreeView pour afficher une collection de données, comme dans l'application affichée dans la section Présentation de la Liaison de données.

Heureusement, notre diagramme de base s’applique toujours. Si vous liez un ItemsControl à une collection, le diagramme ressemble à ceci.

Diagramme montrant l’objet ItemsControl de liaison de données.

Comme illustré dans ce diagramme, pour lier un objet de collection à un ItemsControl, la propriété ItemsControl.ItemsSource est celle à utiliser. Vous pouvez considérer ItemsSource comme le contenu du ItemsControl. La liaison est OneWay parce que la propriété ItemsSource prend en charge la liaison OneWay par défaut.

Comment implémenter des collections

Vous pouvez énumérer n’importe quelle collection qui implémente l’interface IEnumerable . Toutefois, pour configurer des liaisons dynamiques afin que les insertions ou les suppressions dans la collection mettent à jour l’IU automatiquement, la collection doit implémenter l’interface INotifyCollectionChanged. Cette interface expose un événement qui doit être déclenché chaque fois que la collection sous-jacente change.

WPF fournit la ObservableCollection<T> classe, qui est une implémentation intégrée d’une collection de données qui expose l’interface INotifyCollectionChanged . Pour prendre entièrement en charge le transfert de valeurs de données d’objets sources vers des cibles, chaque objet de votre collection qui prend en charge les propriétés pouvant être liées doit également implémenter l’interface INotifyPropertyChanged . Pour plus d’informations, consultez Vue d’ensemble des sources de liaison.

Avant d’implémenter votre propre collection, envisagez d’utiliser ObservableCollection<T> ou l’une des classes de collection existantes, telles que List<T>, Collection<T>et BindingList<T>, entre autres. Si vous avez un scénario avancé et que vous souhaitez implémenter votre propre collection, envisagez d’utiliser IList, qui fournit une collection non générique d’objets qui peuvent être accessibles individuellement par l’index, et fournit ainsi les meilleures performances.

Affichages de collection

Une fois que votre ItemsControl est associé à une collection de données, vous pouvez trier, filtrer ou regrouper les données. Pour ce faire, vous utilisez des vues de collection, qui sont des classes qui implémentent l’interface ICollectionView .

Qu’est-ce que les vues de collection ?

Une vue collection est une couche située au-dessus d’une collection source de liaison qui vous permet de naviguer et d’afficher la collection source en fonction du tri, du filtre et des requêtes de groupe, sans avoir à modifier la collection source sous-jacente elle-même. Une vue collection conserve également un pointeur vers l’élément actif de la collection. Si la collection source implémente l’interface INotifyCollectionChanged , les modifications déclenchées par l’événement CollectionChanged sont propagées aux vues.

Étant donné que les vues ne modifient pas les collections sources sous-jacentes, chaque collection source peut avoir plusieurs vues associées. Par exemple, vous pouvez avoir une collection d’objets Task . Avec l’utilisation des vues, vous pouvez afficher ces mêmes données de différentes manières. Par exemple, sur le côté gauche de votre page, vous pouvez afficher les tâches triées par priorité, et sur le côté droit, regroupées par zone.

Guide pratique pour créer une vue

Une façon de créer et d’utiliser une vue consiste à instancier directement l’objet d’affichage, puis à l’utiliser comme source de liaison. Par exemple, considérez affichée dans la section Qu'est-ce que la liaison de données. L'application est implémentée de telle sorte que le ListBox se lie à une vue sur la collection de données au lieu de la collection de données directement. L’exemple suivant est extrait de l'application de démonstration de liaison de données. La CollectionViewSource classe est le proxy XAML d’une classe qui hérite de CollectionView. Dans cet exemple particulier, la Source vue est liée à la collection AuctionItems (de type ObservableCollection<T>) de l’objet d’application actuel.

<Window.Resources>
    <CollectionViewSource 
      Source="{Binding Source={x:Static Application.Current}, Path=AuctionItems}"   
      x:Key="listingDataView" />
</Window.Resources>

La ressource listingDataView sert ensuite de source de liaison pour les éléments de l’application, comme le ListBox.

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8" 
         ItemsSource="{Binding Source={StaticResource listingDataView}}" />

Pour créer une autre vue pour la même collection, vous pouvez créer une autre CollectionViewSource instance et lui attribuer un autre x:Key nom.

Le tableau suivant montre quels types de données de vue sont générés en tant que vue de collection par défaut ou par CollectionViewSource selon le type de collection source.

Type de collection source Type d'affichage de collection Remarques
IEnumerable Un type interne basé sur CollectionView Impossible de regrouper les éléments.
IList ListCollectionView Le plus rapide.
IBindingList BindingListCollectionView

Utilisation d’une vue par défaut

La spécification d’une vue de collection en tant que source de liaison est un moyen de créer et d’utiliser une vue de collection. WPF crée également une vue de collection par défaut pour chaque collection utilisée comme source de liaison. Si vous liez directement à une collection, WPF est lié à sa vue par défaut. Cette vue par défaut est partagée par toutes les liaisons à la même collection. Par conséquent, une modification apportée à une vue par défaut par un contrôle ou un code lié (par exemple, le tri ou une modification du pointeur d’élément actuel, abordé plus loin) est reflétée dans toutes les autres liaisons à la même collection.

Pour obtenir la vue par défaut, vous utilisez la GetDefaultView méthode. Pour obtenir un exemple, consultez Obtenir la vue par défaut d’une collection de données (.NET Framework).

Vues de collection avec ADO.NET DataTables

Pour améliorer les performances, les vues de collection pour ADO.NET DataTable ou DataView délèguent le tri et le filtrage à DataView, ce qui entraîne le partage du tri et du filtrage entre toutes les vues de collection de la source de données. Pour permettre à chaque vue de collection de trier et de filtrer indépendamment, initialisez chaque vue de collection avec son propre DataView objet.

Tri

Comme mentionné précédemment, les vues peuvent appliquer un ordre de tri à une collection. Comme il existe dans la collection sous-jacente, vos données peuvent ou ne pas avoir un ordre inhérent pertinent. La vue sur la collection vous permet d’imposer une commande ou de modifier l’ordre par défaut, en fonction des critères de comparaison que vous fournissez. Étant donné qu’il s’agit d’une vue basée sur le client des données, un scénario courant est que l’utilisateur peut vouloir trier des colonnes de données tabulaires selon la valeur à laquelle la colonne correspond. À l’aide de vues, ce tri piloté par l’utilisateur peut être appliqué, à nouveau sans apporter de modifications à la collection sous-jacente ou même avoir à demander à nouveau le contenu de la collection. Pour obtenir un exemple, consultez Trier une colonne GridView en cliquant sur l'en-tête (.NET Framework).

L’exemple suivant montre la logique de tri de l’interface utilisateur de l’application « Trier par catégorie et date » CheckBox dans la section Qu'est-ce que la liaison de données .

private void AddSortCheckBox_Checked(object sender, RoutedEventArgs e)
{
    // Sort the items first by Category and then by StartDate
    listingDataView.SortDescriptions.Add(new SortDescription("Category", ListSortDirection.Ascending));
    listingDataView.SortDescriptions.Add(new SortDescription("StartDate", ListSortDirection.Ascending));
}
Private Sub AddSortCheckBox_Checked(sender As Object, e As RoutedEventArgs)
    ' Sort the items first by Category And then by StartDate
    listingDataView.SortDescriptions.Add(New SortDescription("Category", ListSortDirection.Ascending))
    listingDataView.SortDescriptions.Add(New SortDescription("StartDate", ListSortDirection.Ascending))
End Sub

Filtrage

Les vues peuvent également appliquer un filtre à une collection, afin que l’affichage affiche uniquement un certain sous-ensemble de la collection complète. Vous pouvez filtrer sur une condition dans les données. Par exemple, comme le fait l’application dans la section Liaison de données, la fonction « Afficher uniquement les bonnes affaires » CheckBox permet de filtrer les articles qui coûtent 25 $US ou plus. Le code suivant est exécuté pour définir ShowOnlyBargainsFilter comme Filter gestionnaire d’événements lorsqu’il CheckBox est sélectionné.

private void AddFilteringCheckBox_Checked(object sender, RoutedEventArgs e)
{
    if (((CheckBox)sender).IsChecked == true)
        listingDataView.Filter += ListingDataView_Filter;
    else
        listingDataView.Filter -= ListingDataView_Filter;
}
Private Sub AddFilteringCheckBox_Checked(sender As Object, e As RoutedEventArgs)
    Dim checkBox = DirectCast(sender, CheckBox)

    If checkBox.IsChecked = True Then
        AddHandler listingDataView.Filter, AddressOf ListingDataView_Filter
    Else
        RemoveHandler listingDataView.Filter, AddressOf ListingDataView_Filter
    End If
End Sub

Le gestionnaire d’événements ShowOnlyBargainsFilter a l’implémentation suivante.

private void ListingDataView_Filter(object sender, FilterEventArgs e)
{
    // Start with everything excluded
    e.Accepted = false;

    // Only inlcude items with a price less than 25
    if (e.Item is AuctionItem product && product.CurrentPrice < 25)
        e.Accepted = true;
}
Private Sub ListingDataView_Filter(sender As Object, e As FilterEventArgs)

    ' Start with everything excluded
    e.Accepted = False

    Dim product As AuctionItem = TryCast(e.Item, AuctionItem)

    If product IsNot Nothing Then

        ' Only include products with prices lower than 25
        If product.CurrentPrice < 25 Then e.Accepted = True

    End If

End Sub

Si vous utilisez l’une des CollectionView classes directement au lieu de CollectionViewSource, vous utilisez la Filter propriété pour spécifier un rappel. Pour obtenir un exemple, consultez Filtrer les données dans une vue (.NET Framework).

Regroupement

À l’exception de la classe interne qui affiche une IEnumerable collection, tous les affichages de collection prennent en charge le regroupement, ce qui permet à l’utilisateur de partitionner la collection dans la vue collection en groupes logiques. Les groupes peuvent être explicites, où l’utilisateur fournit une liste de groupes ou implicite, où les groupes sont générés dynamiquement en fonction des données.

L’exemple suivant montre la logique du « Grouper par catégorie ». CheckBox

// This groups the items in the view by the property "Category"
var groupDescription = new PropertyGroupDescription();
groupDescription.PropertyName = "Category";
listingDataView.GroupDescriptions.Add(groupDescription);
' This groups the items in the view by the property "Category"
Dim groupDescription = New PropertyGroupDescription()
groupDescription.PropertyName = "Category"
listingDataView.GroupDescriptions.Add(groupDescription)

Pour obtenir un autre exemple de regroupement, consultez Group Items in a ListView That Implements a GridView (.NET Framework).

Pointeurs d’élément actuels

Les vues prennent également en charge la notion d’un élément actuel. Vous pouvez parcourir les objets dans un affichage collection. Lorsque vous naviguez, vous déplacez un pointeur d’élément qui vous permet de récupérer l’objet qui existe à cet emplacement particulier dans la collection. Pour obtenir un exemple, consultez Parcourir les objets d’une collection de données (.NET Framework).

Étant donné que WPF se lie à une collection uniquement à l’aide d’une vue (une vue que vous spécifiez ou la vue par défaut de la collection), toutes les liaisons aux collections ont un pointeur d’élément actuel. Lorsqu'on lie à une vue, le caractère barre oblique (« / ») dans la valeur Path désigne l'élément actuel de la vue. Dans l’exemple suivant, le contexte de données est une vue de collection. La première ligne est liée à la collection. La deuxième ligne se lie à l’élément actuel de la collection. La troisième ligne est liée à la Description propriété de l’élément actuel dans la collection.

<Button Content="{Binding }" />
<Button Content="{Binding Path=/}" />
<Button Content="{Binding Path=/Description}" />

La syntaxe de slash et de propriété peut également être combinée pour naviguer dans une hiérarchie de collections. L’exemple suivant lie l’élément actuel d’une collection nommée Offices, qui est une propriété de l’élément actuel de la collection source.

<Button Content="{Binding /Offices/}" />

Le pointeur d’élément actuel peut être affecté par tout tri ou filtrage appliqué à la collection. Le tri conserve le pointeur de l'élément actuel sur le dernier élément sélectionné, mais la vue de collection est maintenant restructurée autour de celui-ci. (Peut-être que l’élément sélectionné était au début de la liste avant, mais maintenant l’élément sélectionné peut être quelque part au milieu.) Le filtrage conserve l’élément sélectionné si cette sélection reste en mode après le filtrage. Sinon, le pointeur d’élément actuel est défini sur le premier élément de la vue de collection filtrée.

Scénario de liaison maître-détail

La notion d’élément actuel est utile non seulement pour la navigation des éléments dans une collection, mais également pour le scénario de liaison maître-détail. Considérez à nouveau l’interface utilisateur de l’application dans la section Présentation de la liaison de données . Dans cette application, la sélection dans le ListBox détermine le contenu affiché dans le ContentControl. Pour le placer d’une autre manière, lorsqu’un ListBox élément est sélectionné, il ContentControl affiche les détails de l’élément sélectionné.

Vous pouvez implémenter le scénario maître-détail simplement en ayant deux contrôles ou plus liés à la même vue. L’exemple suivant de la démonstration de la démonstration de de démonstration montre le balisage de l’interface ListBox utilisateur de l’application et l’interface ContentControl utilisateur de l’application dans la section Présentation de la liaison de données .

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8" 
         ItemsSource="{Binding Source={StaticResource listingDataView}}" />
<ContentControl Name="Detail" Grid.Row="3" Grid.ColumnSpan="3"
                Content="{Binding Source={StaticResource listingDataView}}"
                ContentTemplate="{StaticResource detailsProductListingTemplate}" 
                Margin="9,0,0,0"/>

Notez que les deux contrôles sont liés à la même source, la ressource statique listingDataView (voir la définition de cette ressource dans la section Comment créer une vue). Cette liaison fonctionne car lorsqu’un objet (dans ContentControl ce cas) est lié à un affichage de collection, il est automatiquement lié à l’affichage CurrentItem . Les CollectionViewSource objets synchronisent automatiquement la devise et la sélection. Si votre contrôle de liste n’est pas lié à un CollectionViewSource objet comme dans cet exemple, vous devez définir la propriété IsSynchronizedWithCurrentItem à true pour que cela fonctionne.

Pour obtenir d’autres exemples, consultez Lier à une collection et afficher des informations basées sur la sélection (.NET Framework) et utiliser le modèle maître-détail avec des données hiérarchiques (.NET Framework).

Vous avez peut-être remarqué que l’exemple ci-dessus utilise un modèle. En fait, les données ne seraient pas affichées comme nous le souhaitons sans l’utilisation de modèles (celui utilisé explicitement par le ContentControl et celui implicitement utilisé par le ListBox). Nous allons maintenant passer à la création de modèles de données dans la section suivante.

Création de modèle de données

Sans l’utilisation de modèles de données, notre interface utilisateur d’application dans la section Exemple de liaison de données ressemble à ce qui suit :

Démonstration de liaison de données sans modèles de données

Comme illustré dans l’exemple de la section précédente, le ListBox contrôle et le ContentControl contrôle sont liés à l’ensemble de l’objet de collection (ou plus spécifiquement, la vue sur l’objet collection) de AuctionItems. Sans instructions spécifiques sur la façon d'afficher la collection de données, le ListBox affiche la représentation sous forme de chaîne de chaque objet dans la collection sous-jacente, et le ContentControl affiche la représentation sous forme de chaîne de l'objet auquel il est lié.

Pour résoudre ce problème, l’application définit DataTemplates. Comme illustré dans l’exemple de la section précédente, le ContentControl est utilisé explicitement avec le modèle de données detailsProductListingTemplate. Le ListBox contrôle utilise implicitement le modèle de données suivant lors de l’affichage des objets AuctionItem dans la collection.

<DataTemplate DataType="{x:Type src:AuctionItem}">
    <Border BorderThickness="1" BorderBrush="Gray"
            Padding="7" Name="border" Margin="3" Width="500">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20"/>
                <ColumnDefinition Width="86"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <Polygon Grid.Row="0" Grid.Column="0" Grid.RowSpan="4"
                     Fill="Yellow" Stroke="Black" StrokeThickness="1"
                     StrokeLineJoin="Round" Width="20" Height="20"
                     Stretch="Fill"
                     Points="9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7"
                     Visibility="Hidden" Name="star"/>

            <TextBlock Grid.Row="0" Grid.Column="1" Margin="0,0,8,0"
                       Name="descriptionTitle"
                       Style="{StaticResource smallTitleStyle}">Description:</TextBlock>
            
            <TextBlock Name="DescriptionDTDataType" Grid.Row="0" Grid.Column="2"
                       Text="{Binding Path=Description}"
                       Style="{StaticResource textStyleTextBlock}"/>

            <TextBlock Grid.Row="1" Grid.Column="1" Margin="0,0,8,0"
                       Name="currentPriceTitle"
                       Style="{StaticResource smallTitleStyle}">Current Price:</TextBlock>
            
            <StackPanel Grid.Row="1" Grid.Column="2" Orientation="Horizontal">
                <TextBlock Text="$" Style="{StaticResource textStyleTextBlock}"/>
                <TextBlock Name="CurrentPriceDTDataType"
                           Text="{Binding Path=CurrentPrice}" 
                           Style="{StaticResource textStyleTextBlock}"/>
            </StackPanel>
        </Grid>
    </Border>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Color</src:SpecialFeatures>
            </DataTrigger.Value>
            <DataTrigger.Setters>
                <Setter Property="BorderBrush" Value="DodgerBlue" TargetName="border" />
                <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
                <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
                <Setter Property="BorderThickness" Value="3" TargetName="border" />
                <Setter Property="Padding" Value="5" TargetName="border" />
            </DataTrigger.Setters>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Highlight</src:SpecialFeatures>
            </DataTrigger.Value>
            <Setter Property="BorderBrush" Value="Orange" TargetName="border" />
            <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
            <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
            <Setter Property="Visibility" Value="Visible" TargetName="star" />
            <Setter Property="BorderThickness" Value="3" TargetName="border" />
            <Setter Property="Padding" Value="5" TargetName="border" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

Avec l’utilisation de ces deux DataTemplates, l’interface utilisateur résultante est celle indiquée dans la section Présentation de la liaison de données . Comme vous pouvez le voir à partir de cette capture d’écran, en plus de vous permettre de placer des données dans vos contrôles, DataTemplates vous permet de définir des visuels attrayants pour vos données. Par exemple, DataTrigger sont utilisés ci-dessus DataTemplate afin que les objets AuctionItem avec la valeur de SpecialFeatures égale à HighLight soient affichés avec une bordure orange et une étoile.

Pour plus d’informations sur les modèles de données, consultez la vue d’ensemble de la création de modèles de données (.NET Framework).

Validation des données

La plupart des applications qui prennent l’entrée utilisateur doivent avoir une logique de validation pour s’assurer que l’utilisateur a entré les informations attendues. Les vérifications de validation peuvent être basées sur des exigences spécifiques au type, à la plage, au format ou à d’autres exigences spécifiques à l’application. Cette section décrit le fonctionnement de la validation des données dans WPF.

Association de règles de validation à une liaison

Le modèle de liaison de données WPF vous permet d’associer ValidationRules à votre Binding objet. Par exemple, l’exemple suivant lie une TextBox propriété nommée StartPrice et ajoute un ExceptionValidationRule objet à la Binding.ValidationRules propriété.

<TextBox Name="StartPriceEntryForm" Grid.Row="2"
         Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5" Grid.ColumnSpan="2">
    <TextBox.Text>
        <Binding Path="StartPrice" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <ExceptionValidationRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Un ValidationRule objet vérifie si la valeur d’une propriété est valide. WPF a deux types d’objets intégrés ValidationRule :

Vous pouvez également créer votre propre règle de validation en dérivant de la ValidationRule classe et en implémentant la Validate méthode. L'exemple suivant montre la règle utilisée par la fonction Ajouter une liste de produits dans la section « Date de début » TextBox de Qu'est-ce que la liaison de données.

public class FutureDateRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        // Test if date is valid
        if (DateTime.TryParse(value.ToString(), out DateTime date))
        {
            // Date is not in the future, fail
            if (DateTime.Now > date)
                return new ValidationResult(false, "Please enter a date in the future.");
        }
        else
        {
            // Date is not a valid date, fail
            return new ValidationResult(false, "Value is not a valid date.");
        }

        // Date is valid and in the future, pass
        return ValidationResult.ValidResult;
    }
}
Public Class FutureDateRule
    Inherits ValidationRule

    Public Overrides Function Validate(value As Object, cultureInfo As CultureInfo) As ValidationResult

        Dim inputDate As Date

        ' Test if date is valid
        If Date.TryParse(value.ToString, inputDate) Then

            ' Date is not in the future, fail
            If Date.Now > inputDate Then
                Return New ValidationResult(False, "Please enter a date in the future.")
            End If

        Else
            ' // Date Is Not a valid date, fail
            Return New ValidationResult(False, "Value is not a valid date.")
        End If

        ' Date is valid and in the future, pass
        Return ValidationResult.ValidResult

    End Function

End Class

StartDateEntryFormTextBox utilise cette FutureDateRule, comme illustré dans l’exemple suivant.

<TextBox Name="StartDateEntryForm" Grid.Row="3"
         Validation.ErrorTemplate="{StaticResource validationTemplate}" 
         Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5" Grid.ColumnSpan="2">
    <TextBox.Text>
        <Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged" 
                 Converter="{StaticResource dateConverter}" >
            <Binding.ValidationRules>
                <src:FutureDateRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Étant donné que la UpdateSourceTrigger valeur est PropertyChanged, le moteur de liaison met à jour la valeur source sur chaque séquence de touches, ce qui signifie qu’il vérifie également chaque règle de la ValidationRules collection sur chaque séquence de touches. Nous abordons cela plus loin dans la section Processus de validation.

Fournir des commentaires visuels

Si l’utilisateur entre une valeur non valide, vous pouvez fournir des commentaires sur l’erreur sur l’interface utilisateur de l’application. Une façon de fournir ces commentaires consiste à définir la propriété attachée Validation.ErrorTemplate sur un élément personnalisé ControlTemplate. Comme indiqué dans la sous-section précédente, StartDateEntryFormTextBox utilise un ErrorTemplate appelé validationTemplate. L’exemple suivant montre la définition de validationTemplate.

<ControlTemplate x:Key="validationTemplate">
    <DockPanel>
        <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
        <AdornedElementPlaceholder/>
    </DockPanel>
</ControlTemplate>

L’élément AdornedElementPlaceholder spécifie l'emplacement où le contrôle appliqué doit être placé.

En outre, vous pouvez également utiliser un ToolTip pour afficher le message d’erreur. Les éléments StartDateEntryForm et StartPriceEntryFormTextBoxes utilisent le style textStyleTextBox, qui crée un ToolTip affichant le message d'erreur. L’exemple suivant montre la définition de textStyleTextBox. La propriété Validation.HasError jointe est true quand une ou plusieurs liaisons sur les propriétés de l’élément lié sont en erreur.

<Style x:Key="textStyleTextBox" TargetType="TextBox">
    <Setter Property="Foreground" Value="#333333" />
    <Setter Property="MaxLength" Value="40" />
    <Setter Property="Width" Value="392" />
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip" 
                    Value="{Binding (Validation.Errors).CurrentItem.ErrorContent, RelativeSource={RelativeSource Self}}" />
        </Trigger>
    </Style.Triggers>
</Style>

Avec le ErrorTemplate personnalisé et le ToolTip, le StartDateEntryFormTextBox ressemble à ce qui suit lorsqu'il y a une erreur de validation.

Erreur de validation de liaison de données pour la date

Si votre Binding possède des règles de validation associées, mais que vous ne spécifiez pas de ErrorTemplate sur le contrôle lié, une ErrorTemplate par défaut sera utilisée pour avertir les utilisateurs en cas d'erreur de validation. La valeur par défaut ErrorTemplate est un modèle de contrôle qui définit une bordure rouge dans la couche ornement. Avec la valeur par défaut ErrorTemplate et le ToolTip, l'interface utilisateur du StartPriceEntryFormTextBox ressemble à ce qui suit lorsqu'il y a une erreur de validation.

Erreur de validation de liaison de données pour le prix

Pour obtenir un exemple de logique permettant de valider tous les contrôles d’une boîte de dialogue, consultez la section Boîtes de dialogue personnalisées dans la vue d’ensemble des boîtes de dialogue.

Processus de validation

La validation se produit généralement lorsque la valeur d’une cible est transférée à la propriété source de liaison. Ce transfert se produit sur les liaisons TwoWay et OneWayToSource. Pour répéter, les causes d’une mise à jour source dépendent de la valeur de la UpdateSourceTrigger propriété, comme décrit dans la section Éléments qui déclenchent les mises à jour sources .

Les éléments suivants décrivent le processus de validation . Si une erreur de validation ou un autre type d’erreur se produit à tout moment pendant ce processus, le processus est arrêté :

  1. Le moteur de liaison vérifie s’il existe des objets personnalisés ValidationRule définis dont ValidationStep est défini sur RawProposedValue pour ce Binding, auquel cas il appelle la méthode Validate sur chacun ValidationRule jusqu’à ce qu’un d’entre eux rencontre une erreur ou jusqu’à ce que tous réussissent.

  2. Le moteur de liaison appelle ensuite le convertisseur, s’il en existe un.

  3. Si le convertisseur réussit, le moteur de liaison vérifie s'il existe des objets personnalisés ValidationRule dont l'attribut ValidationStep est défini sur ConvertedProposedValue pour ce Binding. Dans ce cas, il appelle la méthode Validate sur chacune des ValidationRule ayant ValidationStep défini sur ConvertedProposedValue, jusqu'à ce que l'une d'elles rencontre une erreur ou que toutes réussissent.

  4. Le moteur de liaison définit la propriété de la source.

  5. Le moteur de liaison vérifie s'il existe des objets ValidationRule personnalisés définis dont ValidationStep est défini sur UpdatedValue pour ce Binding. Dans ce cas, il appelle la méthode Validate sur chaque ValidationRule dont ValidationStep est défini sur UpdatedValue, jusqu'à ce que l'une d'elles rencontre une erreur ou que toutes passent. Si une DataErrorValidationRule est associée à une liaison et que son ValidationStep est défini sur la valeur par défaut, UpdatedValue, le DataErrorValidationRule est vérifié à ce stade. À ce stade, toute liaison qui a ValidatesOnDataErrors défini sur true est vérifiée.

  6. Le moteur de liaison vérifie s’il existe des objets personnalisés ValidationRule définis dont ValidationStep est fixé à CommittedValue pour ce Binding, auquel cas il appelle la méthode Validate sur chaque ValidationRule dont ValidationStep est défini à CommittedValue jusqu’à ce que l’un d’eux rencontre une erreur ou jusqu’à ce que tous réussissent.

S’il un ValidationRule ne passe à aucun moment tout au long de ce processus, le moteur de liaison crée un objet ValidationError et l’ajoute à la collection Validation.Errors de l’élément lié. Avant que le moteur de liaison exécute les ValidationRule objets à une étape donnée, il supprime tous les ValidationError ajoutés à la Validation.Errors propriété attachée de l'élément lié pendant cette étape. Par exemple, si un ValidationRule dont ValidationStep est défini à UpdatedValue échoue, la prochaine fois que le processus de validation a lieu, le moteur de liaison supprime ValidationError immédiatement avant d'appeler un ValidationRule avec ValidationStep défini à UpdatedValue.

Lorsque Validation.Errors n'est pas vide, la propriété associée Validation.HasError de l'élément est définie sur true. En outre, si la propriété de Binding est définie sur true, le moteur de liaison déclenche l’événement Validation.Error attaché sur l’élément.

Notez également qu’un transfert de valeur valide dans les deux sens (cible vers la source ou la source vers la cible) efface la Validation.Errors propriété jointe.

Si la liaison est soit associée à ExceptionValidationRule, soit si la propriété ValidatesOnExceptions est définie sur true et qu’une exception est levée lorsque le moteur de liaison définit la source, alors le moteur de liaison vérifie s’il existe un UpdateSourceExceptionFilter. Vous pouvez utiliser le UpdateSourceExceptionFilter rappel pour fournir un gestionnaire personnalisé pour gérer les exceptions. Si un UpdateSourceExceptionFilter n’est pas spécifié sur le Binding, le moteur de liaison crée un ValidationError avec l’exception et l’ajoute à la collection Validation.Errors de l’élément lié.

Mécanisme de débogage

Vous pouvez définir la propriété PresentationTraceSources.TraceLevel jointe sur un objet lié à une liaison pour recevoir des informations sur l’état d’une liaison spécifique.

Voir aussi