This post was written by Microsoft MVP Pavel Yosifovich.
This week we’re featuring guest blog posts from some of the members of our Microsoft developer MVP program. Microsoft MVPs are passionate, expert community leaders who share their real-world knowledge and experience building solutions with Microsoft platforms.
Pavel Yosifovish, CTO at CodeValue; Can Bilgin, Senior Solutions Architect at CompuSight Corp.; and Rob Miles, a university lecturer, have all recently had experience implementing universal Windows apps, so we thought it would be especially useful to showcase the things they’ve learned and how they’ve been able to leverage much of the same code to deliver experiences across multiple device types.
One of the new features introduced during the Build conference is a converged Windows Runtime on Windows Phone 8.1, opening the way to “universal Windows apps.” In this post I’d like to share my first experience with universal Windows apps, their structure and possible future.
Before universal apps came along, creating apps for Windows 8.x Store and Windows Phone 8 was mostly a two-app project. Sure, some logic code could be shared via Portable Class Libraries (PCL), but the common surface of PCLs was too small, leading to many #if/#endif statements. Also, sharing XAML was extremely difficult, and in my experience a futile exercise, so it had better be kept separate.
Other stuff, similar on the outside, such as Live Tiles and push notifications, had to be done differently, because it was a different API, built on different foundations. Windows Store was built on top of the Windows Runtime (sometimes dubbed WinRT) – a native, COM based API, while the Windows Phone API was mostly Silverlight-based – pure .NET mostly. Yes, with the 8.0 version of Windows Phone some WinRT APIs made it to the phone (sometimes referred to as “WinPRT”), common with WinRT, but that common API was relatively small.
With Windows Phone 8.1 and Windows 8.1 Update, things are changing, or should I say – converging. The WinRT API has spread to the phone, replacing Silverlight based APIs. For example, the Windows.UI.Xaml.Controls.Button class from WinRT now exists as a Win(P)RT type in WP 8.1. The “old” button, System.Windows.Controls.Button is still there to support existing apps (and is probably a wrapper around the “new” button). New apps can still use the Silverlight model, but I suspect this will be discouraged in the future.
Another effect of this convergence is the “Universal PCLs”, which are now expanded to include a much larger surface area than non-universal PCLs. I use the term “Universal PCLs” but these are regular PCLs targeting Win8.1/WP8.1 that include more common surface to share, including XAML. “Non-universal” here really means PCLs that target more than Windows 8.1 and Windows Phone 8.1.
Universal Windows Apps
Universal Windows apps are there to allow the same app to be written for Windows 8.1 Store and Windows Phone 8.1 with little code changes. Note that it’s not the same as linking a Windows and Phone app in the Store – this can be done in the non-Universal model as well; here I’m referring to actual code sharing, whether the apps will be linked in the Store or not. Almost everything can be shared, and obviously some things will have to change, such as UI layout, or usage of special features of one platform or the other. But, as it turns out, most code, and even XAML can actually be shared; and that’s a real advantage we didn’t have before.
Let’s create a simple universal Windows app and examine its components. To get support for this, you’ll need Visual Studio 2013 (any SKU) with Update 2. This will install the proper project templates and other required support to get this working.
From Visual Studio, select File -> New Project and navigate to the new Store Apps subfolder:
It’s actually possible to start with a Windows 8.1 app or a Windows Phone 8.1 App and add its “twin” app using a context menu option on the project node in solution explorer.
With the universal app selection, we get two app projects, one for Win 8.1 and the other for Win Phone 8.1. The unusual part is the third, “Shared” project:
Notice the projects look very similar. The Silverlight manifest file WMAppManifest.xml has been transformed into Package.appxmanifest, essentially the same as its Windows 8.1 counterpart.
As we can see, the “Shared” project has App.xaml and App.xaml.cs shared by default. This means these files do not appear in the normal projects, but are “pulled” by each one when built.
The Shared project is a bit weird, as it has no output on its own – it’s not a DLL, EXE, XAP or whatever. It’s nothing; it just contains shared stuff that must be pulled by each of the “real” projects. It doesn’t even have a References node.
So what happens if we want to use some class library (it better be a Universal PCL)? We add it as a reference for each of the real projects, and then its contents is available for use in the Shared project. Get it?
Let’s do some experimentation. Drag the MainPage.xaml file from the Windows Phone project to the Shared project’s node:
Notice the file does not disappear from the original project. Trying to build now yields 2 errors, because there are 2 files with the same name (identity) for each project.
Let’s delete the MainPage.xaml file (along with its code behind) from the Win 8.1 and WP 8.1 projects, leaving it in the Shared project only:
Now the two projects build just fine, and they even run perfectly fine. In this case, the MainPage.xaml file was identical in the original two projects, so no harm, no foul. Let’s add a simple Button control to the Grid that exists in that (now single) MainPage.xaml:
The designer now has an option to switch to one of two views – Win 8.1 or Win Phone 8.1 so we can check out the results at design time:
Running each app shows the button as we expect, in the Grid’s center.
By the way, notice that the XAML has converged, so that Windows Phone and Windows 8 now recognize the same syntax, such as the way to map a XAML namespace to a CLR namespace using the “using” keyword rather than the “clr-namespace” keyword, as was done with the “Silverlight way”. This is just one aspect that makes sharing XAML possible.
Same same but sometimes different
Some aspects of the app would be different between Windows Phone and Windows. For this purpose, the project templates define the WINDOWS_PHONE_APP (for WP) and WINDOWS_APP (for Windows) symbols, so that C# code can use the standard #if/#else/#endif markers to distinguish between the platforms. This is particularly useful for a PCL and any other non-XAML code.
Sharing XAML is more difficult. Let’s say I want to have two pieces of XAML laid out differently in WP and Windows like the following simple example:
One way to achieve that is to use the same page XAML, but do the tweaks using View Model property bindings, such as for alignment, grid row/column, etc. Although that’s possible, it’s a maintenance nightmare for non-trivial views.
A better approach would be to factor out the important controls as user controls in the shared project and have different page XAML for each platform. In the above example, I created a DeviceList user control in the shared project that hosts a list of devices on the current platform. The XAML of the control is simple:
The code behind provides the Devices dependency property of type IReadOnlyList<DeviceInformation> which is mapped from the IVectorView<> WinRT interface.
The two XAML pages for the platforms simply use the control in the correct place. This keeps the page XAML simple and not require any support from the view model or value converters. Here’s the example for Windows:
The binding is set up in the code behind (would be in a PCL or shared code ViewModel in practice):
In this way, user controls can be shared and handle the heavy lifting, while the Page objects would not be shared, but handle the overall layout and common parts (such as images, status or whatever).
What about the Desktop?
From my perspective the only thing missing in this story is the Desktop. Microsoft realized, perhaps a year or a bit more ago that the desktop is not going away and is important as ever, if not more. Many apps run in the desktop (vs. Store), many apps rely on mouse and keyboard for their utilization, and touch is not the answer to everything.
I see no reason why the WinRT model, including the UI and XAML does not bleed into desktop apps as well. WinRT is just COM, after all, and in fact, many types can already be used in desktop apps. For some reason, XAML based UI still can’t but I personally believe it will. .NET developers have WPF, but native desktop developers have just MFC, which is outdated and primitive by comparison to either WPF or WinRT.
Sure, there are other C++ UI non-Microsoft alternatives, such as Qt, wxWidegts and many more, but that’s not enough. Microsoft should provide its own framework, and indeed it’s already here – that’s WinRT. It just needs to be “allowed” to execute in desktop apps.
In fact, I sense that WinRT is not just about “apps”; instead, it’s the new Win32 API. Every developer that’s familiar to some extent with the Win32 API understands these words. This API is old, mostly thousands of C-style functions, is unwieldy, and difficult to learn given the alternative APIs. WinRT is for the most part well designed, object oriented, and is still native, so it has a lot going for it. It could very well be the new Windows API.
Let’s see a quick example of getting that same device list and using that in a WPF app.
WinRT metadata format is the same as .NET, so adding a reference should be possible. However, desktop projects are not configured by default to allow it. We need to edit the .csproj file to allow easy adding of the WinRT metadata information. To do that, unload the desktop project, right click the project node and select the edit option, which will open the file in the text XML editor. Add the following as another property group somewhere in the file:
Save the file and reload the project. Open the Add Reference dialog box and now notice the “Windows” node with the “Core” sub-node and the Windows WinMD file on the right:
Now that the reference is in, we can use the WinRT API. First, here’s a simple XAML for the WPF window:
Notice the async operation. If we try to compile this, it fails, telling us that the method GetAwaiter is not present on that IAsyncOperation<> that’s returned. That’s because the C# compiler has a pattern for methods that are awaitable (the “awaiter pattern”). The regular Task<T> class from .NET supports this, of course, but this is a WinRT type, not a .NET type – so there is no notion of an awaiter.
The solution is to reference another assembly, which provides that support for easier working with WinRT from .NET. We need to add a reference to the System.Runtime.WindowsRuntime.Dll assembly located at a folder like “C:Program Files (x86)Reference AssembliesMicrosoftFramework.NETCorev4.5”. This provides not just await support but also other useful stuff such as the AsTask extension method that can turn IAsyncAction/Operation into a .NET Task (which has a rich API).
Running the app we get something like this:
Clearly, there’s still a way to go, but I believe this is a good direction. What is apparent to me is that WinRT is the present and future, but the Silverlight API is just here for compatibility and maintenance. So, if you’ve been avoiding or neglecting WinRT – don’t. New apps (even if they are not planned to be multi-platform) should be based on the WinRT API and not the Silverlight one.
The Windows Runtime is truly universal, in more than one sense. I expect a common Windows Runtime core to be present in all form factors – phone, tablet, desktop and XBOX.
Updated November 7, 2014 11:28 pm