Aperçu du binding en WPF

Ce tutoriel se propose de donner un aperçu du binding par « vue » en WPF.

Cas pratique

Nous disposons d’instances d’une classe Equipe

C#

using System.Collections.ObjectModel;
using System.ComponentModel;

namespace TestBinding
{
    class Equipe : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;

        private void GenerePropertyChanged(string Propriete)
        {
            if (this.PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(Propriete));
        }

        #endregion


        private Personne entraineur;
        /// <summary>
        /// Entraineur de l'équipe
        /// </summary>
        public Personne Entraineur
        {
            get { return entraineur; }
            set
            {
                if (entraineur != value)
                {
                    entraineur = value;
                    GenerePropertyChanged("Entraineur");
                }
            }
        }

        private string nom;
        /// <summary>
        /// Nom de l'équipe
        /// </summary>
        public string Nom
        {
            get { return nom; }
            set
            {
                if (nom != value)
                {
                    nom = value;
                    GenerePropertyChanged("Nom");
                }
            }
        }

        private string sport;
        /// <summary>
        /// Sport pratiqué
        /// </summary>
        public string Sport
        {
            get { return sport; }
            set
            {
                if (sport != value)
                {
                    sport = value;
                    GenerePropertyChanged("Sport");
                }
            }
        }

        private string niveau;
        /// <summary>
        /// Niveau de l'équipe
        /// </summary>
        public string Niveau
        {
            get { return niveau; }
            set
            {
                if (niveau != value)
                {
                    niveau = value;
                    GenerePropertyChanged("Niveau");
                }
            }
        }

        /// <summary>
        /// Liste des joueurs
        /// </summary>
        public ObservableCollection<Personne> Joueurs { get; set; } = new ObservableCollection<Personne>();
    }
}


using System.ComponentModel;

namespace TestBinding
{
    class Personne : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;

        private void GenerePropertyChanged(string Propriete)
        {
            if (this.PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(Propriete));
        }

        #endregion

        private string nom;
        /// <summary>
        /// Le nom de la personne
        /// </summary>
        public string Nom
        {
            get { return nom; }
            set
            {
                if (nom != value)
                {
                    nom = value;
                    GenerePropertyChanged("Nom");
                }
            }
        }

        private string prenom;
        /// <summary>
        /// Le prénom de la personne
        /// </summary>
        public string Prenom
        {
            get { return prenom; }
            set
            {
                if (prenom != value)
                {
                    prenom = value;
                    GenerePropertyChanged("Prenom");
                }
            }
        }

        private int numero;
        /// <summary>
        /// Numéro de téléphone de la personne
        /// </summary>
        public int Numero
        {
            get { return numero; }
            set
            {
                if (numero != value)
                {
                    numero = value;
                    GenerePropertyChanged("Numero");
                }
            }
        }


        public Personne(string LePrenom, string LeNom, int LeNumero)
        {
            Nom = LeNom;
            Prenom = LePrenom;
            Numero = LeNumero;
        }

    }
}

VB.Net

Imports System.Collections.ObjectModel
Imports System.ComponentModel

Public Class Equipe
    Implements INotifyPropertyChanged
#Region "INotifyPropertyChanged"
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Private Sub GenerePropertyChanged(ByVal Propriete As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(Propriete))
    End Sub

