Nov 022012
 
 November 2, 2012  Posted by at 11:04 pm Telerik, WinRT Tagged with: ,  Add comments

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<Control> _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 x:Name="ButtonGroup" Grid.ColumnSpan="3" Grid.Row="1" Visibility="Collapsed" Margin="0,20,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Height="75" HorizontalAlignment="Stretch" VerticalAlignment="Top" Background="#95A810" Margin="20,0,20,0" BorderBrush="{x:Null}" Click="ViewDetails_Click" >Add / Edit</Button>
<Button Grid.Column="1" Height="75" HorizontalAlignment="Stretch" VerticalAlignment="Top" Background="#95A810" Margin="20,0,20,0" BorderBrush="{x:Null}" Click="Delete_Click" >Delete</Button>
<Button Grid.Column="2" Height="75" HorizontalAlignment="Stretch" VerticalAlignment="Top" Background="#95A810" Margin="20,0,20,0" BorderBrush="{x:Null}" Click="Export_Click" >Export</Button>
</Grid>
[/sourcecode]

The rowdefinitions
[sourcecode language=”XML”]
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</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”]
<DataTemplate x:Key="ActivityDetails">
<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 Orientation="Horizontal">
<TextBlock Text="Importance" Margin="0,0,10,0"/>
<TextBlock Text="{Binding ImportanceScore}" Foreground="Yellow"/>
<TextBlock Text="Urgency" Margin="10,0,10,0"/>
<TextBlock Text="{Binding UrgencyScore}" Foreground="Yellow"/>
<TextBlock Text="Worth" Margin="10,0,10,0"/>
<TextBlock Text="{Binding WorthScore}" Foreground="Yellow"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Time spent" Margin="0,0,10,0"/>
<TextBlock Text="{Binding TimeSpent}" Foreground="Yellow"/>
<TextBlock Text="Time estimate" Margin="10,0,10,0"/>
<TextBlock Text="{Binding TimeEstimate}" Foreground="Yellow"/>
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>

<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>
<!– Visual states reflect the application’s view state –>
<VisualStateGroup x:Name="ApplicationViewStates">
<VisualState x:Name="FullScreenLandscape"/>
<VisualState x:Name="FullScreenPortrait">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="ItemTemplate">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ActivityDetailsPortrait}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonGroupInColumn" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonGroup" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="firstColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="40"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="secondColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="40"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ChartView" Storyboard.TargetProperty="(Grid.ColumnSpan)">
<DiscreteObjectKeyFrame KeyTime="0" Value="2"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ChartView" Storyboard.TargetProperty="(Grid.Column)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="EditAdd" Storyboard.TargetProperty="(Grid.ColumnSpan)">
<DiscreteObjectKeyFrame KeyTime="0" Value="2"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="EditAdd" Storyboard.TargetProperty="(Grid.Column)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Filled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="firstColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="40"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="secondColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="40"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Snapped">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="ItemTemplate">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ActivityDetailsPortrait}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonGroupInColumn" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="firstColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="20"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="secondColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="20"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="column" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="200"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ChartView" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="EditAdd" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="(Grid.ColumnSpan)">
<DiscreteObjectKeyFrame KeyTime="0" Value="3"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</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.

Next time we will export the items as a text file, and some more fun πŸ˜€ πŸ˜€

  4 Responses to “WinRT app guide: Step 14: Adding layout awereness and snapped view, disabling portrait view”

  1. […] WP8. For this project I’m using a Portable Class Library so I can share the code…”WinRT app guide: Step 14: Adding layout awereness and snapped view, disabling portrait view (Iris Classon)“One of the requirements is that the app should support different view modes, […]

  2. 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 :)”

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)

What is 4 + 12 ?
Please leave these two fields as-is:
IMPORTANT! To be able to proceed, you need to solve the following simple math (so we know that you are a human) :-)