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:
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:
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:
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.
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.
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:
Tapping any album brings you to a page for that album:
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.