Pivot and Panorama controls in Windows Phone 7

The phone’s portrait form factor, the ease of multi-touch, and a recent emphasis on fluid user interfaces all suggest other types of layout. Two such alternatives are available in Windows Phone 7 in new controls named Pivot and Panorama.


This chapter is taken from book "Programming Windows Phone 7" by Charles Petzold published by Microsoft press. http://www.charlespetzold.com/phone/index.html

The phone's portrait form factor, the ease of multi-touch, and a recent emphasis on "fluid user interfaces" all suggest other types of layout. Two such alternatives are available in Windows Phone 7 in new controls named Pivot and Panorama.

Both Pivot and Panorama are in the Microsoft.Phone.Controls library and any program that uses these controls will need a reference to that DLL. The controls are defined in the Microsoft.Phone.Controls namespace with subsidiary components in Microsoft.Phone.Controls.Primitives, but it's unlikely you'll need those other classes unless you're customizing the controls.

Compare and Contrast

Both Pivot and Panorama derive from ItemsControl by way of a class with a generic parameter:

public class TemplatedItemsControl<T> : ItemsControl where T : new(), FrameworkElement

This indicates an ItemsControl that is intended to be filled with objects of type T. Both Pivot and Panorama derive from TemplatedItemsControl with a type parameter set to PivotItem or PanoramaItem, respectively:

public class Pivot : TemplatedItemsControl<PivotItem>
public class Panorama : TemplatedItemsControl<PanoramaItem>

Perhaps the best way to explore these classes is to experiment with an actual example. The New Project dialog in Visual Studio allows you to create an project of type Windows Phone Pivot Application or Windows Phone Panorama Application, and you can surely experiment with those. For the demonstration programs in this article I took a different approach.

Here's a MainPage.xaml file from a project named PivotDemonstration. I created this project normally, that is, by selecting Windows Phone Application from the New Project dialog box. But then I deleted most of the contents of MainPage.xaml except the PhoneApplicationPage tags. I added the XML namespace declaration for "controls" (it's the widest one) and I replaced the contents of the page with a Pivot and four nested PivotItem children:

<phone:PhoneApplicationPage
    x:Class="PivotDemonstration.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:controls="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls"
    xmlns:system="clr-namespace:System;assembly=mscorlib"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
    shell:SystemTray.IsVisible
="True">
   
   
<controls:Pivot Title="PIVOT DEMONSTRATION">

       
<controls:PivotItem Header="ListBox">           
     
   ....
       
</controls:PivotItem>
       
        <controls:PivotItem Header="Ellipse">  
     
   ....         
       
</controls:PivotItem>       

       
<controls:PivotItem Header="TextBlock">  
        
....         
       
</controls:PivotItem> 

        <controls:PivotItem Header="Animation"> 
            <controls:PivotItem.Triggers> 
            
....              
           
</controls:PivotItem.Triggers>

        </controls:PivotItem>
    </controls:Pivot>
</
phone:PhoneApplicationPage>

The Pivot control's Title property is set to "PIVOT DEMONSTRATION." By default, this title will appear in the same location and be the same size as the text displayed at the top of the normal Windows Phone page. (That's the text normally displayed by the TextBlock with the name ApplicationTitle.) Each of the four PivotItem controls has a Header property set; this text appears in the same location and is the same size as the customary TextBlock named PageTitle.

The PivotItem control derives from ContentControl, so you can put pretty much anything in those controls. I gave the first PivotItem a ListBox containing all the fonts available to Windows Phone 7 programs, including a simple DataTemplate:

<controls:PivotItem Header="ListBox">
            <ListBox FontSize="{StaticResource PhoneFontSizeLarge}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}"
                                   FontFamily="{Binding}" />
                    </DataTemplate>
                </ListBox.ItemTemplate> 
                <system:String>Arial</system:String>
                 ....
                <system:String>Webdings</system:String>
            </ListBox>
        </controls:PivotItem>

The PivotItem gives the ListBox an amount of space equal to the size of the page less the Title text and the Header text:

a1.gif

The final PivotItem you see in source code, contains a TextBlock with several animations applied:

