Click here to Skip to main content
15,881,139 members
Articles / Desktop Programming / XAML

UWPLib: Include XAML Controls in Plain Win32

Rate me:
Please Sign up or sign in to vote.
5.00/5 (16 votes)
4 Feb 2020CPOL2 min read 43.8K   33   14
A quick way to add UWP controls into plain Win32 apps for Windows 10

Introduction

Microsoft keeps inventing new frameworks but eventually it's plain old Win32 that wins. Here is a way, based on the new C++ 17/WinRT API to wrap any UWP control inside a plain C++ Win32 application.

You need VS 2017+, Windows 10 build 1903 or later, a recent Windows SDK (17763+) and link against "windowsapp.lib" (Nothing related to precompiled headers discussed in the above page is required).

UWP

UWP provides us some nice controls, found here. We first have to initialize winrt and then WindowsXamlManager:

C++
#include <winrt/base.h>
#include <atlbase.h>
#include <winrt/Windows.system.h>
#include <winrt/Windows.UI.Core.h>
#include <winrt/Windows.UI.Input.Inking.h>
#include <winrt/Windows.UI.Text.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.UI.Xaml.Hosting.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
#include <winrt/Windows.UI.Xaml.Data.h>
#include <winrt/Windows.UI.Xaml.Media.h>
#include <windows.ui.xaml.hosting.desktopwindowxamlsource.h>
#include <winrt/Windows.UI.Xaml.Markup.h>
#pragma comment(lib, "windowsapp")

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Foundation::Numerics;
using namespace Windows::UI;
using namespace Windows::UI::Composition;
using namespace Windows::UI::Core;
using namespace Windows::UI::Text;
using namespace Windows::UI::Input::Inking;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Hosting;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::Media::Core;
using namespace Windows::UI::Xaml::Markup;

winrt::init_apartment(apartment_type::single_threaded);
WindowsXamlManager windowsXamlManager = WindowsXamlManager::InitializeForCurrentThread();

The operations are provided by DesktopWindowXamlSource as an interop interface:

C++
DesktopWindowXamlSource xs;
auto interopDetail = xs.as<IDesktopWindowXamlSourceNative>();
interopDetail->AttachToWindow(hh);
interopDetail->get_WindowHandle(&s->hwndDetailXamlIsland);
                
winrt::param::hstring str(LR"(
    <Grid Name="MainGrid" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<CalendarView />
    </Grid>)");

winrt::Windows::Foundation::IInspectable ins = XamlReader::Load(str);
xs.Content(ins.as<UIElement>()); 

All this is really plain COM. as() is like the old QueryInterface in a template form. We attach the control to our own HWND and we also get an "inner" HWND, created by the framework. To create the control, we use XAML, XML or HTML like control configuration. In this case, we specify a calendar inside a grid. We need a hstring (a winrt string). We can use the winrt::param::hstring wrapper (like _bstr_t). Finally, we load the string to an IInspectable (the base interface like IUnknown) using XamlReader and finally, we load it to the DesktopWindowXamlSource.

To interact with the control, we can use the as() to get a type. For example, if I load a button:

XML
<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="BX">BBBB</Button>

We can mess with it:

C++
Button butt = ins.as<Button>();
butt.Content(box_value(L"Hi"));

I found the class from Button in MSDN so you can use any "member function".

To get events about the button, we can use WinRT event handling along with some nice lambdas:

C++
butt.Click([](const IInspectable&  sender, const RoutedEventArgs& rea)
            {
                sender.as<winrt::Windows::UI::Xaml::Controls::Button>().Content
                         (box_value(L"Clicked"));
            });        

Each "callback" takes the IInspectable reference as the first parameter. The rest of the parameters are found in the documentation, for example, the Click() takes a RouterEventArgs argument.

UWPLib

UWPLib is a project to represent UWP controls as Win32 controls with messages and notifications.

  1. Create an app with the attached manifest
  2. Call winrt::init_apartment(apartment_type::single_threaded)
  3. Call WindowsXamlManager::InitializeForCurrentThread()
  4. Call function Register()

After that, you create UWP windows with the UWP_Custom class, then use WM_SETTEXT to load the markup. This is used by my new TurboTransfer app:

XML
    auto pv = LR"(<Pivot xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
    <PivotItem Header="Items">
            <ListView Name="GridItems"/>
    </PivotItem>
    <PivotItem Header="Transfers">
            <ListView Name="TransferItems"/>
    </PivotItem>
    <PivotItem Header="Add">
        <StackPanel>
            <Button x:Name="btn1" Content="Add Files" Margin="5" Width="150" />
            <Button x:Name="btn2" Content="Add Directory" Margin="5" Width="150" />
        </StackPanel>
    </PivotItem>
    <PivotItem Header="Upload">
        <StackPanel>
        </StackPanel>
    </PivotItem>
    <PivotItem Header="Configuration">
        <StackPanel>
            <TextBox Name="portnum" Margin="5" Header="Port Number" Text="7001"/>
            <CheckBox Name="CB_RightClick" Content="Enable right click" />
            <CheckBox Name="CB_ForceOctetStream" 
             Content="Force MIME application/octet-stream" />
            <TextBox x:Name="ip" Margin="5" Header="IP or Hostname (Empty = default) " />
        </StackPanel>
    </PivotItem>