#End Region


    Private lEntraineur As Personne
    ''' <summary>
    ''' Entraineur de l'équipe
    ''' </summary>
    Public Property Entraineur() As Personne
        Get
            Return lEntraineur
        End Get
        Set(ByVal value As Personne)
            If lEntraineur IsNot value Then
                lEntraineur = value
                GenerePropertyChanged("Entraineur")
            End If
        End Set
    End Property

    Private leNom As String
    ''' <summary>
    ''' Nom de l'équipe
    ''' </summary>
    Public Property Nom() As String
        Get
            Return leNom
        End Get
        Set(ByVal value As String)
            If leNom <> value Then
                leNom = value
                GenerePropertyChanged("Nom")
            End If
        End Set
    End Property

    Private leSport As String
    ''' <summary>
    ''' Sport pratiqué
    ''' </summary>
    Public Property Sport() As String
        Get
            Return leSport
        End Get
        Set(ByVal value As String)
            If leSport <> value Then
                leSport = value
                GenerePropertyChanged("Sport")
            End If
        End Set
    End Property

    Private leNiveau As String
    ''' <summary>
    ''' Niveau de l'équipe
    ''' </summary>
    Public Property Niveau() As String
        Get
            Return leNiveau
        End Get
        Set(ByVal value As String)
            If leNiveau <> value Then
                leNiveau = value
                GenerePropertyChanged("Niveau")
            End If
        End Set
    End Property

    ''' <summary>
    ''' Liste des joueurs
    ''' </summary>
    Public Property Joueurs() As ObservableCollection(Of Personne) = New ObservableCollection(Of Personne)()
End Class


Imports System.ComponentModel

Public Class Personne
    Implements INotifyPropertyChanged
#Region "INotifyPropertyChanged"
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Private Sub GenerePropertyChanged(ByVal Propriete As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(Propriete))
    End Sub

#End Region

    Private leNom As String
    ''' <summary>
    ''' Le nom de la personne
    ''' </summary>
    Public Property Nom() As String
        Get
            Return leNom
        End Get
        Set(ByVal value As String)
            If leNom <> value Then
                leNom = value
                GenerePropertyChanged("Nom")
            End If
        End Set
    End Property

    Private lePrenom As String
    ''' <summary>
    ''' Le prénom de la personne
    ''' </summary>
    Public Property Prenom() As String
        Get
            Return lePrenom
        End Get
        Set(ByVal value As String)
            If lePrenom <> value Then
                lePrenom = value
                GenerePropertyChanged("Prenom")
            End If
        End Set
    End Property

    Private leNumero As Integer
    ''' <summary>
    ''' Numéro de téléphone de la personne
    ''' </summary>
    Public Property Numero() As Integer
        Get
            Return leNumero
        End Get
        Set(ByVal value As Integer)
            If leNumero <> value Then
                leNumero = value
                GenerePropertyChanged("Numero")
            End If
        End Set
    End Property

    Public Sub New(ByVal LePrenom As String, ByVal LeNom As String, ByVal LeNumero As Integer)
        Nom = LeNom
        Prenom = LePrenom
        Numero = LeNumero
    End Sub

End Class

Et nous souhaitons afficher une instance dans cette fenêtre

<Window x:Class="TestBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestBinding"
        mc:Ignorable="d"
        Title="FenetreEquipe" Height="265.546" Width="437.815">
    <Grid>
        <GroupBox Header="Général" HorizontalAlignment="Left" Height="122" VerticalAlignment="Top" Width="198" Margin="10,10,0,0">
            <Grid>
                <Label Content="Nom équipe" HorizontalAlignment="Left" Height="25" Margin="4,11,0,0" VerticalAlignment="Top" Width="80"/>
                <TextBox HorizontalAlignment="Left" Height="25" Margin="84,11,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="100"/>
                <Label Content="Sport" HorizontalAlignment="Left" Height="25" Margin="4,41,0,0" VerticalAlignment="Top" Width="80"/>
                <TextBox HorizontalAlignment="Left" Height="25" Margin="84,43,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="100"/>
                <Label Content="Niveau" HorizontalAlignment="Left" Height="25" Margin="4,73,0,0" VerticalAlignment="Top" Width="80"/>
                <TextBox HorizontalAlignment="Left" Height="25" Margin="84,73,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="100"/>
            </Grid>
        </GroupBox>
        <GroupBox Header="Entraineur" HorizontalAlignment="Left" Height="92" VerticalAlignment="Top" Width="198" Margin="10,137,0,0">
            <Grid>
                <Label Content="Nom" HorizontalAlignment="Left" Height="25" Margin="4,11,0,0" VerticalAlignment="Top" Width="80"/>
                <TextBox HorizontalAlignment="Left" Height="25" Margin="84,11,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="100"/>
                <Label Content="Prénom" HorizontalAlignment="Left" Height="25" Margin="4,41,0,0" VerticalAlignment="Top" Width="80"/>
                <TextBox HorizontalAlignment="Left" Height="25" Margin="84,43,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="100"/>
            </Grid>
        </GroupBox>
        <GroupBox Header="Joueurs" HorizontalAlignment="Left" Height="219" Margin="213,10,0,0" VerticalAlignment="Top" Width="207">
            <ListBox HorizontalAlignment="Left" Height="185" Margin="4,7,0,0" VerticalAlignment="Top" Width="187"/>
        </GroupBox>

    </Grid>