<
controls:PivotItem Header="Animation">
            <TextBlock Text="Hello, Windows Phone 7!"
                       HorizontalAlignment="Left"
                       VerticalAlignment="Top"
                       RenderTransformOrigin
="0.5 0.5">
                <TextBlock.RenderTransform>
                    <CompositeTransform x:Name="xform" />
                </TextBlock.RenderTransform>
           </TextBlock>
             <controls:PivotItem.Triggers>
                <EventTrigger>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetName="xform"
                                             Storyboard.TargetProperty="Rotation"
                                             From="0" To="360" Duration="0:0:3"
                                             RepeatBehavior
="Forever" />
                           
                           
<DoubleAnimation Storyboard.TargetName="xform"
                                             Storyboard.TargetProperty="TranslateX"
                                             From="0" To="300" Duration="0:0:5"
                                             AutoReverse="True"
                                             RepeatBehavior
="Forever" />
                           
                           
<DoubleAnimation Storyboard.TargetName="xform"
                                             Storyboard.TargetProperty="TranslateY"
                                             From="0" To="600" Duration="0:0:7"
                                             AutoReverse="True"
                                             RepeatBehavior
="Forever" />
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </controls:PivotItem.Triggers>
        </controls:PivotItem>

The animations make the TextBlock move and spin around:

a2.gif

The PanoramaDemonstration program is extremely similar to PivotDemonstration. For the most part, every place in the MainPage.xaml file of PivotDemonstration where the word "Pivot" occurs is replaced with the word "Panorama." Beyond that, the only other difference was the change in the Title property to lowercase:

<controls:Panorama Title="panorama demonstration">
        <controls:PanoramaItem Header="ListBox">    
         ....       
       
</controls:PanoramaItem> 
     
        <controls:PanoramaItem Header="Ellipse">      
         ....     
       
</controls:PanoramaItem> 

        <controls:PanoramaItem Header="TextBlock">     
         ....
       
</controls:PanoramaItem> 

        <controls:PanoramaItem Header="Animation">             
            <controls:PanoramaItem.Triggers>
             ....
            </controls:PanoramaItem.Triggers>
        </controls:PanoramaItem>
    </controls:Panorama>

Although Pivot and Panorama are conceptually very similar, they have rather different aesthetics. The next several screen shots show the two controls side-by-side with Pivot on the left and Panorama on the right. Notice how the Title is handled in the Panorama: It's much larger and suggests that it stretches to encompass all the other items:

a3.gif

The Pivot control defines several events that the Panorama control does not: LoadingPivotItem, LoadedPivotItem, UnloadingPivotItem, UnloadedPivotItem. These events signal when one item slips out of view and another item slips in. These events don't quite apply to the more fluid nature of the Panorama.

Both Pivot and Panorama define SelectionChanged events, as well as SelectedIndex and SelectedItem. The selection is considered to be the PivotItem or PanoramaItem in full view, and the event isn't fired until the item finishes sliding fully into place.

a4.gif

Both Pivot and Panorama define TitleTemplate and HeaderTemplate properties of type DataTemplate so if you use bindings to set the content of the control you can define a visual tree to indicate how the Title property and Header properties use the data.

The HeaderTemplate property is particularly important if you bind the ItemsSource property of Pivot or Panorama to a collection, in which case you aren't creating the PivotItem or PanoramaItem objects explicitly. You'll need this HeaderTemplate for a binding to set the Header text, but the template can consist solely of a TextBlock.

a5.gif

Music by Composer

Once I started thinking about it, I realized that the Pivot control was the perfect choice for realizing a program I had long been contemplating. This program corrects what I perceive to be a major deficiency of portable music players such as the Zune and Windows Phone 7, so a little explanation is necessary:

As you may know, the landscape of music in the United States and Europe can be roughly divided into performer-centric music and composer-centric music. The performer-centric tradition has close ties with the rise and evolution of recording technologies and encompasses performers from (say) Robert Johnson (1911-1938) through Lady Gaga (b. 1986). Performer-centric music consists predominantly of a musical form known as the song, generally several minutes in length, with a vocalist and instrumental accompaniment.

