Click here to Skip to main content
Click here to Skip to main content

Dynamic Layout Changes in Windows Phone 8

, 23 Feb 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Shows how to use page bindings to automatically adapt your page layout for orientation changes

Introduction

I was recently assigned a Windows Phone 8 project and I wanted a way to make my application dynamically adjust anything I wanted based on things like orientation, screen size, or even be dependent on object properties. It's possible to handle this all in C# code in the specific window by handling events, but I felt a XAML solution would be a cleaner way to go. Two other possibilities for this could be a behavior or a library.

Background

Users should be familiar with XAML in either Silverlight or WPF.

On Android devices (my background), you create two separate layout files (the equivalent of the XAML in WP8). You place the same elements on the layout identified by a unique ID, and the Android operating system alternates between these two layouts for you. This wasn't quite the way Windows Phone 8 worked. Instead you have a single layout page. Inside this page, you can create multiple DataTemplate objects and alternate between them on things like orientation change, etc. I wasn't happy with this solution as it was not very elegant and required a fair amount of C# code and event handlers to make it work.

Using the Code

First, I created a couple of IValueConverter classes. Let's take a look at one that handles layout changes based on Orientation. First the class:

public abstract class OrientationToValueConverter<T> : IValueConverter {

   public T PortraitValue { get; set; }
   public T LandscapeValue { get; set; }

   public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {

      if (value is PageOrientation) {
         var orientation = (PageOrientation)value;

         if ((orientation == PageOrientation.Landscape) ||
             (orientation == PageOrientation.LandscapeLeft) ||
             (orientation == PageOrientation.LandscapeRight)) {

            return LandscapeValue;
         }
         else if ((orientation == PageOrientation.Portrait) ||
                  (orientation == PageOrientation.PortraitDown) ||
                  (orientation == PageOrientation.PortraitUp)) {

            return PortraitValue;
         }
      }

      return null;
   }

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

The class contains two values of any type, PortraitValue and LandscapeValue. It simply takes the PageOrientaion value that gets passed into it, and returns back the corresponding portrait and landscape values as needed. What's the point of this? Well, you can create single liner classes that base off of this as follows:

public class OrientationToVisibilityConverter : OrientationToValueConverter<Visibility> { }

public class OrientationToStackPanelOrientationConverter : OrientationToValueConverter<Orientation> { }

public class OrientationToGridLengthConverter : OrientationToValueConverter<GridLength> { }

public class OrientationToScrollBarVisibilityConverter : OrientationToValueConverter<ScrollBarVisibility> { }
   
public class OrientationToDoubleConverter : OrientationToValueConverter<double> { }

public class OrientationToDoubleNullableConverter : OrientationToValueConverter<double?> { } 

Let's examine just one of these new derived classes to see how they can make life easy for us. We'll look at the OrientationToVisibilityConverter class.

We define this in the resources property of our PhoneApplicationPage, or underlying Grid as follows:

<Grid.Resources>
   <converters:OrientationToVisibilityConverter x:Key="PortraitVisible" LandscapeValue="Collapsed" PortraitValue="Visible" />
   <converters:OrientationToVisibilityConverter x:Key="LandscapeVisible" LandscapeValue="Visible" PortraitValue="Collapsed" /> 
</Grid.Resources> 

Remember to add the namespace as well. Notice that we have two separate instances of the same converter here. One makes items visible in portrait mode only, and one makes items visible in landscape mode only. Let's implement this.

<TextBlock Text="Portrait Only"
           Visibility="{Binding Orientation, ElementName=MyPage, Converter={StaticResource PortraitVisible}}" /> 
<TextBlock Text="Landscape Only"
           Visibility="{Binding Orientation, ElementName=MyPage, Converter={StaticResource LandscapeVisible}}" /> 

In this case, you'd need to give your page a name (e.g. MyPage). You can also use RelativeSource bindings, but that's simpler. The result? Your content has automatically changed based on PageOrientation.

What are the other classes for? Here are a few examples of how I am using them.

  • OrientationToStackPanelOrientationConverter - Allows a StackPanel to orient horizontally on landscape and vertical on portrait.
  • OrientationToGridLengthConverter - Allows a grid row or column to be sized. For example, you may want it auto sized in one case and star sized in another.
  • OrientationToScrollBarVisibilityConverter - Say you want all content to fit horizontally in landscape mode. You can disable the horizontal scroll bar in landscape and set it to auto in portrait.
  • OrientationToDoubleConverter - Can be used to set things like Width, Height, MaxHeight, etc.
  • OrientationToDoubleNullableConverter - I use this specifically to set MaxHeight or MaxWidth. In this case, the x:Null value means that there is no MaxHeight while a numeric value will limit it.

Let's take a look at one more class:

public abstract class BoolToValueConverter<T> : IValueConverter { } 

This class again defines two values of type T called TrueValue and FalseValue. It does the same type of conversion except that it checks the incoming value for bool, string, and finally just a null/not null check:

if (value is bool) {
   return (bool)value;
}
else if (value is string) {
   return !string.IsNullOrWhiteSpace(value.ToString());
}
else { 
   return = (value != null);
} 

Again like before, I created a few classes that expand on this. Here are two of them:

public class BoolToVisibilityConverter : BoolToValueConverter<Visibility> { }

public class BoolToDoubleConverter : BoolToValueConverter<double> { }  

BoolToVisibilityConverter should be pretty obvious. But in WP8, the Visibility.Hidden type was removed. So you can only have items be visible or collapsed. What if you want to hide an item based on a condition but reserve its space on the screen? Simple. Just create a BoolToDoubleConverter and bind it to the opacity! The item can get hidden based on bindings now. This class isn't typically bound to orientation for me but is usually just bound to a DataContext or object property (e.g. If my screen has a DataContext loaded, the 'Save' button can be visible).

No need to limit yourself to these two base classes; these are simply the two I chose for the article.

Points of Interest

XAML is an extremely powerful tool and I am glad to see the subset of it in Windows Phone 8 is as large as it is. There are missing features for those who are familiar with WPF, but nearly all of them can be worked around quite easily. The performance of these bindings and of the higher level languages such as C# and XAML perform very well on a Windows Phone 8 device. I was pleasantly surprised by it all. All in all, Windows Phone 8 is very fun to work with; the only downside being that you must install Windows 8 on your own machine to do so =(.

History

  • Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

gardnerp
Software Developer
United States United States
I've been a developer since 2004. I work in C#, ASP, WPF, XAML, Silverlight, Windows Store, HTML/JavaScript/CSS, Java, Delphi, and SQL. I write for web, desktop, Android, Windows Mobile, Windows Store, and database servers.
Follow on   LinkedIn

Comments and Discussions

 
QuestionSource Code needed PinmemberKaito Hack5-Mar-14 2:00 
AnswerRe: Source Code needed Pinmembergardnerp5-Mar-14 4:48 
AnswerRe: Source Code needed Pinmembergardnerp5-Mar-14 5:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141220.1 | Last Updated 23 Feb 2014
Article Copyright 2014 by gardnerp
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid