Home > C#, WinRT > Variable sized items with GridView control for Windows Store apps

Variable sized items with GridView control for Windows Store apps

28/05/2013

The GridView control displays items with the same height and width. This behavoir is controlled by its ItemsPanel property, that is a WrapGrid by default. If we change it to VariableSizedWrapGrid, we can determine the size of each cell separately. In this way, we can for example create the same UI that we can see in the Store app.

To create variable sized items, we need to set the VariableSizedWrapGrid.RowSpan and VariableSizedWrapGrid.ColSpan attached properties on each GridViewItem. However, there is a problem. These properties can’t be directly bound to GridViewItem, so we need to extend the standard GridView control and override its PrepareContainerForItemOverride method:

public class VariableSizedGridView : GridView
{
    protected override void PrepareContainerForItemOverride(DependencyObject element, 
        object item)
    {
        try
        {
            dynamic localItem = item;
            element.SetValue(VariableSizedWrapGrid.RowSpanProperty, localItem.RowSpan);
            element.SetValue(VariableSizedWrapGrid.ColumnSpanProperty, localItem.ColSpan);
        }
        catch
        {
            element.SetValue(VariableSizedWrapGrid.RowSpanProperty, 1);
            element.SetValue(VariableSizedWrapGrid.ColumnSpanProperty, 1);
        }

        base.PrepareContainerForItemOverride(element, item);
    }
}

The PrepareContainerForItemOverride method is called when an item in the GridView is about to be shown. We convert the item parameter to dynamic type and try to access its RowSpan and ColSpan properties to set the corresponding attached properties. If this fails, tipically because the object does not contain such properties, we use the default values.

To make things work, we need to add the RowSpan and ColSpan properties to our model class. For example:

public class Person
{
    public string Name { get; set; }

    public int ColSpan { get; set; }
    public int RowSpan { get; set; }
}

In XAML, let’s change the default GridView with the new VariableSizedGridView:

<local:VariableSizedGridView
    x:Name="itemGridView">
    <GridView.ItemsPanel>
        <ItemsPanelTemplate>
            <VariableSizedWrapGrid ItemHeight="120" ItemWidth="300" />
        </ItemsPanelTemplate>
    </GridView.ItemsPanel>
    <GridView.ItemTemplate>
        <DataTemplate>
            <Grid Background="Gray" Height="2000" Width="2000">
                <StackPanel VerticalAlignment="Top" Background="White" Opacity="0.75" 
                            Height="40">
                    <TextBlock Text="{Binding Name}" Margin="5" 
                               FontWeight="SemiBold"></TextBlock>
                </StackPanel>
            </Grid>
        </DataTemplate>
    </GridView.ItemTemplate>
</local:VariableSizedGridView>

Note that, at line 5, we set a VariableSizeWrapGrid as ItemsPanelTemplate. The ItemHeight and ItemWidth properties represent the size of a single cell within the GridView.

Then, on line 10, we set the dimension of the Grid Data template to 2000×2000. Actually, the Grid will fit the space available to it according to the ItemHeight and ItemWidth properties of VariableSizedWrapGrid and the RowSpan and ColSpan properties of the model item (i.e., if RowSpan is 2, the actual width of the GridViewItem will be 300*2=600). The size specified for the Grid object ensures that it will always be large enough to fill all the available space. If we have specified, for example, a dimension of 100×100, GridView items would be centered in the space that the VariableSizedWrapGrid assigns to it.

Here is an example of how to use the new VariableSizedGridView:

public IEnumerable<Person> GetPeople()
{
    var list = new List<Person>();

    for (int i = 1; i <= 100; i++)
    {
        list.Add(new Person
        {
            Name = "Name " + i,
        });
    }

    return list;
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    var people = this.GetPeople();

    // We want the first person to be 2-columns and 3-rows wide.
    var firstPerson = people.FirstOrDefault();
    firstPerson.ColSpan = 2;
    firstPerson.RowSpan = 3;

    itemGridView.ItemsSource = people;   
    
    base.OnNavigatedTo(e);
}

This code will produce the following result:

Variable Sized GridView in action

Variable Sized GridView in action

Categories: C#, WinRT
%d bloggers like this: