Calling Windows 10 APIs From Your WPF Application

Did you know that you can call Windows 10 WinRT APIs from your WPF and WinForms apps? In this blog post I’ll show you how to display toast messages from a WPF application by using the Windows 10 ToastNotificationManager.

In the previous blog post you learned that you can host UWP Controls in WPF, WinForms, and Win32 with a technology called XAML Islands.

But sometimes you don’t want to include a UWP Control in your WPF app. Instead, you want to call a Windows 10 API. Or maybe you want to do both: Include a UWP control and call a Windows 10 API. In any case, you need to reference the Windows 10 WinRT APIs in your WPF project. Let’s see how to do this.

Reference the Windows 10 WinRT APIs in your WPF project

If you’ve tried already in the past to use Windows 10 WinRT APIs in WPF, you might have come across documentation that said that you have to reference several files …

  • … from .NET Framework – like System.Runtime.WindowsRuntime.dll
  • and from the corresponding Windows SDK, like Windows.Foundation.UniversalApiContract.winmd

This is not necessary anymore. Microsoft made this whole process much simpler.

All you need to do to use Windows 10 WinRT APIs in your WPF project is to install the NuGet package Microsoft.Windows.SDK.Contracts.

That package is the the so-called Windows 10 WinRT API Pack . Let me quote the package description:

The Windows 10 WinRT API Pack enables you to add the latest Windows Runtime APIs support to your .NET Framework 4.5+ and .NET Core 3.0+ libraries and apps. This package includes all the supported Windows Runtime APIs up to Windows 10 version 1903

Wow, amazing. Just adding a single NuGet package and it works. Let’s try it.

Add the NuGet package

Let’s create a brand new WPF application and let’s install the NuGet package Microsoft.Windows.SDK.Contracts. It’s still in preview, so ensure to check the “Include prerelease” option like below:

When you look at the dependencies of the Microsoft.Windows.SDK.Contracts package, you can see that it brings in the other stuff that you had to add manually before this package existed, like the System.Runtime.WindowsRuntime.dll that I mentioned above:

Switch from packages.config to PackageReference

When you install the Microsoft.Windows.SDK.Contracts NuGet package in a .NET Core WPF app, you’re fine. But in a .NET Framework WPF app, you have to switch the NuGet package management format from packages.config to PackageReference, else it won’t work. The description of the Microsoft.Windows.SDK.Contracts package contains also this statement:

Requires default package management format set to PackageReference, and NuGet 4.0 or higher.

If you have a .NET Framework WPF app that still uses packages.config, just right-click on “References” in the Solution Explorer. In the context menu you find a menu item to“Migrate packages.config to PackageReference”, as you can see in the screenshot below.

After you clicked that menu item, the packages.config file will be deleted and the references to the NuGet packages are stored in the .csproj file. Great, now installing the NuGet package Microsoft.Windows.SDK.Contracts works fine, and you should see that package including its dependencies in the references like below:

Now, no matter if you’re using .NET Core 3 or .NET Framework, you’re ready to display a toast message from your WPF app. Let’s write the code.

Display a Toast Message

Let’s add a simple Button in the MainWindow.xaml file:

<Button Content="Show Toast"
        Padding="10"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Click="ButtonShowToast_Click"/>

In the codebehind file (MainWindow.xaml.cs), let’s add this ButtonShowToast_Click event handler:

private void ButtonShowToast_Click(object sender, RoutedEventArgs e)
{
    string title = "The current time is";
    string timeString = $"{DateTime.Now:HH:mm:ss}";
    string thomasImage = "https://www.thomasclaudiushuber.com/thomas.jpg";

    string toastXmlString =
    $@"<toast><visual>
            <binding template='ToastGeneric'>
            <text>{title}</text>
            <text>{timeString}</text>
            <image src='{thomasImage}'/>
            </binding>
        </visual></toast>";

    var xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(toastXmlString);

    var toastNotification = new ToastNotification(xmlDoc);

    var toastNotifier = ToastNotificationManager.CreateToastNotifier();
    toastNotifier.Show(toastNotification);
}

As you can see in the code snippet above, the ButtonShowToast_Click event handler initializes a toastXmlString variable with a valid toast template. Then it loads that string into an XmlDocument (the WinRT one from the namespace Windows.Data.Xml.Dom). Next a ToastNotification object is created with that XmlDocument.

Then a ToastNotifier instance is created by calling the static CreateToastNotifier method of the ToastNotificationManager class. Finally, the Show method is called on that ToastNotifier instance to show the toast notification.

You can just copy paste that code snippet into your app. When you place the cursor on the ToastNotificationManager class, you can press “CTRL+.” and Visual Studio will suggest you to add a using directive for the WinRT namespace Windows.UI.Notifications, as you can see below:

For the XmlDocument class, you need to add a using directive for Windows.Data.Xml.Dom (and not for the .NET variant that lives in System.Xml). The using directives of the MainWindow.xaml.cs file look like below. As you can see, a nice mixture between .NET and WinRT namespaces:

using System;
using System.Windows;
using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;

Now let’s run the application and let’s push the “Show Toast”-Button in the MainWindow.

Let’s click this nice Button.

Oh no, we get an Exception at the line where the CreateToastNotifier method is called. It says “Element not found”. What does this mean?

The problem here is that your WPF application doesn’t have a package identity. A UWP app has a package identity, and it is installed in Windows 10 with that identity. But a WPF app doesn’t have such an identity, so let’s create one.

Create an Identity for Your WPF App

You can easily create an identity for your WPF app by packaging it as MSIX. To do this, just add a Windows Application Packaging Project to your solution. In the previous blog post we used the Windows Application Packaging Project already to target a specific windows version with WPF. This time we need that Packaging Project to get an identity, and in addition, that Packaging Project let’s us target a specific windows version. So let’s add it. Let’s right-click the solution and let’s add a new Windows Application Packaging Project.

My WPF project is called “WpfCallingWin10Api.Framework”. Let’s call the packaging project “WpfCallingWin10Api.Framework.App. The creation shows the dialog below, where I select 18362 as a minimum version:

After that packaging project was added to the solution, you have to right-click the “Applications” node in Solution Explorer to reference the WPF project. Finally, set the packaging project as a startup project. The final result should look like this:

Now with the Packaging Project set as a start up project, let’s try the app again, as we have now an identity.

Let’s Toast It!

Let’s run the application and let’s click the “Show Toast”-Button.

*drumroll*

And voilà! In the bottom right corner of my screen, above the icon tray, appears a nice toast that displays the current time like we’ve specified it with the toast template, including the thomas.jpg that we’ve also specified in that toast template.

The toast is shown above the icon tray in the bottom right corner of my screen.

Fantastic, it just works!

And all that was necessary were these 2 steps:

Go and Grab the Code!

I have created this sample for you with .NET Framework and .NET Core. Grab the .NET Framework and .NET Core solutions from this Git repository to try it on your own, and don’t forget to set the packaging project as a startup project:

https://github.com/thomasclaudiushuber/Wpf-Calling-Win10-WinRT-Toast-Api

Can we talk again about XAML Islands?

Sure. What’s the question?

Hey Thomas, at the beginning of this blog post you wrote this:
“… sometimes you don’t want to include a UWP Control in your WPF app. Instead, you want to call a Windows 10 API.  Or maybe you want to do both: Include a UWP control and call a Windows 10 API. In any case, you need to reference the Windows 10 WinRT APIs in your WPF project. Let’s see how to do this. “

But in your previous blog post where you used XAML Islands to display the UWP MapControl in a WPF app, you neither referenced the UWP .winmd file, nor did you install the Microsoft.Windows.SDK.Contracts package. But in this blog post you’re saying “In any case, you need to reference the Windows 10 WinRT APIs in your WPF project.”

In your previous blog post you didn’t add a reference, why did it work there?

Ok, let’s answer this.

In the previous blog post we installed the NuGet package Microsoft.Toolkit.Wpf.UI.Controls. That package contains wrapper controls for WPF that make the usage of UWP controls like MapControl and InkCanvas straight forward. Or in other words: That package makes the usage of XAML Islands straight forward.

We installed the preview4 version of that NuGet package. And now go and click here on this NuGet link to look at the Dependencies of the preview4 version of that package, the latest available version.

Instead of clicking on the link above, you can also manage NuGet packages in Visual Studio, search for that preview version of the Microsoft.Toolkit.Wpf.UI.Controls package, click on it and look at its dependencies. The dependencies of that Microsoft.Toolkit.Wpf.UI.Controls package look like below. Do you notice something?

Exactly, the package Microsoft.Toolkit.Wpf.UI.Controls has a dependency on Microsoft.Windows.SDK.Contracts.

That means in other words: If you’re using that XAML Islands NuGet package, you automatically get access to the Windows 10 WinRT APIs, as Microsoft.Windows.SDK.Contracts is added too, as it is a dependency.

Isn’t this awesome? I love it!

Happy coding,
Thomas

Share this post

Comments (3)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

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