Problème de Binding [Résolu]

Signaler
Messages postés
37
Date d'inscription
mardi 11 décembre 2018
Statut
Membre
Dernière intervention
16 mai 2020
-
Messages postés
14880
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
4 juin 2020
-
Bonjour, a tous et a toutes voilà:
J'ai un problème de binding que je n'arrive pas a résoudre, je sollicite donc votre aide.
Voila je remplie un Datagrid avec des donées importées d'une feuille Excel.
Chaque lignes du tableau sont transformées en Objet et envoyés dans le datagrid.
Ma classe objet implémente bien le" INotifyPropertyChanged".
Comme suit:
  namespace Test.Class
{
    [DataContract]
    public class Personn : INotifyPropertyChanged
    {
        private string name ;
        private string sexe ;
        private int age ;

        [DataMember(Name = "Name")]
        public string Name
        {
            get
            {
                return name;
            }

            set
            {
                if (value != name)
                {
                    name = value;
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("Name"));
                    }
                }
            }
        }


        [DataMember(Name = "Age")]
        public int Age
        {
            get
            {
                return age;
            }

            set
            {
                if (value != age)
                {
                    age = value;
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("Age"));
                    }
                }
            }
        }

   [DataMember(Name = "Sexe")]
        public strin Age
        {
            get
            {
                return sexe;
            }

            set
            {
                if (value != sexe)
                {
                    sexe = value;
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("Sexe"));
                    }
                }
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
}


Tout ce passe bien mon datagrid ce remplie, je pose des points d'arret sur le "set" de ma propriété Name afin de vérifier si les notifications ce font bien lorsque je change un Nom dans le datagrid... Pas de soucis ça déclenche.
Jusque la tout va bien.

Dans mon MainWindow je crée une observable collection en Static comme suit:

public static ObservableCollection<Personn> ListPersonn = new ObservableCollection<Personn>();

Pas de soucis elle se remplie bien avec toutes les données.

Ma datagrid en Xaml est gerée comme suit:
 <DataGrid x:Name="PersonnDataGrid" 
                              AutoGenerateColumns="False" 
                              EnableRowVirtualization="True" 
                              ScrollViewer.VerticalScrollBarVisibility="Visible" 
                              ItemsSource="{Binding ListPersonn}" 
                              DataContext="{Binding}"
                              Margin="321,10,70,10" 
                              RowDetailsVisibilityMode="VisibleWhenSelected" 
                              CanUserAddRows="False" 
                               >
                        <DataGrid.Columns>
                            <DataGridTextColumn x:Name="NameColumn" Binding="{Binding Name, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"  Header="Name"/>
                            <DataGridTextColumn x:Name="AgeColumn" Binding="{Binding Age}" Header="Age" />
 <DataGridTextColumn x:Name="SexeColumn" Binding="{Binding Sexe}" Header="Sexe" />


L'utilisateur de l'application peut ajouter des userscontrols à sa guise en runtime.
C'est Usercontrols ont une multitude de textbox.....
Et un combobox qui fait office de filtre de selection.
Je m'explique en Fonction de la l'age et du sexe on rempli les textbox avec le prenoms.
Comme suit:
private void AgeCbx_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
                    TextBox[] mesPrenoms = { PrenomtxtA1,  PrenomtxtA2,  PrenomtxtA3,   PrenomtxtA4,  PrenomtxtA5,  PrenomtxtA6,  PrenomtxtA7,  PrenomtxtA8 };

                    var Prenoms = MainWindow.ListPersonn
                      .Where(x => x.Age ==Convert.toInt32( AgeCbx.SelectedItem))
                      .Where(x => x.CircuitNumber == 1)
                      .Select(x => new { x.Name })
                      .ToList();
                    for (int i = 0; i < Math.Min(8, Prenoms.Count); i++)
                    {
                        mesPrenoms[i].Text = Prenoms[i].ToString();
                    }
            }


La aussi tout fonctionnent mes Textbox se remplissent bien.


Dans le Xaml de mon userControl:
<TextBox x:Name="PrenomtxtA1" Style="{StaticResource Textbox5RoundWhiteStyle}" Foreground="Red" Grid.Column="2" Grid.Row="2" Text="{Binding Name}" />

                <TextBox x:Name="PrenomtxtA2" Style="{StaticResource Textbox5RoundWhiteStyle}" Foreground="Red" Grid.Column="3" Grid.Row="2" Text="{Binding Name}"/>
                <TextBox x:Name="PrenomtxtA3" Style="{StaticResource Textbox5RoundWhiteStyle}" Foreground="Red" Grid.Column="4" Grid.Row="2" Text="{Binding Name}"  />



Mon problème est dans le binding et la notifications des textbox et du datagrid.
Quand je change la valeur du prénom de la premiere ligne du datgrid.
Par exemple Jean devient Michel mes 8 textboxs de mon user control deviennent Michel......
Alors Que j'aurais voulu que Seulement la textbox portant le nom Jean devienne Michel.
De plus si je Change la valeur de n'importe qu'elle autre ligne la notification se fait bien mais rien ne change dans les textboxs.

Désolé pour le pavé mais je suis perdu.

Merci d'avance pour vos réponses

9 réponses

Messages postés
14880
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
4 juin 2020
413
Alors, j'ai copié collé la classe Personn (qui ne contient pas de propriété CircuitNumber, donc je ne m'en suis pas servi)

Ma window de test s'appelle DeuxiemeFenetre.
       public DeuxiemeFenetre()
        {
            InitializeComponent();

            ListPersonn = new ObservableCollection<Personn>
            {
                new Personn { Name = "Jean", Age = 21, Sexe = "H"},
                new Personn { Name = "Amélie", Age = 21, Sexe = "F"},
                new Personn { Name = "Bernard", Age = 50, Sexe = "H"},
                new Personn { Name = "Odette", Age = 50, Sexe = "F"},
                new Personn { Name = "Paul", Age = 42, Sexe = "H"},
                new Personn { Name = "Delphine", Age = 42, Sexe = "F"},
                new Personn { Name = "Thomas", Age = 14, Sexe = "H"},
                new Personn { Name = "Noah", Age = 14, Sexe = "F"},
            };

            DataContext = this;
        }

        public static ObservableCollection<Personn> ListPersonn { get; set; }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            UserControl1 userControl1 = new UserControl1
            {
                Margin = new Thickness(10, 40, 0, 0),
                VerticalAlignment = VerticalAlignment.Top,
                HorizontalAlignment = HorizontalAlignment.Left,
                Height = 100,
                Width = 280
            };
            grid.Children.Add(userControl1);
        }
    }


<Window x:Class="Test_WPF.DeuxiemeFenetre"
        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:Test_WPF"
        mc:Ignorable="d"
        Title="DeuxiemeFenetre" Height="300" Width="600" DataContext="{Binding}">
    <Grid Name="grid" DataContext="{Binding .}">
        <DataGrid x:Name="PersonnDataGrid" 
                              AutoGenerateColumns="False" 
                              EnableRowVirtualization="True" 
                              ScrollViewer.VerticalScrollBarVisibility="Visible" 
                              ItemsSource="{Binding ListPersonn}" 
                              DataContext="{Binding .}"
                              Margin="321,10,70,10" 
                              RowDetailsVisibilityMode="VisibleWhenSelected" 
                              CanUserAddRows="False" 
                               >
            <DataGrid.Columns>
                <DataGridTextColumn x:Name="NameColumn" Binding="{Binding Name, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"  Header="Name"/>
                <DataGridTextColumn x:Name="AgeColumn" Binding="{Binding Age}" Header="Age" />
                <DataGridTextColumn x:Name="SexeColumn" Binding="{Binding Sexe}" Header="Sexe" />
            </DataGrid.Columns>
        </DataGrid>
        <Button Content="Ajoute Usercontrol" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="118" Click="Button_Click"/>
    </Grid>
</Window>


Le userControl
    public partial class UserControl1 : UserControl, INotifyPropertyChanged
    {
        public UserControl1()
        {
            InitializeComponent();

            DataContext = this;

            DeuxiemeFenetre.ListPersonn.CollectionChanged += ListPersonn_CollectionChanged;
        }

        private void ListPersonn_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            switch(e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                case NotifyCollectionChangedAction.Remove:
                case NotifyCollectionChangedAction.Replace:
                case NotifyCollectionChangedAction.Reset:
                    GenerePropertyChnaged("Ages");//si on modifie le nombres d'enregistrements
                    break;
            }
        }

        List<Personn> filtres;
        private void appliqueFiltre()
        {
            filtres = DeuxiemeFenetre.ListPersonn.Where(x => x.Age == Convert.ToInt32(AgeCbx.SelectedItem)).ToList();
            GenerePropertyChnaged("Filtres");
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void AgeCbx_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            appliqueFiltre();
        }

        public List<Personn> Filtres
        {
            get
            {
                return filtres;
            }
        }

        public List<int> Ages
        {
            get
            {
                return DeuxiemeFenetre.ListPersonn.Select(p => p.Age).Distinct().ToList();
            }
        }

        private void GenerePropertyChnaged(params string[] Props)
        {
            foreach(string p in Props)
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(p));
        }
    }


<UserControl x:Class="Test_WPF.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Test_WPF"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="73*"/>
            <RowDefinition Height="237*"/>
            <RowDefinition Height="140*"/>
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="120*"/>
            <ColumnDefinition Width="104*"/>
            <ColumnDefinition Width="177*"/>
            <ColumnDefinition Width="187*"/>
            <ColumnDefinition Width="212*"/>
        </Grid.ColumnDefinitions>

        <ComboBox Name="AgeCbx" ItemsSource="{Binding Ages}" SelectionChanged="AgeCbx_SelectionChanged"/>

        <TextBox x:Name="PrenomtxtA1" Foreground="Red" Grid.Column="2" Grid.Row="2" Text="{Binding Filtres[0].Name}" />
        <TextBox x:Name="PrenomtxtA2" Foreground="Red" Grid.Column="3" Grid.Row="2" Text="{Binding Filtres[1].Name}"/>
        <TextBox x:Name="PrenomtxtA3" Foreground="Red" Grid.Column="4" Grid.Row="2" Text="{Binding Filtres[2].Name}"  />



    </Grid>
</UserControl>



PS 1: plutôt que 2 clause Where, tu peux n'en écrire qu'une avec un & entre chaque conditions
PS 2: les sexes, il n'y en a que 3, Femme, Homme, Transgenre, tu aurais pu utiliser un enum, ça prend moins de place quand on sérialize, et il ne peut pas y avoir d'erreur d'orthographe.
Messages postés
14880
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
4 juin 2020
413
Bonjour

Tout d'abord plutôt que répéter N fois

                     if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("Nom_Propriété"));
                    }


Tu pourrais écrire une petite méthode
GenerePropertyChanged(string Propriete)
{
                     if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs(Propriete));
                    }
}

et dans la propriété
                     GenerePropertyChanged("Nom_Propriété");


On peut même faire en sorte que cette méthode ne soit écrite qu'une seule fois pour toutes tes classes.



Pour ton problème de binding, affecter en code behind une propriété bindée, casse le binding.

Donc je pense que cette ligne
mesPrenoms[i].Text = Prenoms[i].ToString();


Casse ce binding
                <TextBox x:Name="PrenomtxtA2" Style="{StaticResource Textbox5RoundWhiteStyle}" Foreground="Red" Grid.Column="3" Grid.Row="2" Text="{Binding Name}"/>



PS pour la coloration syntaxique xaml = xml
Messages postés
37
Date d'inscription
mardi 11 décembre 2018
Statut
Membre
Dernière intervention
16 mai 2020

Merci pour la réponse je vais de ce pas changer ma class en implémentant ta méthode.
C'est noté pour les balise Xaml.
Aurais- tu une idée pour éviter ce cassage de Binding?
Merci d'avance
Messages postés
14880
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
4 juin 2020
413
Le combobox est sur la window ou dans les controles?
Messages postés
37
Date d'inscription
mardi 11 décembre 2018
Statut
Membre
Dernière intervention
16 mai 2020

Le combobox n'est pas dans la Mainwindow mais dans le usercontrol.
Messages postés
14880
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
4 juin 2020
413
J’y regarde de plus près dans la soirée
Messages postés
37
Date d'inscription
mardi 11 décembre 2018
Statut
Membre
Dernière intervention
16 mai 2020

Merci c'est super sympa.
Ca me retirerais une belle épine du pied.

Ps:j'ai trouvé une combine qui pour moi n'est pas une solution mais vraiment une combine.
C'est de rechercher toutes les combobox contenus dans les usercontrol et d'incrementer et tout de suite après de decrementer l'index des combobox. De mettre ça sur un bouton "Refresh" mais c'est vraiment du bricolage......
Messages postés
37
Date d'inscription
mardi 11 décembre 2018
Statut
Membre
Dernière intervention
16 mai 2020

Salut Whismeril,
Franchement c'est plus qu'un merci que je te dois.....
Grace a ton bout de code je viens d'apprendre plus de chose en 4h que dans un mois de "confinement".
Ça fonctionne du feu de dieu Encore merci mille fois du fond du cœur.
Il y a juste un petit truc que je ne capte pas c'est le datacontext= {Binding .}.
Est ce que ça veut dire que le controle hérite du datacontext de son Parent?
Et que du coup il faut les implémenter sur tout les parents du control que l'on veut Binder?

En gros imaginons j'ai un Window qui contient un Stackpanel qui lui même contient un grid, qui contient un canvas, ect ect.....
Dois je utiliser le datacontext="{Binding .} sur tout les parents?

En tout cas je suis très content je me coucherais moins con ce soir , il me manque plus qu'a digerer tout Ca.

Bonne journée a toi et encore MERCI.
Messages postés
14880
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
4 juin 2020
413
Il y a juste un petit truc que je ne capte pas c'est le datacontext= {Binding .}.
Est ce que ça veut dire que le controle hérite du datacontext de son Parent?


Oui, y'a des fois où ça marche sans et des fois où il le faut, alors je le mets par défaut
Messages postés
14880
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
4 juin 2020
413
Ton problème n'était pas ce que j'avais pensé au premier regard (ou p'tet que ça merdait aussi, mais c'était pas la raison principale).

De la façon dont tu avais fait, les Personn n'étaient pas la source de tes textBox, mais c'était une liste avec juste les noms