Skip to main content
April 14, 2016
Mobile

Tips for porting apps from Windows Phone Silverlight to UWP



hero_silverlight

Windows Store has always had a healthy community of Windows Phone Silverlight developers. Because Windows Store has over 270 million active Windows 10 devices today, we’d like to help our developer base take advantage of all that’s new in the Universal Windows Platform (UWP).

The Windows Dev Center has a porting guide to help you manually migrate your Windows Phone Silverlight apps to UWP. In this post, we’ll look at automated migrations using the Mobilize.NET’s Silverlight bridge, which is available to automate parts of the migration process of your code from Silverlight 8.x to UWP. The tool will generally take care of up to 80% of your migration. You can then fall back on the manual guide to complete the remaining 20%.

There are five steps to updating your app using the bridge:

  1. Download the bridge
  2. Run the tool
  3. Troubleshooting missing DLLs
  4. Troubleshooting unconverted Silverlight code
  5. Troubleshooting controls and events that behave differently between platforms

Download the bridge

You can get the bridge on Mobilize.NET’s download page. You will also need Visual Studio 2015 with at least Update 1. You can get the Visual Studio Community edition for free. Finally, you also need the Windows 10 SDK Build 10586.

Run the tool

The bridge handles recreating your project as a Universal Windows Platform project. It also converts your app manifest files to the new format in UWP. Areas where the bridge can’t automate 100% of the process is in determining how to translate particular XAML markup and API calls. Understanding what is needed after the automated migration as well as why, will help you to better address these remnants and bring your app to the UWP.

Running the tool is fairly straightforward. You specify two parameters. The first parameter is the path of the project file for the Silverlight project you want to move to UWP. The second is the name of a directory where you would like the resulting UWP files to be saved. Then press Start.

1_runthetool

Troubleshooting missing DLLs

If your original Windows Phone Silverlight app references control library DLLs which have not been migrated to UWP and for which there is no source code, the bridge will simply remove the reference in your converted UWP project. This will leave you with code that has unrecognized namespaces.

This scenario is most likely to occur if your older code uses 3rd party libraries. Sometimes this can also occur simply because you have misplaced source code you wrote yourself. The most straightforward way out of this situation is to find an equivalent standard control to take the place of the inaccessible control.

From:

[code language=”csharp”]

<mycontrols:LongListSelector ItemsSource="{Binding Users}" SelectedItem="{Binding SelectedUser, Mode=TwoWay}" ItemTapCommand="{Binding SwitchAccountCommand}" ItemTemplate="{StaticResource UserTemplate}" ScrollViewer.VerticalScrollBarVisibility="Disabled" />

[/code]

Changed to:

[code language=”csharp”]

<ListView ItemsSource="{Binding Users}" SelectedItem="{Binding SelectedUser, Mode=TwoWay}" ItemTapCommand="{Binding SwitchAccountCommand}"
ItemTemplate="{StaticResource userTemplate}"
ScrollViewer.VerticalScrollBarVisibility="Disabled" />

[/code]

In the example above, the custom LongListSelector control is manually replaced with a UWP ListView. This manual fixup can be refined even further. The LongListSelector has flatlist and jumplist modes that aren’t supported by the ListView control. We can mitigate this by surrounding the ListView with a SemanticZoom in order to recreate the original list behavior.

Tip: Control libraries you may have been using in your Windows Phone Silverlight app may not have updated versions for UWP. Make note of these before you start the conversion process.

Manual migration examples on the Windows Dev Center and forums will help you when you are troubleshooting code that Mobilize.NET’s Silverlight bridge doesn’t automatically migrate for you. You should also consult Mobilize.NET’s support forums for additional assistance with conversion problems.

For additional resources, see…

Troubleshooting unconverted Windows Phone Silverlight code

The bridge uses mapping tables to determine how API calls and XAML ought to be translated from Windows Phone Silverlight to UWP. The bridge currently includes over 2,300 such mappings. While this is a lot, this means there will still be APIs remaining that the bridge doesn’t know how to handle. In many cases, this occurs because there is no direct UWP equivalent for the original Silverlight code.

Windows Phone Silverlight to UWP namespace and class mappings is a resource that will help you look up platform mappings as well as identify code that doesn’t have a UWP equivalent (for instance, the System.Environment class and the Microsoft.Phone.Globalization namespace). When there is no equivalent code in UWP, you can do one of two things:

  • Alter the unconverted code so it uses a different UWP class
  • Create a helper class to wrap the unconverted class code

In the following example, the GeoCoordinateCollection class is not supported in UWP and does not have a one-for-one mapping to another class.

[code language=”csharp”]