</Pivot>)";

        SetWindowText(GetDlgItem(hh, 901), pv);
        UWPLIB::UWPCONTROL* u = (UWPLIB::UWPCONTROL*)SendDlgItemMessage
                                (hh, 901, UWPM_GET_CONTROL, 0, 0);
        pivot = u->ins.as<Pivot>();

Image 1

Image 2

Image 3

Image 4

Image 5

Image 6

Image 7

Image 8

The Project

The project uses the programming aspects discussed here to create some UWP controls, displayed above. Have fun with it. I used it in my big FluentTorrent:

Image 9

In addition, there is a DLL and Win32 project that demonstrates using an UWP menu in a plain win32 app, with a function that can convert a HMENU to an UWP menu.

History

  • 4th February, 2020: More screenshots and controls, added DLL+Win32 app
  • 20th March, 2019: Converted to single lib file
  • 18th March, 2019: Added UWP library
  • 14th March, 2019: First release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Greece Greece
I'm working in C++, PHP , Java, Windows, iOS, Android and Web (HTML/Javascript/CSS).

I 've a PhD in Digital Signal Processing and Artificial Intelligence and I specialize in Pro Audio and AI applications.

My home page: https://www.turbo-play.com

Comments and Discussions

 
QuestionCould you provide a working sample of using data binding for this project? Pin
pcuzn1-Dec-20 19:29
pcuzn1-Dec-20 19:29 
AnswerRe: Could you provide a working sample of using data binding for this project? Pin
Michael Chourdakis5-Apr-21 7:51
mvaMichael Chourdakis5-Apr-21 7:51 
QuestionTo use the UWP controls, does it mean my Win32 application has to run under UWP mode aka UWP app? Pin
Shao Voon Wong6-Feb-20 17:24
mvaShao Voon Wong6-Feb-20 17:24 
AnswerRe: To use the UWP controls, does it mean my Win32 application has to run under UWP mode aka UWP app? Pin
Michael Chourdakis7-Feb-20 9:51
mvaMichael Chourdakis7-Feb-20 9:51 
QuestionPlease clearly state if this requires Win 10 v. => 1903 in the introduction to this article ... if that is the case Pin
BillWoodruff28-Mar-19 12:55
professionalBillWoodruff28-Mar-19 12:55 
AnswerRe: Please clearly state if this requires Win 10 v. => 1903 in the introduction to this article ... if that is the case Pin
Michael Chourdakis28-Mar-19 20:03
mvaMichael Chourdakis28-Mar-19 20:03 
QuestionIt doesn't work Pin
mikeduglas28-Mar-19 4:44
mikeduglas28-Mar-19 4:44 
AnswerRe: It doesn't work Pin
Michael Chourdakis28-Mar-19 7:02
mvaMichael Chourdakis28-Mar-19 7:02 
GeneralRe: It doesn't work Pin
mikeduglas28-Mar-19 11:07
mikeduglas28-Mar-19 11:07 
GeneralMy vote of 5 Pin
TheRaven18-Mar-19 20:44
TheRaven18-Mar-19 20:44 
GeneralRe: My vote of 5 Pin
.:floyd:.11-Apr-19 6:18
.:floyd:.11-Apr-19 6:18 
GeneralRe: My vote of 5 Pin
TheRaven11-Apr-19 6:35
TheRaven11-Apr-19 6:35 
GeneralRe: My vote of 5 Pin
.:floyd:.18-Apr-19 20:25
.:floyd:.18-Apr-19 20:25 
Nothing in the article is related to WPF either. WPF is inaccessible from native code, and the article's library doesn't change that. WPF's architecture[^] has an unfortunate twist to it, in that it is mostly implemented in native code (milcore), but only provides a managed public interface. To use WPF from native code you'd have to use something like C++/CLI, incurring up to four managed-unmanaged transitions on every call into the API. WPF is truly .NET-only.

The confusion is understandable, though, as Microsoft have done a very poor job at explaining their new (as of Windows 8) programming interface and UI. While it looks remarkably similar to WPF, its GUI is unrelated. I'm saying "its GUI", because it doesn't even have an official name. Initially called "Metro", later changed to "Modern UI", it appears to go by the name Fluent Design[^] today.

And then there's XAML. WPF uses XAML. The Windows Runtime uses XAML. While remarkably similar, those two kinds of XAML are different languages. Even though it doesn't look like it, they are unrelated, with different parsers and different rules.

Plus the programming languages: C++/CX (initially the only language projection for native code, ignoring the WRL[^]) borrows lots of its syntax from C++/CLI. Unlike C++/CLI, which compiles to mixed-mode assemblies (containing both managed and unmanaged code), C++/CX compiles to 100% native, unmanaged object code.

To reiterate, while it often looks like the Windows Runtime were somehow related to .NET, that is not the case. The Windows Runtime and Windows' new GUI system are (as far as I know) implemented using native, unmanaged code exclusively, with a fully native API surface based on COM. On a personal note, as a proponent of deterministic garbage collection, I very much appreciated that design decision.

.f
QuestionGood Stuff Pin
TheRaven18-Mar-19 13:31
TheRaven18-Mar-19 13:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.