📜 ⬆️ ⬇️

Porting a Windows 8.1 application to Windows Phone 8.0 with troubleshooting



Using the example of a simple Windows 8.1 application, let's see how easy it is to transfer applications from WinRT (Windows 8.1) to Silverlight (WP8.0) and analyze several pitfalls as we go.


You probably heard about the advanced method of developing Windows / Windows Phone applications - Universal Apps. The approach is sound, but the market share of WP8.1 is still only beginning to grow, and the application needs to be done now, so we’ll focus on WP8.0 (Silverlight). Advantages: support for devices on both WP8.0 and WP8.1, support for all types of screens without “black bars” (as opposed to WP7 applications), stable third-party libraries, etc.
')

Briefly about the original application



The guinea pig will be a nearly complete one-page Windows 8.1 application for displaying the basic exchange rates.

Since the application is free and without ads, the functionality is strictly limited. Only the most necessary functions: displaying the average exchange rate (USD, EUR, RUB), fluctuation graph for the last 30 days, displaying bank rates, currency converter and support for Live Tiles.
Leave the server part behind the scenes. there is nothing interesting.

Project structure



With an eye on Universal Apps, the main blocks are moved to the user control, the data classes are moved to the Portable Library, and the code responsible for getting the data is separated from the UI.

We transfer code to Windows Phone 8.0

Create an empty project, throw out all the excess from MainPage, add the necessary references and NuGet packages. First of all, add as links (Add -> Existing Item -> Add As Link ) class files for processing data from the Windows 8 project.

Problems:

â—Š Expecting that neymspeysy will not converge (WinRT vs Silverlight), so we use a warm tube #if #endif, it turns out that something like this:
#if NETFX_CORE using Windows.Web.Http; using System.Runtime.InteropServices.WindowsRuntime; using Windows.Networking.BackgroundTransfer; #endif #if WINDOWS_PHONE using System.Net.Http; #endif 

I should note that even though we do not use Universal Apps, Visual Studio 2013 Update 2 greatly simplifies working with #if #endif for different platforms. Another drop-down list appeared right above the code, allowing you to quickly switch the platform without reopening the file. IntelliSense no longer falls, Resharper does not fall off at the most inappropriate moment. No more "This document is opened project project.", Etc.

â—Š Since we load data using HttpClient (later it will be clear why it is HttpClient), which is missing from WP8.0, we add the NuGet Microsoft.Net.Http package. But here, not without surprises, without #if not done:
  var client = new HttpClient(); byte[] buff; #if NETFX_CORE var ibuff = await client.GetBufferAsync(uri); buff = ibuff.ToArray(); #endif #if WINDOWS_PHONE buff = await client.GetByteArrayAsync(uri); #endif 

Tricky question, how many official implementations of HttpClient do you know?

◊ We store the received data in the device’s memory, which would be something to show next time if there is no internet connection, but ... well, you understood:
 #if NETFX_CORE var file = await ApplicationData.Current.LocalFolder.CreateFileAsync(LOCAL_DATA_FILENAME, CreationCollisionOption.ReplaceExisting); await FileIO.WriteTextAsync(file, json); #endif #if WINDOWS_PHONE var fs = await ApplicationData.Current.LocalFolder.OpenStreamForWriteAsync(LOCAL_DATA_FILENAME, CreationCollisionOption.ReplaceExisting); using (StreamWriter streamWriter = new StreamWriter(fs)) { await streamWriter.WriteAsync(json); } #endif 

Here it is worth remembering the difference between LocalFolder and LocalCache (since WP8.1), the main difference is that LocalFolder, unlike LocalCache, is affected by the built-in beckup / restore (not to be confused with roaming) and as a result your data may appear on a completely different device (or several) with all the consequences. See how it works in more detail here . In our case, LocalCache on WP8.0 is not available, so we use what we have.

◊ Separately, it is worth mentioning support for data encryption, historically, I already had a self-written cross-platform binary compatible encryption class using AES , which roams from project to project. The description of this class is beyond the scope of the article, I’ll just say that under WP8.0 there is a wonderful class AesManaged, and under WinRT I use the mappers for the native implementation:
 #if NETFX_CORE using Windows.Security.Cryptography; using Windows.Security.Cryptography.Core; using Windows.Storage.Streams; #else using System.Security.Cryptography; #endif 


We transfer UI to Windows Phone 8.0



After the auxiliary code is transferred and successfully compiled, we undertake the interface. Since Phone, tablet or desktop is not the same thing at all. Therefore, the application will look different. For this, Windows 8 and Windows Phone projects will have their own MainPage and we will add user controls as links (Add As Link, in the future it will just move to the Shared project ). Plus, the controls themselves will adapt to the situation, for example, the width of the available space (do not forget about snapped mode in Windows 8).

Problems:

â—Š The first thing to do is to divide the namespaces in the code behind classes using the same #if #endif, I will not give an example, just use Shift + Alt + F10 on everything that is highlighted in red.

â—Š It so happened that in WP8.0 there is no DataContextChanged event, so we change the logic a bit to get rid of it.

◊ Since there are no built-in styles from WinRT XAML in WP8.0, such as SymbolThemeFontFamily, BaseTextBlockStyle, BodyTextBlockStyle, etc., we create a separate ResourceDictionary in the WP8.0 project. Open “c: \ Program Files (x86) \ Windows Kits \ 8.1 \ Include \ winrt \ xaml \ design \ generic.xaml” and rip out everything we need and put in the newly created ResourceDictionary, replacing or discarding something that is not supported on the phone (CharacterEllipsis -> WordEllipsis, SemiLight -> Light, Typography. * -> c: \ NUL).
Then we merge this reference book in App.xaml:
  <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="CommonStyles.xaml"/> </ResourceDictionary.MergedDictionaries> <local:LocalizedStrings xmlns:local="clr-namespace:BXFinanceWP" x:Key="LocalizedStrings"/> </ResourceDictionary> </Application.Resources> 

It is worth paying attention if you already have something in Application.Resources (for example, LocalizedStrings, if you have not thrown it out at the first stage) it will need to be placed inside a ResourceDictionary, as shown above.