foreach (var myPoint in myPointList)
{
GeoCoordinateCollection coordCollection = new GeoCoordinateCollection();
foreach (Geocoordinate g in myPoint.Path)
{
coordCollection.Add(new GeoCoordinate(g.Latitude, g.Longitude));
}

MapPolygon mapPolygon = new MapPolygon()
{
Path = coordCollection
};
m.MapElements.Add(mapPolygon);
}

[/code]

This is the sort of task that is difficult for an automated mapper but relatively easy for a human being. Instead of a GeoCoordinateCollection object, you can use a generic List of BasicGeoposition types. The list can then be passed to a new Geopath instance and assigned to the UWP MapPolygon.

[code language=”csharp”]

foreach (var myPoint in myPointList)
{
List<BasicGeoposition> coordCollection = new List<BasicGeoposition>();
foreach (Geocoordinate g in myPoint.Path )
{
coordCollection.Add(new BasicGeoposition() { Latitude = g.Latitude, Longitude = g.Longitude });
}

Geopath geoPath = new Geopath(coordCollection);
MapPolygon mapPolygon = new MapPolygon()
{
Path = geoPath
};
m.MapElements.Add(mapPolygon);
}

[/code]

Alternatively, you can also just implement a custom version of the missing GeoCoordinateCollection class in such a way that the original code structure doesn’t need to change. An additional advantage of this approach is that if the class is used in multiple places, you will have much less code to rewrite by using this wrapper class approach.

[code language=”csharp”]

public class GeoCoordinateCollection
{
private List<BasicGeoposition> coordCollection = new List<BasicGeoposition>();

public void Add(Geocoordinate basicGeoposition)
{
BasicGeoposition bg = new BasicGeoposition()
{
Altitude = basicGeoposition.Point.Position.Altitude,
Latitude = basicGeoposition.Point.Position.Latitude,
Longitude = basicGeoposition.Point.Position.Longitude
};
coordCollection.Add(bg);
}

public static implicit operator Geopath(GeoCoordinateCollection g)
{
return new Geopath(g.coordCollection);
}
}

[/code]

As mentioned previously, not all available mappings have been implemented in the bridge and this requires a manual fix. For example, the ContentPropertyAttribute, which is used to identify that a specific property of the attributed type should be considered the XAML content property, requires that the namespace be updated as well as the way the attribute, MarketName, is declared.

From:

[code language=”csharp”]

using System.Windows.Markup;
[ContentProperty("MarketName")]
public class Market
{
public Market()
{
}

public string MarketName
{
get { return _name; }
set { __name = value; }
}

private string __name;
}

[/code]

Changed to:

[code language=”csharp”]

using System.UI.Xaml.Markup;
[ContentProperty(Name="MarketName")]
public class Market
{
public Market()
{
}

public string MarketName
{
get { return _name; }
set { _name = value; }
}

private string _name;
}

[/code]

Thanks to the conversions that have already been mapped out, the bridge can take care of up to 80% of your code conversions for you. Nevertheless, some unconverted code may manage to fall through the cracks. The namespace and classes mapping reference linked at the start of this section can help you to identify many of them.

For additional resources, see…

Troubleshooting controls and events that behave differently

The last major category of common automation problems concerns mappings that exist but do not work in all real-world scenarios. Take, for instance, the Windows Phone Panorama control. In UWP, this has been replaced by the Hub control. Internally, however, the Panorama and Hub controls are structured differently, and the XAML for the two controls are visibly different.

From:

[code language=”csharp”]

<controls:Panorama>

<controls:PanoramaItem>
<TextBlock x:Name="myText" />
</controls:PanoramaItem>

</control:Panorama>

[/code]

Changed to:

[code language=”csharp”]

<Hub>

<HubSection>
<DataTemplate>
<TextBlock x:Name="myText" />
</DataTemplate>
</HubSection>

</Hub>

[/code]

The best strategy for conversions of this type, as shown in this code walkthrough, is to use MVVM for binding to decouple the control from the state data and avoid the brittleness introduced by the automated conversion. Often, however, it is too late to retrofit an architecture on your app. In these cases, you may just need to work through the necessary conversions for each unique case.

For additional resources, see…

Start bringing your apps to the Universal Windows Platform today

Moving your Windows Phone Silverlight app to a UWP app will enable you to take advantage of all the features of the platform. It will also allow your app, with some UI enhancements, to be available for additional Windows 10 devices, from desktops, tablets, and Surface Hub to Xbox One and potentially even HoloLens. Mobilize.NET’s Silverlight bridge can accelerate the process of migration and the tips in this article can help you with the final stretch.