none
UWP : Affichage de l’entête de groupe dans une ListView groupée RRS feed

  • Question

  • Bonjour, je développe une application UWP pour Windows 10 mobile.

    J’ai une ListView avec des articles provenant d’une base SQLite. Chaque article appartient à une catégorie, et la ListView est groupée par catégorie. A l’affichage, l’entête des groupes est bien le nom de la catégorie, et ces catégories sont triées par ordre alphabétique.

    Voici les principaux éléments du code XAML :

    <Page.Resources>
            <CollectionViewSource x:Name="cvsource" IsSourceGrouped="True" />
            <CollectionViewSource x:Name="cvsourceIn" IsSourceGrouped="True" />
    </Page.Resources>
    


    <ListView x:Name="listBoxobj" 
                                      ItemsSource="{Binding Source={StaticResource cvsource}}"
                                      BorderBrush="#FF141EE4" 
                                      Margin="0,0,0,0"
                                      HorizontalAlignment="Stretch"
                                      SelectionMode="None"
                                      IsItemClickEnabled="True"
                                      RightTapped="listBoxobj_RightTapped"
                                      SelectedItem="{Binding Name}">
    
    
    
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="20*" />
                                            <ColumnDefinition Width="3*" />
                                        </Grid.ColumnDefinitions>
                                        <StackPanel Orientation="Horizontal">
                                            <TextBlock x:Name="NameTxt" Grid.Column="0" Margin="0,0,0,0" TextWrapping="Wrap" Text="{Binding NomArt}" FontSize="20" Foreground="White"/>
                                            <TextBlock x:Name="QteArtTxt" Grid.Column="0" Margin="20,0,0,0" TextWrapping="Wrap" Text="{Binding QuantArt}" FontSize="20" Foreground="DarkGray" FontStyle="Italic"/>
                                            <TextBlock x:Name="UniteMesureTxt" Grid.Column="0" Margin="10,0,0,0" TextWrapping="Wrap" Text="{Binding UnitArt}" FontSize="20" Foreground="DarkGray" FontStyle="Italic"/>
                                        </StackPanel>
                                        <Button x:Name="btnPanier" Grid.Column="1" Height="35" Width="35" Tag="{Binding}" Click="btnPanier_Click">
                                            <Button.Background>
                                                <ImageBrush ImageSource="/Assets/Images/icone_caddie_40x40.png" Stretch="UniformToFill">
                                                </ImageBrush>
                                            </Button.Background>
                                        </Button>
                                        <TextBlock x:Name="NoteTxt" Grid.Row="2" Margin="0,20,0,0" TextWrapping="Wrap" Text="{Binding NoteArt}" FontSize="16" Foreground="DarkGray" FontStyle="Italic"/>
                                    </Grid>
                                </DataTemplate>
                            </ListView.ItemTemplate>
    
                            <ListView.GroupStyle>
                                <GroupStyle>
                                    <GroupStyle.HeaderTemplate>
                                        <DataTemplate>
                                        <TextBlock Text="{Binding Key}" Foreground="CadetBlue" FontSize="18" />
                                        </DataTemplate>
                                    </GroupStyle.HeaderTemplate>
                                </GroupStyle>
                            </ListView.GroupStyle>
    
                            <ListView.ItemContainerStyle>
                                <Style TargetType="ListViewItem">
                                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                                </Style>
                            </ListView.ItemContainerStyle>
    
                            <ListView.Resources>
                                <MenuFlyout x:Name="MenuFlyoutContext" x:Key="FlyoutBaseKey">
                                    <MenuFlyoutItem x:Name="MFSubMenu1" x:Uid="MenuFlyoutModif" Click="MFSubMenu1_Click"/>
                                    <MenuFlyoutItem x:Name="MFSubMenu2" x:Uid="MenuFlyoutDelete" Click="MFSubMenu2_Click"/>
                                </MenuFlyout>
                            </ListView.Resources>
                            
                            <FlyoutBase.AttachedFlyout>
                                <StaticResource ResourceKey="FlyoutBaseKey"/>
                            </FlyoutBase.AttachedFlyout>
    
                        </ListView>

    Comme on peut le voir, l’entête est généré classiquement par la ligne :

    <TextBlock Text="{Binding Key}" Foreground="CadetBlue" FontSize="18" /

    Je voudrais que l’utilisateur puisse ordonner les catégories selon son choix. J’ai donc ajouté un champ OrdreCat (int), qui numérote les catégories de 1 à (nombre de catégories). Dans mon code behind, je groupe la ListView non plus sur le champ Categorie, mais sur OrdreCat, et cela fonctionne bien (les groupes apparaissent dans l’ordre choisi, et non pas par ordre alphabétique). Seul problème à cause du {Binding Key}, l’entête affiche le numéro (OdreCat), et pas le nom de la catégorie. Comment faire pour afficher le nom correspondant au numéro ?

    Quelqu’un pourrait m’aider ? Merci d’avance.

    mercredi 16 janvier 2019 18:20