The composer-centric tradition is much older, stretching from (say) Claudio Monteverdi (1567-1643) through Jennifer Higdon (b. 1962), and encompasses very many different forms (for example, string quartet, piano concerto, symphony, and opera as well as songs) of widely varying lengths, styles, and instrumentation.

So I decided to write a Windows Phone 7 program called MusicByComposer that takes this extra step. The program accesses the music library on the phone and—under the assumption that the album titles begin with one or more composer names followed by a colon—extracts the composers' names from the album titles.. It then arranges the music by composer, where each composer becomes a PivotItem. The content of that PivotItem is a ListBox that lists all the albums containing music by that composer.

The MusicByComposer program begins with a screen that looks something like this:

a6.gif

Tapping any album brings you to a page for that album:

a7.gif

This is a standard PhoneApplicationPage with the standard two TextBlock items for the application title and the page title, but as you can see, the titles are the same size and in the same position as the Pivot control on the opening page. The larger album art is shown with the full album name and artist. Underneath is a ScrollViewer with an ItemsControl with all the tracks from the album. This screen has no touch interface except for scrolling: You control everything with the ApplicationBar buttons: go to the previous track, play and pause, and go to the next track. The currently playing track is indicated with the accent color and time progress.

Silverlight program can get access to the phone's photo library for retrieving photos and saving them, but it needs to use an XNA class named MediaLibrary in the Microsoft.Xna.Framework.Media namespace. You need that same class—and other classes in that namespace—for accessing and playing music.

Any program that uses MediaLibrary needs a reference to the Microsoft.Xna.Framework DLL; The MusicByComposer program also needs a reference to Microsoft.Phone.Controls for the Pivot control.

When you use XNA services to play music from a Silverlight application, some issues are involved. As described in the topic in the XNA documentation entitled "Enable XNA Framework Events in Windows Phone Applications," you need a class that calls the XNA static method FrameworkDispatcher.Update at the same rate as the video refresh rate, thirty times per second. The following class in the MusicByComposer project is basically the class shown in that documentation topic:

using System;
using System.Windows;
using System.Windows.Threading;
using Microsoft.Xna.Framework;
 
namespace MusicByComposer
{
    public class XnaFrameworkDispatcherService : IApplicationService
    {
        DispatcherTimer timer; 
        public XnaFrameworkDispatcherService()
        {
            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromTicks(333333);
            timer.Tick += OnTimerTick;
            FrameworkDispatcher.Update();
        } 
        void OnTimerTick(object sender, EventArgs args)
        {
            FrameworkDispatcher.Update();
        } 
        void IApplicationService.StartService(ApplicationServiceContext context)
        {
            timer.Start();
        } 
        void IApplicationService.StopService()
        {
            timer.Stop();
        }
    }
}

You'll need to instantiate that class in the ApplicationLifetimeObjects section of the App.xaml file. Notice the XML namespace declaration for "local":

<Application
    x:Class="MusicByComposer.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"      
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:local="clr-namespace:MusicByComposer">
 
    <!--Application Resources-->
    <Application.Resources>
    </Application.Resources> 
    <Application.ApplicationLifetimeObjects>
 
        <!-- Required for playing music from a Silverlight app -->
        <local:XnaFrameworkDispatcherService />
 
        <!--Required object that handles lifetime events for the application-->
        <shell:PhoneApplicationService
            Launching="Application_Launching" Closing="Application_Closing"
            Activated="Application_Activated" Deactivated
="Application_Deactivated"/>
    </Application.ApplicationLifetimeObjects> 
</Application>

For complete code please download the source code file.

For testing purposes, the phone emulator has a music library that consists of a single album with three short songs, which is great for establishing basic album retrieval and playing logic, but it hardly gives the program a real workout.

For debugging a program running on the actual phone from Visual Studio, you'll need to exit the desktop Zune program (because it wants exclusive access to the music library) and instead run the Connect tool, WPDTPTConnect32 on 32-bit Windows or WPDTPTConnect64 on 64-bit Windows.