none
Binding ComboBox depuis LINQ XML RRS feed

  • Question

  • Bonjour à tous,

    Voilà, je débute (depuis 2 jours) en WPF et avec LINQ

    De plus je n'ai qu'un an d'une pratique occasionnel de C#, et sans jamais utiliser de DataBinding

    Je viens vers vous car j'ai bloqué toute la journée sur un problème, et je commence à saturer sur les différentes doc.

    Je vous serais très reconnaissant de privilégier une réponse descriptive pour résoudre précisément ma question avant toute réflexion du genre "demandez moi de quoi vous avez besoin, je vous dirais comment vous en passer". En effet, la solution que je met en place n'est peut être pas ce qu'il y a de plus optimal, mais j'ai besoins de comprendre comment la faire marcher. Par contre, je suis aussi ouvert à tout autre solution illustré.

    Pour résumer mon problème, je cherche à faire un DataBinding complexe depuis un fichier XML vers une ComboBox editable.

    La partie que me pose problème se trouve dans un contrôle, et je ne passe qu'une partie du fichier XML sous forme d'un XElement

    Voici un exemple du contenu XML du XElement en question :

    <racine>
        <element nom="Element 1">
          <rubrique nom="Element 1 - rubrique 1">
            <etat valeur="Hors service">
              <option valeur="En service" />
              <option valeur="Hors service service" />
            </etat>
            <observation>lorem ipsum</observation>
          </rubrique>
          <rubrique nom="Element 1 - rubrique 2">
            <etat>
            </etat>
            <observation>titi toto</observation>
          </rubrique>
        </element>
        <element nom="Element 2">
          <rubrique nom="Element 2 - rubrique 1">
            <etat valeur="foo">
            </etat>
            <observation>youpi</observation>
          </rubrique>
          <rubrique nom="Element 2 - rubrique 2">
            <etat valeur="bar">
              <option valeur="En service" />
            </etat>
            <observation></observation>
          </rubrique>
        </element>
    </racine>
    

    Voici le code behind de mon contrôle MonControle.xaml.cs :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Xml.Linq;
    using System.ComponentModel;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    
    namespace MonProjet.Controles
    {
        /// <summary>
        /// Logique d'interaction pour MonControle.xaml
        /// </summary>
        public partial class MonControle : UserControl
        {
            XElement xRacine;
    
            ObservableCollection<XElement> xElementsObservable = new ObservableCollection<XElement>();
    
            public MonControle()
            {
                InitializeComponent();
                DataContext = xElementsObservable;
            }
    
            #region Propriété Attribus
            [Category("Configuration"), Browsable(false), Description("Element XML racine")]
            public XElement xRacine
            {
                get
                {
                    return xRacine;
                }
                set
                {
                    this.xRacine = value;
                    MajXElementsObservable();
    
                }
            }
            #endregion
    
            private void MajXElementsObservable()
            {
                var requette = from xElements in xRacine.Descendants("element")
                               select (XElement)xElements;
                xElementsObservable.Clear();
                foreach (XElement xElement in requette)
                {
                    xElementsObservable.Add(xElement);
                }
            }
    
        }
    }
    
    

    pour finir, voici le fichier xaml MonControle.xaml :

    <UserControl x:Class="MonProjet.Controles.MonControle"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Height="Auto" Width="Auto">
        <!--
        http://www.youdev.net/post/2008/09/23/WPF-SplitContainer-2.aspx
        http://www.youdev.net/post/2009/03/19/WPF-SplitContainer-Part-2.aspx
        -->
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="25*"/>
                <ColumnDefinition Width="Auto" MinWidth="4"/>
                <ColumnDefinition Width="75*"/>
            </Grid.ColumnDefinitions>
            <DockPanel Grid.Column="0" LastChildFill="True">
                <ListBox Name="lbxElements" ItemsSource="{Binding UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Attribute[nom].Value" />
            </DockPanel>
            <GridSplitter Grid.Column="1" ResizeBehavior="PreviousAndNext" Width="4" VerticalAlignment="Stretch"/>
            <DockPanel Grid.Column="2" LastChildFill="True" DataContext="{Binding Path=SelectedItem.Elements[rubrique], ElementName=lbxElements, UpdateSourceTrigger=PropertyChanged}">
                <ListBox ItemsSource="{Binding UpdateSourceTrigger=PropertyChanged}"
                         IsSynchronizedWithCurrentItem="True">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <GroupBox Header="{Binding Path=Attribute[nom].Value}">
                                <StackPanel Orientation="Horizontal">
                                    <!-- http://stackoverflow.com/questions/561166/binding-wpf-combobox-to-a-custom-list -->
                                    <ComboBox MinWidth="75" IsEditable="True"
                                              ItemsSource="{Binding Path=Element[etat].Elements[option], UpdateSourceTrigger=PropertyChanged}"
                                              DisplayMemberPath="Attribute[valeur].Value"
                                              SelectedValuePath="Attribute[valeur].Value" 
                                              SelectedValue="{Binding Path=Element[etat].Element[option].Attribute[valeur].Value}"
                                              />
                                    <TextBox MinWidth="150" AcceptsReturn="False" AcceptsTab="False" TextWrapping="NoWrap"
                                             Text="{Binding Path=Element[observation].Value, UpdateSourceTrigger=PropertyChanged}" />
                                </StackPanel>
                            </GroupBox>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </DockPanel>
        </Grid>
    </UserControl>
    
    

    Le contexte étant posé, voici mes questions :

    • Lors du premier chargement, pas de problème, les bonne valeurs s'affiche dans la combobox, cepandant, si je passe d'un élément à l'autre sur la liste de gauche, je me retrouve avec le code XML complet de l'élément option. Je comprend plus ou moins pourquoi, mais je ne voix pas comment régler le problème
    • J'aimerais pouvoir rentrer des valeurs à ma guise dans la combobox, mais elles ne restent pas en mémoire. De la même manière, il me semble voir ce qui pose problème, mais j'ai beau triturer le code dans tout les sens, pas possible de régler le problème
    • Le dernier points, c'est que j'aimerais lever un évènement lors d'une modification pour pouvoir le retransmetre d'une manière ou d'une autre pour que le fichier XML qui est dans un XDocument soit mis à jour en temps réel. je sais comment faire un sorte qu'un controle génère ses propres évènement, mon probleme est de capturer l'évènement de l'ObservableCollection, mais rien n'y fait... j'ai bien essayé un truc du genre xElementsObservable.CollectionChanged += new NotifyCollectionChangedEventHandler(XElementsObservable_CollectionChanged); mais ça ne semble jamais être appelé...

    Merci d'avance pour votre aide précieuse, car là, je patauge, et je suis à deux doigst de retourner à mes Windows Form, avec lesquelles j'ai déjà réussi à faire ce que je veux (je tente un portage), ce qui serait vraiment dommage, car je trouve WPF vraiment puissant...


    mercredi 24 février 2010 17:57

Réponses

  • Bon, après plusieurs jours de galère, j'ai fini par m'en sortir...

    Voici la solution à titre indicatif :

    Dans un premier temps, la ComboBox XAML doit ressembler à ça :

    <ComboBox MinWidth="75" IsEditable="True"
              IsSynchronizedWithCurrentItem="False"
              ItemsSource="{Binding Path=Element[etat].Elements[option]}"
              DisplayMemberPath="Attribute[valeur].Value"
              Text="{Binding Element[etat].Attribute[valeur].Value, UpdateSourceTrigger=PropertyChanged}"
              />

    Voili voilou, comme ça, on se focus sur la valeur de la node qui contient les option, on peut même taper un valeur non répertorié !

    Pour ce qui es de l'enregistrement du fichier lorsque les valeurs sont modifiés, j'ai compris mon erreur : il ne faut pas chercher à lever d'évènement sur la collection observable, mais sur le XDocument dont sont tirés tout les XElements.

    Je fait ça dans la fenêtre où je charge ledit document :

            private void InitPerso()
            {
    
                xDoc = XDocument.Load(@"C:\fichier.xml");
    
                xDoc .Changed += new EventHandler<XObjectChangeEventArgs>(XDoc_Changed);
                
            }
    
            private void XEdls_Changed(object sender, XObjectChangeEventArgs e)
            {
                xDoc .Save(@"C:\fichier.xml");
            }

    Et voilà !

    J'espère que ça pourra aider quelqu'un...
    mercredi 3 mars 2010 10:54

Toutes les réponses

  • Excusez moi mais j'aurais vraiment besoin d'aide... j'asseye tout ce que je peux, mais rien n'y fait !
    vendredi 26 février 2010 15:08
  • Bon, après plusieurs jours de galère, j'ai fini par m'en sortir...

    Voici la solution à titre indicatif :

    Dans un premier temps, la ComboBox XAML doit ressembler à ça :

    <ComboBox MinWidth="75" IsEditable="True"
              IsSynchronizedWithCurrentItem="False"
              ItemsSource="{Binding Path=Element[etat].Elements[option]}"
              DisplayMemberPath="Attribute[valeur].Value"
              Text="{Binding Element[etat].Attribute[valeur].Value, UpdateSourceTrigger=PropertyChanged}"
              />

    Voili voilou, comme ça, on se focus sur la valeur de la node qui contient les option, on peut même taper un valeur non répertorié !

    Pour ce qui es de l'enregistrement du fichier lorsque les valeurs sont modifiés, j'ai compris mon erreur : il ne faut pas chercher à lever d'évènement sur la collection observable, mais sur le XDocument dont sont tirés tout les XElements.

    Je fait ça dans la fenêtre où je charge ledit document :

            private void InitPerso()
            {
    
                xDoc = XDocument.Load(@"C:\fichier.xml");
    
                xDoc .Changed += new EventHandler<XObjectChangeEventArgs>(XDoc_Changed);
                
            }
    
            private void XEdls_Changed(object sender, XObjectChangeEventArgs e)
            {
                xDoc .Save(@"C:\fichier.xml");
            }

    Et voilà !

    J'espère que ça pourra aider quelqu'un...
    mercredi 3 mars 2010 10:54