[WPF-MVVM] - ItemsControl dynamiques ?

CH4BRN Messages postés 49 Date d'inscription lundi 19 février 2018 Statut Membre Dernière intervention 6 février 2021 - 16 avril 2018 à 16:22
CH4BRN Messages postés 49 Date d'inscription lundi 19 février 2018 Statut Membre Dernière intervention 6 février 2021 - 17 avril 2018 à 16:27
Commençons par un haïku du XII eme siècle:

Résonne, clavier,

Sous les doigts, c'est l'été,

Bon sang de #*%$@ d'items control.



Après ce cours manque de respect envers la culture japonaise ( c'était pour vous épater, je ne le cache pas.), le vif du sujet :

Dans une application WPF respectant scrupuleusement le pattern MVVM, j'ai une vue avec une liste déroulante (ComboBox) et un wrap panel contenant divers petits stack panels, contenant eux même chacun un ensemble de controles ( Label + comboBox ou Label+TextBox).

L'idée est que, quand on sélectionne une option de la ComboBox, le stack panel contenant les contrôles correspondant au choix s'affiche.

Par exemple :

Je choisis "Type" dans la première comboBox, et le panel contenant le label type et la combobox type apparait.

J'ai résolu ce problème de la manière suivante :

Les éléments de ma première comboBox sont des CheckBox, avec les propriété "IsChecked" Bindées à des propriétés "AddNomDuControl_IsChecked", de type bool, dans mon ViewModel .

Mes "petits" stackpanels ont leur propriété "visibility" bindées à des propriété "NomDuControle_IsVisible" de type "Visibility".

Quand je "coche" une checkbox, "AddNomDuControl_IsChecked" passe a true, et son mutateur (set), en plus d'affecter "value" au champs correspondant, passe la propriété "NomDuControle_IsVisible" de "Collapsed" à "Visible".

J'obtiens le résultat attendu, et comme ils sont dans un WrapPanel, il s'affiche les uns à la suit des autres, dans l'ordre ou je les ai créé.

MAIS ...

Je trouve pas ça propre, et encore moins élégant. J'en ai parlé avec mon "supérieur", et il m'a conseillé d'utiliser à la place des "ItemsControl". Seulement voila, j'ai du mal, avec les exemples que j'ai trouvé, à comprendre comment faire.

D'après ce que j'ai compris, il faut déclarer un ItemsControl avec un ItemsTemplate qui sert de modèle, mais après je sèche complétement.

Quelqu'un aurait-il la patience de m'aider ?

ma combo box:

 <ComboBox FontSize="17" SelectedIndex="0" Width="Auto" Margin="5"
                    SelectedValue="{Binding SelectedComboBoxValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                    <ComboBox.Items>
                        <ComboBoxItem  IsEnabled="False">
                            <TextBlock Text ="Choisissez une propriété :" />
                        </ComboBoxItem> 
                        <CheckBox IsChecked="{Binding AddAllIsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Content="{x:Static localization:CommonMessage.LogToolView_AllCriteria}"/>
                        <CheckBox IsChecked="{Binding AddActionIsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Content="{x:Static localization:CommonMessage.LogToolView_Action}" />
                        <CheckBox IsChecked="{Binding 
                    </ComboBox.Items>
 </ComboBox>


un de mes panels :

 <!-- Action Panel -->
                <Border Background="#32484F" CornerRadius="10" Margin="5"   Width="150" Height="85"
                        Visibility="{Binding ActionVisibility, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
                    <StackPanel>

                        <Label Foreground="#00b5e2" FontSize="15" Margin="5"
                               Content="{x:Static localization:CommonMessage.SecondView_Action}"
                            />
                        <ComboBox Foreground="#004151" Margin="5"  FontSize="15"  Width="Auto"
                              SelectedItem="{Binding SelectedAction}"
                              ItemsSource="{Binding Path=ActionList}" />
                    </StackPanel>
                </Border>

Une de mes propriété Visibility

        /// <summary>
        ///     Gets or sets _actionVisibility
        /// </summary>
        public Visibility ActionVisibility
        {
            get => _actionVisibility;
            set
            {
                _actionVisibility = value;
                RaisePropertyChanged(() => ActionVisibility);
            }
        }


Une de mes propriété "IsChecked"
 /// <summary>
        ///     Gets or sets _isActionChecked
        /// </summary>
        public bool AddActionIsChecked
        {
            get => _isActionChecked;
            set
            {
                _isActionChecked = value;
                ActionVisibility = ActionVisibility == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed;

                RaisePropertyChanged(() => AddActionIsChecked);
            }
        }


Grand merci à ceux qui se pencheraient sur mon problème !
A voir également:

4 réponses

CH4BRN Messages postés 49 Date d'inscription lundi 19 février 2018 Statut Membre Dernière intervention 6 février 2021
17 avril 2018 à 09:51
Bon, je pense que je suis sur la bonne voie :

D'abord une classe "DisplayedItem" :
    public class DisplayedItem
    {
        /// <summary>
        /// Item's title
        /// </summary>
        private string _title;
        /// <summary>
        /// Item's children
        /// </summary>
        private List<string> _children;
        /// <summary>
        /// Item's visibility
        /// </summary>
        private Visibility _visible;
        /// <summary>
        /// Does item is visible ?
        /// </summary>
        private bool _isVisible;
        /// <summary>
        /// Gets or sets Item's title
        /// </summary>
        public string Title
        {
            get => _title;
            set => _title = value; 
        }
        /// <summary>
        /// Gets or sets Item's children
        /// </summary>
        public List<string> Children
        {
            get => _children;
            set => _children = value;
        }
        /// <summary>
        /// Gets or sets Item's visibility
        /// </summary>
        public Visibility Visible
        {
            get => _visible;
            set => _visible = value;
        }
        /// <summary>
        /// Gets or sets if item is visible 
        /// </summary>
        public bool IsVisible
        {
            get => _isVisible;
            set
            {
                _isVisible = value;
                if (value == true)
                {
                    Visible = Visibility.Visible;
                }
                else
                {
                    Visible = Visibility.Collapsed;
                }                
            }
        }    
    }


Puis mon ItemsControl :
<ItemsControl Name="ICChoice" ItemsSource="{Binding Items, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Width="350" Height="150" >
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid Visibility="{Binding Visible, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
                        <TextBlock Text="{Binding Title}" Grid.Row="0"/>
                        <ComboBox ItemsSource="{Binding Children}" Grid.Row="1"/>
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>


Le constructeur de ma classe ViewModel :

   public HelloWorldViewModel()
        {
            var listA = new List<string>();
            listA.Add("ListeA-1");
            listA.Add("ListeA-2");
            listA.Add("ListeA-3");
            listA.Add("ListeA-4");
            var listB = new List<string>();
            listB.Add("listB-1");
            listB.Add("listB-2");
            listB.Add("listB-3");
            listB.Add("listB-4");

            Items = new List<DisplayedItem>();
            Items.Add(new DisplayedItem(){Children = listA, Title = "Liste A", IsVisible = true});
            Items.Add(new DisplayedItem() { Children = listB, Title = "Liste B", IsVisible = false });


        }


A partir de là, me reste plus qu'a adapter.

Merci de m'avoir lu.
0
CH4BRN Messages postés 49 Date d'inscription lundi 19 février 2018 Statut Membre Dernière intervention 6 février 2021
Modifié le 17 avril 2018 à 11:49
Alors, pour adapter, j'ai trouvé une solution, mais si quelqu'un à mieux à proposer, je prends!

J'ai modifié ma classe DisplayedItem :
j'ai ajouté :
 
private Visibility _textBoxVisibility;
 private Visibility _comboBoxVisibility;

    public Visibility TextBoxVisibility
        {
            get => _textBoxVisibility;
            set
            {
                _textBoxVisibility = value;
                this.RaisePropertyChanged(() => this.TextBoxVisibility);
            }
        }
  
        public Visibility ComboBoxVisibility
        {
            get => _comboBoxVisibility;
            set
            {
                _comboBoxVisibility = value;
                this.RaisePropertyChanged(() => this.ComboBoxVisibility);
            }
        }


et
private bool _textBoxIsVisible = false;

        private bool _comboBoxIsVisible = false;

 public bool TextBoxIsVisible
        {
            get => _textBoxIsVisible;
            set
            {
                _textBoxIsVisible = value;
                if (value)
                {
                    TextBoxVisibility = Visibility.Visible;
                }
                else
                {
                    TextBoxVisibility = Visibility.Collapsed;
                }
                this.RaisePropertyChanged(() => this.TextBoxIsVisible);
            }
        }
 
        public bool ComboBoxIsVisible
        {
            get => _comboBoxIsVisible;
            set
            {
                _comboBoxIsVisible = value;
                if (value)
                {
                    ComboBoxVisibility = Visibility.Visible;
                }
                else
                {
                    ComboBoxVisibility = Visibility.Collapsed;
                }
                this.RaisePropertyChanged(() => this.ComboBoxIsVisible);
            }
        }


ainsi que
     private string _textBox;

        public string TextBox
        {
            get => _textBox;
            set
            {
                _textBox = value;
                this.RaisePropertyChanged(()=>this.TextBox);
            }
        }


puis le Binding du choix de la ComboBox principale:
        public int SelectedChoice
        {
            get => _selectedIndex;
            set
            {
                var itemsToAdd = new List<DisplayedItem>();

                _selectedIndex = value;
                Console.WriteLine("Choice" + value);
                if ( value == 1)
                {
                    
                    var item = Items.Where(w => w.Title == "Liste A").ToList();

                    foreach (var displayedItem in item)
                    {
                        int i = Items.IndexOf(displayedItem);
                        if (Items[i].IsVisible)
                        {
                           
                            Items[i].IsVisible = false;
                        }
                        else
                        {
                            Items[i].ComboBoxIsVisible = true;
                            Items[i].TextBoxIsVisible = false;
                            Items[i].IsVisible = true;
                        }
                    }
                    
                }
                else
                {
                    
                    var item = Items.Where(w => w.Title == "Liste B").ToList();
                    foreach (var displayedItem in item)
                    {
                        int i = Items.IndexOf(displayedItem);
                        if (Items[i].IsVisible)
                        {
                            Items[i].IsVisible = false;
                        }
                        else
                        {
                            Items[i].ComboBoxIsVisible = false;
                            Items[i].TextBoxIsVisible = true;
                            Items[i].IsVisible = true;
                        }
                    }

                }

            }
        }


Ne restait plus qu'à exploiter tout ça dans mon XAML :
    <WrapPanel>

        <ComboBox Name="cbChoice" SelectedIndex="{Binding SelectedChoice, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="250" Height="25">
          
            <ComboBoxItem>Choix 1</ComboBoxItem>
            <ComboBoxItem>Choix 2</ComboBoxItem>
           
            
            
        </ComboBox>
        
        <ItemsControl Name="IcChoice" ItemsSource="{Binding Items, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Width="350" Height="150" >
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid Visibility="{Binding Visible, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="*"/>

                        </Grid.RowDefinitions>
                        <TextBlock Text="{Binding Title}" Grid.Row="0"/>
                        <ComboBox ItemsSource="{Binding Children}" Visibility="{Binding ComboBoxVisibility}" Grid.Row="1"/>
                        <TextBox Text="{Binding TextBox}" Visibility="{Binding TextBoxVisibility}" Grid.Row="1"/>
                    </Grid>
                </DataTemplate>

            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </WrapPanel>



Maintenant il faudrait que j'arrive a cumuler les affichages.

EDIT : J'arrive a cumuler les affichages, mais là j'ai un vrai blocage :

Comment faire pour récupérer la valeur d'une textBox dans un items control?
Parce que la valeur va bien dans une variable de mon objet DisplayedItem, mais pas du tout dans mon ViewModel...
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
17 avril 2018 à 13:31
Salut, en tant que contributeur je devrais faire la promotion de CodeS SourceS.
Cependant, il faut se rendre à l’évidence, la communauté n’est plus ce qu’elle a été.
Nous ne sommes plus qu’une poignée à tenter d’aider, et je suis le seul en WPF. Or tes questions me dépassent.
Quand j’ose répondre à l’un de tes sujets, je ne fais que chercher sur d’autres forums.
Autant éviter une étape, tourne toi vers StackOverFlow et MSDN.
C’est en anglais, c’est peut-être un frein. Mais ça n’est malheureusement pas chez nous que tu trouveras quelqu’un du niveau nécessaire à t’aider, et quoiqu’il en soit l’anglais est obligatoire en programmation.
Il y a aussi devellopez.net où la communauté C# est plus active, et là c’est en français.
0
CH4BRN Messages postés 49 Date d'inscription lundi 19 février 2018 Statut Membre Dernière intervention 6 février 2021
17 avril 2018 à 13:53
Salut.
Merci de ta franchise. Je suis déjà sur stackOverflow, c'était presque, disons, par "chauvinisme" que je m'accrochais à CodeS SourceS, et le côté "petit comité" n'était pas pour me déplaire.. Mais je ne veux pas t’assommer de questions obscures!
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
17 avril 2018 à 14:58
Tu ne m’assomes pas. Mais malheureusement on ne peut rien t’apporter.
0
CH4BRN Messages postés 49 Date d'inscription lundi 19 février 2018 Statut Membre Dernière intervention 6 février 2021
17 avril 2018 à 16:27
je donne quand même la solution, si jamais ça peut servir à quelqu'un :

il faut déclarer un DataContext propre au controle :
  <TextBox   
                            Text="{Binding DataContext.ReceivedText, 
                            ElementName=Hello,  
                            UpdateSourceTrigger=PropertyChanged, 
                            Mode=TwoWay}" 
                            Visibility="{Binding TextBoxVisibility}" 
                            Grid.Row="1"/>

Ou ReceivedText est une propriété de ma classe ViewModel et ou Hello est le Name de ma vue.
0
Rejoignez-nous