Follow @PDSAInc Change Templates When Orientation Changes on Windows Phone - Paul Sheriff's Blog for the Real World

Paul Sheriff's Blog for the Real World

This blog is to share my tips and tricks garnered over 25+ years in the IT industry

Paul's Favorites

Change Templates When Orientation Changes on Windows Phone

Developing for Windows Phone does require you to think a little differently. For example on a regular computer you only have to worry about the screen orientation being in portrait mode. However, on a phone the user can turn the phone sideways and even upside down. If the user switches from portrait orientation (Figure 1) to landscape orientation (Figure 2) you end up with more room to display your data horizontally. You can take advantage of this extra room by switching between two different XAML templates when the phone orientation changes.

This article assumes that you have VS.NET 2010 and the Windows Phone tools installed along with it. The Windows Phone tools must be downloaded separately and installed with VS.NET 2010. You may also download the free VS.NET 2010 Express for Windows Phone developer environment.

 

Figure 1: Windows Phone in Portrait Mode

Figure 2: Windows Phone in Landscape Mode

The Product Class

For this example, I will be using a Product class with three properties, and a collection class of Product objects using the Generic List class. You can create this sample by creating a Product class as shown in the following code:

public class Product {

  public Product() { }

  public Product(string name, decimal price, string imageUri)
  {
    this.ProductName = name;
    this.Price = price;
    this.ImageUri = imageUri;
  }

  public string ProductName { get; set; }
  public decimal Price { get; set; }
  public string ImageUri { get; set; }
}

Create a collection class that initializes a property called DataCollection with some sample data as shown in the code below:

public class Products : List<Product> {
  public Products() {
    BuildCollection();
  }

  public List<Product> DataCollection { get; set; }

  List<Product> BuildCollection() {
    DataCollection = new List<Product>();

    DataCollection.Add(new Product(
      "Haystack Code Generator for .NET", 799, "Haystack.jpg”));
    DataCollection.Add(new Product(
      "Fundamentals of N-Tier eBook", 19.95, "NTier.jpg"));

    // MORE PRODUCTS HERE – REMOVED FOR BREVITY

    return DataCollection;
  }
}

Value Converter

As you can see in Figure 1 and Figure 2, the Price is shown in a currency format. Unfortunately in the Windows Phone version of Silverlight, the StringFormat attribute on the Binding class is absent. So you will need to create a value converter for displaying a value in a currency format. Create the following class that implements the IValueConverter interface. In the Convert method you will then take the “value” parameter passed in, convert it to a decimal value, then return that value as a string formatted with the ”c” option in the ToString() method.

public class PriceConverter : IValueConverter
{
  public object Convert(object value, Type targetType,
     object parameter, System.Globalization.CultureInfo culture)
  {
    decimal price;

    price = (decimal)value;

    return price.ToString("c");
  }

  public object ConvertBack(object value, Type targetType,
    object parameter, System.Globalization.CultureInfo culture)
  {
    throw new NotImplementedException();
  }
}

Create Classes in XAML

Now that you have the Product class, the Products class and the PriceConverter classes created, you now need to create an instance of the Products collection class and the PriceConverter class in your XAML. In your MainPage.xaml, add an xml namespace to the name of your project as shown here:

xmlns:local="clr-namespace:WPListBoxImage"

Next you create an instance of the Products and PriceConverter class in XAML. The constructor for the Products class will create the initial collection of product objects.

<phone:PhoneApplicationPage.Resources>
  <local:Products x:Key="productCollection" />
  <local:PriceConverter x:Key="priceConvert" />
</phone:PhoneApplicationPage.Resources>

These two classes may now be used within the rest of your XAML by referencing them by their Key name.

Create Portrait Template

Also in the Resources section of your phone application page is where you will create two different XAML templates for displaying data in a list box. The first template you will create is for when the phone is in portrait mode.

<DataTemplate x:Key="listPortrait">
  <StackPanel>
    <TextBlock Margin="8"
                Width="250"
                TextWrapping="Wrap"
                VerticalAlignment="Top"
                HorizontalAlignment="Left"
                Text="{Binding Path=ProductName}" />
    <TextBlock Width="100"
                Margin="8,0,8,8"
                VerticalAlignment="Top"
                HorizontalAlignment="Left"
                Text="{Binding
                   Path=Price,
                   Converter={StaticResource priceConvert}}" />
  </StackPanel>
</DataTemplate>

This <DataTemplate> with the key name “listPortrait” has a <StackPanel> control to display the text for the product name and the price stacked vertically on top of each other. Notice in the text block that displays the actual price the use of the value converter class you created earlier. You bind to the Price property using the Path attribute of the Binding class. However, before the Binding class displays the Price it passes it through the converter. The Convert() method of the PriceConverter class formats the price and returns it as a string to be displayed in the TextBlock.

Create Landscape Template

The next XAML template you create in the Resources section of the phone application page has a key name of “listLandscape”. The XAML for this <DataTemplate> is shown below.

<DataTemplate x:Key="listLandscape">
  <StackPanel Orientation="Horizontal">
    <Image Margin="8"
            VerticalAlignment="Top"
            Source="{Binding Path=ImageUri}"
            Width="100"
            Height="100" />
    <StackPanel>
      <TextBlock Margin="8"
                  Width="400"
                  TextWrapping="Wrap"
                  VerticalAlignment="Top"
                  HorizontalAlignment="Left"
                  Text="{Binding Path=ProductName}" />
      <TextBlock Width="100"
                  Margin="8,0,8,8"
                  VerticalAlignment="Top"
                  HorizontalAlignment="Left"
                  Text="{Binding
                     Path=Price,
                     Converter={StaticResource priceConvert}}" />
    </StackPanel>
  </StackPanel>
</DataTemplate>

In this template you add a <StackPanel> control around the <StackPanel> you created in the “listPortrait” template. Set the Orientation property of this stack panel to be horizontal. Then you place an <Image> control that will display the image for the product before the product name and price. Notice also that the Width property of the product name TextBlock control has also been increased so the product name may not have to wrap as it might have to in portrait mode.

The List Box Definition

The List Box control used for this phone application page is fairly simple. You simply set the ItemsSource property and the ItemsTemplate property to the appropriate static resources defined in the Resources section of this page.

<ListBox x:Name="lstData"
     ItemsSource="{Binding
                    Source={StaticResource productCollection},
                    Path=DataCollection}"
     ItemsTemplate="{StaticResource listPortrait}" />

The ItemsTemplate is set to listPortrait to start out because in the Page definition the Orientation attribute is set to “Portrait”.

OrientationChanged Event

Now all you need to do is to hook up an event so when the user changes the orientation of the phone you can switch between the two templates you have created. There are just a couple of things you have to do to prepare the page. First you must set the attribute in the PhoneApplicationPage called SupportedOrientation to the value “PortraitOrLandscape”. This attribute will allow the OrientationChanged event to fire.

SupportedOrientations="PortraitOrLandscape"

So, next you add the OrientationChanged event to the Page. Type in OrientationChanged in the page XAML and have it build the event procedure for you.

OrientationChanged="PhoneApplicationPage_OrientationChanged"

In the OrientationChanged event procedure you will write the following code:

private void PhoneApplicationPage_OrientationChanged(
   object sender, OrientationChangedEventArgs e)
{
  if (e.Orientation.ToString().Contains("Portrait"))
    lstData.ItemTemplate =
      (DataTemplate)this.Resources["listPortrait"];
  else
    lstData.ItemTemplate =
      (DataTemplate)this.Resources["listLandscape"];
}

When the OrientationChanged event is fired the new orientation is passed in the “e” argument as the Orientation property. This property can be one of 7 enumeration values; None, Portrait, PortraitUp, PortraitDown, Landscape, LandscapeRight, and LandscapeLeft. Since we are just concerned with Portrait or Landscape, you will just convert the Orientation property to a string and see if the value contains “Portrait”. If it contains “Portrait”, then you find the “listPortrait” resource, cast it as a DataTemplate and assign it to the list box’s ItemTemplate property. This causes the list box to redraw itself using the defined data template.

Summary

In this article you learned how to respond to the OrientationChanged event and switch between two pre-defined templates. Since the user can physically move the phone into almost any orientation it is useful for us to take advantage of the extra screen width. By creating different XAML templates and responding to just one event you can give your users a great experience when using your application.

NOTE: You can download the complete sample code at my website. http://www.pdsa.com/downloads. Choose Tips & Tricks, then "Change Orientation/Templates on Windows Phone" from the drop-down.

Good Luck with your Coding,
Paul Sheriff

** SPECIAL OFFER FOR MY BLOG READERS **
Visit http://www.pdsa.com/Event/Blog for a free videos on Silverlight entitled Silverlight XAML for the Complete Novice - Part 1.

Posted: Nov 17 2010, 12:00 PM by psheriff | with 6 comment(s)
Filed under:

Comments

Matthias said:

How would you implement this with the rotation animation in place? Multiple templates attached to multiple content presenters?

# November 20, 2010 2:23 PM

Michel said:

The OrientationChanged event arise (as i can control using a break point), the new template is set to the item template but nothing change in the display of the list(using emulator or phone device).

Is there a refresh event we must call?

# January 11, 2012 6:18 AM

Michel again said:

It seem's that we must refill the listbox for the new template to be active.

Products arProducts = new Products();

lstData.ItemTemplate = (DataTemplate this.Resources["listLandscape"];

lstData.ItemsSource = arProducts.DataCollection;

# January 11, 2012 8:01 AM

psheriff said:

Michel,

Since the time that I published this, something has changed in the OrientationChanged event. You do need to refresh the ItemsSource.

Instead of recreating the whole Products class, you can use the following code:

 if (e.Orientation.ToString().Contains("Portrait"))

   lstData.ItemTemplate =

     (DataTemplate)this.Resources["listPortrait"];

 else

   lstData.ItemTemplate =

     (DataTemplate)this.Resources["listLandscape"];

 lstData.ItemsSource = null;

 lstData.ItemsSource = ((Products)this.Resources["productCollection"]).DataCollection;

Paul

# January 11, 2012 10:22 AM