In This Chapter
• Assigning element Visibility based on page orientation
• Changing page orientation at runtime
• Animation using the VisualStateManager
• Page animation using a custom PhoneApplicationFrame
• Animating page transitions
Pages in Silverlight for Windows Phone apps can support either the landscape or portrait orientation, or both. When creating a page that supports both orientations, it is prudent to define the UI in XAML in such a way that it produces the layout you want in either orientation without relying on code-beside. Fortunately, Silverlight’s dynamic layout system makes it easy to build pages that can support both orientations. The Grid
and StackPanel
elements, for example, resize themselves to inhabit available space when the page orientation changes and the dimensions of the page change.
This chapter begins by exploring the properties and events that govern page orientation. You look at using a custom IValueConverter
to hide and display page elements based on the page orientation and at setting the orientation of a page programmatically.
The chapter then looks at animating UI elements when the page orientation changes using the VisualStateManager
and examines how to substitute an application’s RootVisual
with a custom PhoneApplicationFrame
to animate the entire page when the orientation changes.
Finally, the chapter looks at animating page transition using the Silverlight Toolkit.
The PhoneApplicationPage
class includes two orientation related properties: SupportedOrientations
and Orientation
.
The SupportedOrientations
attribute allows you to restrict the orientation of the page and, if set to either Portrait
or Landscape
, prevent the orientation from being changed when the device is rotated. If the page has been designed to support both portrait and landscape, set SupportedOrientation
to PortraitOrLandscape
, which allows the orientation to be switched automatically when the device is rotated.
The Orientation
property indicates the actual orientation of the page and can be set only at design-time. The Orientation
property is discussed in greater detail later in the chapter.
When you create a new PhoneApplicationPage
within Visual Studio, both SupportedOrientations
and Orientation
are set, by default, to Portrait in XAML.
Both the PhoneApplicationFrame
and PhoneApplicationPage
include an OrientationChanged
event that allows you to detect when the orientation of the page changes. In addition, the PhoneApplicationPage
includes an OnOrientationChanged
virtual method. When the page orientation changes, the method is called, allowing you to update the UI, trigger animations, and so forth. See the following excerpt:
protected override void OnOrientationChanged(
OrientationChangedEventArgs e)
{
base.OnOrientationChanged(e);
/* Update UI, trigger animations etc. */
}
The OnOrientationChanged
method is called before other OrientationChanged
event handlers.
The ActualWidth
and ActualHeight
of the page are not changed until after the OrientationChanged
event has been raised.
To change the size of a UI element based on the dimensions of the page after the orientation change occurs use the Dispatcher
.
By using the Dispatcher
to invoke layout changes, the correct height and width of the page can be determined after the OrientationChanged
event has been handled, as shown in the following excerpt:
protected override void OnOrientationChanged(OrientationChangedEventArgs e)
{
Debug.WriteLine("Orientation changed to " + e.Orientation.ToString("G"));
Dispatcher.BeginInvoke(
delegate
{
Debug.WriteLine(string.Format(
"Using dispatcher: ActualWidth: {0}, ActualHeight: {1}",
ActualWidth, ActualHeight));
});
Debug.WriteLine(string.Format(
"Without dispatcher: ActualWidth: {0}, ActualHeight: {1}",
ActualWidth, ActualHeight));
base.OnOrientationChanged(e);
}
The following is the output when switching orientations:
Orientation changed to LandscapeLeft
Without dispatcher: ActualWidth: 0, ActualHeight: 0
Using dispatcher: ActualWidth: 800, ActualHeight: 480
Orientation changed to PortraitUp
Without dispatcher: ActualWidth: 800, ActualHeight: 480
Using dispatcher: ActualWidth: 480, ActualHeight: 800
The OrientionChanged
event is always raised before the page is loaded. This explains the zero values for the ActualWidth
and ActualHeight
in the previous output. The Dispatcher
allows the correct width and height values to be obtained because by the time each value is read, the page has already loaded and the properties are populated with the correct values.
The OrientationChangedEventArgs
class contains an Orientation
property, which is an enum of type PageOrientation
, indicating the new page orientation. PageOrientation
has the following values:
• Landscape
• LandscapeLeft
• LandscapeRight
• None
• Portrait
• PortraitDown
• PortraitUp
The only values you will ever see in the OrientationChangedEventArgs
are, however, LandscapeLeft
, LandscapeRight
, and PortraitUp
. These values indicate the location of the display in relation to the phone hardware buttons (see Figure 4.1).
While PortraitDown
exists as an enum value, at the time of writing, no device supports this orientation, nor does the emulator.
To determine whether the OrientationChangedEventArgs.Orientation
value is either landscape or portrait, the value can be ANDed with the PageOrientation.Landscape
or PageOrientation.Portrait
values, respectively. The PageOrientationExtensions
class in the downloadable sample code includes two extension methods for performing this directly on a PageOrientation
value (see Listing 4.1).
public static class PageOrientationExtensions
{
public static bool IsLandscape(this PageOrientation pageOrientation)
{
return (pageOrientation & PageOrientation.Landscape) != 0;
}
public static bool IsPortrait(this PageOrientation pageOrientation)
{
return (pageOrientation & PageOrientation.Portrait) != 0;
}
}
The PhoneApplicationPage
includes an Orientation
dependency property, which is shown in the following excerpt (take note of the set accessor):
public PageOrientation Orientation
{
get
{
return (PageOrientation)base.GetValue(OrientationProperty);
}
[EditorBrowsable(EditorBrowsableState.Never)]
set
{
if (Frame.IsInDesignMode())
{
base.SetValue(OrientationProperty, value);
}
}
}
You see that changing the page orientation at runtime is not as straightforward as it might first appear. The Orientation
property’s set accessor has an effect only at design-time and not at runtime. At runtime, the Orientation
property indicates the physical orientation of the device, or the orientation of the emulator window. Setting the dependency property directly is also futile and has no effect on runtime page orientation either. A technique to set page orientation programmatically is discussed in the following section, “Setting Page Orientation at Runtime.”
The Orientation
property can be used in data binding expressions and allows you to adjust the layout depending on the availability of space. You can maximize space utilization by hiding or revealing content when the orientation changes. For example, when changing to a landscape orientation where horizontal space is more abundant, a TextBlock
can be shown in the title section of an application. Conversely, in portrait mode, the TextBlock
can be hidden to conserve space. See the following excerpt:
<TextBlock Text="Application Title"
Visibility="{Binding ElementName=Page, Path=Orientation,
Converter={StaticResource PageOrientationToVisibilityConverter},
ConverterParameter=Landscape}" />
You can see that the Orientation
property of the PhoneApplicationPage
is used to set the Visibility
property of the TextBlock
using a custom IValueConverter
called PageOrientationToVisibilityConverter
(see Listing 4.2).
The converter’s ConvertTo
method translates the PageOrientation
enum value to a System.Windows.Visibility
enum value. The ConverterParameter
from the previous excerpt indicates when to show the UIElement
. If the PageOrientation
value is a portrait orientation for example, and the ConverterParameter
is equal to Portrait, then Visibility.Visible
is returned.
public class PageOrientationToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
var orientation = (PageOrientation)value;
string showWhenOrientation
= ArgumentValidator.AssertNotNullAndOfType<string>(
parameter, "parameter").ToLower();
if (showWhenOrientation != "portrait"
&& showWhenOrientation != "landscape")
{
throw new ArgumentException(
"ConverterParameter must be either Portrait or Landscape.");
}
bool show;
switch (orientation)
{
case PageOrientation.Portrait:
case PageOrientation.PortraitDown:
case PageOrientation.PortraitUp:
show = showWhenOrientation == "portrait";
break;
case PageOrientation.Landscape:
case PageOrientation.LandscapeLeft:
case PageOrientation.LandscapeRight:
show = showWhenOrientation == "landscape";
break;
default:
throw new ArgumentException("Unknown orientation: "
+ orientation);
}
return show ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
As you saw in the previous section, the Orientation
property of the PhoneApplicationPage
class cannot be assigned at runtime. Another approach is required to change the page orientation. For this, turn to the SupportedOrientations
property, which is assignable at runtime.
To recap, the SupportedOrientations
property defines the allowed orientation or orientations of the page, which can be Portrait
, Landscape
, or PortraitOrLandscape
.
Setting the SupportedOrientations
property to the desired orientation forces the page to be shown in that orientation. For example, if the page is being viewed in portrait orientation, and you want to change the page orientation to landscape, then you can set the SupportedOrientations
to Landscape
, thereby restricting the allowed orientation to landscape and forcing the frame to change the orientation to landscape.
This technique is demonstrated in the OrientationView
page in the downloadable sample code (see Figure 4.2).
The OrientationView
class’s Switch Supported Orientation button alternates between the three different SupportedPageOrientation
values. This is performed in the button’s Tap
event handler, demonstrating how the orientation can be forcibly set by restricting the orientation, as shown in the following excerpt:
void Button_Tap(object sender, GestureEventArgs e)
{
switch (SupportedOrientations)
{
case SupportedPageOrientation.Landscape:
SupportedOrientations = SupportedPageOrientation.Portrait;
break;
case SupportedPageOrientation.Portrait:
SupportedOrientations = SupportedPageOrientation.PortraitOrLandscape;
break;
default:
SupportedOrientations = SupportedPageOrientation.Landscape;
break;
}
}
The VisualStateManager
can be used to animate UIElements
when the page orientation changes. This can be done by defining a set of VisualStateGroups
corresponding to the PageOrientation
values.
Each VisualStateGroup
contains a collection of VisualState
objects, each containing a collection of Storyboard
objects that specify how an element’s properties change when the control is placed in a particular visual state.
In Listing 4.3, you see how the LandscapeRight VisualState
includes a set of DoubleAnimations
, which move various TextBlocks
on the page to new positions when the page orientation changes.
<Grid x:Name="LayoutRoot">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="OrientationStates">
<!--Portrait up is the default state-->
<VisualState x:Name="PortraitUp">
<Storyboard />
</VisualState>
<VisualState x:Name="LandscapeRight">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="topLeft"
Storyboard.TargetProperty="
(UIElement.RenderTransform).(TranslateTransform.X)"
To="650" />
<!-- Content omitted. -->
</Storyboard>
</VisualState>
<!-- Content omitted. -->
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<!-- Content omitted. -->
<Grid x:Name="ContentGrid" Grid.Row="1">
<TextBlock Text="Top-left corner" x:Name="topLeft"
HorizontalAlignment="Left" VerticalAlignment="Top">
<TextBlock.RenderTransform>
<TranslateTransform/>
</TextBlock.RenderTransform>
</TextBlock>
<!-- Content omitted. -->
</Grid>
</Grid>
We subscribe to OrientationChanged
event within the view’s constructor, as shown in the following excerpt:
OrientationChanged += (sender, args) => VisualStateManager.GoToState(
this, args.Orientation.ToString(), true);
When the OrientationChanged
event is raised, the VisualStateManager
is directed to the state identified by the Orientation
property of the OrientationChangedEventArgs
(see Figure 4.3).
While animating UIElements
is performed within the page, to animate the page itself requires implementation of a custom PhoneApplicationFrame
. This is achieved by subclassing the PhoneApplicationFrame
class, and then subscribing to the frame’s OrientationChanged
event to initiate the animation.
To replace your application’s standard PhoneApplicationFrame
with a custom frame, modify the App
class, either in XAML or in the code-beside, to use the custom frame as the application’s RootVisual
.
The following excerpt shows how the App.xaml file can be modified to use a custom PhoneApplicationFrame
:
<Application.RootVisual>
<unleashed:AnimateOrientationChangesFrame x:Name="RootFrame">
<!-- Content omitted. -->
</unleashed:CustomFrame>
</Application.RootVisual>
If the RootVisual
is assigned in the App.xaml.cs file, as is the case by default, modify the InitializedPhoneApplication
method to assign a custom frame, as shown in the following excerpt:
void InitializePhoneApplication()
{
if (phoneApplicationInitialized)
{
return;
}
RootFrame = new AnimateOrientationChangesFrame();
// Content omitted.
}
Microsoft’s David Anson has written two custom PhoneApplicationFrames
that perform rotation and fade animation when an orientation change occurs. With David’s permission I have included them in the downloadable sample code.
The first is a frame that fades in content, called FadeOrientationChangesFrame
. This class uses a WriteableBitmap
to create an image overlay, which animates the Opacity
property of the image. By using a WriteableBitmap
to capture the screen, the performance of the transition is optimized and is unaffected by the page composition, that is, more controls won’t risk slowing the animation.
The second frame, the AnimateOrientationChangesFrame
, is less subtle and rotates the page when the orientation changes (see Figure 4.4).
To modify the default behavior of either the FadeOrientationChangesFrame
or AnimateOrientationChangesFrame
, use the AnimationEnabled
, Duration
, and EasingFunction
properties.
AnimationEnabled
allows you to turn off the animation at runtime.
The Duration
property dictates how long the animation will take to complete, which if made too long, more than half a second, risks frustrating the user.
The EasingFunction
property is used to control the speed of the animation. With easing, you can create a more realistic rate of acceleration and deceleration, such as when creating a bounce effect, or control other types of motion.
The following is a list of the various IEasingFunction
available in the Windows Phone FCL (Framework Class Library), located in the System.Windows.Media.Animation
namespace of the System.Windows assembly:
• BackEase—Retracts the motion of an animation slightly before it begins to animate along the path.
• BounceEase—Creates an animated bouncing effect.
• CircleEase—Creates an animation that accelerates and/or decelerates using a circular function.
• CubicEase—Creates an animation that accelerates and/or decelerates using the formula f(t) = t3.
• ElasticEase—Creates an animation that resembles a spring oscillating back and forth until it comes to rest.
• ExponentialEase—Creates an animation that accelerates and/or decelerates using an exponential formula.
• PowerEase—Creates an animation that accelerates and/or decelerates using the formula f(t) = tp, where p is equal to the Power property.
• QuadraticEase—Creates an animation that accelerates and/or decelerates using the formula f(t) = t2. This is the default IEasingFunction
of the FadeOrientationChangesFrame
class.
• QuarticEase—Creates an animation that accelerates and/or decelerates using the formula f(t) = t4. This is the default IEasingFunction
for the AnimateOrientationChangesFrame
class.
• QuinticEase—Creates an animation that accelerates and/or decelerates using the formula f(t) = t5.
• SineEase—Creates an animation that accelerates and/or decelerates using a sine formula.
Adding animated page transitions to your app can help it look more polished and, when used modestly, can increase the user’s perception of the quality of your app.
The Silverlight Toolkit includes a set of classes that make it easy to add animated page transitions. The Toolkit is discussed in depth in Chapter 9, “Silverlight Toolkit Controls.”
Within the Toolkit, transitions are defined by assigning a transition effect represented by TransitionElement
objects such as TurnstileTransition
to various transition events. These events occur during navigation and indicate the direction of the navigation (see Figure 4.5).
Forward in navigation occurs when the page is first navigated to; forward out occurs when navigating to a new page. Backward in and backward out occur when a backward navigation takes place, such as when the user taps the hardware Back button.
Navigation events are married with TransitionElements
. The Silverlight Toolkit comes with the following five built-in TransitionElements
:
• RollTransition
• RotateTransition
• SlideTransition
• TurnstileTransition
In the next section you see how to add animated page transitions to an app, and how to specify the type of transition for each navigation event.
The following steps outline how to add page transitions to your app:
1. Download and install the latest version of the Silverlight for Windows Phone Toolkit from http://silverlight.codeplex.com. Once installed, add a reference to the Microsoft.Phone.Controls.Toolkit.dll
assembly.
2. Replace the default PhoneApplicationFrame
with a TransitionFrame
from the Silverlight Toolkit. To replace your app’s standard PhoneApplicationFrame
, modify the App
class, either in XAML or in the code-beside, and set the custom frame as the application’s RootVisual
.
The following excerpt shows how the App.xaml file can be modified to use a Silverlight Toolkit TransitionFrame
:
<Application.RootVisual>
<toolkit:TransitionFrame x:Name="RootFrame"
Navigated="RootFrame_Navigated"
Navigating="RootFrame_Navigating"
NavigationFailed="RootFrame_NavigationFailed">
<!-- Content omitted. -->
</toolkit:TransitionFrame>
<!-- Content omitted. -->
</Application.RootVisual>
If the RootVisual
is assigned in the App.xaml.cs file, as is the case by default, modify the InitializedPhoneApplication
method to assign the TransitionFrame
, as shown in the following excerpt:
void InitializePhoneApplication()
{
if (phoneApplicationInitialized)
{
return;
}
RootFrame = new TransitionFrame();
// Content omitted.
}
3. In the page that you want to add a transition effect, add the toolkit namespace definition as shown:
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
4. Associate a transition with each navigation event by adding the TransitionService.NavigationInTransition
and NavigationOutTransition
attached properties to the page, as shown in the following excerpt:
<phone:PhoneApplicationPage
...
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit">
<toolkit:TransitionService.NavigationInTransition>
<toolkit:NavigationInTransition>
<toolkit:NavigationInTransition.Backward>
<toolkit:TurnstileTransition Mode="BackwardIn"/>
</toolkit:NavigationInTransition.Backward>
<toolkit:NavigationInTransition.Forward>
<toolkit:TurnstileTransition Mode="ForwardIn"/>
</toolkit:NavigationInTransition.Forward>
</toolkit:NavigationInTransition>
</toolkit:TransitionService.NavigationInTransition>
<toolkit:TransitionService.NavigationOutTransition>
<toolkit:NavigationOutTransition>
<toolkit:NavigationOutTransition.Backward>
<toolkit:TurnstileTransition Mode="BackwardOut"/>
</toolkit:NavigationOutTransition.Backward>
<toolkit:NavigationOutTransition.Forward>
<toolkit:TurnstileTransition Mode="ForwardOut"/>
</toolkit:NavigationOutTransition.Forward>
</toolkit:NavigationOutTransition>
</toolkit:TransitionService.NavigationOutTransition>
<!-- Content omitted. -->
</phone:PhoneApplicationPage>
At this point, a turnstile animation will be applied to the page when navigating to and from the page.
You can view the transition effect by uncommenting the toolkit:TransitionFrame
element in the App.xaml file in the WindowsPhone7Unleashed.Examples project. Then launch the downloadable sample, and tap the TransitionPage1 page in the PageOrientation section.
The transition attached properties are rather verbose, and adding them to every page in your app adds a substantial amount of duplication. Fortunately you can create a reusable style that can be applied to each page.
Listing 4.4 shows a style called TransitionPageStyle
, whose TargetType
is of PhoneApplicationPage
. The transition properties are placed within the style, so that when the style is applied to a page, so too are the navigation transition properties.
<Application.Resources>
<Style x:Key="TransitionPageStyle" TargetType="phone:PhoneApplicationPage">
<Setter Property="toolkit:TransitionService.NavigationInTransition">
<Setter.Value>
<toolkit:NavigationInTransition>
<toolkit:NavigationInTransition.Backward>
<toolkit:TurnstileTransition Mode="BackwardIn"/>
</toolkit:NavigationInTransition.Backward>
<toolkit:NavigationInTransition.Forward>
<toolkit:TurnstileTransition Mode="ForwardIn"/>
</toolkit:NavigationInTransition.Forward>
</toolkit:NavigationInTransition>
</Setter.Value>
</Setter>
<Setter Property="toolkit:TransitionService.NavigationOutTransition">
<Setter.Value>
<toolkit:NavigationOutTransition>
<toolkit:NavigationOutTransition.Backward>
<toolkit:TurnstileTransition Mode="BackwardOut"/>
</toolkit:NavigationOutTransition.Backward>
<toolkit:NavigationOutTransition.Forward>
<toolkit:TurnstileTransition Mode="ForwardOut"/>
</toolkit:NavigationOutTransition.Forward>
</toolkit:NavigationOutTransition>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
The style can then be applied to each page in your app, like so:
<phone:PhoneApplicationPage
...
Style="{StaticResource TransitionPageStyle}">
...
</phone:PhoneApplicationPage>
Silverlight Toolkit transitions come with several out-of-the-box transitions that can immediately add pizzazz to your app.
This chapter began by exploring the properties and events that govern page orientation. The PhoneApplicationPage.Orientation
property only affects orientation at design-time. The PhoneApplicationPage.SupportedOrientations
property is used to change the orientation at runtime.
The chapter then looked at using a custom IValueConverter
to hide and display page elements depending on the page’s orientation. You then saw how to animate page elements using the VisualStateManager
, and how to substitute an application’s RootVisual
with a custom PhoneApplicationFrame
to animate the entire page when the orientation changes.
Finally, the chapter looked at animating page transitions using the Silverlight Toolkit.
3.133.158.36