none
Combobox et binding, help please! RRS feed

  • Question

  • Bonjour,

    Je n'arrive pas à me dépêtrer de mon problème avec un combobox et un binding (c'est pourtant un problème simple je pense...c'est moi qui nage complètement)

    J'ai un combobox dont la source est un objet sélectionné dans un listbox
    Dans l'exemple ci-dessous, je voudrais:
    1. que le fait de sélectionner le premier item du combobox renvoi la valeur "Element_1" à la propriété "Element" de l'objet bindé
    2. dans l'autre sens, que l'item sélectionné dans le combobox (quand je relancerai le programme) soit celui repris de la propriété "Element" de l'objet bindé

    <ComboBox DataContext="{Binding SelectedItem, ElementName=ListBox}" SelectedValue="{Binding Element}" IsSynchronizedWithCurrentItem="True">
          <ComboBoxItem>
                <StackPanel>
                      <Label Content="Element_1"/>
                      <Image Source="Resources/image_1.png" />
                </StackPanel>
          </ComboBoxItem>
          <ComboBoxItem>
                <StackPanel>
                      <Label Content="Element_2"/>
                      <Image Source="Resources/2.png" />
                </StackPanel>
          </ComboBoxItem>
    </ComboBox>

    Merci beaucoup de m'aider à y voir plus clair!



    • Modifié jmdeb dimanche 15 avril 2018 17:19
    dimanche 15 avril 2018 17:15

