Vue d'ensemble de la liaison de données
La liaison de données Windows Presentation Foundation (WPF) offre un moyen simple et cohérent aux applications de présenter et d'interagir avec les données. Les éléments peuvent être liés aux données de diverses sources de données sous la forme d'objets common language runtime (CLR) et XML. Les ContentControl tels que Button et les ItemsControl tels que ListBox et ListView possèdent des fonctionnalités intégrées pour permettre l'application de styles flexibles aux éléments de données uniques ou aux collections d'éléments de données. Des vues de tri, de filtre et de groupe peuvent être générées en plus des données.
La fonctionnalité de liaison de données dans WPF présente plusieurs avantages sur les modèles traditionnels, y compris une large gamme de propriétés qui prennent fondamentalement en charge la liaison des données, la représentation flexible des données via une UI et la séparation propre de la logique métier de l'UI.
Cette rubrique traite d'abord des concepts fondamentaux de la liaison de données WPF, puis traite ensuite de l'utilisation de la classe Binding et d'autres fonctionnalités de liaison de données.
Cette rubrique comprend les sections suivantes.
- Qu'est-ce que la liaison de données ?
- Concepts de base des liaisons de données
- Création d'une liaison
- Conversion de données
- Liaisons de collections
- Modèles de données
- Validation des données
- Mécanisme de débogage
- Rubriques connexes
Qu'est-ce que la liaison de données ?
La liaison de données est le processus qui établit une connexion entre l'UI de l'application et la logique métier. Si la liaison possède les paramètres corrects et que les données fournissent les notifications requises, lorsque les données modifient leur valeur, les éléments liés aux données reflètent automatiquement ces 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 mises à jour automatiquement pour refléter la modification. Par exemple, si l'utilisateur modifie la valeur dans un élément TextBox, la valeur de données sous-jacente est mise à jour automatiquement pour refléter cette modification.
Une utilisation typique de liaison de données consiste à placer des données de configuration locales ou d'un serveur dans des formulaires ou d'autres contrôles d'UI. Ce concept est développé pour inclure la liaison d'une large gamme de propriétés à diverses sources de données dans WPF. Dans WPF, des propriétés de dépendance d'éléments peuvent être liées à des objets CLR (y compris des objets ADO.NET ou des objets associés à des services Web et à des propriétés Web) et des données XML.
Pour obtenir un exemple de liaison de données, consultez l'UI de l'application suivante de la section Liaison de données, démonstration :
Ce qui précède est l'UI d'une application qui affiche une liste d'éléments de vente aux enchères. L'application montre les fonctionnalités suivantes de liaison de données :
Le contenu de ListBox est lié à une collection d'objets AuctionItem. Un objet AuctionItem possède des propriétés telles que Description, StartPrice, StartDate, Category, SpecialFeatures, etc.
Les données (objets AuctionItem) affichées dans le ListBox sont basées sur des modèles de sorte que la description et le prix actuel soient indiqués pour chaque élément. Cela est fait à l'aide d'un DataTemplate. De plus, l'apparence de chaque élément dépend de la valeur SpecialFeatures du AuctionItem affiché. Si la valeur SpecialFeatures de AuctionItem est Color, l'élément a une bordure bleue. Si la valeur est Highlight, l'élément a une bordure orange et une étoile. La section Modèles de données fournit des informations sur les modèles de données.
L'utilisateur peut grouper, filtrer ou trier les données à l'aide des CheckBox fournis. Dans l'image ci-dessus, "Grouper par catégorie" et "Trier par catégorie et date" CheckBox sont sélectionnés. Vous avez pu remarquer que les données sont groupées selon la catégorie du produit et que le nom de catégorie est en ordre alphabétique. C'est difficile à observer dans l'image, mais les éléments sont également triés par date de début dans chaque catégorie. Ce résultat est obtenu à l'aide d'une vue de collection. La section Liaison avec des collections traite des vues de collection.
Lorsque l'utilisateur sélectionne un élément, le ContentControl affiche les détails de l'élément sélectionné. Cela s'appelle le scénario maître/détail. La section Scénario maître/détail fournit des informations sur ce type de scénario de liaison.
Le type de la propriété StartDate est DateTime, qui retourne une date incluant l'heure à la milliseconde près. Dans cette application, un convertisseur personnalisé a été utilisé afin d'afficher une chaîne de date plus courte. La section Conversion de données fournit des informations sur les convertisseurs.
Lorsque l'utilisateur clique sur le bouton Add product, le formulaire suivant s'affiche :
L'utilisateur peut modifier les champs dans le formulaire, consulter la liste des produits à l'aide de l'aperçu court et les volets d'aperçu plus détaillés, puis cliquer sur submit pour ajouter la liste des nouveaux produits. Toutes les fonctionnalités de regroupement, de filtrage et de tri existantes s'appliqueront à la nouvelle entrée. Dans ce cas particulier, l'élément entré dans l'image ci-dessus sera affiché comme deuxième élément dans la catégorie Ordinateur.
La logique de validation fournie dans la TextBox Start Date n'est pas représentée dans cette image. Si l'utilisateur entre une date non valide (format non valide ou date révolue), l'utilisateur sera notifié avec un ToolTip et un point d'exclamation rouge à côté de la TextBox. La section Validation des données traite de la création de la logique de validation.
Avant d'étudier plus en détail les différentes fonctionnalités de liaison de données décrites ci-dessus, nous couvrirons en premier dans la section suivante les concepts fondamentaux qui sont critiques pour la compréhension de la liaison de données WPF.
Concepts de base des liaisons de données
Cette section comprend les sous-sections suivantes.
- Direction du flux de données
- Qu'est-ce qui déclenche les mises à jour de la source ?
Indépendamment de l'élément que vous liez et de la nature de votre source de données, chaque liaison suit toujours le modèle représenté par l'illustration suivante :
Comme représenté par l'illustration ci-dessus, la liaison de données est essentiellement la passerelle entre votre cible de la liaison et votre source de liaison. L'illustration montre les concepts fondamentaux suivants de liaison de données WPF :
En général, chaque liaison possède ces quatre composants : un objet de cible de liaison, une propriété cible, une source de liaison et un chemin d'accès à la valeur dans la source de liaison à utiliser. Par exemple, si vous souhaitez lier le contenu d'une TextBox à la propriété Name d'un objet Employee, votre objet cible est la TextBox, la propriété cible est la propriété Text, la valeur à utiliser est Name et l'objet source est l'objet Employee.
La propriété cible doit être une propriété de dépendance. La plupart des propriétés UIElement sont des propriétés de dépendance et la plupart des propriétés de dépendance, sauf celles en lecture seule, prennent en charge la liaison de données par défaut. (Seuls les types DependencyObject peuvent définir les propriétés de dépendance et tous les UIElement dérivent de DependencyObject.)
Bien que non spécifié dans l'illustration, il doit être noté que l'objet de la source de liaison n'est pas simplement un objet CLR personnalisé. La liaison de données WPF prend en charge des données dans le formulaire d'objets CLR et XML. Pour fournir quelques exemples, votre source de liaison peut être un UIElement, tout objet de liste, un objet CLR associé à des données 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.
Lorsque vous lirez d'autres rubriques software development kit (SDK), n'oubliez pas que lorsque vous établissez une liaison, vous liez une cible de la liaison à une source de liaison. Par exemple, si vous affichez des données XML sous-jacentes dans une ListBox à l'aide d'une liaison de données, vous liez votre ListBox aux données XML.
Pour établir une liaison, vous utilisez l'objet Binding. La suite de cette rubrique traite de nombreux concepts connexes et de quelques-unes des propriétés et utilisations de l'objet Binding.
Direction du flux de données
Comme mentionné précédemment et comme indiqué par la flèche dans l'illustration ci-dessus, le flux de données d'une liaison peut aller de la cible de liaison à la source de liaison (par exemple, la valeur source change lorsqu'un utilisateur modifie la valeur d'une TextBox) et/ou de la source de liaison à la cible de liaison (par exemple, votre contenu TextBox est mis à jour avec des modifications dans la source de liaison) si cette dernière fournit les notifications correctes.
Vous pouvez souhaiter que votre application permette aux utilisateurs de modifier les données et de les propager vers l'objet source. Ou peut-être ne souhaitez-vous pas permettre aux utilisateurs de mettre à jour les données sources. Vous pouvez contrôler cette situation en définissant la propriété Mode de votre objet Binding. L'illustration suivante montre les différents types de flux de données :
La liaison OneWay provoque la mise à jour automatique dans la propriété cible 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 créer une liaison avec une source comme un Stock Ticker, ou peut-être votre propriété cible n'a-t-elle aucune interface de contrôle prévue pour apporter des modifications, comme une couleur d'arrière-plan liée aux données d'un tableau. Si aucun suivi des modifications apportées à la propriété cible n'est requis, utilisez le mode de liaison OneWay pour éviter toute charge mémoire du mode de liaison TwoWay.
La liaison TwoWay entraîne la mise à jour automatique de la propriété source lorsque la propriété cible est modifiée et inversement. Ce type de liaison convient aux formulaires modifiables ou autres scénarios d'UI complètement interactifs. La plupart des propriétés ont OneWay comme valeur de liaison par défaut, mais certaines propriétés de dépendance (en général les propriétés de contrôles modifiables par l'utilisateur telles que la propriété Text de TextBox et la propriété IsChecked de CheckBox) ont TwoWay comme valeur de liaison par défaut. Vous pouvez déterminer par programmation si une propriété de dépendance effectue par défaut une liaison monodirectionnelle ou bidirectionnelle en obtenant les métadonnées de la propriété à l'aide de GetMetadata ; vérifiez ensuite la valeur booléenne de la propriété BindsTwoWayByDefault.
OneWayToSource est l'inverse de la liaison OneWay ; elle met à jour la propriété source lorsque la propriété cible est modifiée. Un exemple de scénario est si vous devez seulement réévaluer la valeur source de l'UI.
La liaison OneTime, non représentée dans l'illustration, entraîne l'initialisation de la propriété cible par la propriété source, mais les modifications ultérieures ne sont pas propagées. Cela signifie que si le contexte de données fait l'objet d'une modification ou si l'objet change dans le contexte des données, la modification n'est pas répercutée à la propriété cible. Ce type de liaison est approprié si vous utilisez des données pour lequel un instantané de l'état actuel peut être utilisé ou si les données sont totalement statiques. Ce type de liaison est également utile lorsque vous souhaitez initialiser votre propriété cible avec une valeur d'une propriété source sans connaître à l'avance le contexte de données. Il s'agit d'une forme simplifiée de liaison OneWay qui offre de meilleures performances dans les situations où la valeur source n'est pas modifiée.
Notez que pour détecter des modifications dans la source (applicables aux liaisons OneWay et TwoWay), la source doit implémenter un mécanisme de notification des modifications de propriétés approprié tel que INotifyPropertyChanged. Consultez Comment : implémenter la notification des modifications de propriétés pour obtenir un exemple d'implémentation de INotifyPropertyChanged.
La page de propriétés Mode fournit plus d'informations sur les modes de liaison et un exemple pour spécifier la direction d'une liaison.
Qu'est-ce qui déclenche les mises à jour de la source ?
Les liaisons qui sont TwoWay ou OneWayToSource sont à l'écoute des modifications dans la propriété cible et les propagent à la source. Il s'agit de la mise à jour de la source. Par exemple, vous pouvez modifier le texte d'une zone de texte pour modifier la valeur source sous-jacente. Comme décrit dans la dernière section, la direction du flux de données est déterminée par la valeur de la propriété Mode de la liaison.
Toutefois, votre valeur source est-elle mise à jour pendant que vous modifiez le texte ou après avoir fini de le modifier et que vous éloignez votre souris de la zone de texte ? La propriété UpdateSourceTrigger de la liaison détermine ce qui déclenche la mise à jour de la source. Les points des flèches droites dans l'illustration suivante montrent le rôle de la propriété UpdateSourceTrigger :
Si la valeur UpdateSourceTrigger est PropertyChanged, la valeur indiquée par la flèche droite des liaisons TwoWay ou OneWayToSource est mise à jour dès que la propriété cible est modifiée. Toutefois, si la valeur UpdateSourceTrigger est LostFocus, alors cette valeur ne sera mise à jour avec la nouvelle valeur que lorsque la propriété cible aura perdu le focus.
Comme avec la propriété Mode, différentes propriétés de dépendance possèdent différentes valeurs UpdateSourceTrigger par défaut. La valeur par défaut de la plupart des propriétés de dépendance est PropertyChanged, alors que la valeur par défaut de la propriété Text est LostFocus. Cela signifie que les mises à jour de la source se produisent habituellement chaque fois que la propriété cible est modifiée, ce qui convient pour les CheckBox et autres contrôles simples. Toutefois, pour les champs de texte, une mise à jour après chaque frappe au clavier peut réduire les performances et empêcher l'utilisateur de revenir en arrière pour corriger des erreurs de frappe avant de valider la nouvelle valeur. C'est pourquoi la propriété Text a une valeur par défaut de LostFocus au lieu de PropertyChanged.
Consultez la page des propriétés UpdateSourceTrigger pour plus d'informations sur la recherche de la valeur UpdateSourceTrigger par défaut d'une propriété de dépendance.
Le tableau suivant fournit un exemple de scénario pour chaque valeur UpdateSourceTrigger utilisant la TextBox comme exemple :
Valeur UpdateSourceTrigger |
Lorsque la valeur source est mise à jour |
Exemple de scénario pour zone de texte |
---|---|---|
LostFocus (valeur par défaut pour TextBox.Text) |
Quand le contrôle TextBox perd le focus. |
TextBox associée à la logique de validation (consulter la section Validation des données) |
PropertyChanged |
Lorsque vous tapez dans la TextBox |
Contrôles TextBox dans une fenêtre de salle de conversation |
Explicit |
Lorsque l'application appelle UpdateSource |
Contrôles TextBox dans un formulaire modifiable (met à jour les valeurs sources uniquement lorsque l'utilisateur clique sur le bouton Soumettre) |
Pour obtenir un exemple, consultez Comment : contrôler quand le texte TextBox met à jour la source.
Création d'une liaison
Cette section comprend les sous-sections suivantes.
- Spécification de la source de liaison
- Spécification du chemin d'accès à la valeur
- Binding et BindingExpression
Pour récapituler quelques-uns des concepts couverts 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 : la cible de la liaison, la propriété cible, la source de liaison et le chemin d'accès à la valeur source. Cette section décrit comment installer une liaison.
Considérez l'exemple suivant, dans lequel l'objet de source de liaison est une classe nommée MyData définie dans l'espace de noms SDKSample. À des fins de démonstration, la classe MyData a une propriété de type chaîne appelée ColorName, dont la valeur est "Rouge". Cet exemple génère donc un bouton avec un arrière-plan rouge.
<DockPanel
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://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 pour des exemples d'installation d'une liaison dans le code, consultez Vue d'ensemble des déclarations de liaison.
Si nous appliquons cet exemple à notre diagramme de base, l'illustration qui en résulte présente l'aspect suivant. Il s'agit d'une liaison OneWay parce que la propriété Background prend en charge la liaison OneWay par défaut.
Vous vous demandez peut-être pourquoi cela fonctionne bien que la propriété ColorName soit de type chaîne alors que la propriété Background est de type Brush. Cela est dû à la conversion de type par défaut qui est traitée dans la section Conversion de données.
Spécification de la source de liaison
Remarquez que dans l'exemple précédent, la source de liaison est spécifiée en définissant la propriété DataContext sur l'élément DockPanel. Le Button hérite ensuite de la valeur DataContext du DockPanel, qui est son élément parent. Pour rappel, l'objet de source de liaison est l'un des quatre composants nécessaires d'une liaison. Par conséquent, la liaison n'a aucun effet tant que l'objet de source de liaison n'est pas spécifié.
Il y a plusieurs méthodes pour spécifier l'objet de source de liaison. L'utilisation de la propriété DataContext sur un élément parent est utile lorsque vous liez plusieurs propriétés à la même source. Toutefois, il peut être parfois 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 propriété DataContext, vous pouvez spécifier la source de liaison en définissant directement la propriété Source sur la déclaration de liaison du bouton, comme dans l'exemple suivant :
<DockPanel.Resources>
<c:MyData x:Key="myDataSource"/>
</DockPanel.Resources>
<Button Width="150" Height="30"
Background="{Binding Source={StaticResource myDataSource},
Path=ColorName}">I am bound to be RED!</Button>
Hormis les techniques qui consistent à définir directement la propriété DataContext sur un élément, à hériter la valeur DataContext d'un ancêtre (tel que le bouton dans le premier exemple) et à spécifier explicitement la source de liaison en définissant la propriété Source sur Binding (tel que le bouton dans le dernier exemple), vous pouvez également utiliser la propriété ElementName ou la propriété RelativeSource pour spécifier la source de liaison. La propriété ElementName est utile lorsque vous créez une liaison avec d'autres éléments dans votre application, comme lorsque vous utilisez un curseur pour ajuster la largeur d'un bouton. La propriété RelativeSource est utile lorsque la liaison est spécifiée dans un ControlTemplate ou un Style. Pour plus d'informations, consultez Comment : spécifier la source de liaison.
Spécification du chemin d'accès à la valeur
Si votre source de liaison est un objet, vous utilisez la propriété Path pour spécifier la valeur à utiliser pour votre liaison. Si vous créez une liaison avec des données XML, vous utilisez la propriété XPath pour spécifier la valeur. Dans certains cas, il peut s'avérer nécessaire d'utiliser la propriété Path, même avec des données XML. Par exemple, si vous souhaitez accéder à la propriété Name d'un XmlNode retourné (par suite d'une requête XPath), vous devez utiliser la propriété Path en plus de la propriété XPath.
Pour plus d'informations sur la syntaxe et pour obtenir des exemples, consultez Path et les pages des propriétés XPath.
Bien que nous ayons insisté sur le fait que la Path vers la valeur à utiliser est l'un des quatre composants nécessaires d'une liaison, sachez que dans les scénarios où vous souhaitez effectuer une liaison avec un objet entier, la valeur à utiliser est la même que l'objet de source de liaison. Dans ces cas de figure, il convient de ne pas spécifier de Path. Prenons l'exemple suivant :
<ListBox ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="true"/>
L'exemple précédent utilise la syntaxe de liaison vide : {Binding}. Dans ce cas, le ListBox hérite du DataContext d'un élément DockPanel parent (non affiché dans cet exemple). Lorsque le chemin d'accès n'est pas spécifié, la valeur par défaut est de créer une liaison avec l'objet entier. En d'autres termes, le chemin d'accès a été éliminé dans cet exemple, parce que nous lions la propriété ItemsSource à l'objet entier. (Consultez la section Liaison avec des collections pour une plus d'informations.)
Outre la création d'une liaison avec une collection, ce scénario est également utile lorsque vous souhaitez créer une liaison avec un objet entier plutôt qu'avec une propriété unique d'un objet. Par exemple, si votre objet source est de type chaîne et que vous souhaitez seulement créer une liaison avec la chaîne elle-même. Un autre scénario courant est lorsque vous souhaitez lier un élément à un objet avec plusieurs propriétés.
Notez que vous devrez peut-être appliquer une logique personnalisée afin que les données soient pertinentes 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 Conversion de données pour plus d'informations sur les convertisseurs.
Binding et BindingExpression
Avant d'approcher d'autres fonctionnalités et utilisations des liaisons de données, il est utile d'introduire la classe BindingExpression. Comme vous avez vu dans les sections précédentes, la classe Binding est la classe de niveau supérieur pour la déclaration d'une liaison ; la classe Binding fournit de nombreuses propriétés qui vous permettent de spécifier les caractéristiques d'une liaison. Une classe connexe, BindingExpression, est l'objet sous-jacent qui gère la connexion entre la source et la cible. Une liaison contient toutes les informations pouvant être partagées entre plusieurs expressions de liaison. BindingExpression est une expression d'instance qui ne peut pas être partagée et qui contient toutes les informations d'instance de Binding.
Par exemple, considérez les éléments suivants, où myDataObject est une instance de la classe MyData, myBinding est l'objet source Binding, et la classe MyData est une classe définie qui contient une propriété de type chaîne appelée MyDataProperty. Cet exemple lie le contenu textuel de mytext, une instance de TextBlock, à MyDataProperty.
Dim data1 As New MyData(DateTime.Now)
Dim binding1 As New Binding("MyDataProperty")
binding1.Source = data1
Me.myText.SetBinding(TextBlock.TextProperty, binding1)
//make a new source
MyData myDataObject = new MyData(DateTime.Now);
Binding myBinding = new Binding("MyDataProperty");
myBinding.Source = myDataObject;
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 de texte d'une case à cocher à MyDataProperty. Dans ce scénario, deux instances de BindingExpression partageront l'objet myBinding.
Un objet BindingExpression peut être obtenu via la valeur de retour de l'appel GetBindingExpression sur un objet lié aux données. Les rubriques suivantes montrent quelques-unes des utilisations de la classe BindingExpression :
Comment : obtenir l'objet de liaison d'une propriété cible liée aux données
Comment : contrôler quand le texte TextBox met à jour la source
Conversion de données
Dans l'exemple précédent, le bouton est rouge parce que sa propriété Background est liée à une propriété de type chaîne avec la valeur "Rouge". Cela fonctionne parce qu'un convertisseur de type est présent sur le type Brush pour convertir la valeur de chaîne en un Brush.
Pour ajouter cette information à l'illustration dans la section Création d'une liaison, le diagramme présente l'aspect suivant :
Toutefois, que se passe-t-il si au lieu d'avoir une propriété de type chaîne, votre objet de source de liaison a une propriété Color de type Color ? Dans ce cas, pour que la liaison fonctionne, vous devrez d'abord transformer la valeur de propriété Color en une valeur que la propriété Background accepte. Vous devez créer un convertisseur personnalisé en implémentant l'interface IValueConverter, comme dans l'exemple suivant :
<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
[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;
}
}
La page de référence IValueConverter fournit plus d'informations.
Maintenant que le convertisseur personnalisé est utilisé au lieu de la conversion par défaut, notre diagramme présente l'aspect suivant :
Pour rappel, des conversions par défaut peuvent être disponibles parce que des convertisseurs de type sont présents dans le type auquel on se lie. Ce comportement dépendra des convertisseurs de type disponibles dans la cible. En cas de doute, créez votre propre convertisseur.
Vous trouverez ci-dessous quelques scénarios classiques pour lesquels il est logique d'implémenter un convertisseur de données :
Vos données doivent être affichées différemment, selon la culture. Par exemple, vous pouvez souhaiter implémenter un convertisseur de devise ou un convertisseur de date/heure du calendrier en fonction des valeurs ou des normes utilisées dans une culture particulière.
Les données qui sont utilisées ne sont pas nécessairement prévues pour modifier la valeur de texte d'une propriété, mais pour modifier une autre valeur, telle que la source d'une image, ou la couleur ou le style du texte affiché. Les convertisseurs peuvent être utilisés dans cette instance en convertissant la liaison d'une propriété qui peut ne pas sembler appropriée, tels que la liaison d'un champ de texte à la propriété Background d'une cellule de tableau.
Plusieurs contrôles ou propriétés de contrôles sont liés aux mêmes données. Dans ce cas, la liaison principale peut n'afficher que le texte, tandis que d'autres liaisons gèrent des problèmes d'affichage spécifiques en utilisant néanmoins la même liaison comme information source.
Jusqu'à présent, nous n'avons pas encore traité de MultiBinding, lorsqu'une propriété cible possède une collection de liaisons. Dans le cas d'une MultiBinding vous utilisez un IMultiValueConverter pour produire une valeur finale à partir des valeurs des liaisons. Par exemple, la couleur peut être calculée à partir de valeurs de rouge, de bleu et de vert, qui peuvent être des valeurs provenant des mêmes objets ou de différents objets de source de liaison. Pour plus d'informations, consultez les exemples fournis dans la page de classe MultiBinding.
Liaisons de collections
Cette section comprend les sous-sections suivantes.
- Comment implémenter des collections
- Vues de collection
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 polymorphiques souvent rassemblés (tel que le résultat d'une requête à une base de données). Jusqu'à présent, nous n'avons traité que des liaisons avec des objets uniques ; cependant, les liaisons avec une collection de données sont des scénarios courants. Par exemple, un scénario courant consiste à utiliser un ItemsControl tel qu'un ListBox, ListView ou un TreeView pour afficher une collection de données, comme dans l'application représentée dans la section Qu'est-ce que la liaison de données ?.
Heureusement, notre diagramme de base est toujours applicable. Si vous liez un ItemsControl à une collection, le diagramme présente l'aspect suivant :
Comme représenté dans ce diagramme, pour lier un ItemsControl à un objet de collection, il convient d'utiliser la propriété ItemsSource. Vous pouvez considérer la propriété ItemsSource comme étant le contenu du ItemsControl. Notez que la liaison est OneWay car la propriété ItemsSource prend en charge la liaison OneWay par défaut.
Comment implémenter des collections
Vous pouvez énumérer toute collection qui implémente l'interface IEnumerable. Toutefois, pour paramétrer des liaisons dynamiques afin que les insertions ou suppressions dans la collection mettent automatiquement à jour l'UI, 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 est modifiée.
WPF fournit la classe ObservableCollection<T>, qui est une implémentation intégrée d'une collection de données qui expose l'interface INotifyCollectionChanged. Notez que pour prendre entièrement en charge le transfert des valeurs de données depuis les objets sources vers les cibles, chaque objet dans votre collection qui prend en charge des propriétés pouvant être liées doit aussi implémenter l'interface INotifyPropertyChanged. Pour plus d'informations, consultez Vue d'ensemble des sources de liaison.
Avant d'implémenter votre propre collection, pensez à 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 évolué et que vous souhaitez implémenter votre propre collection, pensez à utiliser IList qui fournit une collection non générique d'objets accessibles individuellement par index et assure donc les meilleures performances.
Vues de collection
Une fois que votre ItemsControl est lié à une collection de données, il peut être utile de trier, filtrer ou grouper les données. Pour ce faire, vous utilisez des vues de collection, qui sont des classes implémentant l'interface ICollectionView.
Cette section comprend les sous-sections suivantes.
- Que sont les vues de collection ?
- Comment créer une vue
- Tri
- Filtrage
- Regroupement
- Pointeurs d'éléments actuels
- Scénario de liaison maître/détail
Que sont les vues de collection ?
Une vue de collection est une couche posée sur la collection de la source de liaison qui permet de naviguer dans la collection source et de l'afficher selon des requêtes de tri, de filtrage et de regroupement, sans devoir modifier la collection source sous-jacente. Une vue de collection maintient également un pointeur vers l'élément actuel dans 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.
Parce que les vues ne modifient pas les collections sources sous-jacentes, plusieurs vues peuvent être associées à chaque collection source. Par exemple, vous pouvez avoir une collection d'objets de Tâche. En utilisant les vues, vous pouvez afficher ces mêmes données de différentes façons. Par exemple, vous pouvez afficher des tâches triées par priorité sur le côté gauche de votre page et des tâches regroupées par zone sur le côté droit.
Comment créer une vue
L'une des manières de créer et d'utiliser une vue consiste à instancier l'objet de vue puis de l'utiliser comme la source de liaison. Par exemple, considérez l'application Liaison de données, démonstration (page éventuellement en anglais)https://go.microsoft.com/fwlink/?LinkID=163703 indiquée dans la sectionQu'est-ce que la liaison de données ?. L'application est implémentée de telle sorte que la ListBox se lie à une vue au-dessus de la collection de données plutôt que directement à celle-ci. L'exemple suivant est extrait de l'application Liaison de données, démonstration. La classe CollectionViewSource est le proxy Extensible Application Markup Language (XAML) d'une classe qui hérite de CollectionView. Dans cet exemple particulier, la Source de la vue est liée à la collection AuctionItems (de type ObservableCollection<T>) de l'objet de l'application actuelle.
<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 des éléments dans l'application, tels que ListBox :
<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8"
ItemsSource="{Binding Source={StaticResource listingDataView}}">
...
</ListBox>
Pour créer une autre vue pour la même collection, vous pouvez créer une autre instance CollectionViewSource et lui affecter un nom x:Key différent.
Le tableau suivant montre quels types de données de vue sont créés comme vue de collection par défaut ou par CollectionViewSource selon le type collection de source.
Type de collection source |
Type de vue de collection |
Remarques |
---|---|---|
Type interne basé sur CollectionView |
Impossible de grouper des éléments. |
|
Le plus rapide. |
||
Utilisation d'une vue par défaut
Spécifier une vue de collection comme source de liaison est une manière 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 crée une liaison avec sa vue par défaut. Notez que cette vue par défaut est partagée par toutes les liaisons vers la même collection, de sorte qu'une modification apportée à une vue par défaut par un contrôle lié ou le code (comme trier ou modifier le pointeur d'élément actuel, discuté ultérieurement) se répercute dans toutes les autres liaisons vers la même collection.
Pour obtenir la vue par défaut, vous utilisez la méthode GetDefaultView. Pour obtenir un exemple, consultez Comment : obtenir la vue par défaut d'une collection de données.
Vues de collection avec ADO.NET DataTables
Pour améliorer les performances, les vues de collection pour les objets DataTable ou DataView ADO.NET délèguent le tri et le filtrage à DataView. Cela a pour effet de partager le tri et le filtrage à travers 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 objet DataView.
Tri
Comme mentionné précédemment, des vues peuvent appliquer un ordre de tri à une collection. Telles qu'elles existent dans la collection sous-jacente, vos données peuvent posséder ou non un ordre inhérent pertinent. La vue au-dessus de la collection vous permet d'imposer un ordre, ou de modifier celui par défaut, en fonction de critères de comparaison que vous fournissez. Comme il s'agit d'une vue des données basée sur le client, un scénario courant est que l'utilisateur peut souhaiter trier des colonnes de données sous forme de tableau en fonction de la valeur à laquelle la colonne correspond. À l'aide de vues, ce tri défini par l'utilisateur peut être appliqué, là encore sans modifier aucunement la collection sous-jacente ou sans même devoir lancer une nouvelle requête sur le contenu de la collection. Pour obtenir un exemple, consultez Comment : trier une colonne GridView lors d'un clic sur un en-tête.
L'exemple suivant présente la logique de tri de la CheckBox « Trier par catégorie et date » de l'UI de l'application dans la section Qu'est-ce que la liaison de données ? :
private void AddSorting(object sender, RoutedEventArgs args)
{
// This sorts the items first by Category and within each Category,
// by StartDate. Notice that because Category is an enumeration,
// the order of the items is the same as in the enumeration declaration
listingDataView.SortDescriptions.Add(
new SortDescription("Category", ListSortDirection.Ascending));
listingDataView.SortDescriptions.Add(
new SortDescription("StartDate", ListSortDirection.Ascending));
}
Filtrage
Les vues peuvent aussi appliquer un filtre à une collection. Cela signifie que même si un élément peut exister dans la collection, cette vue particulière est destinée à afficher uniquement un sous-ensemble spécifique de la collection complète. Vous pouvez opérer un filtrage sur une condition dans les données. Par exemple, comme le fait l'application dans la section Qu'est-ce que la liaison de données ?, la CheckBox « Show only bargains » contient la logique permettant de filtrer les éléments qui ont coûté 25 $ ou plus. Le code suivant est exécuté pour définir ShowOnlyBargainsFilter comme le gestionnaire d'événements Filter lorsque CheckBox est activé :
listingDataView.Filter += new FilterEventHandler(ShowOnlyBargainsFilter);
Le gestionnaire d'événements ShowOnlyBargainsFilter présente l'implémentation suivante :
private void ShowOnlyBargainsFilter(object sender, FilterEventArgs e)
{
AuctionItem product = e.Item as AuctionItem;
if (product != null)
{
// Filter out products with price 25 or above
if (product.CurrentPrice < 25)
{
e.Accepted = true;
}
else
{
e.Accepted = false;
}
}
}
Si vous utilisez directement l'une des classes CollectionView au lieu de CollectionViewSource, vous devrez utiliser la propriété Filter pour spécifier un rappel. Pour obtenir un exemple, consultez Comment : filtrer les données d'une vue.
Regroupement
Hormis la classe interne qui affiche une collection IEnumerable, toutes les vues de collection prennent en charge la fonctionnalité de regroupement qui permet à l'utilisateur de partitionner la collection en groupes logiques dans la vue correspondante. Les groupes peuvent être explicites, si l'utilisateur fournit une liste de groupes, ou implicites, lorsque les groupes sont générés de manière dynamique en fonction des données.
L'exemple suivant illustre la logique de la CheckBox.« Grouper par catégorie » :
// This groups the items in the view by the property "Category"
PropertyGroupDescription groupDescription = new PropertyGroupDescription();
groupDescription.PropertyName = "Category";
listingDataView.GroupDescriptions.Add(groupDescription);
Pour un autre exemple de regroupement, consultez Comment : grouper des éléments dans un ListView implémentant un GridView.
Pointeurs d'éléments actuels
Les vues prennent également en charge la notion d'un élément actuel. Vous pouvez naviguer parmi les objets d'une vue de collection. Lorsque vous naviguez, vous déplacez un pointeur d'élément qui permet de récupérer l'objet qui existe à cet emplacement précis de la collection. Pour obtenir un exemple, consultez Comment : naviguer dans les objets d'un CollectionView de données.
WPF créant uniquement une liaison avec une collection en utilisant une vue (ou 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. Lors de la création d'une liaison avec une vue, le caractère de barre oblique ("/") dans une 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 effectue la liaison avec la collection. La deuxième ligne effectue la liaison à l'élément actuel dans la collection. La troisième ligne effectue la liaison avec la propriété Description de l'élément actuel dans la collection.
<Button Content="{Binding }" />
<Button Content="{Binding Path=/}" />
<Button Content="{Binding Path=/Description}" />
La barre oblique et la syntaxe de propriété peuvent également être associées pour parcourir une hiérarchie de collections. L'exemple suivant effectue une liaison avec 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 les opérations de tri ou de filtrage appliquées à la collection. Le tri conserve le pointeur d'élément actuel sur le dernier élément sélectionné, mais la vue de collection est désormais restructurée par rapport à celui-ci. (Peut-être l'élément sélectionné était-il auparavant au début de la liste, mais maintenant il peut être quelque part au milieu.) Le filtrage conserve l'élément sélectionné si cette sélection reste dans la vue après le filtrage. Sinon, le pointeur d'élément actuel a pour valeur le premier élément de la vue de collection filtrée.
Scénario de liaison maître/détail
La notion d'un élément actuel est utile non seulement pour la navigation parmi les éléments dans une collection, mais également pour le scénario de liaison maître/détail. Prenons de nouveau l'UI de l'application dans la section Qu'est-ce que la liaison de données ?. Dans cette application, la sélection à l'intérieur de la ListBox détermine le contenu représenté dans ContentControl. Exprimé différemment , lorsqu'un élément ListBox est sélectionné, le ContentControl montre les détails de l'article sélectionné.
Vous pouvez implémenter le scénario maître/détail en liant simplement deux ou plusieurs contrôles à une même vue. L'exemple suivant tiré de Liaison de données, démonstration (page éventuellement en anglais) affiche le balisage de la ListBox et le ContentControl que vous voyez sur l'UI de l'application dans la section Qu'est-ce que la liaison de données ?.
<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8"
ItemsSource="{Binding Source={StaticResource listingDataView}}">
...
</ListBox>
...
<ContentControl Name="Detail" Grid.Row="3" Grid.ColumnSpan="3"
Content="{Binding Source={StaticResource listingDataView}}"
ContentTemplate="{StaticResource detailsProductListingTemplate}"
Margin="9,0,0,0"/>
Vous remarquerez 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). Ceci fonctionne car lorsqu'un objet singleton (le ContentControl dans ce cas) est lié à une vue de collection, il se lie automatiquement à la CurrentItem de la vue. Notez que les objets CollectionViewSource synchronisent automatiquement la sélection et la devise. Si votre contrôle de liste n'est pas lié à un objet CollectionViewSource comme dans cet exemple, vous devrez alors affecter à sa propriété IsSynchronizedWithCurrentItem la valeur true pour que cela fonctionne.
Pour obtenir d'autres exemples, consultez Comment : effectuer une liaison à une collection et afficher des informations basées sur la sélection et Comment : utiliser le modèle maître/détail avec des données hiérarchiques.
Vous aurez 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 utilisé implicitement par la ListBox). Nous passons maintenant aux modèles de données dans la section suivante.
Modèles de données
Sans modèle de données, notre UI d'application dans la section Qu'est-ce que la liaison de données ? ressemblerait à ce qui suit :
Comme indiqué dans l'exemple de la section précédente, à la fois le contrôle ListBox et le ContentControl sont liés à l'objet de collection entier (ou plus spécifiquement, la vue sur l'objet de collection) d'AuctionItems. Sans instructions spécifiques pour afficher la collection de données, la ListBox affiche une représentation de chaîne de chaque objet dans la collection sous-jacente et le ContentControl affiche une représentation de chaîne de l'objet auquel il est lié.
Pour résoudre ce problème, l'application définit DataTemplates. Comme indiqué dans l'exemple de la section précédente, le ContentControl utilise le detailsProductListingTemplate DataTemplate de manière explicite. Le contrôle ListBox utilise implicitement le DataTemplate 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 DataTemplate, l'interface utilisateur obtenue est celle représentée dans la section Qu'est-ce que la liaison de données ?. Comme vous pouvez le voir sur cette copie d'écran, DataTemplate vous permet non seulement de placer des données dans vos contrôles, mais aussi de définir des éléments visuels agréables pour vos données. Par exemple, des DataTrigger sont utilisés dans le DataTemplate ci-dessus afin que les AuctionItem dont la valeur SpecialFeatures est HighLight s'affichent avec une bordure orange et une étoile.
Pour plus d'informations sur les modèles de données, consultez Vue d'ensemble des modèles de données.
Validation des données
Cette section comprend les sous-sections suivantes.
- Association de règles de validation à une liaison
- Fourniture de commentaires visuels
- Processus de validation
La plupart des applications acceptant une entrée utilisateur doivent posséder une logique de validation pour s'assurer que l'utilisateur a entré les informations prévues. Les contrôles de validation peuvent être basés sur le type, la plage, le format ou sur toute autre exigence spécifique à l'application. Cette section traite du fonctionnement de la validation des données dans le WPF.
Association de règles de validation à une liaison
Le modèle WPF de liaison de données vous permet d'associer des ValidationRules à votre objet Binding. Par exemple, l'exemple suivant lie un TextBox à une propriété nommée StartPrice et ajoute un objet ExceptionValidationRule à la propriété Binding.ValidationRules.
<TextBox Name="StartPriceEntryForm" Grid.Row="2" Grid.Column="1"
Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5">
<TextBox.Text>
<Binding Path="StartPrice" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Un objet ValidationRule vérifie si la valeur d'une propriété est valide. WPF inclut les deux types suivants d'objets ValidationRule intégrés :
Une ExceptionValidationRule recherche des exceptions levées pendant la mise à jour de la propriété de la source de liaison. Dans l'exemple précédent, StartPrice est de type entier. Lorsque l'utilisateur entre une valeur qui ne peut pas être convertie en nombre entier, une exception est levée, ce qui entraîne l'invalidation de la liaison. Une autre syntaxe pour définir la ExceptionValidationRule de manière explicite consiste à affecter à la propriété ValidatesOnExceptions la valeur true sur votre objet Binding ou MultiBinding.
Un objet DataErrorValidationRule recherche des erreurs déclenchées par les objets qui implémentent l'interface IDataErrorInfo. Pour obtenir un exemple d'utilisation de cette règle de validation, consultez DataErrorValidationRule. Une autre syntaxe pour définir la DataErrorValidationRule de manière explicite consiste à affecter à la propriété ValidatesOnDataErrors la valeur true sur votre objet Binding ou MultiBinding.
Vous pouvez également créer votre propre règle de validation par une dérivation à partir de la classe ValidationRule et l'implémentation de la méthode Validate. L'exemple suivant montre la règle utilisée par Add Product Listing "Start Date" TextBox de la section Qu'est-ce que la liaison de données ? :
class FutureDateRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
DateTime date;
try
{
date = DateTime.Parse(value.ToString());
}
catch (FormatException)
{
return new ValidationResult(false, "Value is not a valid date.");
}
if (DateTime.Now.Date > date)
{
return new ValidationResult(false, "Please enter a date in the future.");
}
else
{
return ValidationResult.ValidResult;
}
}
}
Le StartDateEntryForm TextBox utilise cette FutureDateRule, comme représenté dans l'exemple suivant :
<TextBox Name="StartDateEntryForm" Grid.Row="3" Grid.Column="1"
Validation.ErrorTemplate="{StaticResource validationTemplate}"
Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5">
<TextBox.Text>
<Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged"
Converter="{StaticResource dateConverter}" >
<Binding.ValidationRules>
<src:FutureDateRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Notez que comme la valeur UpdateSourceTrigger est PropertyChanged, le moteur de liaison met à jour la valeur source après chaque frappe au clavier, ce qui signifie qu'il vérifie également chaque règle dans la collection ValidationRules après chaque frappe au clavier. Nous en reparlerons plus en détail dans la section consacrée à la validation des processus.
Fourniture de commentaires visuels
Si l'utilisateur entre une valeur non valide, vous pouvez fournir des commentaires à propos de l'erreur sur l'application UI. Une façon de fournir de tels commentaires consiste à affecter à la propriété Validation.ErrorTemplate jointe un ControlTemplate personnalisé. Comme représenté dans la sous-section précédente, le TextBox StartDateEntryForm utilise un ErrorTemplate appelé validationTemplate. L'exemple suivant illustre la définition du 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 orné doit être positionné.
Vous pouvez en outre utiliser également un ToolTip pour afficher le message d'erreur. Les StartDateEntryForm et StartPriceEntryForm TextBox utilisent tous deux le style textStyleTextBox, qui crée un ToolTip affichant le message d'erreur. L'exemple suivant illustre la définition de textStyleTextBox. La propriété jointe Validation.HasError est true lorsqu'une ou plusieurs des liaisons sur les propriétés de l'élément lié présentent une 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 RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
Avec le ErrorTemplate personnalisé et le ToolTip, la TextBox StartDateEntryForm présente l'aspect suivant en cas d'erreur de validation :
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 valeur par défaut ErrorTemplate sera utilisée pour notifier les utilisateurs en cas d'erreur de validation. Le ErrorTemplate par défaut est un modèle de contrôle qui définit une bordure rouge dans la couche d'ornements. Avec le ErrorTemplate par défaut et le ToolTip, l'UI de la TextBox StartPriceEntryForm présente l'aspect suivant en cas d'erreur de validation :
Pour obtenir un exemple de la façon de fournir une logique de validation de tous les contrôles dans une boîte de dialogue, consultez la section Boîtes de dialogue personnalisées dans Vue d'ensemble des boîtes de dialogue.
Processus de validation
En règle générale, la validation se produit lorsque la valeur d'un élément cible est transférée dans la propriété de source de liaison. Ceci survient dans les liaisons TwoWay et OneWayToSource. Pour rappel, ce qui entraîne la mise à jour d'une source dépend de la valeur de la propriété UpdateSourceTrigger, comme décrit dans la section Qu'est-ce qui déclenche les mises à jour de la source.
Les éléments suivants décrivent le processus de validation. Notez que si une erreur de validation ou tout autre type d'erreur survient à un moment quelconque de ce processus, ce dernier est arrêté.
Le moteur de liaison vérifie s'il existe des objets personnalisés ValidationRule définis dont la propriété ValidationStep a la valeur RawProposedValue pour cette Binding, auquel cas il appelle la méthode Validate à chaque ValidationRule jusqu'à ce que l'une d'elles rencontre une erreur ou jusqu'à ce que toutes passent.
Le moteur de liaison appelle ensuite le convertisseur, s'il existe.
Si le convertisseur réussit, le moteur de liaison vérifie s'il existe des objets personnalisés ValidationRule définis dont la propriété ValidationStep a la valeur ConvertedProposedValue pour cette Binding, auquel cas il appelle la méthode Validate à chaque ValidationRule avec ValidationStep défini sur ConvertedProposedValue jusqu'à ce que l'une d'elles rencontre une erreur ou jusqu'à ce que toutes passent.
Le moteur de liaison définit la propriété source.
Le moteur de liaison vérifie s'il existe des objets personnalisés ValidationRule définis pour lesquels la propriété ValidationStep a la valeur UpdatedValue pour ce Binding, auquel cas il appelle la méthode Validate sur chaque objet ValidationRule ayant la propriété ValidationStep définie à UpdatedValue jusqu'à ce que l'un d'eux génère une erreur ou que tous passent. Si un DataErrorValidationRule est associé à une liaison et que sa propriété ValidationStep est définie à la valeur par défaut, UpdatedValue, DataErrorValidationRule est vérifié à ce stade. C'est également à ce stade que les liaisons pour lesquelles la propriété ValidatesOnDataErrors a la valeur true sont vérifiées.
Le moteur de liaison vérifie s'il existe des objets personnalisés ValidationRule définis pour lesquels la propriété ValidationStep a la valeur CommittedValue pour ce Binding, auquel cas il appelle la méthode Validate sur chaque objet ValidationRule ayant la propriété ValidationStep définie à CommittedValue jusqu'à ce que l'un d'eux génère une erreur ou que tous passent.
Si une règle ValidationRule ne passe pas à un moment donné 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 d'exécuter les objets ValidationRule à une étape donnée, le moteur de liaison supprime toutes les ValidationError qui ont été ajoutées à la propriété jointe Validation.Errors de l'élément lié au cours de cette étape. Par exemple, si une ValidationRule dont ValidationStep a la valeur UpdatedValue a échoué, lors de la prochaine validation, le moteur de liaison supprime immédiatement cette ValidationError avant d'appeler toute ValidationRule dont le jeu ValidationStep a la valeur UpdatedValue.
Lorsque Validation.Errors n'est pas vide, la propriété jointe Validation.HasError de l'élément a la valeur true. De même, si la propriété NotifyOnValidationError de Binding a la valeur true, le moteur de liaison déclenche l'événement attaché Validation.Error sur l'élément.
Notez également qu'un transfert de valeur valide dans l'une ou l'autre direction (cible à source ou inversement) efface la propriété jointe Validation.Errors.
Si la liaison est associée à un ExceptionValidationRule ou si sa propriété ValidatesOnExceptions a la valeur true et qu'une exception est levée au moment où le moteur de liaison définit la source, le moteur de liaison vérifie s'il existe une propriété UpdateSourceExceptionFilter. Vous pouvez choisir d'utiliser le rappel UpdateSourceExceptionFilter pour fournir un gestionnaire personnalisé pour gérer des exceptions. Si aucun UpdateSourceExceptionFilter n'est spécifié pour Binding, le moteur de liaison crée une 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é jointe PresentationTraceSources.TraceLevel sur un objet connexe à la liaison pour recevoir des informations à propos de l'état d'une liaison spécifique.
Voir aussi
Tâches
Comment : effectuer une liaison avec les résultats d'une requête LINQ
Comment : effectuer une liaison à une source de données ADO.NET
Référence
Concepts
Optimisation des performances : liaison de données
Autres ressources
Démonstration de liaison de données (page éventuellement en anglais)