Réponses

  • Bonjour,

    Je suis désolé pour tous ceux qui ont tenté de m’aider, mais il leur était impossible de m’apporter une réponse, car je n’avais pas fourni un élément important : la classe CatSorter.

    C’est là qu’il fallait apporter la modification. La voici avant modification :

    public class CatSorter : Comparer<Achat>
        {
            public override int Compare(Achat x, Achat y)
            {
                int result = x.CatArt.CompareTo(y.CatArt);
    
                if (result != 0)
                {
                    return result;
                }
                else
                {
                    result = x.CatOrder.CompareTo(y.CatOrder);
    
                    if (result != 0)
                    {
                        return result;
                    }
                    else
                    {
                        return x.NomArt.CompareTo(y.NomArt);
                    }
                }
            }
        }

    Et après modification :

    public class CatSorter : Comparer<Achat>
        {
            public override int Compare(Achat x, Achat y)
            {
                int result = x.CatOrder.CompareTo(y.CatOrder);
    
                if (result != 0)
                {
                    return result;
                }
                else
                {
                    result = x.CatArt.CompareTo(y.CatArt);
    
                    if (result != 0)
                    {
                        return result;
                    }
                    else
                    {
                        return x.NomArt.CompareTo(y.NomArt);
                    }
                }
            }
        }

    Et dans la méthode qui lit et groupe la Listview, il faut grouper la liste par le nom de l’article (CatArt) et pas par le numéro d’ordre (CatOrder).

    Voici la ligne à rectifier :

    _groupingCollection.ArrangeItems(new CatSorter(), (x => x.CatArt));

    Ces modifications étant faites, le programme regroupe les articles par catégorie, les catégories sont classées selon l’ordre choisi par l’utilisateur, et le plus important, c’est le nom de la catégorie qui apparaît comme entête de groupe, et non pas le numéro d’ordre.

    J’espère que ma réponse est suffisamment claire.

    • Marqué comme réponse Mani035 vendredi 25 janvier 2019 13:58
    vendredi 25 janvier 2019 13:56

