Using .NET Core Libraries from UWP

Not all your users upgraded to Windows 10? That’s why you can’t write Windows 10 apps using the Universal Windows Platform (UWP). One way to use UWP features from WPF applications is to use Project Centennial, the Desktop App Converter. With this you can use UWP features from your existing WPF app, offering these features to your users already running Windows 10, while the main part of your application is still built with older technologies to serve clients running Windows 7.

This is one way to use new features offered by UWP. While this route is perfect with existing applications, I wouldn’t take this route creating new applications – even if you still need to support Windows 7. My preferred way to support old clients is by using the MVVM pattern, and sharing a lot of code using portable libraries (or .NET Core libraries) that are used both with WPF, UWP, and Xamarin apps. You need to create the user interface separately, but you can share a lot of code.

This article shows using .NET Core 1.0 libraries and packages for implementing the MVVM pattern with UWP. It doesn’t give you an introduction to the MVVM pattern.

MVVM

Create a .NET Core 1.0 Library

The sample application is very simple – just a text is shown that is bound to a property in the view-model, and a command can be activated that opens a message box. For this, first a .NET Core Class Library (RC2) named SharedLibrary is created. This library will contain a simple view-model for the main page offering a Tile property, and a command that can be bound.

Using a MVVM Framework

To get the core features needed by the MVVM pattern, a few classes are required. This time I’m not doing these classes on my own – instead I’m using the Prism.Core NuGet package.

You can use any MVVM framework that supports all the technologies you would like to target, e.g. MVVM Light.

Creating a .NET Core 1.0 RC2 library with Visual Studio 2015, this project.json file gets generated:

{
  "version": "1.0.0-*",
  "dependencies": {
    "NETStandard.Library": "1.5.0-rc2-24027",
  },

  "frameworks": {
    "netstandard1.5": {
      "imports": "dnxcore50"
    }
  }
}

Adding the Prism.Core NuGet package with version 6.1.0 results in the error that this package is not compatible with netstandard1.5. This can be solved by adding to support one of the technologies that is supported by Prism.Core.

The dnxcore50 framework listed within the frameworks section comes from .NET Core RC1 when DNX was the environment to create .NET Core applications. dnxcore50 was used instead of the now used netstandard1.5 for .NET Core applications. Using the imports keyword allows to define other target frameworks if the netstandard1.5 framework is not supported by a library that should be used. Because many packages still list to support dnxcore50, this name is added by default to the imports. Checking the Package Manager output in the Output window within Visual Studio, you can see the frameworks supported by Prism.Core. Changing the frameworks section to include this target framework, allows adding Prism.Core 6.1.0 successfully:

  "frameworks": {
    "netstandard1.5": {
      "imports": 
      [ 
        "dnxcore50", 
        "portable-monoandroid10+monotouch10+net45+win+win81+wp8+wpa81+xamarinios10" 
      ]
    }
  }

You can use the imports section to specify other frameworks supported by packages you need. You can read more about this in the NuGet documentation

Creating a Service Contract

From a shared library you can’t use features offered by specific technologies directly. For example, you can’t show a MessageBox as this is only offered by WPF and not UWP. Similar, you can’t show a MessageDialog as this is not offered by WPF. However, you can specify a contract where a specific implementation will be added using dependency injection.

The interface to show a message is specified with the IMessageService type:

using System.Threading.Tasks;

namespace SharedLibrary.Services
{
  public interface IMessageService
  {
    Task MessageAsync(string message);
  }
}

Creating the View-Model

The implementation of the view-model receives IMessageService using dependency injection. The Title property implements change notification using the base class BindableBase from Prism.Core. The command type DelegateCommand is defined in the namespace Prism.Commands, as well coming from the package Prism.Core. The Action method that is invoked when he command is invoked calls the method MessageAsync

using Prism.Commands;
using Prism.Mvvm;
using SharedLibrary.Services;
using System;

namespace SharedLibrary.ViewModels
{
  public class MainViewModel : BindableBase
  {
    private readonly IMessageService _messageService;

