Note: the official view of //Build comes, of course, from the keynote session at //Build and the individual speaker sessions so if you find that anything that I write here seems out of sync with what you see at //Build then, of course, you should apply a pinch of salt to what I’ve written – I can be wrong and it’s early days yet.
You should also check out these 2 blog posts:
Cortana (yes!) and Many, Many Other Great Features coming in Windows Phone 8.1
Extending platform commonality through universal Windows apps
I got a couple of comments in response to my previous post about “what kind of PCLs are these?” and “what’s a ‘Universal PCL’?” and so I thought I’d dig into that a little bit here as there are a number of ways of re-using .NET code in Windows/Phone 8.1 that weren’t all available to a Windows Phone 8.0 developer.
I’ll start with a blank, universal project which has the 2 separate Windows/Phone projects (or “heads” as you might hear Microsoft people calling them in //Build sessions) and a shared folder.
Adding a Portable Class Library
I think the key word with “portable class libraries” is the word “portable” and the question I always find myself asking is “portable across what?”. That is – I can make a portable class library to share across Windows/Phone 8.1 by doing this;
and that creates a .NET class libary (i.e. an assembly) which can be referenced by either/both of the Windows/Phone projects (usually, I think you’d be creating this library to reference from both). You can see this from the Properties page;
The important things about portable class libraries come from http://msdn.microsoft.com/en-us/library/gg597391(v=vs.110).aspx which tells me that the types for a PCL;
-
They must be shared across the targets you selected.
-
The must behave similarly across those targets.
-
They must not be candidates for deprecation.
-
They must make sense in a portable environment, especially when supporting members are not portable.
That portable class library that I make above is configured for Windows 8.1 and Windows Phone 8.1. I’m not sure of the best way to do this but if I look at the Object Browser in Visual Studio, it suggests that what I’ve got available to me here as a “surface area” of common APIs includes;
So that shows that I’ve got this “.NET Core” from .NET 4.5.1 framework and it also shows that I’ve got the common pieces of WinRT that Windows 8.1 contributes;
and I’ve also got the common pieces of WinRT that Windows Phone 8.1 contributes;
and if I take some representative class like XDocument (LINQ to XML) then I can use that from that portable class library;
namespace Portable { public class Class1 { public static void DoSomething() { XDocument xDoc = XDocument.Parse("<foo/>"); } } }
equally, if I use some piece of WinRT that’s available to both Windows/Phone then I can do that too. For example;
namespace Portable { public class Class1 { public static void DoSomething() { Button button = new Button(); button.Content = "Hello Portable XAML World"; } } }
but what I can’t do is make use of some UI control that doesn’t exist to both Windows/Phone. For example;
namespace Portable { public class Class1 { public static void DoSomething() { // This doesn't compile. Pivot doesn't exist on Windows 8.1. Pivot pivot = new Pivot(); } } }
or for an example the other way around;
namespace Portable { public class Class1 { public static void DoSomething() { // This doesn't compile. SearchBox doesn't exist on Windows Phone 8.1 SearchBox searchBox = new SearchBox(); } } }
or for a non-UI example I can use a shared API like;
namespace Portable { public class Class1 { public static async Task DoSomething() { StorageFolder photos = KnownFolders.PicturesLibrary; StorageFile file = await photos.CreateFileAsync( "myNewFile.txt", CreationCollisionOption.GenerateUniqueName); } } }
but I can’t use a non-UI API that’s not available in both places;
namespace Portable { public class Class1 { public static void DoSomething() { // This doesn't compile. ToastNotificationManager can't do History // on Windows 8.1 ToastNotificationManager.History.Remove("burntToast"); } } }
and for one the other way around;
namespace Portable { public class Class1 { public static void DoSomething() { // This doesn't compile. SearchPane doesn't exist on Windows Phone 8.1 SearchPane searchPane = searchPane.GetForCurrentView(); } } }
Where can I use this “Universal Portable Class Library” from? I can really only use it from 2 places – a Windows 8.1 project and a Windows Phone 8.1 project or from another class library that’s targeting the same target platforms.
If I try to reference it from (e.g.) a Windows Console Application then I’m going to get an error;
what if I tried to reference this library from a Silverlight 8.1 project? Same error. But maybe I can change that, perhaps I can go and change the target platforms for my portable library;
and then, sure enough, I can reference this library now from Silverlight 8.1, Windows 8.1, Windows Phone 8.1 but what does this mean to the API set that I can call? As you’d expect, it reduces it because we’re intersecting an intersection or subsetting a subset or whatever you want to call it.
Can I now create a Button in my portable code? No, because a Silverlight 8.1 Button is not a Windows Phone 8.1 Button.
namespace Portable { public class Class1 { public static void DoSomething() { // This does not compile. Silverlight's Button is not Windows/Phone's // Button. Button b = new Button(); } } }
But can I still use XDocument? You bet;
namespace Portable { public class Class1 { public static void DoSomething() { // This does not compile. Silverlight's Button is not Windows/Phone's // Button. XDocument xDoc = XDocument.Parse("<foo/>"); } } }
and can I still use async and Tasks etc? Yes;
namespace Portable { public class Class1 { public static async Task DoSomethingAsync() { await Task.Delay(1000); } } }
and can I still use portable WinRT APIs that are available across Windows/Phone/Silverlight8.1 ? Yes;
namespace Portable { public class Class1 { public static void DoSomething() { var someTileXml = TileUpdateManager.GetTemplateContent( TileTemplateType.TileSquare150x150Block); } } }
and so, when I’m creating a portable class library with that option in Visual Studio;
I don’t really think it’s doing anything different than creating a portable library for “Windows Desktop”;
and then pre-selecting these 2 target platforms for you;
As an aside, while I’ve got Xamarin.Android and Xamarin.iOS on the screen unless some magic happened at //Build that I didn’t see yet (it’s always possible!) if I add those 2 platforms here then I’m going to be subsetting down my available APIs such as to remove the possibility of invoking WinRT APIs as, of course, they aren’t portable across to Android/iOS. As I’ve said before if they were then things might be very interesting indeed but they aren’t.
What About WinRT Components?
You can write WinRT components in .NET. A similar looking dialog can be used to do that portably. Like this;
I want to point out that this is not interchangeable with making a .NET class library. A WinRT component is a different beast. I’ll try and illustrate that like this;
namespace PortableWinRT { public sealed class Class1 { public static async Task<int> DoSomethingAsync() { await Task.Delay(5000); return (42); } } }
This code doesn’t compile. Why? Because we’re making a WinRT component. A WinRT component’s type system is not the .NET type system which makes sense because a WinRT component can be used from a JavaScript, C++ or .NET application and so tying it to the .NET type system (or the JS or C++ type system) wouldn’t make sense. It has its own type system.
Consequently, I can’t use Task<int> as a return type. Now…there are fairly simple ways around this in this instance. I can so something like this;
namespace PortableWinRT { public sealed class Class1 { public static IAsyncOperation<int> DoSomethingAsync() { return (InternalDoSomethingAsync().AsAsyncOperation<int>()); } static async Task<int> InternalDoSomethingAsync() { await Task.Delay(5000); return (42); } } }
but the point is more that I wouldn’t just go and make a custom WinRT component unless I had a reason to do that. For me, that generally boils down to one of;
- You want to use the component in different language environments.
- You need to write a custom WinRT component because the system needs one – a prime example for me would be writing a background task implementation which (I think) has to be a custom WinRT component.
No doubt there are other reasons to make them but those are the primary two that come to mind for me at the time of writing the post and I’d take care with doing (1).
I hope I got that right – feel free to let me know if I’ve goofed and I’ll fix.