</Window>
A noter, en WPF un GroupBox ne peut contenir qu’un seul contrôle, il faut donc y placer un conteneur (ici un Grid) qui sera chargé de disposer les contrôles enfants.

Ce que nous pourrions faire

Nous pourrions donner un nom à chaque contrôle, puis affecter une à une les propriétés de l’équipe au contrôle adéquat.
Nous pourrions aussi affecter la liste de joueurs à l’ItemsSource de la ListBox.
Mais ce serait passer à coté d’un des points forts de WPF.

La vue

Le principe d’une vue est qu’une instance regroupe toutes les données nécessaires à l’affichage d’une fenêtre.
Cette vue est donnée « à manger » à la fenêtre en une seule fois, sans se soucier de conversion de type (au moins pour les types de base).

Le code behind

Pour l’exemple, il nous faut une instance d’Equipe, et binder cette instance à la fenêtre

C#

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = InitDatas();
        }

        /// <summary>
        /// Crée une équipe pour l'exemple
        /// </summary>
        /// <returns></returns>
        private Equipe InitDatas()
        {
            Equipe equipe = new Equipe();
            equipe.Nom = "Les champions";
            equipe.Niveau = "Top 14";
            equipe.Sport = "Rugby";
            equipe.Entraineur = new Personne("Gérard", "Manvu", 0101010101);
            equipe.Joueurs.Add(new Personne("Alain", "Di", 0202020202));
            equipe.Joueurs.Add(new Personne("Jean","Sort", 0303030303));
            equipe.Joueurs.Add(new Personne("Jérémie", "Lecouvert", 0404040404));
            equipe.Joueurs.Add(new Personne("Paul", "Ochon", 0505050505));
            equipe.Joueurs.Add(equipe.Entraineur);
            return equipe;
        }

    }

VB.Net

 Partial Public Class MainWindow
  Inherits Window
  Public Sub New()
   InitializeComponent()

   Me.DataContext = InitDatas()
  End Sub

  ''' <summary>
  ''' Crée une équipe pour l'exemple
  ''' </summary>
  ''' <returns></returns>
  Private Function InitDatas() As Equipe
   Dim equipe As New Equipe()
   equipe.Nom = "Les champions"
   equipe.Niveau = "Top 14"
   equipe.Sport = "Rugby"
   equipe.Entraineur = New Personne("Gérard", "Manvu", 0101010101)
   equipe.Joueurs.Add(New Personne("Alain", "Di", 0202020202))
   equipe.Joueurs.Add(New Personne("Jean","Sort", 0303030303))
   equipe.Joueurs.Add(New Personne("Jérémie", "Lecouvert", 0404040404))
   equipe.Joueurs.Add(New Personne("Paul", "Ochon", 0505050505))
   equipe.Joueurs.Add(equipe.Entraineur)
   Return equipe
  End Function

 End Class

Et c’est tout.

Le xaml

Il faut maintenant décrire dans le xaml comment afficher tout cela.

Tout d’abord, puisque l’Equipe est bindée sur le DataContext de la fenêtre, il faut que celui-ci soit averti que ses données sont issues du binding

<Window x:Class="TestBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestBinding"
        mc:Ignorable="d"
        Title="MainWindow" Height="265" Width="437"
        DataContext="{Binding}"><!--C'est ici-->

