Windows 8 GridView, ListView and SnapView

In a previous posting I demonstrated how you can create a powerful display of grouped data using the SnappedState GridView, which is typically used to fill the screen.  Every Windows 8 application must also support SnapView, however, in which your application is allocated 320×768 pixels – that is, your application is squeezed into a relatively thin sliver on the left or right of the screen.

GridView leaves much to be desired in SnapView and the common solution is to hide your GridView and to display the same data in a ListView, which works well in those dimensions as it scrolls vertically.

(Don’t be confused: GridView and ListView are controls, SnapView is a state of the application)

In the image to the right (click on the image to expand), Visual Studio is shown in the Fill state and our application is shown in the SnapView.  In the SnapView we display the data in a ListView and we make the title smaller.

There are a  few steps to implementing an application that supports SnapView well with these controls.  What you want is:

  • In Full or Landscape View show the GridView
  • In FillView show the GridView
  • In SnapView show the ListView

First, you need to know which view you are in, and you need to respond to a change in views.  Here are the steps:

  • Handle the Window SizeChanged event to notice that the user may have put you into or out of snapped view
  • Obtain the current visual state (e.g., Snapped)
  • Tell the VisualStateManager to go to the appropriate corresponding state
  • Implement the Storyboard that handles the ViewState change
    • Collapse the GridView
    • Show the ListView
    • Make any other appropriate changes (e.g., change the title)

 

Creating The Sample Application

To get started, create a new Blank App application.  Recreate the previous application, or download the previous application here

At this point you should have an application that displays a GridView.  Put that application into snapped view (Windows – dot) and notice that what you get is a thin slice of a GridView.  The GridView itself is unchanged.

We want to add a title to the page, so update the outermost grid with two rows and place the title TextBlock in the top row,

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock x:Name="Title"
Text="Cities"
FontSize="42"
Grid.Row="0" />

Run the application and notice that your GridView scrolls underneath the title, which is just what we want.

Adding the ListView

When we are in SnappedView we want to display this same data in a ListView.  Since ListView and GridView share all the same methods, events and properties, you can copy and paste the GridView so that you have two, one above the other.  Change the second GridView to a ListView and fix up anyplace in that ListView that says GridView (e.g., change the ItemContainerStyle TargetType from GridViewItem to ListViewItem).  Name the GridView FullView and name the ListView SnappedView (this is arbitrary and you can name them anything you want as long as you match up the names with the storyboard targets).

Here’s the complete ListView,

<ListView x:Name="SnappedView"
Grid.Row="1"
ItemsSource="{Binding Source={StaticResource cvs}}"
Visibility="Collapsed">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment"
Value="Left" />
<Setter Property="Padding"
Value="5" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}"
FontSize="26.67" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid Orientation="Vertical"
ItemWidth="220" />
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ItemsControl.GroupStyle>
</ListView>

Notice that it is nearly identical to the GridView and that, most important, its ItemsSource is bound to the same CollectionViewSource as the GridView.  This is very important as it will enable both controls to point to the same data.  In fact, by using the CollectionViewSource, if we select an item in the GridView and then switch to SnapView, the same item will be selected. 

The ViewStateManager Markup

You now need to add the ViewStateManager markup to move into and out of the SnapView.  Put this code below the GridView and ListView but within the outermost Grid. (Thanks to Michael Crump for pointing out the importance of where you place the VSM markup)

You begin by declaring the Visual State Groups and within that the ApplicationViewStates Group.  Within the ApplicationViewStatesGroup you’ll declare four Visual States:

  • FulllScreenLandscape
  • Filled
  • FullScreenPortrait
  • Snapped

They must have these names.  The first three will be empty but the fourth, Snapped, will have a Storyboard to implement the changes you want to have occur when you move into Snapped view.

<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ApplicationViewStates">
<VisualState x:Name="FullScreenLandscape" />
<VisualState x:Name="Filled" />
<VisualState x:Name="FullScreenPortrait" />
<VisualState x:Name="Snapped">
<Storyboard>

</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>

Inside the Storyboard we’ll add markup to change the size of the Title from its initial size of 42 to 20,

<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Title"
Storyboard.TargetProperty="FontSize">
<DiscreteObjectKeyFrame KeyTime="0"
Value="20" />
</ObjectAnimationUsingKeyFrames>
 

Notice that, while we are using animation syntax, there really is no animation; the change happens instantly (KeyTime=”0”).  We’ll add two other AnimationKeyFrames, one to collapse the GridView and one to show the ListView,

<ObjectAnimationUsingKeyFrames Storyboard.TargetName="FullView"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0"
Value="Collapsed" />
</ObjectAnimationUsingKeyFrames>

<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SnappedView"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0"
Value="Visible" />
</ObjectAnimationUsingKeyFrames>

All of these changes happen, essentially, instantaneously. 

We’re all set, when the view state changes to snapped we’ll diminish the size of the title and we’ll hide the GridView and show the ListView.  When we move out of SnappedView we’ll return to the default settings (since there is no storyboard for those states).

But how do we get the view state to change?

LayoutAwarePage

Many of the templates used to create new applications create a class, LayoutAwarePage, in the Common folder.  This page is somewhat complex, and we’ll create our own simpler version.  Create a new class named LayoutAwarePage in the Common folder.    The new class will inherit from Page and will implement two event handlers: one for when the window changes size and one for when the page is loaded,

public class LayoutAwarePage : Page
{
public LayoutAwarePage()
{
Window.Current.SizeChanged += WindowSizeChanged;
Loaded += LayoutAwarePage_Loaded;
}

Both event handlers call a helper method: SetVisualState,

private void LayoutAwarePage_Loaded( object sender, RoutedEventArgs e )
{
SetVisualState();
}

private void WindowSizeChanged( object sender, WindowSizeChangedEventArgs e )
{
SetVisualState();
}

The job of SetVisualState is to obtain the current visual state and to ask the VisualStateManager to go to the state with that name.  This forces the change in ViewState that is picked up by the page. 

public void SetVisualState()
{
string viewValue= ApplicationView.Value.ToString();
VisualStateManager.GoToState( this, viewValue, false );
}
We want our MainPage to derive from LayoutAwarePage.  To accomplish this,  open MainPage.xaml.cs and add a using statement,
 
using GridView.Common;

and then set the MainPage class to derive from LayoutAwarePage,
 
public sealed partial class MainPage : LayoutAwarePage

In addition, add a namespace to MainPage.xaml,

xmlns:common="using:GridView.Common"

and change the type of the page from Page to LayoutAwarePage

<common:LayoutAwarePage x:Class="GridView.MainPage"
 
Remember to do the same in the resources section,
 
<common:LayoutAwarePage.Resources>SnapView
<CollectionViewSource x:Name="cvs"
IsSourceGrouped="True"
ItemsPath="Items" />
</common:LayoutAwarePage.Resources>

You can now run the application and switch between snapped and unsnapped views and you should see the application “do the right thing.”
 
When you switch from Full View to Snapped View the WindowSizeChanged event registered in LayoutAwarePage, fires.  This causes a call to  SetVisualState which in turn causes a call to the Visual StateManager’s GoToState method, passing in the new viewValue.  This in turn starts up the animation defined in MainPage.xaml causing the font size of the title to shrink and the GridView to be hidden and the ListView to be shown.
 

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in Data, Mini-Tutorial, Styles and Templates, Windows 8 and tagged . Bookmark the permalink.

One Response to Windows 8 GridView, ListView and SnapView

Comments are closed.