Windows Phone Silverlight to Windows Runtime 8 case study: Bookstore1
[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]
Note For info about porting to a Universal Windows Platform (UWP) app for Windows 10, see Move from Windows Phone Silverlight to UWP.
This topic presents a case study of porting a very simple Windows Phone Silverlight app to a Universal Windows app (using the Windows Runtime, or WinRT). The app consists of a ListBox bound to a view model. The view model has a list of books that shows title, author, and book cover. The book cover images have Build Action set to Content and Copy to Output Directory set to Do not copy.
The previous topics in this section describe the differences between the platforms, and they provide deep details and guidance on the porting process for various aspects of an app from XAML markup, through binding to a view model, down to accessing data. A case study aims to complement that guidance by showing it in action in a real example. The case studies assume you've read the guidance, which they do not repeat.
Downloads
Download the Bookstore1WPSL8 Windows Phone Silverlight app.
Download the Bookstore1Universal Universal Windows app.
The Windows Phone Silverlight app
Here’s what Bookstore1WPSL8—the app that we're going to port—looks like. It's just a vertically-scrolling list box of books beneath the heading of the app's name and page title:
Porting to a Universal App project
It only takes a minute or two to create a Universal App project (named Bookstore1Universal) in Visual Studio, copy files over from Bookstore1WPSL8, and include the copied files in the Universal App project. The book cover image PNG files (Assets\CoverImages\*.*), the view model source file (ViewModel\BookstoreViewModel.cs), and MainPage.xaml are files that we need to copy over to the Shared project node, making sure to retain the source folder hierarchy. We can keep the App.xaml, and App.xaml.cs that Visual Studio generated with the Universal App project. Move the MainPage.xaml.cs from the Windows project node to the Shared project node, and make sure all the source code and markup files are defining their types in the Bookstore1Universal
namespace. Delete any remaining copies of MainPage.xaml and MainPage.xaml.cs from the Windows and Windows Phone project nodes.
In the imperative code in the view model source file, these porting changes are needed:
- Change
System.ComponentModel.DesignerProperties
toDesignMode
and then use the Resolve command on it. Delete theIsInDesignTool
property and use IntelliSense to add the correct property name:DesignModeEnabled
. - Use the Resolve command on
ImageSource
. - Use the Resolve command on
BitmapImage
. - Delete using
System.Windows.Media;
andusing System.Windows.Media.Imaging;
. - Change the value returned by the Bookstore1Universal.BookstoreViewModel.AppName property from "BOOKSTORE1WPSL8" to "BOOKSTORE1UNIVERSAL".
In MainPage.xaml, these porting changes are needed:
- Change
phone:PhoneApplicationPage
toPage
(don't forget the occurrences in property element syntax). - Delete the
phone
andshell
namespace prefix declarations. - Change "clr-namespace" to "using" in the remaining namespace prefix declaration.
We can choose to correct markup compilation errors very cheaply if we want to see results soonest, even if that means temporarily removing markup. But let's keep a record of the debt we accrue by doing so. Here it is in this case:
- In the root Page element in MainPage.xaml, deleted
SupportedOrientations="Portrait"
. - In the root Page element in MainPage.xaml, deleted
Orientation="Portrait"
. - In the root Page element in MainPage.xaml, deleted
shell:SystemTray.IsVisible="True"
. - In the
BookTemplate
data template, deleted the references to thePhoneTextExtraLargeStyle
andPhoneTextSubtleStyle
TextBlock styles. - In the
TitlePanel
StackPanel, deleted the references to thePhoneTextNormalStyle
andPhoneTextTitle1Style
TextBlock styles.
Let’s focus on the phone app first and get the views updated before making it universal. Here’s how the Windows Phone Store app looks:
The view and the view model are working together correctly, and the ListBox is functioning. We mostly just need to fix the styling and get the images to show up.
Paying off the debt items
By default, all orientations are supported. The Windows Phone Silverlight app explicitly constrains itself to portrait-only, though, so debt items #1 and #2 are paid off by going into the app package manifest in the Windows Phone project and checking Portrait under Supported orientations.
For this app, item #3 is not a debt since the status bar (formerly called the system tray) is shown by default. For items #4 and #5, we need to find four Windows Runtime TextBlock styles that correspond to the Windows Phone Silverlight styles that we were using. For this first pass, let’s just get the phone app looking right. It might turn out that it doesn’t make sense to use the exact styles for the PC and tablet version of the app, anyway, due to form factor differences. Let’s figure that out when we come to it. I can run the Windows Phone Silverlight app in the emulator and compare it side-by-side with the illustration for Windows Phone Store apps in the Text section. From doing that, and from looking at the properties of the Windows Phone Silverlight system styles, we can make this table.
Windows Phone Silverlight style key | Windows Runtime style key |
---|---|
PhoneTextExtraLargeStyle | ListViewItemTextBlockStyle |
PhoneTextSubtleStyle | ListViewItemSubheaderTextBlockStyle |
PhoneTextNormalStyle | TitleTextBlockStyle |
PhoneTextTitle1Style | HeaderTextBlockStyle |
To set those styles I can just type them into the markup editor or I can use the Visual Studio XAML Tools and set them without typing a thing. To do that, you right-click a TextBlock and click Edit Style > Apply Resource. To do that with the TextBlocks in the item template, right click the ListBox and click Edit Additional Templates > Edit Generated Items (ItemTemplate).
This is how the app looks now:
There is an 80% opaque white background behind the items, because the default style of the ListBox control sets its background to the ListBoxBackgroundThemeBrush
system resource. Set Background="Transparent"
on the ListBox to clear that background. To left-align the TextBlocks in the item template, edit it again the same way as described above and set a Margin of "9.6,0"
on both TextBlocks.
That done, because of changes related to view pixels, we need to go through and multiply any fixed size dimension that we haven’t yet changed (margins, width, height, etc) by 0.8. So, for example, the images should change from 70x70px to 56x56px.
But let’s get that image to render before we show the result of the styling and layout.
Binding an Image to a view model
In Bookstore1WPSL8, we did this:
// this.BookCoverImagePath contains a path of the form "/Assets/CoverImages/one.png".
return new BitmapImage(new Uri(this.CoverImagePath, UriKind.Relative));
In Bookstore1Universal, we use the ms-appx URI scheme. So that we can keep the rest of our code the same, we can use a different overload of the System.Uri constructor to put the ms-appx URI scheme in a base URI and append the rest of the path onto that. Like this:
// this.BookCoverImagePath contains a path of the form "/Assets/CoverImages/one.png".
return new BitmapImage(new Uri(new Uri("ms-appx://"), this.CoverImagePath));
Here’s the original Windows Phone Silverlight app again, on the left, and our ported Windows Phone Store app on the right:
Making the app universal
Let's take stock of where we are. At this stage, we don't need to refer any longer to Bookstore1WPSL8—the Windows Phone Silverlight version of the app. We've brought over all the raw materials from it that we need and we've made a Windows Runtime version. But we've only made the Windows Phone Store app version of Bookstore1Universal, so we can't call it a Universal Windows app quite yet. Before we can, we'll have to support PCs and tablets by creating the Windows Store app version of Bookstore1Universal, using as much of the markup and code in the Shared project as we can. And doing that is our next step.
With a more sophisticated app, this would be the point at which we'd use the guidance in Porting for form factors and user experience and really make optimal use of the PC and tablet form factors. Instead, let's use this first very simple app as an exercise to explore just how much of our code is technically shareable as it is. So with that intention we'll only make changes that are strictly necessary.
The view model (ViewModel\BookstoreViewModel.cs), view files (MainPage.xaml and MainPage.xaml.cs), and all the asset files, are in the Shared project node of Solution Explorer so they're already visible to the Windows project. Invoke the Set as StartUp Project command on the Windows project node but, before trying to build and run it, we have a little work to do.
We're using some system resource keys (for example, ListViewItemTextBlockStyle
) that don't exist for Windows Store apps. We need to be able to reference different resources on different platforms, but we can only set a single resource key in markup. So let's put into practice the technique of assigning a common alias to two system keys described in Maximizing markup and code reuse.
First, we invent a new common resource key. ListViewItemTextBlockStyle
is being used in the BookTemplate
data template to style the TextBlock showing the book's title. So let's name the new key BookTemplateTitleTextBlockStyle
in MainPage.xaml:
<TextBlock Text="{Binding Title}" Style="{StaticResource BookTemplateTitleTextBlockStyle}" TextWrapping="NoWrap" Margin="9.6,0"/>
Next, we add new ResourceDictionary project items to the Windows and the Windows Phone project nodes, naming them both BookstoreStyles.xaml. In the Windows Phone version, we need to map our common key BookTemplateTitleTextBlockStyle
to the Windows Phone Store app-specific key ListViewItemTextBlockStyle
. This is how we do that:
<Style x:Key="BookTemplateTitleTextBlockStyle" TargetType="TextBlock" BasedOn="{StaticResource ListViewItemTextBlockStyle}"/>
Looking at the Text section, Windows Store apps have a system resource called SubheaderTextBlockStyle
that's a close match for ListViewItemTextBlockStyle
. So this is what we put in the Windows version of BookstoreStyles.xaml:
<Style x:Key="BookTemplateTitleTextBlockStyle" TargetType="TextBlock" BasedOn="{StaticResource SubheaderTextBlockStyle}"/>
Lastly, we need to merge the BookstoreStyles.xaml ResourceDictionary into MainPage.xaml and here's the syntax to do that:
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="BookstoreStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
[<the "BookTemplate" data template>]
</ResourceDictionary>
</Page.Resources>
We need to do the same thing for ListViewItemSubheaderTextBlockStyle
, which is used to style the TextBlock showing the book's author. Let's name the new key BookTemplateAuthorTextBlockStyle
, and in the Windows Store apps we'll map it to the map it to the SubtitleTextBlockStyle
key. That's all we need to do to get the Windows Store apps to build and run. Here's a first look at it:
Some last styling tweaks
Fixing the remaining styling issues is fairly straightforward now that we know how to use indirection to maximize markup reuse. Here are the issues together with suggestions for fixing them. If you use the links at the top of this topic to download the projects, you'll be able to see exactly what all the fixes look like when implemented in markup.
- The app name and page title are a bit crowded at the top of the page, but only in the Windows Store app. This is because
HeaderTextBlockStyle
differs slightly between Windows Store apps and Windows Phone Store apps. In a Windows Store app, the style setsLineHeight
to40px
, although it setsFontSize
to56px
. In a Windows Phone Store app, those properties are both57.5px
. If we weren't sharing code, the correct fix would be to set a localLineHeight
value of56px
on the TextBlock. And in fact that's close enough to the phone value that we can get away with doing that in this case. But for code sharing, the more generally correct fix is to invent a new common style key name (say,PageTitleTextBlockStyle
) and change the reference on the TextBlock to use that key. In the BookstoreStyles.xaml ResourceDictionary in the phone project, add an empty style with the keyPageTitleTextBlockStyle
, based onHeaderTextBlockStyle
. In the Windows project do the same but also add<Setter Property="LineHeight" Value="56"/>
to the style. - The bottom of the page title was a little clipped before, now it's a little more clipped. That's easily fixed by resetting the top margin of
-5.6
to0
on the TextBlock, and that gives both form factors a little more breathing room. - The default list box style's
Focused
state sets a light background on the list box, but only in a Windows Store app. Edit a copy of that style, locate the copy in the BookstoreStyles.xaml ResourceDictionary in the Windows project and give it a key ofBookstoreListBoxStyle
. In the copy, find theFocused
state and delete its Storyboard. In the phone project just add an empty style like this:<Style x:Key="BookstoreListBoxStyle" TargetType="ListBox"/>
. - The book titles and authors are being rendered in dark text instead of light text. So, in BookstoreStyles.xaml in the Windows project only, add this setter to
BookTemplateTitleTextBlockStyle
andBookTemplateAuthorTextBlockStyle
:<Setter Property="Foreground" Value="{ThemeResource DefaultTextForegroundThemeBrush}"/>
. - And finally, the end of some of the book titles are being clipped in the Windows Store app. Edit a copy of the list box's ItemContainerStyle in the BookstoreStyles.xaml ResourceDictionary in the Windows project and give it a key of
BookstoreListBoxItemStyle
. In the copy, find theHorizontalContentAlignment
setter and change its value toStretch
. You can actually delete all of the other setters from the markup. In the phone project just add an empty style like this:<Style x:Key="BookstoreListBoxItemStyle" TargetType="ListBoxItem"/>
.
That last sequence of styling operations leaves the Windows Phone Store app looking unchanged, and the Windows Store app looking like this:
Conclusion
This case study showed the process of porting a very simple app—arguably an unrealistically simple one. For instance, a list box can be used for selection or for establishing a context for navigation; the app navigates to a page with more details about the item that was tapped. This particular app does nothing with the user's selection, and it has no navigation. Even so, the case study served to break the ice, to introduce the porting process, and to demonstrate important code-sharing techniques that you can use in real Universal Windows apps.
The next case study is Bookstore2, in which we look at accessing and displaying grouped data.