sticky
How To: Create a Variable Sized Grouped GridView (like the store)

    General discussion

  • There has been quite a few requests for help in creating a Variable Sized Grouped GridView similar to what you see in the Windows Store.

    Sample: https://skydrive.live.com/redir?resid=3A5CA8204EC61147!35841&authkey=!AAOLlNWZh4K-FkQ

    In general, creating a grid with variable sized items in XAML does require a few advanced techniques – specifically you have to derive from GridView and then override PrepareContainerForItemOverride. But let's take a look at how you determine the layout.

    When using a VariableSizedWrapGrid in a GridView (grouped or not) you need to look at its "Orientation" property. For Wrapping grids such as this the Orientation property determines how items will "fill" and then "wrap". In the image below the red lines show that I want the items to fill vertically from top to bottom, then left to right. 

    The next thing to look at is what the item size is. In trying to recreate the Store look I took some screen shots and saw that tiles are a multiple of a common size of 75 wide by 150 tall. See the image below – the black squares. I use the VariableSizedWrapGrid ItemWidth and ItemHeight properties to determine this.

     

    Next I need to determine when wrapping will occur. Using these common size squares, I see that the max height is 3 of these tall. We'll worry about the width of the items later. To tell VariableSizeWrapGrid this constraint you set the MaxRowsOrColumns. Think of this property as the "max constraint in the orientation direction". For us that is 3.

    Ok now that those properties are all set I look at the layout of the tiles. Think of this as an actual Grid or HTML Table that you are creating with the rows and columns values being a multiple of the ItemWidth and ItemHeight values we set earlier. In the store the "big" item is 6 of these common squares wide and 2 tall so we would set a ColumnSpan of 6 and RowSpan of 2. You get the idea.

    Hopefully you have downloaded and started to take a look at the sample. Note that the sample is based upon the Grid application Visual Studio Template (c#). The points of interest are in GroupedItemsPage.xaml and in MyGridView.cs.

    In GroupedItemsPage.xaml search for MyGridView and you'll see where I setup a horizontal VirtualizingStackPanel as the ItemsPanel. This contains the groups in a horizontal fashion. Since this is a grouped sample I set the GroupStyle to primarily specify the Panel used to show my items in the group. This is where I specify the VariableSizedWrapGrid and setup the properties as mentioned above. You'll also see some styles that I use but that is just visual look.

    In the MyGridView.cs file you'll see some interesting things. As mentioned at the start I had to create a derived GridView class and override PrepareContainerForItemOverride. I also created a "LayoutSizes" static which specifies the Row and Column spans. I use these to specify the layout using the "_sequence" variable. In the PrepareContainerForItemOverride method I use the index of the item in the group to index into the layout sizes sequence to determine the layout. In my simple example I just cast the item parameter to my SampleDataItem type to get the index, for model-view purists you'll have to figure out how you want to know the index of the item in the group. Lastly I set the attached properties for RowSpan and ColumnSpan using the helper SetRowSpan and SetColumnSpan.

     

    Feel free to ask questions about my sample. This sample is provided "as-is" and is based upon the Microsoft Ms-PL license: http://www.microsoft.com/opensource/licenses.mspx#Ms-PL

    -mark




    Thursday, May 17, 2012 10:16 PM

All replies

  • Thanks Mark for explaining this and providing a sample. Its something its something I've found to be really poorly documented. I do have a question though, when following this tutorial - http://msdn.microsoft.com/en-us/library/windows/apps/br211380 it allows you to create a feed reader. What I need to do is group the RSS feeds and use them for the data source of each Group in your example.

    This means the Group Title: 1 would be the title of the first feed and the Item Title: 1 would become the title of the first post in that feed and so on.

    I have been down the path of using a Stack Panel and multiple GridViews individually mapped to each feed, but now I'm up to Symantec Zoom, I find it only works if you use Grouped Data. Any help would be great appreciated in getting the code for grouping multiple feeds.


    techAU http://techAU.tv


    • Edited by techAU Saturday, May 19, 2012 5:34 AM
    Saturday, May 19, 2012 5:27 AM
  • There is no easy way to do what you asked, unless you implement the (slightly) complex ISemanticZoomInformation. Stack Panel and ScrollViewer do not know about SemanticZoom to get things to communicate. You could manually try and get things to work but it wouldn't be a perfect science. I suggest creating a new post with the topic of SeZo and grouping sample.

    -mark
    Program Manager
    Microsoft
    This post is provided "as-is"

    Wednesday, May 23, 2012 7:06 PM
  • Hey Mark,

    I'm attempting to override PrepareContainerForItemOverride() in C++. I'm doing this in order to get ItemContainerStyleSelector working. I noticed that when I override this, the ItemClick event never gets called anymore. I'm assuming its because unlike in your example, I am not calling the base's PrepareContainerForItemOverride() method. However, when I do so using GridView::PrepareContainerForItemOverride(), i get a stack overflow as the method just keeps calling itself recursively. Is there a way I can get this working or push the event handlers onto the elements?

    Friday, May 25, 2012 5:58 PM
  • For C++ you should ask in the "Building Metro style apps with C++" forum but I believe you need to do __super:PrepareContainerForItemOverride

    -mark
    Program Manager
    Microsoft
    This post is provided "as-is"

    Friday, May 25, 2012 10:31 PM
  • Hi Mark,

    Thanks for this, I implemented it in http://metrorssreader.codeplex.com and it works beautifully, checking it in soon to codeplex.com


    Zubair Ahmed zubairahmed.net MetroRssReader.codeplex.com

    Sunday, May 27, 2012 9:13 AM
  • Thanks Mark for explaining this and providing a sample. Its something its something I've found to be really poorly documented. I do have a question though, when following this tutorial - http://msdn.microsoft.com/en-us/library/windows/apps/br211380 it allows you to create a feed reader. What I need to do is group the RSS feeds and use them for the data source of each Group in your example.

    This means the Group Title: 1 would be the title of the first feed and the Item Title: 1 would become the title of the first post in that feed and so on.

    I have been down the path of using a Stack Panel and multiple GridViews individually mapped to each feed, but now I'm up to Symantec Zoom, I find it only works if you use Grouped Data. Any help would be great appreciated in getting the code for grouping multiple feeds.


    techAU http://techAU.tv


    I was working on something similar maybe this helps, you could modify the data model and create a hierarchy of observableCollections such as:

    feedsCollection(with feed items, name of feed, etc) -> feedItems(with names and descriptions and so on...)

    this way each feed would contain N feedItems and you could bind to only the feedsCollection, for example creating a class called "feeds", assign it as dataContext to the dataGridView and bind to the feedsCollection, you would dynamically create new feeds and add them to the collection and that's it.

    -----------

    This information is very good, great post Mark!


    Monday, June 11, 2012 1:37 AM