◊ I don’t know why, but the IValueConverter interfaces are slightly different between WinRT and Silverlight, so we rule all common (As Link added) converters in the project:
  public object Convert(object value, Type targetType, object parameter #if NETFX_CORE ,string language #endif #if WINDOWS_PHONE ,System.Globalization.CultureInfo culture #endif ) 


â—Š The next thing I encountered was the difference between third-party namespaces in XAML. In WinRT, neymspeysy are described through "using:", and in Silverlight through "clr-namespace:", an example:
 xmlns:Common="using:BXFinanceDashboard.Common" xmlns:Common="clr-namespace:BXFinanceDashboard.Common" 

There are not many solutions to this problem, and there is not a single good one, #ifdef for XAML is not. In such cases, based on the specific situation, you can create the necessary controls in the code, bother with the custom build action, take out everything you can in styles and / or divide the interface into common and platform-specific files. In extreme cases, copy-paste.
But in our case, not everything is so difficult, because in most cases, the namespace ad was needed to connect the converters. Therefore, I simply created one ResourceDictionary one at a time in each project and declared the converters there. And since the application is not large, I connected it globally in App.xaml. The plus is that converters are not re-created, but you need to remember that names are now global and cannot be repeated.
Just in case, here is the listing, WinRT (Windows 8.1):
 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Common="using:BXFinanceDashboard.Common"> <Common:HighlightValueConverter x:Key="HighlightUSDBuyConverter" HighlightBrush="#FFFF4646" /> <Common:HighlightValueConverter x:Key="HighlightUSDSellConverter" HighlightBrush="#FFFF4646" /> <Common:HighlightValueConverter x:Key="HighlightEURBuyConverter" HighlightBrush="#FFFF4646" /> <Common:HighlightValueConverter x:Key="HighlightEURSellConverter" HighlightBrush="#FFFF4646" /> <Common:HighlightValueConverter x:Key="HighlightRUBBuyConverter" HighlightBrush="#FFFF4646" /> <Common:HighlightValueConverter x:Key="HighlightRUBSellConverter" HighlightBrush="#FFFF4646" /> <Common:BoolToVisibilityConverter x:Key="BoolVisibilityConverter"/> <Common:BoolToVisibilityConverter x:Key="BoolNotVisibilityConverter" IsReversed="True"/> <Common:OpacityConverter x:Key="OpacityConverter"/> <Common:OpacityConverter x:Key="OpacityNotConverter" IsReversed="True"/> </ResourceDictionary> 

Silverlight (WP8.0):
 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Common="clr-namespace:BXFinanceDashboard.Common"> <Common:HighlightValueConverter x:Key="HighlightUSDBuyConverter" HighlightBrush="#FFFF4646" NormalBrush="{StaticResource PhoneForegroundBrush}" /> <Common:HighlightValueConverter x:Key="HighlightUSDSellConverter" HighlightBrush="#FFFF4646" NormalBrush="{StaticResource PhoneForegroundBrush}" /> <Common:HighlightValueConverter x:Key="HighlightEURBuyConverter" HighlightBrush="#FFFF4646" NormalBrush="{StaticResource PhoneForegroundBrush}" /> <Common:HighlightValueConverter x:Key="HighlightEURSellConverter" HighlightBrush="#FFFF4646" NormalBrush="{StaticResource PhoneForegroundBrush}" /> <Common:HighlightValueConverter x:Key="HighlightRUBBuyConverter" HighlightBrush="#FFFF4646" NormalBrush="{StaticResource PhoneForegroundBrush}" /> <Common:HighlightValueConverter x:Key="HighlightRUBSellConverter" HighlightBrush="#FFFF4646" NormalBrush="{StaticResource PhoneForegroundBrush}" /> <Common:BoolToVisibilityConverter x:Key="BoolVisibilityConverter"/> <Common:BoolToVisibilityConverter x:Key="BoolNotVisibilityConverter" IsReversed="True"/> <Common:OpacityConverter x:Key="OpacityConverter"/> <Common:OpacityConverter x:Key="OpacityNotConverter" IsReversed="True"/> </ResourceDictionary> 


◊ Surprise was the difference between fonts on Windows and Windows Phone. For some reason, I chose the symbols # 128314 / # 128315 from “Segoe UI Symbol”, but overlooked what they are called “Up / Down-Pointing Red Triangle”. On Windows, they look exactly as needed, but on Windows Phone, they are both red and do not repaint, and besides, the symbol is already in width. Having rummaged still, found more suitable # 9650 / # 9660. It was not immediately clear what the matter was, I even had to make a comparison on the phone:


By the way, do not forget to use exactly the glyphs (especially in the buttons on the app bar ), and not your pictures. Zadolbatsya generate a dozen pictures for different DPI, for example, the screen of another application with the Nokia 1520:


Adapt the application to the phone


The application has gathered, well, but you should not stop. Still, mobile is not the same as a tablet or desktop (by the way, the application on the desktop itself is updated if it hangs on the screen, which is convenient for all information panels).

Layout

To switch the chart of rates, the list of banks and the currency converter, we take the more familiar Pivot control on Windows Phone. We include the tray (hiding the tray without a good reason is bad), we adjust the upper indent, so that the tray does not eat much space. Fortunately, WP8 with a tray has fewer problems than WP7, it jumps less often, but do not forget that in the code the tray color can be specified not earlier than Page.Loaded. To avoid an ugly stripe between the tray and the page, we need a transparency of 0.99.

Since the screen of the phone is already in width, it is necessary that all the main controls adapt accordingly (this is also useful for snapped mode on the desktop).

The list of banks automatically displays only one currency if there is not enough space, and a switch appears below. Moreover, an important requirement here was that the list scrolling would remain in place when switching currencies. Those. that would not have to re-scroll to the required bank.


The currency converter, in turn, wraps up some elements and is justified in width:


You can achieve this behavior in several ways, for example, using states (states), but this time I just added the HorizontalAlignment properties to the control itself, the main thing is to scatter elements with grids and not StackPanel. Example:
 <UserControl … x:Name="Parent" HorizontalAlignment="Left"> <Grid x:Name="RootPanel"> … <Grid Grid.Column="1" HorizontalAlignment="{Binding HorizontalAlignment, ElementName=Parent}"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <ToggleButton x:Name="btnActionSell" Content="" Grid.Column="0" VerticalAlignment="Center" /> <ToggleButton x:Name="btnActionBuy" Content="" Grid.Column="1" VerticalAlignment="Center" /> </Grid> 

And in the MainPage on the phone we add Stretch:
 <Controls:CurrencyConverter x:Name="currencyConverterControl" HorizontalAlignment="Stretch" /> 

Also from the point of view of usability, it was very desirable that the keyboard would not close the selection buttons on all DPI supported. Plus he added that when entering (or inserting) the amount, it was possible directly on the keyboard to press the dollar sign or the euro (there is no ruble sign on the keyboard and in the font yet ) with the corresponding currency switching.


Live tiles

Since the application is under Windows Phone 8.0, we don’t have to dream about WNS , so on the phone we use the good old Periodic Background Agent. The code is nothing special, you just need to remember to ask the user at the first start if he wants to receive updates in the background. A side effect of using your agent is that when the user, for example, on the road and without the Internet, we already have some old data for displaying (on Windows 8.1 this problem is solved a little differently, see below). When preparing images for tiles, remember that in Windows Phone the logo is strictly centered (with or without a signature), and in Windows 8 it moves up a bit.


Charts

Separately worth mentioning the transfer schedule fluctuations. Out of habit on Windows, the graph was implemented using Controls.DataVisualization (the application was made with an eye to Universal Apps, and the Telerikov control was not yet released (at the time of writing)). And as it turned out, it is under Windows Phone 8.0 that there is no normal DataVisualization port. Under WinRT (Windws 8 and Windows Phone 8.1) there is WinRTXamlToolkit , even on Windows Phone 7 you could use either from the usual Silverlight. But under WP8.0 in no way (in WPToolkit, when transferred to WP8, DataVisualization was lost). In general, even with WP7, WPToolkit does not cease to “delight”.
In general, after spending half an hour searching, I threw Telelerk control on my phone (maybe I’ll switch to Windows 8 when it goes to Universal Apps), adjusted the look and forgot it like a bad dream.


Caching

In Windows 8.1 (missing in WP8.1), a wonderful feature appeared: you can tell the OS which URLs to cache. Those. if the stars converge, (or rather, if your application is used regularly), Windows itself will cache the data you need before the application starts. This thing is called Content Prefetcher . You can add specific URLs or xml file-list from the server (if the data is dynamic, for example, news). A simple example:
  public static void RegisterPrefetchUrls() { if (!ContentPrefetcher.ContentUris.Any(u => u.AbsoluteUri == LIVE_DATA_URL)) { ContentPrefetcher.ContentUris.Clear(); ContentPrefetcher.ContentUris.Add(new Uri(LIVE_DATA_URL)); } } 

Respectively we load data as usual through HttpClient. But it works only with Windows.Web.Http.HttpClient (the one that works through WinInet, not to be confused with other implementations of HttpClient). If you need to load data only from the cache, if there is one, use the Filters.HttpCacheReadBehavior.OnlyFromCache filter.
By the way, in VS Update 2, a useful menu appeared that would force the OS to cache data for debugging purposes, before it was only from the console. But you need to remember to start the application itself first, in order to indicate the URLs, more details here .


Usage statistics


This time it was decided to connect Google Analytics, not Flurry. It hurts Flurry shows the wrong numbers compared to its own statistics ( not just me ). Plus, poor support for platforms other than iOS / Android and a terribly dull web panel (80+ http requests with each reboot). As for me, it makes sense to use Flurry only for cross-platform games because There are very useful installation tracking tools, in-app, etc.

For Windows / Windows Phone, there is a ready-made Google Analytics SDK for Windows 8 and Windows Phone . The integration is simple, only I would advise calling SendView () not from OnNavigatedTo () as in the example, but from Page.Loaded to count the impressions when the user returns with the back button. I also added a platform to the version field in analytics.xml:
 <appVersion>1.0.0.0 (WP)</appVersion> 

Depending on the specifics of the application, it makes sense to add custom events (SendEvent ()). As an example, I’m wondering how many users will actually use the currency converter, or what percentage has turned off Live Tile.

Final touches


We make the application page, do not forget to add msApplication-ID in the meta tags, so that the user could put the application from the IE menu. If there is a desire, register the application in Bing, so that the user can put the application directly from the web search results page (on Windows 8.1 / Windows Phone 8.0 / 8.1).
The only thing that upsets is the lack of the Russian-language “download” button for the Windows Phone Store (can MS representatives comment?). If you follow the rules of using Microsoft trademarks, you need to use only ready-made images, there is even a whole guide where and how to use them. The result is that for the Windows Phone Store there is no button in Russian, and for the Windows Store there is no button without the word “Download”. I have to use English versions of the buttons:


Conclusion


It turned out somehow long and messy, even though I tried to choose only interesting moments. If you missed something, ask in the comments.

Source: https://habr.com/ru/post/227197/


All Articles