[C#] Comment bien utiliser la GridView dans une application Windows Store
Introduction
Si vous avez fait un peu de développement WinRT, vous avez surement déjà du utiliser ce composant terriblement efficace quand il est bien utilisé. Afin (d’essayer) de faire le tour des utilisations possible, nous allons regarder de plus près quelques exemples d’utilisations avec ses avantages et inconvénients.
Dans ce billet, on se limitera à la version Windows 8 et non Windows 8.1
1 - GridView avec une seule source de données
Exemple de code :
ViewModel
namespace ExtendedGridView { public class FolderModel { public string Name { get; set; } } public class MainViewModel : ViewModelBase { public ObservableCollection<FolderModel> Folders { get; set; } public MainViewModel() { Folders = new ObservableCollection<FolderModel>(); for (int i = 0; i < 50; i++) { Folders.Add(new FolderModel { Name = string.Concat("Folder ", i) }); } } } }
View
<Page x:Class="ExtendedGridView.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ExtendedGridView" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <DataTemplate x:Key="FolderDataTemplate"> <Border BorderBrush="Gray" Background="#7FD1CECE" BorderThickness="2" Margin="3" CornerRadius="0,0,15,0" Width="250" Height="100"> <TextBlock Text="{Binding Name}" Margin="8,0" HorizontalAlignment="Left" VerticalAlignment="Center" TextTrimming="WordEllipsis" /> </Border> </DataTemplate> </Page.Resources> <Grid> <GridView HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding Folders}" ItemTemplate="{StaticResource FolderDataTemplate}" VirtualizingStackPanel.VirtualizationMode="Recycling"> <GridView.ItemsPanel> <ItemsPanelTemplate> <VariableSizedWrapGrid Orientation="Vertical" /> </ItemsPanelTemplate> </GridView.ItemsPanel> </GridView> </Grid> </Page>
Capture d’écran
Avantages
C’est l’utilisation de base de la GridView. Elle permet de tirer pleinement partie de la virtualisation des données. A noter qu’il existe deux mode de virtualisation possible :
- Recycling : Les éléments créés sont réutilisé lorsque l’on veut les afficher de nouveau
- Standard : Les éléments sont recréés à chaque affichage
Inconvénients
Pas vraiment
2 – GridView avec plusieurs sources de données (avec groupe)
Exemple de code :
ViewModel
namespace ExtendedGridView { public class FolderModel { public string Name { get; set; } } public class Group { public string Title { get; set; } public ObservableCollection<FolderModel> Items { get; set; } public Group() { Items = new ObservableCollection<FolderModel>(); } } public class MainViewModel : ViewModelBase { public ObservableCollection<Group> Groups { get; set; } public MainViewModel() { Groups = new ObservableCollection<Group>(); for (int i = 0; i < 5; i++) { Group group = new Group(); ObservableCollection<FolderModel> folders = new ObservableCollection<FolderModel>(); for (int j = 0; j < 10; j++) { folders.Add(new FolderModel { Name = string.Concat("Folder ", j) }); } group.Title = string.Concat("Group ", i); group.Items = folders; Groups.Add(group); } } } }
View
<Page x:Class="ExtendedGridView.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ExtendedGridView" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <DataTemplate x:Key="FolderDataTemplate"> <Border BorderBrush="Gray" Background="#7FD1CECE" BorderThickness="2" Margin="3" CornerRadius="0,0,15,0" Width="250" Height="100"> <TextBlock Text="{Binding Name}" Margin="8,0" HorizontalAlignment="Left" VerticalAlignment="Center" TextTrimming="WordEllipsis" /> </Border> </DataTemplate> <DataTemplate x:Key="HeaderDataTemplate"> <TextBlock Text="{Binding Title}" Foreground="Brown" FontSize="16" Margin="10" /> </DataTemplate> <CollectionViewSource x:Key="ItemsSource" IsSourceGrouped="True" ItemsPath="Items" Source="{Binding Groups}" /> </Page.Resources> <Grid> <GridView HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemTemplate="{StaticResource FolderDataTemplate}" ItemsSource="{Binding Source={StaticResource ItemsSource}}" VirtualizingStackPanel.VirtualizationMode="Recycling"> <GridView.ItemsPanel> <ItemsPanelTemplate> <VariableSizedWrapGrid Orientation="Vertical" /> </ItemsPanelTemplate> </GridView.ItemsPanel> <GridView.GroupStyle> <GroupStyle HeaderTemplate="{StaticResource HeaderDataTemplate}"> <GroupStyle.Panel> <ItemsPanelTemplate> <VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,20,0" /> </ItemsPanelTemplate> </GroupStyle.Panel> </GroupStyle> </GridView.GroupStyle> </GridView> </Grid> </Page>
Capture d’écran
Avantages
L’utilisation des groupes s’avère très performant quand chacun d’eux ne contient que peu d’éléments…
Inconvénients
...mais elle peut tout aussi bien être catastrophique avec beaucoup. La virtualisation étant faite sur les groupes, c’est à dire qu’elle ne prend pas en compte les éléments à l’intérieur de ceux-ci, cela peut dégrader fortement les performances dans le cas de groupes avec un grand nombre d’éléments ou des éléments avec un arbre visuel important. On pourra remarquer des retards à l’affichage, des ralentissements, …
3 – GridView avec 2 sources de données (sans groupe)
Exemple de code :
ViewModel
namespace ExtendedGridView { public class FolderModel { public string Name { get; set; } } public class FileModel { public string Name { get; set; } } public class MainViewModel : ViewModelBase { public ObservableCollection<FileModel> Files { get; set; } public ObservableCollection<FolderModel> Folders { get; set; } public MainViewModel() { Files = new ObservableCollection<FileModel>(); Folders = new ObservableCollection<FolderModel>(); for (int i = 0; i < 200; i++) { Files.Add(new FileModel { Name = string.Concat("File ", i) }); } for (int i = 0; i < 50; i++) { Folders.Add(new FolderModel { Name = string.Concat("Folder ", i) }); } } } }
View
<Page x:Class="ExtendedGridView.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ExtendedGridView" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <DataTemplate x:Key="FileDataTemplate"> <Border BorderBrush="Gray" BorderThickness="2" Margin="3" CornerRadius="0,0,15,0" Width="250" Height="100"> <TextBlock Text="{Binding Name}" Margin="8,0" HorizontalAlignment="Left" VerticalAlignment="Center" TextTrimming="WordEllipsis" /> </Border> </DataTemplate> <DataTemplate x:Key="FolderDataTemplate"> <Border BorderBrush="Gray" Background="#7FD1CECE" BorderThickness="2" Margin="3" CornerRadius="0,0,15,0" Width="250" Height="100"> <TextBlock Text="{Binding Name}" Margin="8,0" HorizontalAlignment="Left" VerticalAlignment="Center" TextTrimming="WordEllipsis" /> </Border> </DataTemplate> </Page.Resources> <Grid> <GridView HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding Files}" ItemTemplate="{StaticResource FileDataTemplate}" VirtualizingStackPanel.VirtualizationMode="Recycling"> <GridView.Header> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <GridView HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding Folders}" ItemTemplate="{StaticResource FolderDataTemplate}" VirtualizingStackPanel.VirtualizationMode="Recycling" ScrollViewer.IsHorizontalScrollChainingEnabled="False" ScrollViewer.HorizontalScrollMode="Disabled"> <GridView.ItemsPanel> <ItemsPanelTemplate> <VariableSizedWrapGrid Orientation="Vertical" /> </ItemsPanelTemplate> </GridView.ItemsPanel> </GridView> <Border Grid.Column="1" HorizontalAlignment="Stretch" BorderBrush="LightGray" BorderThickness="3" Margin="10,5" /> </Grid> </GridView.Header> <GridView.ItemsPanel> <ItemsPanelTemplate> <VariableSizedWrapGrid Orientation="Vertical" /> </ItemsPanelTemplate> </GridView.ItemsPanel> </GridView> </Grid> </Page>
Capture d’écran
Avantages
Dans cet exemple, la principale particularité que l’on remarque est la non-utilisation des groupes. Comme vu précédemment, l’utilisation des groupes fait que la virtualisation ne s’applique pas aux items de la source de données affichée à l’écran mais au groupe tout entier. Ici, nous utilisons donc la propriété Header de la GridView afin d’y afficher notre première collection. La deuxième liste est affichée tout à fait normalement.
Inconvénients
Le Header faisant parti du même ScrollViewer, la virtualisation se passe sans problème, oui, mais attention tout de même ! Tout ce qui est dans le Header fait parti d’un même bloc. Nous n’avons donc pas de virtualisation à l’intérieur de ce bloc. Il en va donc de faire très attention à ne pas avoir trop d’élément à afficher dans celui-ci sous peine de dégrader fortement l’expérience utilisateur sur des machines peu puissante.
Conclusion
Soyez donc très prudent dans l’utilisation de la GridView car l’expérience utilisateur peut s’en ressortir très fortement.
Merci Simon pour l’astuce du Header !
Commentaires