Building ScePhotoViewer
ScePhotoViewer is a sample application designed as a reference for developers
creating rich client-based multimedia viewing experiences in WPF. It's a
fairly sophisticated WPF application which is more or less complete though several
features are stubbed out as placeholders.
The first section of this document is an overview of how the ScePhotoViewer
application was developed using the Syndicated Client Experiences (SCE) Starter
Kit. The second section is an overview of how we built ScePhotoViewer features
such as setup, sync, effects, etc. Read on to learn more about how we created
ScePhotoViewer and how you can leverage the innovations in ScePhotoViewer to build
great experiences in your own applications.
Using the SCE Starter Kit to build a Media Viewer (ScePhotoViewer)
ScePhotoViewer was built using the Syndicated
Client Experiences (SCE) Starter Kit SDK. The Syndicated Client Experiences (SCE)
Starter Kit is a foundation for creating syndicated multimedia experiences for any type
of media, including text, images, videos, and audio. The SCE Starter Kit was designed
from the ground up to help WPF developers easily create rich, syndicated multimedia and
content experiences to engage the end user. The sample application included in the SCE
\Starter Kit SDK (SceReaderSample) is optimized for reading text content, so the first
step in building ScePhotoViewer was to convert this sample into the ScePhotoViewer
sample.
If you’d like to learn more about the SCE Starter Kit, please check out the following resources:
- Getting Started with the SCE Starter Kit
contains instructions for getting your development environment set up, downloading and running the
Starter Kit, and FAQs.
- SCE Starter Kit Architecture
explains the architecture in detail. It describes the key classes and concepts used by the
Starter Kit.
- Data Feed Spec describes
how the data feeds are constructed.
Basic Architecture of the Starter Kit
First let's look at the pieces that make up the SCE Starter Kit. The following diagram illustrates
the architecture of the SCE Starter Kit.
Data comes down to the client via a Data Feed. Then the data passes to the Data Converters where
it is transformed from XML-based feeds into data objects that are consumed by the UI Layer. The UI
Layer is an abstraction between the data and the application itself. It understands the structure
of the data and how to display it and navigate through it. The Starter Kit Application can make
calls to the UI Layer based on user interaction with the XAML controls and the UI Layer passes the
correct data to the Application for display.
In the SCE Starter Kit sample application (SceReaderSample), the data is news stories, but this same
model can be applied to any number of multimedia experiences, including photos.
The Data Feed
The data feed for the Starter Kit is based on RSS 2.0. The content from the NITF-based XML
documents is grouped into a larger set of related items (see Data Feed Spec
for more information). In the case of ScePhotoViewer, the data feed was generated from
photos in a web-based photo service. ScePhotoViewer uses a FeedConverter to create the
expected XML feed format.
SceReader has a base type that is the individual story. Each story has properties such as
a headline, date, byline, body, etc. ScePhotoViewer has an individual photo feed which
corresponds to the story in the data hierarchy. The FeedConverter in ScePhotoViewer takes
the data from an RSS feed generated by a web-based photo service and converts it into the
XML format which can be read by the Data Converter. The photo also has properties such as
title, description, date, etc. You can see that you have similar properties for the photo
and the story – title instead of headline, pubDate instead of dateline, etc. In addition,
the photo has some properties which are unique, such as the hyperlink to the photo.
In addition to the individual data type, each application creates larger feeds that contain
links to the individual feeds. In the SceReader, the larger feeds are called editions which
contain sections and subsections that point to story feeds. There is also the concept of a
master feed which points to the editions. In ScePhotoViewer there are gallery feeds which
contain albums that point to individual photos.
It was necessary to define the feeds for each data type and their aggregates (e.g. photo,
album, and gallery). Once the feed data formats were created, then the data converters
needed to be modified to read in the feeds.
Data Converters
The data is read in by DataFeedSource.cs and passed to the data converters. The data converters
take the XML feeds and put the data into the data objects that are used by the UI Layer. The
converters are adjusted so that a story is now a photo, a section is now a photo album, etc.
While the data sources and outputs are different, the logic that reads in the data is very similar.
For example, let's look at the code that reads in the RSS feed and converts it to the data
feed:
// rx:stories
childNavigator = navigator.SelectSingleNode("rx:stories", Nsmgr);
if (childNavigator != null)
{
stories = GetGuidsFromNavigator(childNavigator, "story");
if (stories.Count == 0)
{
isDataValid = false;
}
}
// Photo Tags
childNavigator = navigator.SelectSingleNode("media:category",
this.NamespaceManager);
string tags = GetTextFromNavigator(childNavigator);
In both cases, a navigator, which is an instance of XpathNavigator, is being called. The navigator
is pulling out a single node from the feed. The methods GetGuidsFromNavigator and GetTextFromNavigator
are defined in the converter classes but are simply using XPath navigation to traverse the navigator
node and pull out the desired data.
Data Objects
The data converters place the data into the data objects. Each data object that is used in the
application needs to be defined. For example, the SceReader contains the Story class while
ScePhotoViewer contains the Photo class. These classes define the methods and properties necessary
to contain and interact with the data. You can see these two classes summarized below.
Data objects must be created for each type of data the application intends to display, along
with collection objects that will contain the types.
View Manager
The ViewManager.cs class contains the concept of active items which are currently being displayed in
the application. The SCE Starter Kit has the ActiveStory and ActiveSection classes. For ScePhotoViewer,
these were modified to become ActivePhoto and ActivePhotoAlbum.
Navigator
The navigation logic is updated in ScePhotoViewer to take into account changes in the data
structure. For example, in SceReader sections can contain other sections, but in ScePhotoViewer
photo albums cannot contain other photo albums. For this reason, some of the recursive searching
was removed for efficiency. In general, though, most of the generic navigation code is reused
(for example, NavigateToStoryCommand became NavigateToPhotoCommand). Looking at the definitions
for these classes, they both derive from NavigationCommand and override PerformNavigate, where
they call ViewManager.NavigateByCommand.
From SceReader\View\NavigationCommands.cs
public class NavigateToStoryCommand : NavigationCommand
{
…
protected override void PerformNavigate(object parameter)
{
StoryNavigator storyNavigator = parameter as StoryNavigator;
if (storyNavigator != null)
{
ViewManager.NavigateByCommand(storyNavigator,
SceReaderNavigationMode.Normal);
}
}
}
From ScePhoto\View\NavigationCommands.cs
public class NavigateToPhotoCommand : NavigationCommand
{
…
protected override void PerformNavigate(object parameter)
{
PhotoNavigator photoNavigator = parameter as PhotoNavigator;
if (photoNavigator != null)
{
ViewManager.NavigateByCommand(photoNavigator, ScePhotoNavigationMode.Normal);
}
else
{
…
}
}
}
The Control Authoring Overview is
a good resource for help with creating new controls, such as the ScePhotoViewer controls that were
created to handle displaying photos and groups of photos and to allow user interaction with them.
Creating the ScePhotoViewer Features
Once the Data Feed and the Starter Kit Core are updated, it's time to update the UI and turn it
into ScePhotoViewer.
Sync
As explained above, content is read into SCE applications by way of a rich RSS2.0 feed. For ScePhotoViewer,
we extended the basic RSS feed with
CSX extensions for hierarchical RSS items (e.g. Gallery, Albums, Photos)
supporting both the UI structure as well as intelligent sync logic, FeedSync extensions for enabling bi-directional
sync (e.g. comments, ratings), Media RSS standard for media-based RSS (a la Flickr/Yahoo), and NITF (News Industry
Text Format) standard. In this example, we have created an RSS feed with a utility that talks to Flickr's API
to read in a stream of photos from a Flickr account.
Content is synched down to the local machine from the cloud for high performance navigation as well
as offline access, creating a robust content viewing experience. The Subscription Center service runs
in the background to sync “subscription” content from SCE apps even when the app isn’t running. This
means that the user’s content will always be up to date. Content is (optionally) stored in a SQL Compact
Edition database which is private to the application, and credentials can be stored for offline access
to authenticated feeds.
The photos which appear in the Details view of ScePhotoViewer’s subscription in Subscription Center
are specified as items in the master feed:
<rss version="2.0" xmlns:csx="http://schemas.microsoft.com/rss/2007/contentsyncextensions"
xmlns:media="http://search.yahoo.com/mrss"
xmlns:sx="http://feedsync.org/2007/feedsync">
<channel>
<title>ScePhotoViewer Master Feed</title>
<description />
<link />
<pubDate>8/21/2008 11:31:03 AM</pubDate>
<lastBuildDate>8/21/2008 11:31:03 AM</lastBuildDate>
<item csx:hiddenItem="true">
<title>wpfphotos</title>
<description />
<link>http://www.flickr.com/photos/24218090@N03/</link>
<guid isPermaLink="false">24218090@N03</guid>
<pubDate>8/21/2008 11:31:03 AM</pubDate>
<csx:lastBuildDate>8/21/2008 11:31:03 AM</csx:lastBuildDate>
<csx:link nestedFeed="true">pc_24218090@N03.xml</csx:link>
</item>
<item>
<title>Fields of Hay</title>
<description>Hay is grass or legumes that has been cut, dried, and stored for
use as animal feed, particularly for grazing animals like cattle, horses,
goats, and sheep. Small pets such as guinea pigs and rabbits …</description>
<link>http://www.flickr.com/photos/24218090@N03/2556304785/</link>
<guid isPermaLink="false">2556304785</guid>
<pubDate>8/9/2003 5:53:47 PM</pubDate>
<csx:lastBuildDate>8/6/2008 8:51:57 PM</csx:lastBuildDate>
</item>
<item>
<title>Apocalyptical Yellowstone</title>
<description>Yellowstone National Park, set aside as a national park on
March 1, 1872, is located mostly in the U.S. state of Wyoming, though it
also extends into Montana and Idaho. The park was the
first of its k…</description>
<link>http://www.flickr.com/photos/24218090@N03/2567415751/</link>
<guid isPermaLink="false">2567415751</guid>
<pubDate>5/5/2007 10:29:08 AM</pubDate>
<csx:lastBuildDate>8/6/2008 8:52:26 PM</csx:lastBuildDate>
</item>
<item>
<title>Grand Canyon South Rim</title>
<description>The Grand Canyon is a steep-sided gorge carved by the Colorado
River in the U.S. state of Arizona,and parts of Nevada.
It is largely contained within the Grand Canyon National Park —
one of the first …</description>
<link>http://www.flickr.com/photos/24218090@N03/2567415367/</link>
<guid isPermaLink="false">2567415367</guid>
<pubDate>4/28/2007 11:13:09 AM</pubDate>
<csx:lastBuildDate>8/6/2008 8:52:44 PM</csx:lastBuildDate>
</item>
</channel>
</rss>
Dynamic Layout
The dynamic layout of the galleries and albums in ScePhotoViewer was created using adaptive templates
to enable pixel-perfect presentation scaling to screens of all sizes. When the window is resized, UI
elements will flow and resize, and at specific thresholds defined by the UI designer, the app will
switch layout templates. Authoring these templates is straightforward and part of the basic architecture
of the Starter Kit SDK.
You can learn more about customizing these templates in "How To: Customizing Section Fronts."
In ScePhotoViewer, the templates for the Gallery Home are applied in the GalleryHomeResources.xaml
file.
<!-- Collection of templates to use for the gallery home control,
depending on the amount of space available -->
<ScePhotoControls:SizeControlTemplateCollection x:Key="GalleryHomeTemplateCollection">
<ScePhotoControls:SizeControlTemplate MinWidth="0" MinHeight="0" MaxWidth="800"
MaxHeight="350" Template="{StaticResource GalleryHome_MiniList}" />
<ScePhotoControls:SizeControlTemplate MinWidth="300" MinHeight="350" MaxWidth="1000"
Template="{StaticResource GalleryHome_Tall}" />
<ScePhotoControls:SizeControlTemplate MinWidth="800" MaxHeight="455"
Template="{StaticResource GalleryHome_Tall}" />
<ScePhotoControls:SizeControlTemplate MinWidth="1000" MinHeight="450" MaxHeight="650"
Template="{StaticResource GalleryHome_Wide}" />
<ScePhotoControls:SizeControlTemplate MinWidth="1000" MinHeight="650" MaxHeight="800"
Template="{StaticResource GalleryHome_Medium}" />
<ScePhotoControls:SizeControlTemplate MinWidth="1000" MinHeight="800"
Template="{StaticResource GalleryHome_Large}" />
</ScePhotoControls:SizeControlTemplateCollection>
Navigation
Effortless and intuitive navigation gestures and super-fast navigation performance were high priorities for
ScePhotoViewer. The high performance navigation in ScePhotoViewer was accomplished using some innovative
techniques, including:
- Worker thread pool: ThreadPool, which comes with .NET, executes
all threads with the same priority (Normal). Since normal priority is also used
for all UI processing, that could result in a negative impact on performance. For
this reason, we developed our own worker thread pool which adds the possibility to
change the thread priority. All network and file IO operations are run with the
lowest priority in SCE apps.
- Event-based async pattern: For any potentially expensive operation,
we execute in asynchronous mode. For all asynchronous operations we are extensively using
event-based async pattern and cancelation functionality. If an operation is initiated and
then becomes unnecessary due to user action (for example, a navigation request) we cancel
the operation to avoid unnecessary work.
- XML data loading and conversion is done asynchronously: Background worker
threads (worker thread pool) are used when loading XML files and converting those to the
data model.
- Image loading and processing is done asynchronously: Background worker
threads (worker thread pool) are used when loading image files and decompressing them
(converting to BitmapImage).
- Requesting data when UI is initialized: During navigation, UI is initialized
without binding it directly to actual data. Then after UI initialization is completed, data is
requested asynchronously.
- Priority queues for network access: When accessing network resources
(HttpWebRequest) we have priority queues that control the order of execution requests.
Requests associated with UI are executed as high priority.
Search and Tag Explorer
With data binding, templates and styles, WPF makes it really easy to create new experiences reusing
the same business logic. The different views in ScePhotoViewer are examples of this. The spider-like
Tag Explorer view allows the user to “surf” the same metadata that the default photo viewing mode uses
to create the UI. All of this metadata is included in the content feed and the feed can be easily modified
to include many different types of metadata, from comments and ratings, to file sizes and photo formats.
In ScePhotoViewer, the SearchViewControl.xaml and SearchViewControl.xaml.cs files contain the implementation
of the search control and tag explorer. The PhotoExplorerControl class in ScePhoto defines some properties
(such as CoefficientOfDampening or LinePen) which can be used to customize the look and behavior of the tag
explorer, and the commands for switching nodes.
/// <summary>
/// Switches the current center node for the one passed in as a parameter.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">Arguments describing the routed event.</param>
private static void OnSwitchCenterNodeCommand(object sender,
ExecutedRoutedEventArgs e)
{
PhotoExplorerControl photoExplorer = sender as PhotoExplorerControl;
PhotoExplorerBaseNode nextNode = e.Parameter as PhotoExplorerBaseNode;
if (photoExplorer != null && nextNode != null)
{
if (nextNode == photoExplorer.CenterNode)
{
PhotoExplorerPhotoNode photoNode = photoExplorer.CenterNode as
PhotoExplorerPhotoNode;
if (photoNode != null)
{
ServiceProvider.ViewManager.NavigationCommands.
NavigateToPhotoCommand.Execute(photoNode.PhotoNavigator);
}
}
else
{
photoExplorer.CenterNode = nextNode;
}
}
}
Shader Effects
In .NET 3.5 SP1, we added the ability to create custom hardware accelerated
bitmap/shader effects. ScePhotoViewer demonstrates a few of these effects in
the "FX" panel and in the Slideshow transitions. Effects can be
applied to other elements (e.g. video) or to the entire UI, and hit testing,
navigation, and other functions will still work normally. In ScePhotoViewer,
a blur effect has been applied to the photo filmstrip to create a nice navigation
effect as you zip through photos. Some 20 sample effects are included in the
ScePhotoViewer source, and it’s also straightforward to author custom effects.
Sample effects can be found in the TransitionEffects and EffectsLibrary projects
which are included with the ScePhotoViewer source code. You can learn more about
building your own shader effects on WindowsClient.net or the WPF Codeplex site.
Reading Mode
ScePhotoViewer also takes advantage of one of WPF’s other great strengths: typography.
This application demonstrates some of the advanced text and typography features including
OpenType, ligatures, ClearType, and Flow/Pagination. Application authors can utilize these
features to create highly designed, distinctive user interfaces. Fonts can be built into
the resources of such that they are private to the application, but can still be viewed even
on computers which do not have that particular font installed.
The articles "How To: Customizing the Appearance of Articles"
and "How To: Using Fonts with the SCE Starter Kit" provide more information about customizing the reading mode.
Setup
Finally, one of the most exciting features in ScePhotoViewer is the new install and
update experience. ScePhotoViewer is our showcase application for the deployment improvements
made in .Net 3.5 SP1, including a branded installer created using our new Configuration
utility for custom, optimized install experiences. With only three clicks, one window,
and under eight minutes from download to launch on a clean Windows XP machine with a
typical broadband connection (and as little as three minutes on a faster connection),
this the best install experience created with the .NET Framework yet. A lot of improvements
went into creating this deployment experience, including:
- A smaller, faster NET Framework redistributable. The .NET Framework
Client Profile is only about 28 mb in size and setup optimizations were devised for
NGen and downloading.
- Brandable deployment experience. Developers can customize their app
deployment experience to match their brand instead of using a generic installer window.
- Easier deployment of applications. Developers can now use a deployment
bootstrapper to manage the installation of their application and pre-requirements. The
bootstrapper will install the application after the Framework and launch it.
- ClickOnce. Including added support for FireFox and seamless app update
with no ClickOnce UI necessary, ClickOnce has raised the bar for update experiences. The application
will check for an update in the background (with no impact on startup performance) and if an update
is found, it will be installed in the background. Upon application restart, the updated app is invoked,
or developers can choose to optionally prompt the user and restart or refresh automatically, replicating
a web application in terms of seamlessness of update.
ClickOnce in .NET 3.5 or later also supports arguments, which allows ScePhotoViewer to pull up specific
photos via the Subscription Center details view. This also enables users to pass around URLs to specific
pieces of content in the application (images in this case). If the application is not installed, ClickOnce
can be invoked to install it.
Learn More
Install the full ScePhotoViewer source code here and read more about
customizing your own SCE application here. You can also install the
ScePhotoViewer sample application here.