Ensuite, il faut que les contrôles enfants aient accès au même contexte que leurs parents (le premier Grid, le GroupBox contenant les informations générales etc…)

    <Grid DataContext="{Binding .}"><!--C'est ici-->
        <GroupBox Header="Général" HorizontalAlignment="Left" Height="122" VerticalAlignment="Top" Width="198" Margin="10,10,0,0" DataContext="{Binding .}"><!--Là-->
            <Grid DataContext="{Binding .}"><!--et là-->
                <Label Content="Nom équipe" HorizontalAlignment="Left" Height="25" Margin="4,11,0,0" VerticalAlignment="Top" Width="80"/>
                <TextBox HorizontalAlignment="Left" Height="25" Margin="84,11,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="100" Text="{Binding Nom}"/>
                <Label Content="Sport" HorizontalAlignment="Left" Height="25" Margin="4,41,0,0" VerticalAlignment="Top" Width="80"/>
                <TextBox HorizontalAlignment="Left" Height="25" Margin="84,43,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="100" Text="{Binding Sport}"/>
                <Label Content="Niveau" HorizontalAlignment="Left" Height="25" Margin="4,73,0,0" VerticalAlignment="Top" Width="80"/>
                <TextBox HorizontalAlignment="Left" Height="25" Margin="84,73,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="100" Text="{Binding Niveau}"/>
            </Grid>
<Grid DataContext="{Binding .}">

ça veut dire : « Comme au-dessus »

Le GroupBox entraineur, et surtout ses contrôles enfants, ont besoin d’avoir accès à la propriété Entraineur

        <GroupBox Header="Entraineur" HorizontalAlignment="Left" Height="92" VerticalAlignment="Top" Width="198" Margin="10,137,0,0" DataContext="{Binding Entraineur}"><!--C'est plus un point-->
            <Grid>
                <Label Content="Nom" HorizontalAlignment="Left" Height="25" Margin="4,11,0,0" VerticalAlignment="Top" Width="80"/>
                <TextBox HorizontalAlignment="Left" Height="25" Margin="84,11,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="100" Text="{Binding Nom}"/>
                <Label Content="Prénom" HorizontalAlignment="Left" Height="25" Margin="4,41,0,0" VerticalAlignment="Top" Width="80"/>
                <TextBox HorizontalAlignment="Left" Height="25" Margin="84,43,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="100" Text="{Binding Prenom}"/>
            </Grid>
        </GroupBox>

Pour la ListBox, ça se complique un peu. Nous voulons sur une ligne voir le prénom et le nom d’un joueur.
Il faut définir cet affichage. Il peut être décrit au niveau de la fenêtre, dans un autre fichier ou directement au niveau de la ListBox


        <GroupBox Header="Joueurs" HorizontalAlignment="Left" Height="219" Margin="213,10,0,0" VerticalAlignment="Top" Width="207" DataContext="{Binding .}"><!--ici c'est un point-->
                <ListBox HorizontalAlignment="Left" Height="185" Margin="4,7,0,0" VerticalAlignment="Top" Width="187" ItemsSource="{Binding Joueurs}"><!--là on binde l'itemsSource-->
                    <ListBox.ItemTemplate><!--Et là on décrit le modèle d'affichage d'un joueur-->
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding Prenom}" Margin="2"/>
                                <TextBlock Text="{Binding Nom}" Margin="2"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
       </GroupBox>

Jusqu’ici aucun contrôle n’a de nom.
Et pourtant, tout s’affiche bien.

Maintenant, on souhaite afficher le numéro de téléphone du joueur sélectionné.
Une petite retouche du xaml, il faut donner un nom à la ListBox, pour pouvoir la référencer auprès de l’affichage du numéro.

        <GroupBox Header="Joueurs" HorizontalAlignment="Left" Height="214" Margin="213,10,0,0" VerticalAlignment="Top" Width="207" DataContext="{Binding .}"><!--ici c'est un point-->
            <Grid>
                <ListBox Name="lstJoueurs" HorizontalAlignment="Left" Height="141" Margin="4,7,0,0" VerticalAlignment="Top" Width="187" ItemsSource="{Binding Joueurs}"><!--là on binde l'itemsSource-->
                    <ListBox.ItemTemplate><!--Et là on décrit le modèle d'affichage d'un joueur-->
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding Prenom}" Margin="2"/>
                                <TextBlock Text="{Binding Nom}" Margin="2"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
                <Label Content="Téléphone" HorizontalAlignment="Left" Height="29" Margin="10,153,0,0" VerticalAlignment="Top" Width="64"/>
                <TextBox HorizontalAlignment="Left" Height="29" Margin="79,153,0,0" TextWrapping="Wrap" DataContext="{Binding ElementName=lstJoueurs, Path=SelectedItem}" Text="{Binding Numero}" VerticalAlignment="Top" Width="106"/><!--le dataconext est bindé sur la listBox-->
            </Grid>
        </GroupBox>

Mais le zéro de devant a disparu!
Et oui, car la propriété est un int, les zéro à gauche sont considérés comme« inutiles ».
Vous le vouliez ce zéro? il suffit de formater

Text="{Binding Numero, StringFormat={}{0:D10}}"

comme on ferait avec string.Format.

Et que diriez-vous d’une infobulle qui affiche la date de naissance du joueur? Cela nécessite d’ajouter une propriété DateTime à la classe et de la renseigner (je vous passe le code…)

                    <ListBox.ItemTemplate><!--Et là on décrit le modèle d'affichage d'un joueur-->
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal" ToolTip="{Binding Naissance}"><!--rien de plus, pas de conversion-->
                                <TextBlock Text="{Binding Prenom}" Margin="2"/>
                                <TextBlock Text="{Binding Nom}" Margin="2"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>


Le format par défaut est à l’américaine, mais vous avez vu plus haut qu’il est facile de choisir son format d’affichage.

Maintenant, nous souhaitons que les prénoms des joueurs soient affichés en différentes couleurs selon leur âge:

  • Bleu pour les 8 ans.
  • Vert pour les 10 ans.
  • Rouge pour les adultes.

Nous ajoutons une propriété Age à la classe Personne (là aussi, je vous passe le code).

A noter, pour les besoins de l’article, le projet utilise l’année 2018 pour le calcul de l’âge

Il faut ajouter un trigger de données à la TextBox correspondante

                                <TextBlock Text="{Binding Prenom}" Margin="2">
                                    <TextBlock.Style>
                                        <Style TargetType="TextBlock">
                                            <Style.Triggers>
                                                <DataTrigger Binding="{Binding Age}" Value="8">
                                                    <Setter Property="Foreground" Value="Blue"/>
                                                </DataTrigger>
                                                <DataTrigger Binding="{Binding Age}" Value="10">
                                                    <Setter Property="Foreground" Value="Green"/>
                                                </DataTrigger>
                                                <DataTrigger Binding="{Binding Age}" Value="28">
                                                    <Setter Property="Foreground" Value="Red"/>
                                                </DataTrigger>
                                            </Style.Triggers>
                                        </Style>
                                    </TextBlock.Style>
                                </TextBlock>

On peut trigger à partir

  • de plusieurs propriétés => MultiDataTrigger.
  • de l’état d’un autre contrôle (un survol de souris, une case cochée etc…) => Trigger.
  • de plusieurs états => MultiTrigger.
  • d’un évènement => EventTrigger.

Les exemples sont légions sur le Net.
Par contre, on ne peut utiliser qu’une valeur fixe: on ne peut pas écrire

Value < "9"

par exemple.
Il faut utiliser un convertisseur personnalisé.

Imaginons maintenant que la GroupBox des joueurs doit avoir un fond à la couleur de l’équipe et que cette couleur est saisie en string dans la classe.
Il nous faut donc un convertisseur

C#

    public class ConvertisseurStringVersCouleur : IValueConverter
    {
        /// <summary>
        /// Décrit la conversion entre la donnée et le type attendu par la propriété
        /// Ici un texte donne la couleur, et le controle attend un SolidColorBrush
        /// </summary>
        /// <param name="value">Valeur de la propriété</param>
        /// <param name="targetType">Type attendu</param>
        /// <param name="parameter">Paramètre (s'il y en a)</param>
        /// <param name="culture">Culture à utliser</param>
        /// <remarks>dans notre cas seul value est utilisée, mais souvent tous les paramètres sont utiles</remarks>
        /// <returns></returns>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string couleurStringEnMinuscule = ((string)value).ToLower();
            switch (couleurStringEnMinuscule)
            {
                case "bleu":
                    return new SolidColorBrush(Colors.LightBlue);

                case "rouge":
                    return new SolidColorBrush(Colors.Red);

                default:
                    return new SolidColorBrush(Colors.Transparent);
            }
        }

        /// <summary>
        /// Décrit si besoin la conversion inverse.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

VB.Net

Imports System.Globalization

Public Class ConvertisseurStringVersCouleur
    Implements IValueConverter
    ''' <summary>
    ''' Décrit la conversion entre la donnée et le type attendu par la propriété
    ''' Ici un texte donne la couleur, et le controle attend un SolidColorBrush
    ''' </summary>
    ''' <param name="value">Valeur de la propriété</param>
    ''' <param name="targetType">Type attendu</param>
    ''' <param name="parameter">Paramètre (s'il y en a)</param>
    ''' <param name="culture">Culture à utliser</param>
    ''' <remarks>dans notre cas seul value est utilisée, mais souvent tous les paramètres sont utiles</remarks>
    ''' <returns></returns>
    Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.Convert
        Dim couleurStringEnMinuscule As String = (CStr(value)).ToLower()
        Select Case couleurStringEnMinuscule
            Case "bleu"
                Return New SolidColorBrush(Colors.LightBlue)

            Case "rouge"
                Return New SolidColorBrush(Colors.Red)

            Case Else
                Return New SolidColorBrush(Colors.Transparent)
        End Select
    End Function

    Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
        Throw New NotImplementedException()
    End Function
End Class
A noter, ce convertisseur traduit de string vers SolidColorBrush mais pas dans l’autre sens.

Et il doit être référencé et utilisé dans le xaml.

<Window x:Class="TestBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestBinding"> <!--si le namespace local n'existe pas on l'ajoute-->

Puis on référence une ressource à la fenêtre

<Window x:Class="TestBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestBinding"
        mc:Ignorable="d"
        Title="MainWindow" Height="265" Width="437"
        DataContext="{Binding}"><!--C'est ici-->
    <Window.Resources>
        <local:ConvertisseurStringVersCouleur x:Key="monConvertisseur"/>
    </Window.Resources>

Et enfin, on affecte la couleur au GroupBox

        <GroupBox Header="Joueurs" HorizontalAlignment="Left" Height="214" Margin="213,10,0,0" VerticalAlignment="Top" Width="207" DataContext="{Binding .}" Background="{Binding Couleur, Converter={StaticResource monConvertisseur}}"><!--Le BackGround fait appel à notre convertisseur-->

Conclusion

Un seul contrôle a dû être nommé.
Pas besoin de gérer les conversions de int et de DateTime, cependant on peut personnaliser le format.
Tant que l’interface est simple, tout se gère au niveau du xaml, ce qui implique qu’un designer n’a pas forcément besoin d’apprendre le C# ou VB.Net. Bien sûr du code peut être nécessaire si l’on doit écrire un convertisseur de type personnalisé, mais cela peut être réalisé par le codeur behind.
A l’inverse, il existe des outils permettant de créer facilement une interface XAML: Blend, Kaxml, etc

Codes Sources

Vous pouvez télécharger les projets C# et VB.Net ici

Merci à VB95 et JMO pour leurs corrections et commentaires.

A voir également
Ce document intitulé « Aperçu du binding en WPF » issu de CodeS SourceS (codes-sources.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.
Rejoignez-nous