Iris Classon
Iris Classon - In Love with Code

WinRT app guide: Step 14: Adding layout awereness and snapped view, disabling portrait view

To read the other steps in the step by step guide for creating a Metr0/ WInRT application go here

We are getting closer to a final first version of this app! One of the requirements is that the app should support different view modes, such as the snapped view, filled view,- and you can choose if you want your app to work in both the landscape view and the portrait view. While it it not a requirement that you app supports both those modes, as it is with snapped and filled view, it is recommended that your app does that.

.

If you want to disable portrait view you can, but be aware that the simulator will still support all orientations,- basically because it doesnt really switch orientation- it just changes the size of the screen. So don’t panic :)
Oki , now that is the easy part. We must now make the magic happen!Time for some layout awereness. This can be done in many ways, but we will base our implementation on the one seen/found in the templates for the Windows Store Apps. If you were to open a default template you would get a folder calle ‘Common’ with a lot of neat classes to help you kickstart the app-making. One of them classes is called LayoutAwarePage.

The LayoutAwarePage inherits from the page class, and adds a few extra super neat features. I’ve stripped away a few of them and just kept the bare minimum that we will need, which is the functions that helps us map the application view states to the visual states. By letting our mainpage be of this type, we don’t have to have an explicit implementation and handle the view states on every single page ‘manually’, the code is instead centralized by using inheritance, so as long as pages are of this type, they will get this function. In many cases you would want the pages to have the same basic behavior when it comes to rearranging the layout- and inheriting from a class with that behavior added is a way to do that. We have only one page, but I still see a befenefit in using a baseclass as it lets us seperate some of the code out.

.

.

[sourcecode language=“csharp”]
using System.Collections.Generic;
using Windows.UI.Core;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace Mihsp.Common
{
[Windows.Foundation.Metadata.WebHostHidden]
public class LayoutAwarePage : Page
{
private List _layoutAwareControls;

    public LayoutAwarePage()  
    {  

        if (Windows.ApplicationModel.DesignMode.DesignModeEnabled) return;  

        this.Loaded += this.StartLayoutUpdates;  

        this.Unloaded += this.StopLayoutUpdates;  
    }  

    public void StartLayoutUpdates(object sender, RoutedEventArgs e)  
    {  
        var control = sender as Control;  
        if (control == null) return;  
        if (this.\_layoutAwareControls == null)  
        {  
            // Start listening to view state changes when there are controls interested in updates  
            Window.Current.SizeChanged += this.WindowSizeChanged;  
            this.\_layoutAwareControls = new List<Control>();  
        }  
        this.\_layoutAwareControls.Add(control);  

        // Set the initial visual state of the control  
        VisualStateManager.GoToState(control, DetermineVisualState(ApplicationView.Value), false);  
    }  

    private void WindowSizeChanged(object sender, WindowSizeChangedEventArgs e)  
    {  
        this.InvalidateVisualState();  
    }  

    public void StopLayoutUpdates(object sender, RoutedEventArgs e)  
    {  
        var control = sender as Control;  
        if (control == null || this.\_layoutAwareControls == null) return;  
        this.\_layoutAwareControls.Remove(control);  
        if (this.\_layoutAwareControls.Count != 0) return;  
        // Stop listening to view state changes when no controls are interested in updates  
        this.\_layoutAwareControls = null;  
        Window.Current.SizeChanged -= this.WindowSizeChanged;  
    }  

    protected virtual string DetermineVisualState(ApplicationViewState viewState)  
    {  
        return viewState.ToString();  
    }  
    public void InvalidateVisualState()  
    {  
        if (this.\_layoutAwareControls == null) return;  

        string visualState = DetermineVisualState(ApplicationView.Value);  

        foreach (var layoutAwareControl in this.\_layoutAwareControls)  
        {  
            VisualStateManager.GoToState(layoutAwareControl, visualState, false);  
        }  
    }  

}  

}
[/sourcecode]

And since we talked about seperating some of the code out, I was going to keep this guide super-easy and actually not use usercontrols, but I’ve decided to break out some of the XAML as it would make it easier for us to have a look at how we can handle the different viewstates. Create a new usercontrol and call it Stats (Add => new item => usercontrol). Cut the code for the flipview out and paste it in the usercontrol. Add the usercontrol where the FlipView used to be, and move the name and grid properties to this element from the Flipview.

[sourcecode language=“XML”]
<local:Stats Grid.Column=“2” x:Name=“ChartView”/>
[/sourcecode]

Give the button group for the Add/Edit, Delete and the new button Export a name, ButtonGroupInColumn, create a similar group beneath the FlipView usercontrol on the mainpage but using a grid with three columns for layout. Add a different name, and set visibility to collapsed, and grid.Row to 1 and columnspan to 3. We don’t have more than the one default row in this grid, so go up to the beginning of the grid and add two row defintions.

The new button group
[sourcecode language=“XML”]

<Grid.ColumnDefinitions>



</Grid.ColumnDefinitions>
Add / Edit
Delete
Export

[/sourcecode]

The rowdefinitions
[sourcecode language=“XML”]
<Grid.RowDefinitions>


</Grid.RowDefinitions>
[/sourcecode]

For the Portrait and Snapped mode we want a different Datatemplate for the activity items, so we move out the datatemplate to the resource dictionary. We create a second template with a different layout. I’ve also made some changes to the edit panel for the activities- the name of the score is now above each score. Set the original template as the ItemTemplate like so: ItemTemplate="{StaticResource ActivityDetails}"

The templates
[sourcecode language=“XML”]



<StackPanel.Resources>

</StackPanel.Resources>



















<DataTemplate x:Key="ActivityDetailsPortrait">  
    <Border Background="#004FC6" Padding="20" >  
        <StackPanel Orientation="Vertical" HorizontalAlignment="Stretch">  
            <StackPanel.Resources>  
                <Style BasedOn="{StaticResource TitleTextStyle}" TargetType="TextBlock" />  
            </StackPanel.Resources>  
            <TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextStyle}"/>  
            <TextBlock Text="{Binding CategoryName}"/>  
            <TextBlock TextWrapping="Wrap" Text="{Binding Comment}" Style="{StaticResource BodyTextStyle}"/>  
            <StackPanel >  
                <StackPanel Orientation="Horizontal">  
                    <TextBlock Text="Importance"/>  
                    <TextBlock Text="{Binding ImportanceScore}" Foreground="Yellow" Margin="10,0,0,0"/>  
                </StackPanel>  
                <StackPanel Orientation="Horizontal">  
                    <TextBlock Text="Urgency"/>  
                    <TextBlock Text="{Binding UrgencyScore}" Foreground="Yellow" Margin="10,0,0,0"/>  
                </StackPanel>  
                <StackPanel Orientation="Horizontal">  
                    <TextBlock Text="Worth"/>  
                    <TextBlock Text="{Binding WorthScore}" Foreground="Yellow" Margin="10,0,0,0"/>  
                </StackPanel>  
            </StackPanel>  
            <StackPanel>  
                <StackPanel Orientation="Horizontal">  
                    <TextBlock Text="Time spent"/>  
                    <TextBlock Text="{Binding TimeSpent}" Foreground="Yellow" Margin="10,0,0,0"/>  
                </StackPanel>  
                <StackPanel Orientation="Horizontal">  
                    <TextBlock Text="Time estimate"/>  
                    <TextBlock Text="{Binding TimeEstimate}" Foreground="Yellow" Margin="10,0,0,0"/>  
                </StackPanel>  
            </StackPanel>  
        </StackPanel>  
    </Border>  
</DataTemplate>  

[/sourcecode]

The next bit is to implement the visual states on the page (remember to change the page type to the new type, LayoutAwarePage, and reference the namspace where we put the class)
[sourcecode language=“XML”]
<VisualStateManager.VisualStateGroups>









































































</VisualStateManager.VisualStateGroups>
[/sourcecode]
As you can see, we are collapsing the stats and the buttons in snapped view but keeping the list. In portrait view we are showing the bottom buttons and we are using a different template for the activity items. We are also handling the columnspans so we can span across all three columns in snapped mode and so on.

landscape

portrait

snapped

Next time we will export the items as a text file, and some more fun :D :D

Comments

Leave a comment below, or by email.
Dileep P G
3/26/2013 8:50:53 PM
Thank you for calling this out :). I was beginning to.. panic!

"If you want to disable portrait view you can, but be aware that the simulator will still support all orientations,- basically because it doesnt really switch orientation- it just changes the size of the screen. So don’t panic :)" 
Iris Classon
3/27/2013 12:44:15 AM
Reply to: Dileep P G
NP :) Things like that can be rather annoying 


Last modified on 2012-11-02

comments powered by Disqus