The GridView control, introduced with Windows Store apps, together with its counterpart named ListView is for sure a key actor into these applications. Born to support a grid representation of items that goes outside the size of the screen, it is so flexible and customizable to support multiple form factors automatically, to be intuitively usable with mouse, touch and keyboard. It supports grouping and may be part of a semantic zoom control taking part of both zoomed-in and zoomed-out views.
Unfortunately with great flexibility comes a great complexity that makes difficult to bring the GridView to represent exactly what you need. There is a number of options and handles to tune up the visualization but it is not so straightforward to find the correct point of intervention. This article would like to be a kind of recipe for customization points, that apply to GridView but also to its sister the ListView.
Grouped or ungrouped.
The very first thing to understand, when you use a GridView, is the difference between a grouped or ungrouped view. The control supports both the views but this directly affects how you have to use its templates. When the GridView works without grouping, that means to bind the source collection directly to the ItemsSource, it behave exactly as another ItemsControl. In this scenario the grid simply repeat the ItemTemplate and place the generated items inside the panel defined in ItemsPanel. You can view and example on the right side where the orange area is the ItemsPanel.
When the source is grouped, the GridView instead works repeating a number of ItemsControl, one for each group in the source, so the ItemTemplate is again the template of the single item, but the ItemsPanel define the panel that contains the children ItemsControl. This difference usually deceives the people when they start using the GridView, but after you understand the point you are on the right way. Again in the figure below an example that shows the panel in orange.
So, when you go with grouped items there is another panel that enter the scene. It is defined inside the GroupStyle.Panel property and in the figure is the grey part. This panel is the container that is used inside the children to organize the items. This let you organize the layout in a number of ways: using a StackPanel withe horizontal orientation creates groups that are side by side, no matter the number of items inside of them. This is useful when you need only a stripe of items that organize horizontally some groups like you see in the Store application. If you use a VariableSizedWrapGrid you are able create multiple rows of items each one containing a group label and a limited number of items. In the figure below you see an example. It is created limiting the number of the rows for each group to 4. This make room for another stripe of items just below of the first.
Handling correctly the outer panel means also using the right margin and alignment. The panels are usually aligned left-top but you can set the VerticalAlignment to center or bottom and the containet items will be aligned ad requested. From the user experience point of view it is much more beautiful to align the panel centered because gives a better feeling of order.
Finally, there are a number of times when you need to have different marging for your starting view. Usually, when the grid is loaded, the items are aligned with the upper title and this creates a wider margin on the left. This effect comes from the use of a Margin on the outer panel, and not on the GridView itself. This because the panel is able to scroll the margin outside the left side. On the last figure an example of alignment and margins. Remember that, the same margin you put on the left side should be used also on the right side. So, when items goes to the right end you have a balanced space remaining.
Define the item template
The most easy way to customize the GridView, and probably the first one everyone tries, is the item template. This template is probably already known to whom has already used an ItemsControl in Windows Presentation Foundation or Silverlight. It describes the aspect of a single item and is made of a DataTemplate repeated once for every item in the source. In the GridView this template affects both the grouped and ungrouped view. Using this template is really easy, also if tuning the size of items sometimes requires some attention, there is nothing strange.
The scene changes if you need to implement items of different size. To understand the point, you have to watch at the figure on the right side. In this example I've assigned a different size - vertically or horizontally - to the items. Unfortunately, also if this effect is very popular, it cannot be created easily. The first thing to do is to use a VariableSizedWrapGrid panel for the items. Then, you have to assign a size to the cells using the ItemWidth and ItemHeight properties on the panel. In my case I determined a grid with 60x60 pixel cells. This creates a square grid but you can also specify different sizes for width and height and get a rectangular matrix. Also if the grid is made of squares you can get different sizes because you can dock items to cells and set its column and row span to multiple cells.
1:
2: <GroupStyle.Panel>
3: <ItemsPanelTemplate>
4: <VariableSizedWrapGrid ItemWidth="60" ItemHeight="60" Background="LightGray" Margin="10" />
5: </ItemsPanelTemplate>
6: </GroupStyle.Panel>
For this purpose you have to use the SetColumnSpan and SetRowSpan methods. These are two attached properties that apply to the VariableSizedWrapGrid but - and here comes the hard part - the properties cannot be set on the item template. The items, when they are generated, are wrapped inside a GridViewItem element and then this is put directly into the panel. So, to make things work, you have to set these values on the GridViewItem instead of the root element of the template. To achieve this result you can only create an extended GridView and override the PrepareContainerForItemOverride method.
1: public class SuperGridView : GridView
2: {
3: int count = 0;
4:
5: protected override void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item)
6: {
7: GridViewItem gvi = element as GridViewItem;
8:
9: if (gvi != null)
10: {
11: gvi.HorizontalContentAlignment = Windows.UI.Xaml.HorizontalAlignment.Stretch;
12: gvi.VerticalContentAlignment = Windows.UI.Xaml.VerticalAlignment.Stretch;
13:
14: count++;
15:
16: if ((count % 2) == 0)
17: {
18: VariableSizedWrapGrid.SetColumnSpan(gvi, 2);
19: VariableSizedWrapGrid.SetRowSpan(gvi, 1);
20: }
21: else
22: {
23: VariableSizedWrapGrid.SetColumnSpan(gvi, 1);
24: VariableSizedWrapGrid.SetRowSpan(gvi, 2);
25: }
26: }
27:
28: base.PrepareContainerForItemOverride(element, item);
29: }
30: }
This method is called by the ItemsControl to prepare the GridViewItem just before it is added to the panel. Usually this means that the DataTemplate is applied and it generates the content of the GridViewItem. Into the method I take the GridViewItem and set a bunch of properties to give the expected aspect. Pratically speaking, in the snipper above I set the content alignment to Stretch to let the content to fill the entire space available inside the GridViewItem and then, on the base of the index of the item I span it on two columns when it is even or two rows when it is odd.
Stiling the container
One common feel, when you are using a GridView, is not being completely in control of what it is rendering. If you ever tried to tune the distance between elements on the grid, you for sure have experienced what I'm saying. The reality is that until you use the control as expected it works without any problem, but when you need to get something more refined, you clash with lot of difficulties.
The solution often comes by using the ItemContainerStyle. This property is made to offer the capability of tuning the aspect of the GridViewItem (the container), the same control I've already explained in the previous paragraphs. This property, as the name said, is a style and not a real template. Thanks to it you can tune up margins, padding, alignments, colors and so on. As an example, to change content alignment as in the previous snippet you can alternatively specify a style like this:
1: <GridView.ItemContainerStyle>
2: <Style TargetType="GridViewItem">
3: <Setter Property="HorizontalContentAlignment" Value="Stretch" />
4: <Setter Property="VerticalContentAlignment" Value="Stretch" />
5: </Style>
6: </GridView.ItemContainerStyle>
What you have to take in serious consideration, is the fact that, thanks to styling capabilities in XAML, you can redefine the template of the GridViewItem. This element has a slightly complex template, since it supports selection, hovering and so on. So you need your friend "Blend" to get rid of the template but at the end the result is really interesting and put you totally in control of rendering issues. The figure on the right side shows the deep hierarchy of the GridViewItem.
To oversimplify you have to consider that the GridView Item is finally a ContentControl so the sole required component is the ContentPresenter (hightlighted in grey). All other elements are nested to create the artifacts to support selection and other features. But if you don't need selection you can trim the Visual Tree and you will also get an improvement in rendering performances. In the following snippet I show a very minimal version of the template. It is made to reduce to the minimum the margin around items, so at the end they will appear attached one to the other.
1: <GridView.ItemContainerStyle>
2: <Style TargetType="GridViewItem">
3: <Setter Property="Margin" Value="0" />
4: <Setter Property="Padding" Value="0" />
5: <Setter Property="Template">
6: <Setter.Value>
7: <ControlTemplate TargetType="GridViewItem">
8: <ContentPresenter Margin="0" />
9: </ControlTemplate>
10: </Setter.Value>
11: </Setter>
12: </Style>
13: </GridView.ItemContainerStyle>
In a project of mine I had to reduce the space like in the example due to the need of simulate a tabular representation.
GridView vs ListView
At the end, I would like to remember that, the GridView and ListView are to controls that are very similar in their structure. Where the GridView has a GridViewItem the ListView has a ListViewItem but the differences are very subtle and what I've explained for the first are true also for the second. Don be afraid to experiment, Blend is a good tool to extract inner templates that need to be customized. Happy template everyone.