Monday, 3 March 2014

Creating a custom GridView control that responds to layout changes

In Windows Store apps for Windows 8.1 one of the UX guidelines is to scroll vertically when in PortraitLayout or MinimalLayout, and to scroll horizontally when in DefaultLayout (landscape). The conventional approach to implementing this is to provide two separate controls to handle the scenario – a GridView to handle horizontal scrolling views, and a ListView to handle vertical scrolling views. However, its possible to run into difficulties with this approach quite easily as it can be difficult to synchronize the two controls. For instance, if you have scrolled to a certain position in a GridView in DefaultLayout and then switch to the ListView in MinimalLayout, it can be difficult to make the ListView scroll to the same position. Similarly, if you’ve selected an item in a GridView in DefaultLayout and then switch to the ListView in MinimalLayout, it can be difficult in certain scenarios to automatically select the same item in the ListView.

This is a topic that we gave much consideration while working on Prism for the Windows Runtime for Windows 8.1, and the solution we came up with is the AutoRotatingGridView custom control. This control is a view state detecting GridView control created for the AdventureWorks Shopper reference implementation, which ships as part of Prism for the Windows Runtime. When, for example, the view state changes from DefaultLayout to PortraitLayout the items displayed by the control will be automatically rearranged to use an appropriate layout for the view state. The advantage of this approach is that only one control is required to handle all the view states, rather than having to define multiple controls to handle the different view states.

The control listens to the SizeChanged event and updates its layout between DefaultLayout, PortraitLayout, and MinimalLayout based on the window size and shape. In order to take advantage of the functionality provided by the control you must specify additional properties on your AutoRotatingGridView instance. The control allows you to set the ItemTemplate, ItemsPanel, and GroupStyle properties, which are used for defining how the control responds in DefaultLayout. The control also defines the PortraitItemTemplate, PortraitItemsPanel, and PortraitGroupStyle properties, which are used for defining how the control responds in PortraitLayout. In addition, the control defines the MinimalItemTemplate, MinimalItemsPanel, and MinimalGroupStyle properties, which are used for defining how the control responds in MinimalLayout. Finally, the control defines the MinimalLayoutWidth property which has a default value of 500. All of these additional properties are defined in the AutoRotatingGridView class.

In this blog post I’ll extended my sample PhotoViewer app to use the AutoRotatingGridView custom control to handle view states changes, and to ensure that content scrolls in the correct direction when in a specific layout.

Implementation

Using the AutoRotatingGridView custom control in the MainPage is simply a case of replacing the GridView control with the AutoRotatingGridView custom control.

<controls:AutoRotatingGridView x:Name="photosGridView"
                               Grid.Row="1"
                               IsItemClickEnabled="True"
                               ItemsSource="{Binding Photos}"
                               Loaded="photosGridView_Loaded"
                               Margin="140,0,0,0"
                               ScrollViewer.IsHorizontalScrollChainingEnabled="False"
                               SelectionMode="None">
    <interactivity:Interaction.Behaviors>
        <core:EventTriggerBehavior EventName="ItemClick">
            <behaviours:NavigateWithEventArgsToPageAction TargetPage="PhotoViewer.Views.PhotoPage"
                                                          EventArgsParameterPath="ClickedItem.Path" />
        </core:EventTriggerBehavior>
    </interactivity:Interaction.Behaviors>
    <controls:AutoRotatingGridView.ItemTemplate>
        <DataTemplate>
            <Border Background="#FF939598">
                <Image Height="130"
                       Source="{Binding Path=Thumbnail, Converter={StaticResource ThumbnailConverter}}"
                       Stretch="Uniform"
                       Width="190" />
            </Border>
        </DataTemplate>
    </controls:AutoRotatingGridView.ItemTemplate>
</controls:AutoRotatingGridView>

This XAML defines an AutoRotatingGridView custom control with an ItemTemplate for DefaultLayout. Because no values are specified for PortraitLayout or MinimalLayout, the control falls back to using the ItemTemplate specified for DefaultLayout. This is adequate for the sample app because the DataTemplate is sufficiently simple that it can be displayed in DefaultLayout, PortraitLayout, and MinimalLayout, without modification. For information about the purpose of the NavigateWithEventArgsToPageAction custom interaction, see Writing a custom Blend interaction to perform page navigation in Windows Store apps.

The result of using the AutoRotatingGridView custom control is that when in DefaultLayout scrolling is horizontal, and when in PortraitLayout or MinimalLayout scrolling is vertical. Contrast this to using the GridView control in which scrolling is horizontal in all layouts.

This scenario only demonstrates a very simple example of the AutoRotatingGridView custom control. It’s real power lies in displaying more complex data where it becomes necessary to define ItemTemplates, ItemsPanels, and GroupStyles for each layout. For examples of these scenarios see the AdventureWorks Shopper reference implementation.

Summary

In this blog post I’ve further extended the sample photo viewer app so that the MainPage uses the AutoRotatingGridView custom control to respond to view state changes. This simply involved replacing the GridView control in the MainPage with the AutoRotatingGridView custom control. Take a look at the AdventureWorks Shopper reference implementation for more complex examples of the AutoRotatingGridView custom control.

The sample app can be downloaded here.

No comments:

Post a comment