Toutes les réponses

  • Bonjour Mani035,

    Pour que la communauté puisse vous aider au mieux possible, il sera utile de publier la partie de votre code behind où vous utilisez "OrdreCat".

    Cordialement, 
    Nina


    Microsoft propose ce service gratuitement, dans le but d'aider les utilisateurs et d'élargir les connaissances générales liées aux produits et technologies Microsoft. Ce contenu est fourni "tel quel" et il n'implique aucune responsabilité de la part de Microsoft.

    jeudi 17 janvier 2019 14:38
    Modérateur
  • Bonjour Nina,

    Vous avez tout à fait raison. Voici donc le code qui me permet d'afficher la ListView :

    protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
                currentListeAchat = e.Parameter as string;
                textBlock.Text = resourceLoader.GetString("PanierTitre") + " " + currentListeAchat;
                //
                Achats = new ObservableCollection<Achat>();
                AchatsIn = new ObservableCollection<Achat>();
                //
                DB_PanierList = dbpaniers.GetAllPaniers();
                var query = DB_PanierList.Where(x => x.NomListe == currentListeAchat & x.InOut == "Out");
                //
                foreach (var item in query)
                {
                    string nomart = item.Name;
                    string quantart = item.QteArt;
                    string noteart = item.Note;
                    string catart = item.NomCat;
                    int catorder = item.OrdreCat; 
                    string unitart = item.UniteMesure;
                    string inout = item.InOut;
                    Achats.Add(new Achat()
                    {
                        NomArt = nomart,
                        QuantArt = quantart,
                        NoteArt = noteart,
                        CatArt = catart,
                        CatOrder = catorder,
                        UnitArt = unitart,
                        InOut = inout
                    });
                }
                //
                _groupingCollection = new ObservableGroupingCollection<string, Achat>(Achats);
                _groupingCollection.ArrangeItems(new CatSorter(), (x => x.CatOrder.ToString()));
                GroupedAchats = _groupingCollection.Items;
                cvsource.Source = GroupedAchats;
                //
                //
                var queryin = DB_PanierList.Where(x => x.NomListe == currentListeAchat & x.InOut == "In");
                //
                foreach (var item in queryin)
                {
                    string nomart = item.Name;
                    string quantart = item.QteArt;
                    string noteart = item.Note;
                    string catart = item.NomCat;
                    int catorder = item.OrdreCat;
                    string unitart = item.UniteMesure;
                    string inout = item.InOut;
                    AchatsIn.Add(new Achat()
                    {
                        NomArt = nomart,
                        QuantArt = quantart,
                        NoteArt = noteart,
                        CatArt = catart,
                        CatOrder = catorder,
                        UnitArt = unitart,
                        InOut = inout
                    });
                }
                //
                _groupingCollectionIn = new ObservableGroupingCollection<string, Achat>(AchatsIn);
                _groupingCollectionIn.ArrangeItems(new CatSorter(), (x => x.CatArt));
                GroupedAchatsIn = _groupingCollectionIn.Items;
                cvsourceIn.Source = GroupedAchatsIn;
                //
                // Register for hardware and software back request from the system
                SystemNavigationManager systemNavigationManager = SystemNavigationManager.GetForCurrentView();
                systemNavigationManager.BackRequested += OnBackRequested;
            }

    Remarque : sur la page, il y a 1 pivot avec 2 éléments. Le premier contient la liste des articles triés par le numéro de catégorie (CatOrder), ceci pour tester l'affichage, tandis que le deuxième contient la même liste triée par le nom de la catégorie (CatArt). Pour l'instant, l'app est publiée avec le nom de la catégorie pour chaque élément du pivot.

    Pour mieux comprendre le code, j'ajoute le détail de la classe ObservableGroupingCollection :

    public class ObservableGroupingCollection<K, T> where K : IComparable
        {
            public ObservableGroupingCollection(ObservableCollection<T> collection)
            {
                _rootCollection = collection;
                _rootCollection.CollectionChanged += _rootCollection_CollectionChanged;
            }
    
            ObservableCollection<T> _rootCollection;
            private void _rootCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                HandleCollectionChanged(e);
            }
    
            ObservableCollection<Grouping<K, T>> _items;
            public ObservableCollection<Grouping<K, T>> Items
            {
                get { return _items; }
            }
    
            IComparer<T> _sortOrder;
            Func<T, K> _groupFunction;
    
            public void ArrangeItems(IComparer<T> sortorder, Func<T, K> group)
            {
                _sortOrder = sortorder;
                _groupFunction = group;
    
                var temp = _rootCollection
                    .OrderBy(i => i, _sortOrder)
                    .GroupBy(_groupFunction)
                    .ToList()
                    .Select(g => new Grouping<K, T>(g.Key, g));
    
                _items = new ObservableCollection<Grouping<K, T>>(temp);
    
            }
    
            private void HandleCollectionChanged(NotifyCollectionChangedEventArgs e)
            {
                if (e.Action == NotifyCollectionChangedAction.Add)
                {
                    var item = (T)(e.NewItems[0]);
                    var value = _groupFunction.Invoke(item);
    
                    // find matching group if exists
                    var existingGroup = _items.FirstOrDefault(g => g.Key.Equals(value));
    
                    if (existingGroup == null)
                    {
                        var newlist = new List<T>();
                        newlist.Add(item);
    
                        // find first group where Key is greater than this key
                        var insertBefore = _items.FirstOrDefault(g => ((g.Key).CompareTo(value)) > 0);
                        if (insertBefore == null)
                        {
                            // not found - add new group to end of list
                            _items.Add(new Grouping<K, T>(value, newlist));
                        }
                        else
                        {
                            // insert new group at this index
                            _items.Insert(_items.IndexOf(insertBefore), new Grouping<K, T>(value, newlist));
                        }
                    }
                    else
                    {
                        // find index to insert new item in existing group
                        int index = existingGroup.ToList().BinarySearch(item, _sortOrder);
                        if (index < 0)
                        {
                            existingGroup.Insert(~index, item);
                        }
                    }
                }
                else if (e.Action == NotifyCollectionChangedAction.Remove)
                {
                    var item = (T)(e.OldItems[0]);
                    var value = _groupFunction.Invoke(item);
    
                    var existingGroup = _items.FirstOrDefault(g => g.Key.Equals(value));
    
                    if (existingGroup != null)
                    {
                        // find existing item and remove
                        var targetIndex = existingGroup.IndexOf(item);
                        existingGroup.RemoveAt(targetIndex);
    
                        // remove group if zero items
                        if (existingGroup.Count == 0)
                        {
                            _items.Remove(existingGroup);
                        }
                    }
                }
    
            }
        }


    Encore merci à celui qui pourra m'aider.


    • Modifié Mani035 jeudi 17 janvier 2019 17:07
    jeudi 17 janvier 2019 17:05
  • Bonjour,

    Je suis désolé pour tous ceux qui ont tenté de m’aider, mais il leur était impossible de m’apporter une réponse, car je n’avais pas fourni un élément important : la classe CatSorter.

    C’est là qu’il fallait apporter la modification. La voici avant modification :

    public class CatSorter : Comparer<Achat>
        {
            public override int Compare(Achat x, Achat y)
            {
                int result = x.CatArt.CompareTo(y.CatArt);
    
                if (result != 0)
                {
                    return result;
                }
                else
                {
                    result = x.CatOrder.CompareTo(y.CatOrder);
    
                    if (result != 0)
                    {
                        return result;
                    }
                    else
                    {
                        return x.NomArt.CompareTo(y.NomArt);
                    }
                }
            }
        }

    Et après modification :

    public class CatSorter : Comparer<Achat>
        {
            public override int Compare(Achat x, Achat y)
            {
                int result = x.CatOrder.CompareTo(y.CatOrder);
    
                if (result != 0)
                {
                    return result;
                }
                else
                {
                    result = x.CatArt.CompareTo(y.CatArt);
    
                    if (result != 0)
                    {
                        return result;
                    }
                    else
                    {
                        return x.NomArt.CompareTo(y.NomArt);
                    }
                }
            }
        }

    Et dans la méthode qui lit et groupe la Listview, il faut grouper la liste par le nom de l’article (CatArt) et pas par le numéro d’ordre (CatOrder).

    Voici la ligne à rectifier :

    _groupingCollection.ArrangeItems(new CatSorter(), (x => x.CatArt));

    Ces modifications étant faites, le programme regroupe les articles par catégorie, les catégories sont classées selon l’ordre choisi par l’utilisateur, et le plus important, c’est le nom de la catégorie qui apparaît comme entête de groupe, et non pas le numéro d’ordre.

    J’espère que ma réponse est suffisamment claire.

    • Marqué comme réponse Mani035 vendredi 25 janvier 2019 13:58
    vendredi 25 janvier 2019 13:56