Chapter 4. Page Orientation


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.

Orientation and the PhoneApplicationPage Class

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.

OrientationChanged Event

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.


Note

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).

Image

Figure 4.1. Device orientations

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).

Listing 4.1. PageOrientationExtensions Class


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;
    }
}


PhoneApplicationPage Orientation Property

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.

Listing 4.2. PageOrientationToVisibilityConverter Class


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();
    }
}


Setting Page Orientation at Runtime

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).

Image

Figure 4.2. Forcing the orientation of a page

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;
    }
}

Animating Page Elements When the Page Orientation Changes

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.

Listing 4.3. OrientationView.xaml (excerpt)


<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).

Image

Figure 4.3. TextBlocks are animated into position when page orientation changes.

Animating the Entire Page When Orientation Changes

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).

Image

Figure 4.4. The AnimateOrientationChangesFrame class rotates content when the page orientation changes.

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.

Silverlight Toolkit Animated Page Transitions

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).

Image

Figure 4.5. Transition navigation events

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

SwivelTransition

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.

Using Silverlight Toolkit Transitions

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.

Reusing the Transition Attached Properties

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.

Listing 4.4. TransitionPageStyle in App.xaml


<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.

Summary

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.28.94