Toutes les réponses

  • Bonjour,

    utilisez vous la pattern MVVM? je met un code de la structure d'un example ou on a une listbox contenant des categories et chaque categorie a une information specifique selectionne dans un combobox. 
    considerant cette classe: 

    public class ViewModelBase: INotifyPropertyChanging, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public event PropertyChangingEventHandler PropertyChanging;
    
        protected void SetPropertyAndNotifyChanged<T>(ref T field, T value, [CallerMemberName]string propertyName = "")
        {
            field = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    on vas utiliser cette classe comme base pour nos viewModel, par example le model representant l'info specifique et comme ce qui suit:

    public class RelatedInfos: ViewModelBase, INotifyPropertyChanged
    {
        private string _text;
        public string Text {
            get =>_text;
            set
            {
                SetPropertyAndNotifyChanged(ref _text, value);
            }
        }
    
        private string _image;
        public string Image
        {
            get=>_image;
            set
            {
                SetPropertyAndNotifyChanged(ref _image, value);                
            }
        }
    }

    remarque l'appel a SetAndNotifyPropertyChanged et la pour simplifier les setters, voici la classe de categorie parent: 

    public class ParentCategory: ViewModelBase, INotifyPropertyChanged
    {
        private string _text;
        public string Text
        {
            get => _text;
            set => SetPropertyAndNotifyChanged(ref _text, value);
        }
    
        private RelatedInfos _relatedInfos;
        public RelatedInfos RelatedInfos
        {
            get=>_relatedInfos;
            set => SetPropertyAndNotifyChanged(ref _relatedInfos, value);
        }
    }

    et le viewModel de notre MainWindow: 

    public class MainWindowViewModel: ViewModelBase, INotifyPropertyChanged
    {
        // remarque l'utilisation de la variable statique ici est la pour representer un la source de donnees, 
       // par example un service EF, ainsi on a un objet unique qui represente la selection dans le combo, 
       // ce qui permet la synchronisation entre les deux collections
        static ObservableCollection<RelatedInfos> _relatedInfosCollection = new ObservableCollection<RelatedInfos>(new List<RelatedInfos>
            {
                new RelatedInfos { Text= "Element_1", Image="Resources/image_1.jpg"},
                new RelatedInfos { Text= "Element_2", Image="Resources/image_2.png"}
            });
    
        public MainWindowViewModel()
        {
        }
    
        private ParentCategory _selectedCategory;
        public ParentCategory SelectedCategory
        {
            get => _selectedCategory;
            set => SetPropertyAndNotifyChanged(ref _selectedCategory,value);
        }
    
    /*    public ICommand SelectedCategoryChanged
        {
            get {
                return new DelegatingHandler((o) =>
                {
                });
            }
        }
    */
        public ObservableCollection<ParentCategory> Categories { get; set; } = new ObservableCollection<ParentCategory>(new List<ParentCategory>
        {
            new ParentCategory{ Text="item1", RelatedInfos = _relatedInfosCollection[0]},
            new ParentCategory{ Text="item2", RelatedInfos = _relatedInfosCollection[1] }
        });
    
        public ObservableCollection<RelatedInfos> RelatedInfosCollection { get; set; } = _relatedInfosCollection;
    }

    sur le viewModel de notre Window on a utiliser un objet pour pointer sur l'element selectione et ainsi avoir access aux informations en relation avec ce dernier, ainsi notre binding pour le combo est diviser en deux parties, la premiere qui fait office de source ItemsSource, et le deuxieme pour pointer sur l'element selectione SelectedItem, voici le Xaml de notre MainWindow:

    <Window x:Class="WpfApp1.MainWindow"
            ........
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <!-- on definis notre DataContext pour la Window-->
        <Window.DataContext>
            <local:viewmodel x:Name="MainWindowViewModel"/>
        </Window.DataContext>
        <Grid>
            <ListBox x:Name="listBox" HorizontalAlignment="Left" Height="100" Margin="10,10,0,0" VerticalAlignment="Top" Width="100" 
                    ItemsSource="{Binding Categories}"  SelectedItem="{Binding Path=SelectedCategory, Mode=TwoWay}" >
            <!--<i:Interaction.Triggers>
                    <i:EventTrigger  EventName="SelectionChanged">
                        <i:InvokeCommandAction Command="{Binding ListBoxSelectionChanged}" CommandParameter="{Binding ElementName=listBox, Path=SelectedItem}"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>-->
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Text}"></TextBlock>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
            <ComboBox x:Name="comboBox" ItemsSource="{Binding RelatedInfosCollection}" 
                      SelectedItem="{Binding ElementName=listBox, Path=SelectedItem.RelatedInfos}"  
                      IsSynchronizedWithCurrentItem="True"
                      Height="30" Width="100" Margin="115,10,577,379">
                <ComboBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <Label Content="{Binding Path=Text}"/>
                            <Image Source="{Binding Path=Image}" Height="20" />
                        </StackPanel>
                    </DataTemplate>
                </ComboBox.ItemTemplate>
            </ComboBox>
        </Grid>
    </Window>

    j'espere que ca donne une idee du concept, aussi ya la possibilite d'utiliser les interactions, pour ce faire il faudra passer par le package Expression Blen Interactivity, si y'a besoin j'ajouterais un example montrant son utilisation.

    Note: veuillez poster une partie du code que vous utilisez pour que je puisse reproduire la cas de figure sur ma machine, ainsi la solution proposer serra precise et efficace.

    Cordialement,

    Mouad.

    Good Coding;



    dimanche 11 novembre 2018 11:19
  • Bonjour jmdeb,

    ma réponse pourrait se rapprocher de celle de Cherkaoui.Mouad, en utilisant la technique des binding (mais non MVVM), construisez votre vue de cette façon : 

    <StackPanel>
                <ComboBox x:Name="ComboBox1"
                          ItemsSource="{Binding Elements}"
                          SelectedValuePath="Name"
                          SelectedValue="{Binding ElementName}"
                          IsSynchronizedWithCurrentItem="True" >
                    <ComboBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel>
                                <Label Content="{Binding Name}" />
                                <Image Source="{Binding ImagePath}"></Image>
                            </StackPanel>
                        </DataTemplate>
                    </ComboBox.ItemTemplate>
                </ComboBox>
                <Label Content="{Binding ElementName}"></Label>
            </StackPanel>

    Elements sera une liste d'éléments bindable pour votre combobox, ensuite chaque élément sélectionné sera lié à une propriété de type string contenant le contenu du Label de l'élément sélectionné. Notez que j'utilise un DataTemplate pour éviter de répéter le style pour chaque élément.

    Le code behind donne quelque chose comme :

    /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window, INotifyPropertyChanged
        {
            public MainWindow()
            {
                InitializeComponent();
                this.Loaded += MainWindow_Loaded;
                this.Closed += MainWindow_Closed;
                this.DataContext = this;
            }
    
            private void MainWindow_Closed(object sender, EventArgs e)
            {
                using (var stream = File.Open("data.txt", FileMode.Create))
                {
                    var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                    binaryFormatter.Serialize(stream, (Element)ComboBox1.SelectedItem);
                }
            }
    
            private void MainWindow_Loaded(object sender, RoutedEventArgs e)
            {
                Elements.Add(new Element("Elément 1", ""));
                Elements.Add(new Element("Elément 2", ""));
                Elements.Add(new Element("Elément 3", ""));
                Elements.Add(new Element("Elément 4", ""));
                Elements.Add(new Element("Elément 5", ""));
    
                if (File.Exists("data.txt"))
                {
                    using (var stream = File.Open("data.txt", FileMode.Open))
                    {
                        var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                        var element = (Element)binaryFormatter.Deserialize(stream);
                        ElementName = element.Name;
                    }
                }
            }
    
            public ObservableCollection<Element> Elements { get; set; } = new ObservableCollection<Element>();
    
            private string _element;
            public string ElementName
            {
                get => _element;
                set
                {
                    _element = value;
                    OnPropertyChanged();
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
            protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    
        [Serializable]
        public class Element
        {
            public Element(string name, string imagePath)
            {
                Name = name;
                ImagePath = imagePath;
            }
    
            public string Name { get; set; }
            public string ImagePath { get; set; }
        }

    Pour retenir un élément sectionné lorsque l'application se ferme et ainsi pouvoir le réutiliser lorsque celle ci se réouvrira, j'utilise une méthode de sérialisation pour l'exemple, mais libre à vous de choisir un autre moyen.

    N'hésitez pas à revenir vers la communauté en cas de question.

    Cordialement,

    mardi 13 novembre 2018 14:02