    public MainViewModel(IMessageService messageService)
    {
      if (messageService == null) throw new ArgumentNullException(nameof(messageService));
     _messageService = messageService;
      ActionCommand = new DelegateCommand(Action);
    }

    private string _title = "Sample";
    public string Title
    {
      get { return _title; }
      set { SetProperty(ref _title, value); }
    }

    public DelegateCommand ActionCommand { get; private set; }

    public async void Action()
    {
      // TODO: implement exception handling using another local service
      await _messageService.MessageAsync("Hello from the view-model");
    }
  }
}

Using .NET Core Libraries from UWP

Creating a UWP application, the shared library project can be used. However, there are a few issues:

  • References to .NET Core projects in the same solution are not possible with Preview 1 of the tools. This should work with Preview 2. Instead, you can click on Browse and reference the DLL directly – or create a NuGet package and use this.
  • UWP with Build 10240 does not support netstandard1.5. You can either change the targeting of the UWP app in the project properties to build 14295, or change the netstandard1.5 to netstandard1.3.

The version number of netstandard specifies a list of APIs that are available (which .NET Core packages are used), as well as the platforms supported. For example, netstandard1.5 supports .NET 4.6.2, while netstandard1.3 supports .NET 4.6. You can use .NET 4.6.2 with netstandard1.3 libraries as well, just not all of the .NET 4.6.2 APIs are available for the library. A great table describes the netstandard versions with the platforms supported

Implementing the Service Contract

With the UWP application, the service interface specified with the shared library is implemented in the MessageService class:

using SharedLibrary.Services;
using System;
using System.Threading.Tasks;
using Windows.UI.Popups;

namespace UWPClient.LocalServices
{
  class MessageService : IMessageService
  {
    public async Task MessageAsync(string message)
    {
      await new MessageDialog(message).ShowAsync();
    }
  }
}

Using a Dependency Injection Framework

To inject the MessageSevice class with the view-model, I’m using the dependency injection framework Microsoft.Extensions.DependencyInjection. This framework is part of .NET Core.

Microsoft.Extensions.DependencyInjection is a dependency injection framework that offers core features which are great for many applications. Several other frameworks give you more features. Microsoft.Extensions.DependencyInjection (like many other features from .NET Core) also allows using other frameworks by using adapters.

The types that should be resolved via dependency injection are defined in the RegisterServices method. This method is invoked on application-startup.

private void RegisterServices()
{
  var services = new ServiceCollection();
  services.AddSingleton<IMessageService, MessageService>();
  services.AddTransient<MainViewModel>();
  Container = services.BuildServiceProvider();
}

public IServiceProvider Container { get; private set; }

Within the code-behind of the main page, the MainViewModel is retrieved using the dependency injection container.

public MainPage()
{
  this.InitializeComponent();
  ViewModel = (Application.Current as App).Container.GetService<MainViewModel>();
}

public MainViewModel ViewModel { get; }

To use the generic version of the GetService method, you need to open the namespace Compiled data binding not only gives you compiler error if bound properties are not available – using this binding syntax also gives performance advantages.

Running the application, you can see the value of the Title property from the view-model class, and the command of the view-model in turn invokes the MessageDialog class that is implemented directly in the UWP application.

This article gives an introduction to the Microsoft.Extensions.DependencyInjection framework: Dependency Injection with .NET Core

Summary

With this implementation of the MVVM pattern you’ve seen a .NET Core library that has been used by an UWP application, and the .NET Core package System.Extensions.DependencyInjection as a dependency injection container used with UWP.

There’s a lot more about this and other scenarios in my book Professional C# 6 and .NET Core 1.0, and in my MVVM workshop.

Using .NET Core RC2 there are some issues using custom .NET Core libraries from non .NET Core projects. It can be done as you’ve seen, but you can also use – in the mean time before preview 2 is released – portable libraries instead.

In a future blog post I’m adding a Xamarin and probably a WPF application to use the same shared library with the view-model implementation.

Advertisement

4 thoughts on “Using .NET Core Libraries from UWP

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.