Click here to Skip to main content
13,900,534 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

6.1K views
10 bookmarked
Posted 14 Mar 2019
Licenced CPOL

UWPLib: Include XAML Controls in Plain Win32

, 21 Mar 2019
Rate this:
Please Sign up or sign in to vote.
A quick way to add UWP controls into plain Win32 apps

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, a recent Windows SDK 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. Maps, calendar, pickers, spinners, WebView, etc. are provided. We first have to initialize winrt and then WindowsXamlManager:

#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:

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:

<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:

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:

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 have a generic complex and some simple options:

1. Create UWP windows with the UWP_Custom class, then use WM_SETTEXT to load the markup. This is used by my new TurboTransfer app:

    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>();

 

 

    2. Create UWP windows using the class names like UWP_CheckBox etch and a UNIQUE title.

 

All controls send WM_NOTIFY notification messages.

struct UWPNOTIFICATION
{
	NMHDR n = { 0 };
	UWPCONTROL* s = 0;
};

struct UWPCONTROL
{
	DesktopWindowXamlSource xs;
	HWND hParent = 0;
	HWND hwnd = 0;
	HWND hwndDetailXamlIsland = 0;
	winrt::Windows::Foundation::IInspectable ins;
};

Work on this library is continuous! At the moment, there is:

  • Custom Control (use with WM_SETTEXT)
  • Rating
  • ProgressBar
  • ProgressRing
  • Hyperlink
  • TimePicker
  • ToggleSwitch
  • Calendar
  • DatePicker
  • CheckBox
  • ProgressBar

The Project

The project uses the programming aspects discussed here to create some UWP controls. Have fun with it!

 

History

  • 20/3/2019: Converted to single lib file
  • 18/3/2019: Added UWP Library
  • 14/3/2019: First Release

License

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

Share

About the Author

Michael Chourdakis
Engineer
Greece Greece
I'm working in C++, PHP , Java, Windows, iOS and Android.

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

My home page: http://www.michaelchourdakis.com

You may also be interested in...

Pro

Comments and Discussions

 
GeneralMy vote of 5 Pin
TheRaven18-Mar-19 20:44
memberTheRaven18-Mar-19 20:44 
QuestionGood Stuff Pin
TheRaven18-Mar-19 13:31
memberTheRaven18-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.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web02 | 2.8.190306.1 | Last Updated 21 Mar 2019
Article Copyright 2019 by Michael Chourdakis
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid