Automatic Rotation Support or Automatic Multi-Orientation Layout Support for Windows Phone

I guess it is a little too late for an introductory post about programming for the Windows Phone. Instead, I’ll focus on one part of the first Windows Phone 7 application that I built: the built-in support for both landscape and portrait views in WP7 Silverlight (SL) applications. But before we start, let me give you some info about the application itself.

My first Windows Phone 7 application was a rather simple Twitter Search04 application. Not as simple as the one Scott Guthrie showed during his Mix10 keynote demo, which only retrieves specific user history, but rather an application that allow users to search for tweets on a specific topic using the public free Twitter API.

The end results looks like the this image, where you can see a search box, a button, a list of tweets, and for each tweet its user avatar.

Implementing this application is simple. All you need to do is use a WebClient to invoke a REST Web request to the Twitter Search API with the search query that the user has typed. Remember that WebClient supports the ASync programming model, so When the results return, you simply need to parse the return XAML (ATOM feed) using LINQ to XML, and bind the output to the ListBox. Of course you will need to define a data template in order to display the content properly.

You can download the code, and even better, read the complete Building Your First Windows Phone 7 Application chapter that explains in a step-by-step approach how to build this Twitter Search application

But as I said before, this blog focuses on the built-in automatic orientation support. As is true for most Windows Phone (WP) SL applications, this twitter application is comprised of a PhoneApplicationPage that holds all the page content. Visual Studio creates the MainPage.xaml for you as part of the WP Project template. This file provides the default user interface of your application. Essentially this is your application’s first page. When compiled with MainPage.xaml.cs (the “code behind”) it creates a PhoneApplicationPage class that later is used to instantiate this page object.

The PhoneApplicationPage includes a SupportedPageOrientation property that you can set in your application. By default, a new PhoneApplicationPage supports both orientations. The following line of code is generated by Visual Studio by default in the PhoneApplicationPage constructor.

SupportedOrientations = 

    SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;

This line of code sets the supported orientation of your application to both Portrait and Landscape modes. It is one of Silverlight’s most important features on the phone since, combined with its support of automatic layout and rescaling, SL provides a runtime environment that is agnostic to the physical size of the screen form factor.

But to fully enjoy the built-in orientation support, you need to make sure you use the right tools, that is, the right Silverlight controls. The following is basically the think-and-experiment process that I went through while writing this Twitter Search.

Starting from the Top

The default WP project template generates a PhoneApplicationPage with a grid as its layout root (the first UI Element on this page). It is a very good idea to use the grid as the top level UI Element since a grid is probably the most powerful layout control SL has, as you will soon see. If you check the MainPage.xaml, you will notice that the LayoutRoot grid has two rows, each of which contains its own grid. The more interesting is the second row content grid, which holds the entire content page in addition to the two titles. Here is the basic XAML of the template:

<Grid x:Name="LayoutRoot" 

      Background="{StaticResource PhoneBackgroundBrush}">

    <Grid.RowDefinitions>

        <RowDefinition Height="Auto"/>

        <RowDefinition Height="*"/>

    </Grid.RowDefinitions>

 

    <Grid x:Name="TitleGrid" Grid.Row="0">

        <TextBlock Text="Twitter Search" 

            x:Name="textBlockPageTitle" 

            Style="{StaticResource PhoneTextPageTitle1Style}"/>

        <TextBlock Text="Search Results" 

            x:Name="textBlockListTitle" 

            Style="{StaticResource PhoneTextPageTitle2Style}"/>

    </Grid>

 

    <Grid x:Name="ContentGrid" Grid.Row="1">

    </Grid>

</Grid>

Note, that we do NOT define a fixed pixel size to any of the row heights. Rather we use Auto and * to tell the SL layout engine to use as much space as needed for the first row (using Auto), and the rest for the second row (using *).

Adding a ListBox to Display the Search Results

Now that we have the content grid ready, adding the TextBox for the search query and the Button to invoke the search is rather trivial. More interesting is the process required to display the search’s resulting tweets. To do so, we’ll bind a collection of tweet objects to the ListBox. A tweet object is a simple data object including relevant properties we wish to display for each tweet. Once we bind the collection, we need to define a data template. If you are not familiar with date templating, take a look at this short intro. The idea behind data templating is to explain the SL runtime “how”; that is, to display the data that is found in each object that is bound to a ListBox.

In our case, the tweets collection holds a list of tweet objects, but the ListBox has no idea of how to display a tweet object. It is our job to “explain” to the ListBox how to present each tweet object in its list.

Defining the Data Template

In order to complete the Twitter Search application, we need to define the data template of the lstSearchResults ListBox. A ListBox control uses a data template to predetermine the layout of each item, essentially controlling the way its rows are displayed. To define such a template you need to use the ItemTemplate property that refers to a DataTemplate element. The DataTemplate defines a set of elements that are grouped together to represent a single item in the list (by default, a single row).

The easiest and quickest solution I thought of was to use a StackPanel to display each tweet. So I started with the following XAML code:

<DataTemplate>

    <StackPanel >

           <Image Source="{Binding Mode=OneWay, Path=Avatar}" 

                  Margin="5,0,10,0" Width="75" Height="75" 

                  HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>

         <TextBlock Text="{Binding Mode=OneWay, Path=Title}" 

                  HorizontalAlignment="Stretch" VerticalAlignment="Center"

                  Style="{StaticResource PhoneTextSmallStyle}"

                  TextWrapping="Wrap" />

    </StackPanel>

</DataTemplate>

This yielded the following result:

03

Obviously I forgot to set the orientation of the stack panel to Horizontal. But I was happy because I noticed that the text wrapped and, beside the fact that the images are placed in the center (as expected from the default Vertical alignment stack panel), everything worked. So I set the orientation of the stack panel to Horizontal (simply add an attribute to the StackPanel element: Orientation=”Horizontal”). Happily, I pressed F5 expecting to complete the app very soon. However to my surprise I got the following result:

01

This was strange! What is going on here? The TextBlock object displaying the text clearly indicates TextWrapping. In addition the HorizontalAlignment is set to stretch, which implies as much as possible, NOT infinite space – as clearly shown by the above image.

It turns out that during the measuring step (part of the two steps – measure and draw, that take place each time SL needs to redraw objects), a stack panel with Horizontal orientation doesn’t limit the total size of object; that is, these objects think they have infinite horizontal space. By the way, this is by design (and don’t ask me why).

OK, I get it (or not…) but there is nothing I can do about it. So let’s do the following. Let’s set the width of the content grid that holds the stack panel to, let’s say 440 pixels, and by doing so limit the total size of all its child elements, mainly the TextBlock. Surprisingly (again), adding the Width=”440” attribute to the grid had no effect on the text wrapping (remember, size property drill do – or up the visual tree). So I tried adding the width to the StackPanel, again with no effect on the text wrapping. Only when I set the width on the TextBlock itself did I get the expected result.

But that would be too easy, right? Correct. Adding a fixed width size to the TextBlock and/or StackPanel introduced a different problem—a problem that is related to the title of this post, automatic rotation support. Once I changed the emulator orientation, I got following result:

02 Come to think of it, this is expected behavior. The StackPanel is centering its content and setting the TextBlock to occupy a fix width of 440 pixels. You can add a listener to the OrientationChanged event, and then “manually” change the width of the relevant controls, but this is no fun, and doesn’t really help, right? So what should we do?

Go Grid Go

Well, remember at the beginning of this post, we talked about the Grid control? It turns out that by using Grid, you will be able to create a UI that will automatically rescale and reposition itself according to the phone’s orientation. Therefore the following DataTemplate definition takes care of the entire problem we listed above. You should also add ScrollViewer.HorizontalBarVisibility=”Disable” to the ListBox to signal your intention to disable horizontal scrolling and that the list box should only occupy the current visible space and not any virtual off-screen space.

<DataTemplate>

    <Grid HorizontalAlignment="Stretch" VerticalAlignment="Bottom" >

           <Grid.ColumnDefinitions>

                  <ColumnDefinition Width="Auto"/>

                  <ColumnDefinition Width="*"/>

            </Grid.ColumnDefinitions>

            <Image Source="{Binding Mode=OneWay, Path=Avatar}" 

                     Margin="5,0,10,0" Width="75" Height="75" Grid.Column="0"

                     HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>

 

            <TextBlock Text="{Binding Mode=OneWay, Path=Title}" Grid.Column="1"

                     HorizontalAlignment="Stretch" VerticalAlignment="Center"

                     Style="{StaticResource PhoneTextSmallStyle}"

                     TextWrapping="Wrap" />

     </Grid>

</DataTemplate>

Each item in the ListBox is comprised of a Grid element that includes an Image, displaying the Twitter user’s avatar, and a TextBlock displaying the tweet’s title. You should note that we defined two columns, one for each piece of content. We set a fixed size for the image (and that is actually the only place in the entire XAML where we set a fixed size for any content element) in order to limit the size of a picture that might be too big. Note again, that we use Auto and * to define the space each column should occupy. The rest is done for you automatically and, just like magic (or just like Silverlight J), the end result is as expected, as shown in the first image in this post.

But wait, what about the landscape display? Well, as I said, use grid to do the dirty work for you. As expected, grid solves the landscape problem too, as shown in the following image:

05 You can download the code for this sample. But you will becover better off reading the  entire “Building Your First Windows Phone 7 Application” chapter that provides step-by-step instructions for how to build this Twitter Search application, including a more detailed explanation of the code in this post.

This chapter is part of a book that I am writing with Jaime Rodriguez, who also writes about Windows Phone.

The LearningWindowsPhone.com site includes one more sample chapter – Chapter 6: A journey into Silverlight on Windows Phone, as well as a bunch of code samples.