Random geekery since 2005.

Husband, father, programmer, speaker, writer, blogger, podcaster, collector, traveler, golfer.

This article is Day #2 in a series called 31 Days of Windows 8.  Each of the articles in this series will be published for both HTML5/JS and XAML/C#. You can find additional resources, downloads, and source code on our website.

advertisementsample

Today we are going to talk about screen sizes, and why they are important to Windows 8 development.  In the first part of this article, we will discuss orientation, and some simple ways we can make our application more useful based on the way our user holds their device.  In the second part, we’re going to look at our application in a “snapped” state, and how we might change our interface to accommodate a much smaller screen size.

Orientation and snap are important because if you don’t consider them in your app, your app won’t be approved for the Windows Store.

If you look in the Windows 8 app certification requirements, in section 3.6, it reads:

Your app must support a snapped layout. In landscape orientation, your app’s functions must be fully accessible when the app’s display size is 1024 x 768. Your app must remain functional when the customer snaps and unsnaps the app.

What this says is that our application already needs to support three visual states, at a minimum:

  • 1024 x 768 (minimum screen resolution & filled state)
  • 320 x 768 (snapped)
  • Your default resolution that you are planning for, generally 1366 x 768.

Here’s an example of a full screen application moving to a snapped state:A multi-column layout in the unsnapped and snapped states

You can see that in this case, we have re-arranged our content to fill the smaller snapped state.  There is also the opportunity to move your application to a “filled” state, which is represented by the light gray block to the right of our snapped view.

Thankfully, there are some simple ways to recognize which state our application is in, and the rest of this article will be dedicated to showing exactly how this is done.

Supporting Rotation

To get started let’s create our almost famous app from the blank template in Visual Studio 2012. Once you’ve created it, run your app in the simulator or on a remote machine (more on that later). You will see your awesome blank app and if you rotate it, the app will in fact automatically rotate as well. Why, How?

By default, all templates in Visual Studio are setup to support all rotations. Remember that package.appxmanifest file? In the Application UI tab you will find a section called Supported Rotations. By checking one or more orientation preference you are selecting which orientations your app will support. Again by default we support all.

image_thumb4

Depending on your situation, this may be something of value to your apps overall experience. For example, if you’re building a game you might only want to support landscape mode.

Recognizing Orientation Change

Our first step in working with orientation is making sure that we can recognize when an orientation change has happened.  Thankfully, the Windows 8 SDK has provided us with the SimpleOrientationSensor that has events for this exact purpose.  For this example, we have again started with only a Blank App template in Visual Studio 2012.

First, we added a simple TextBox control to our MainPage.xaml file.  Here is the entire set of code for that file currently:

<Page

    x:Class="Day2_OrientationAndSnap.MainPage"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:local="using:Day2_OrientationAndSnap"

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    mc:Ignorable="d">


    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">

        <TextBlock Text="No orientation reading."

                   x:Name="AlertBox"

                   FontSize="50"

                   TextAlignment="Center"

                   Margin="0,100,0,0" />

    </Grid>

</Page>

Now, open the MainPage.xaml.cs file.  We need to add some code in order to use this sensor.

First, we’ll add a new using statement: using Windows.Devices.Sensors;.  Next, we will add a new instance of the SimpleOrientationSensor class, and add some event handlers to be able to actively recognize orientation change.  Here’s the entirety of the code from my MainPage.xaml.cs file.  We will explain the code afterwards. There are a few pieces of the C# code above that I should explain.

using System;

using System.Collections.Generic;

using System.IO;

using System.Linq;

using Windows.Foundation;

using Windows.Foundation.Collections;

using Windows.UI.Xaml;

using Windows.UI.Xaml.Controls;

using Windows.UI.Xaml.Controls.Primitives;

using Windows.UI.Xaml.Data;

using Windows.UI.Xaml.Input;

using Windows.UI.Xaml.Media;

using Windows.UI.Xaml.Navigation;

using Windows.Devices.Sensors;


namespace Day2_OrientationAndSnap

{

    public sealed partial class MainPage : Page

    {

        private SimpleOrientationSensor orientationSensor;


        public MainPage()

        {

            this.InitializeComponent();

            orientationSensor = SimpleOrientationSensor.GetDefault();

        }


        protected override void OnNavigatedTo(NavigationEventArgs e)

        {

            if (orientationSensor != null)

                orientationSensor.OrientationChanged += new TypedEventHandler<SimpleOrientationSensor, SimpleOrientationSensorOrientationChangedEventArgs>(orientationSensor_OrientationChanged);

        }


        protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)

        {

            if (orientationSensor != null)

                orientationSensor.OrientationChanged -= orientationSensor_OrientationChanged;

            base.OnNavigatingFrom(e);

        }


        async private void orientationSensor_OrientationChanged(SimpleOrientationSensor sender, SimpleOrientationSensorOrientationChangedEventArgs args)

        {

            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>

            {

                ShowOrientationText(args.Orientation);

            });

        }


        private void ShowOrientationText(SimpleOrientation simpleOrientation)

        {

            switch (simpleOrientation)

            {

                case SimpleOrientation.NotRotated:

                    AlertBox.Text = "Not Rotated";

                    break;

                case SimpleOrientation.Rotated90DegreesCounterclockwise:

                    AlertBox.Text = "90 Degrees CounterClockwise";

                    break;

                case SimpleOrientation.Rotated180DegreesCounterclockwise:

                    AlertBox.Text = "180 Degrees Rotated";

                    break;

                case SimpleOrientation.Rotated270DegreesCounterclockwise:

                    AlertBox.Text = "270 Degrees Rotated CounterClockwise";

                    break;

                case SimpleOrientation.Facedown:

                    AlertBox.Text = "Face Down";

                    break;

                case SimpleOrientation.Faceup:

                    AlertBox.Text = "Face Up";

                    break;

                default:

                    AlertBox.Text = "Unknown";

                    break;

            }

        }

    }

}

First, we created a new SimpleOrientationSensor object, named orientationSensor.  In the constructor method, MainPage(), we instantiate that object with the default orientation sensor on the device.

In the OnNavigatedTo() and OnNavigatingFrom() event handlers, we add and remove an OrientationChanged event to/from our new object.  It is important to make sure that the object is not null, because on devices that don’t have this sensor, we will get an error.

Next, we have a new event handler named orientationSensor_OrientationChanged().  You should note that it is decorated with an async descriptor, and uses the await keyword when taking its action.  This is ultimately done to avoid bottlenecks in our code that would otherwise hold up the application.  (You can read more about async/await on MSDN.)

Once the data has been obtained, we call a new method, ShowOrientationText(), passing the Orientation data along.

Finally, ShowOrientationText() does a simple switch statement against all of the possible orientations that can exist: NotRotated, Rotated90DegreesCounterclockwise, Rotated180DegreesCounterclockwise, Rotated270DegreesCounterclockwise, Facedown, Faceup, or Unknown.  I think it’s funny that they named one of them Rotated180DegreesCounterclockwise, as 180 degrees shouldn’t matter which direction they went.

Here’s a quick look at the application in its current state:

 

Remote Debugging

Now, if you’re anything like Clark and I, you like writing your code on a beefy quad-core desktop machine, maybe 8-12 GB of RAM, dual 27” monitors, mouse, keyboard, the whole 9 yards.  Unfortunately, these machines are unlikely to have an orientation sensor, and picking up your monitor to change the orientation just isn’t going to work.  In addition the SimpleOrientationSensor we’re using is not emulated by the Simulator, so we’ll need an actual device to make this happen.

Another important lesson we learned:  It’s a simulator, not an emulator like Windows Phone.  This means that it will only simulate the machine you’re currently working on, not act as a completely different, fully capable device.  No orientation sensor?  You need another device.

Thankfully, Microsoft has provided a way for us to make this happen on a remote secondary device, much like we do when building Windows Phone applications.  Here’s the short story on how it works (MSDN has the longer, more thorough story):

Install the Remote Debugging Tools on the secondary device.  I’m using a Samsung Series 7 Slate, but any Windows 8 device in a tablet form factor should suffice.  You can download the Remote Debugging Tools here.  Make sure you choose the appropriate flavor, x86, x64, or ARM, depending on your device.

Run the Remote Debugging Tools on the secondary device.  You’ll see an icon that looks like this:

RemoteDebuggerIcon

Once the Remote Debugger is running on your secondary device, go back to your primary machine and select “Remote Machine” as your target for deployment.

RemoteMachineDebuggingOption

When you choose “Remote Machine” for the first time, you will be presented with a dialog box that looks like the image below.  Remember, devices on your subnet will only appear if the Remote Debugger Tools have been installed and are currently running.

RemoteDebuggerConnections

Later, when you want to switch devices, you’re going to struggle to find where this option is stored.  I’m here, my dear readers, to save you that hassle.    Open up your project properties (Alt + Enter), and choose the Debug tab.  From there, you can change or remove your previous choice.  If you remove the choice, the next time you choose remote debugging, you’ll get the dialog box from earlier.

ProjectPropertiesDebug

Back to coding…

OK, so at this point, we have an application that recognizes that device orientation has changed.  That’s all well and good for when we want to do something specific in code with orientation, but what if we just want to have our application re-orient itself to be readable/useable to our user?  There’s a much easier way, thanks to VisualStateManager.

Go back to your project, and right-click to the get Add…New Item… dialog.

AddNewBasicPageDialog

Add a new Basic Page, we named ours OrientationPage.xaml.  If you take a look at the XAML for this page, you’ll discover that we’re working with an entirely different page object right out of the box.  This is a LayoutAwarePage, and by default it already has enough structure to orient itself appropriately, and even provides a VisualState for the snapped view as well.

In addition, by using this type of page, the simulator also respects orientation changes.

This means that just by using this type of page, we automatically get an orientation and snap-aware page that has a static set of visual states that we can manipulate to make our page do what we’d like.  To make this more obvious, I am going to modify my visual states so that each one has a different background color.

LandscapeOrientation

PortraitOrientation

SnappedState

Here’s a look at the modified VisualState values from our OrientationPage.xaml:

<VisualStateManager.VisualStateGroups>

    <VisualStateGroup x:Name="ApplicationViewStates">

        <VisualState x:Name="FullScreenLandscape">

            <Storyboard>

                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="layoutRoot" Storyboard.TargetProperty="Background">

                    <DiscreteObjectKeyFrame KeyTime="0" Value="Purple"/>

                </ObjectAnimationUsingKeyFrames>

            </Storyboard>

        </VisualState>

        <VisualState x:Name="Filled">

            <Storyboard>

                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="layoutRoot" Storyboard.TargetProperty="Background">

                    <DiscreteObjectKeyFrame KeyTime="0" Value="Orange"/>

                </ObjectAnimationUsingKeyFrames>

            </Storyboard>

        </VisualState>

        <VisualState x:Name="FullScreenPortrait">

            <Storyboard>

                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="layoutRoot" Storyboard.TargetProperty="Background">

                    <DiscreteObjectKeyFrame KeyTime="0" Value="DarkGreen"/>

                </ObjectAnimationUsingKeyFrames>

                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">

                    <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PortraitBackButtonStyle}"/>

                </ObjectAnimationUsingKeyFrames>

            </Storyboard>

        </VisualState>

        <VisualState x:Name="Snapped">

            <Storyboard>

                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="layoutRoot" Storyboard.TargetProperty="Background">

                    <DiscreteObjectKeyFrame KeyTime="0" Value="Blue"/>

                </ObjectAnimationUsingKeyFrames>

                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">

                    <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedBackButtonStyle}"/>

                </ObjectAnimationUsingKeyFrames>

                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="pageTitle" Storyboard.TargetProperty="Style">

                    <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedPageHeaderTextStyle}"/>

                </ObjectAnimationUsingKeyFrames>

            </Storyboard>

        </VisualState>

    </VisualStateGroup>

</VisualStateManager.VisualStateGroups>

As you can see, we’ve simply added a new node to each section that modifies the Background property of my layoutRoot Grid.  What this new LayoutAwarePage allows us to do, instead of writing tons of code to manage our orientation, is to worry about the stuff that matters: our styling.  By writing seperate styles for our page elements based on the visual state of our page, we simplify the entire process.

Summary

Today, we took a look at how we can determine the orientation of a user’s device, as well as how to use the new LayoutAwarePage item to manage the different visual states our app might encounter.  There are tons of great examples on the web related to orientation and snap, but if there is ONE lesson you take away from today’s article, it’s this:

Your application MUST acknowledge the snapped state.  Make sure you accomodate it.

To download the entire sample solution from this article, you can download it here:

downloadXAML

Tomorrow, we’re going to look at the Splash Screen.  It’s a valuable tool for loading your application, as well as monetizing it.  We’ll dive head first into that tomorrow.  See you then!

downloadTheTools

Tags

15 responses to “31 Days of Windows 8 | Day #2: Orientation & Snap”

  1. asierba Avatar
    asierba

    Hey Jeff,
    Good tutorial. Easy and simple to read. Carry on like this! 🙂

  2. Paul Avatar

    This is excellent. Just enough to give me the basics but not so much that I don’t have to dig to understand what’s going on. Looking forward to the rest of the articles.

    1. jeffblankenburg Avatar
      jeffblankenburg

      I hope to keep them all at that level. I want easy, simple tutorials that can be read or coded along with. I’m not looking intimidate people with deep, obscure topics. 🙂

      Glad you like it so far!

  3. […] scope of making your application alive also if it has not been started by the user…”31 Days of Windows 8 | Day #2: Orientation & Snap (Jeff Blankenburg)“Today we are going to talk about screen sizes, and why they are important […]

  4. Alan Feekery Avatar

    Great job Jeff, just a quick thing I noticed though:

    It would seem that by standard the basic page template does not name the root grid “layoutRoot”, it has no name actually. Might be handy for other readers if you include that modification after the visual state manager stuff.

    Keep up the good work! =D

  5. […] 31 Días de Windows 8 | Día 2: Orientación y snapping by Amin Espinoza on Nov 16, 2012 • 6:30 pm No Comments Este artĂ­culo es una traducciĂłn de “DĂ­a 2: OrientaciĂłn y snapping” de Jeff Blakenburg. Puede… […]

  6. YJ Avatar
    YJ

    please provide Table-Of-Contents rightside on the page!!
    It is too hard to navigate your tutorials.

    1. jeffblankenburg Avatar
      jeffblankenburg

      There is an entire website dedicated to browsing all of the articles, as well as their HTML counterparts. There’s also tons of other resources and links for each day. http://31daysofwindows8.com

  7. Sam Avatar

    Jeff,

    Just wanted to clarify one thing you guys said in this article. It is true that most dev laptops will not have many of the sensors found in tablets & remote debugging really helps. However, the Simulator does a little more than inherit everything from the host machine, namely being capable of touch emulation, rotation & multiple resolutions. So, in the case of supporting multiple orientations, the simulator should actually be enough to test, rather than having to remote debug.

    Thoughts?

    Thanks again for this great series. I do owe a big part of clearing 70-484 test to you guys! 🙂

  8. durga Avatar
    durga

    Just a few observations based on my walk through of this.
    1. If you just add a page (OrientationPage.xaml), it won’t show up unless you wire it up in the app.xaml.cs (instead of mainpage).
    2. Since the Grid occupied the page, I needed to set the background colors
    on the Grid, not on the layoutroot. Perhaps I am missing something.
    Thanks for these posts (I got 2 down, 29 to go :-))

  9. KillerSpaz Avatar
    KillerSpaz

    Just started your tutorials today… Amazing content, and well explained (unlike the many others I’ve looked at).

    I think the SimpleOrientation.Rotated180DegreesCounterclockwise could be useful for assuming a hand-orientation of the user (i.e., left-handed vs right-handed).

    Also maybe for games, you could require the user to rotate a certain direction to achieve a goal.

    Only thing I can think of…

  10. Jun Avatar
    Jun

    Ok, so I followed the tutorial and added a Basic Page to my project, but now I get a silly error:

    The name “LayoutAwarePage” does not exist in the namespace “using:App8.Common”.

    This is ridiculous. If anyone on here works for Microsoft can you please try and make things a little less cryptic and nonsensical in the future? I mean not only is the documentation convoluted and unreadable but now we have to deal with Cryptic dependencies problems simply on adding what is basically a form. What happened, did you guys hire the losers who created XCode to write Visusal Studio 2012? It feels almost as clunky as XCode! That’s horrible.

  11. Jun Avatar
    Jun

    Lol. I went to the Basic Pages’s CS file and removed App8.Common. from before LayoutAwarePage and tried to compile and it still gave an error. I then put App8.Common. back and all the errors magically went away. You just have to love buggy IDEs.

  12. Jun Avatar
    Jun

    I’m sorry for my rant. Your tutorial are awesome and I really do appreciate your hard work. Its just that Microsoft has really frustrated me by going an XCode-ish route with VS 2012. Having to use things like Canvas.SetLeft(circle1, Canvas.GetLeft(circle1) + (m_ballDirectionX * 1)); instead of a more normative Visual Studio syntax like circle1.Left = circle1.Left + (m_ballDirectionX * 1); is a huge and annoying step back. First, its no longer object oriented! Its like using C-style functions to edit the parameters of classes rather than either making the members of the class public or at least giving a setter. circle1.setLeft(x) would be fine, but Canvas.SetLeft(circle1, x) is just annoyingly stupid and Apple-ish. Death to objectiveC!

  13. Deb Avatar
    Deb

    Hi I just followed what you have done and written the code exactly same, but then using simulator I couldn’t get the rotate status message like AlertBox.Text = “not rotated” etc, later i went to the debug mode and I found that

    protected override void OnNavigatedTo(NavigationEventArgs e) method does not hit at all when I rotate the simulator. Is there anything worng? Or I am not following the correct steps? I am using windows 8, VS 2012 simulator in my laptop.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a website or blog at WordPress.com

%d bloggers like this: