WinUI 3 is a modern UI framework for Windows apps. WinUI 3 can be used with C++/WinRT, a C++17 language projection that provides access to the Windows Runtime (WinRT) APIs. Usually, XAML is used to define the UI. Therefore, XAML and C++ are combined to create applications. However, there are those who want to create simple applications in C++ without XAML. This repository provides a step-by-step guide to creating WinUI3 apps in C++ without XAML.
Creating WinUI 3 apps without XAML is an unsupported scenario and some features are limited; it is strongly recommended to use XAML when creating Winui 3 apps. If you still want to create a winui 3 app without XAML, proceed to the next section.
You can download a sample zip file from winui3-without-xaml repository.
Issues is closed. When you have a problem, please comment in windows-ui-xaml Discussions to show needs of WinUI 3 without XAML to WinUI 3 team.
Normally, when creating a WinUI 3 application, start with a WinUI 3 project template. Here, we start with an empty project.
-
Create an empty project.
-
Set project properties for unpackage WinUI 3 app
-
Open
.vcxproj
file in any text editor. Or, right-click the project on Solution Explorer of Visual Studio and click "Unload Project". -
Add the following properties inside the PropertyGroup element.
<PropertyGroup Label="Globals"> <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained> <WindowsPackageType>None</WindowsPackageType> <AppxPackage>false</AppxPackage> ... </PropertyGroup>
If you have unloaded the project, right-click on the project and click "Reload Project". Usually, WinUI 3 projects are packaged with MSIX. If you do not want to package it, set the
WindowsPackageType
option and theAppxPackage
option. In addition, if you want to run the app without installing the Windows App SDK, set theWindowsAppSDKSelfContained
option to make the app self-contained, including dependencies. -
-
Install the Microsoft.Windows.CppWinRT NuGet package to enable support for C++/WinRT and Microsoft.WindowsAppSDK for WinUI 3.
- Right-click your project in Solution Explorer and choose Manage NuGet Packages.
- Select the Browse tab, search for the Microsoft.Windows.CppWinRT and Microsoft.WindowsAppSDK package, and install the latest version of these packages.
-
Set
/SUBSYSTEM
liker option for the GUI application.- Right-click on the project and click "Properties".
- Set
Windows
in "Linker->System->SubSystem".
When the Windows operating system (OS) runs an app, the OS begins execution in the app's entry point. That entry point takes the form of wWinMain
function instead of main
. wWinMain
function is declared in Windows.h
. Include Windows.h
and define wWinMain
function in your code.
#include <Windows.h>
int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
return 0;
}
C++/WinRT header files for access to Windows SDK APIs and Windows App SDK APIs are generated in your project folder. Include some required header files.
#include <Windows.h>
#undef GetCurrentTime
#include <winrt/Microsoft.UI.Xaml.h>
using namespace winrt;
using namespace Microsoft::UI::Xaml;
#undef GetCurrentTime
is needed to avoid compile error on duplicated macros. Microsoft.UI.Xaml.h
header file provides general XAML APIs including Application
class and Window
class. APIs provided by Microsoft.UI.Xaml.h
has winrt::Microsoft::UI::Xaml
namespace. In general, Windows Runtime APIs have winrt
namespace, Windows SDK APIs have Windows
namespace, and Windows App SDk APIs have Microsoft
namespace. using namespace
is useful for simplifying code.
Application
class provides an entry point for the WinUI 3 application. OnLaunched
method is invoked when the application is launched. Inherit Application
class and override this method to perform application initialization and to create a new window.
class App : public ApplicationT<App>
{
public:
void OnLaunched(LaunchActivatedEventArgs const&)
{
window = Window();
window.Activate();
}
private:
Window window{ nullptr };
};
This inheritance pattern is called F-bound Polymorphism pattern or Curiously Recurring Template Pattern. In OnLaunched
method, create a new windows and activate it.
In wWinMain
function, start application by instantiating App
derived from application
.
int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
init_apartment();
Application::Start([](auto&&) {make<App>(); });
return 0;
}
init_apartment
initializes the thread in the Windows Runtime. Application::Start
provides the entry point and requests initialization of the application.
The lambda expression is passed that make App
to be invoked in the initialization sequence.
Here is the complete code for the program:
#include <Windows.h>
#undef GetCurrentTime
#include <winrt/Microsoft.UI.Xaml.h>
using namespace winrt;
using namespace Microsoft::UI::Xaml;
class App : public ApplicationT<App>
{
public:
void OnLaunched(LaunchActivatedEventArgs const&)
{
window = Window();
window.Activate();
}
private:
Window window{ nullptr };
};
int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
init_apartment();
Application::Start([](auto&&) {make<App>(); });
return 0;
}
Build and run the app. You can see a blank window.
Add stack panel and button.
...
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Microsoft.UI.Xaml.Controls.h>
#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h>
...
using namespace Microsoft::UI::Xaml::Controls;
...
void OnLaunched(LaunchActivatedEventArgs const&)
{
window = Window();
StackPanel stackPanel;
stackPanel.HorizontalAlignment(HorizontalAlignment::Center);
stackPanel.VerticalAlignment(VerticalAlignment::Center);
Button button;
button.Content(box_value(L"WinUI 3 Without XAML!"));
window.Content(stackPanel);
stackPanel.Children().Append(button);
window.Activate();
}
...
StackPanel
arranges child elements into a single line that can be oriented horizontally or vertically. Horizontal alignment and vertical alignment are set by properties (Accessors and Mutators in C++). Button
represents a templated button control that interprets a Click user interaction. Content
can be passed an instance of any runtime class. But you can't directly pass to such a function a scalar value (such as a numeric or text value), nor an array. Instead, a scalar or array value needs to be wrapped inside a reference class object. That wrapping process is known as boxing the value. C++/WinRT provides the winrt::box_value
function, which takes a scalar or array value, and returns the value boxed into a reference class object. After creating conrtols, connect child elements to the parent controls.
You can add WinUI 3 controls, but it looks UWP controls. You need to add WinUI 3 themes. WinUI 3 themes are written in XAML, so the App
requires XAML integration. What you need to do is implementing a IXamlMetadataProvider
interface. This interface implements XAML type resolution and provides the mapping between types used in XAML markup and the corresponding classes implemented in an application or component. The interface is passed as a template parameter.
...
#include <winrt/Windows.UI.Xaml.Interop.h>
...
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
#include <winrt/Microsoft.UI.Xaml.Markup.h>
...
using namespace Microsoft::UI::Xaml::XamlTypeInfo;
using namespace Microsoft::UI::Xaml::Markup;
using namespace Windows::UI::Xaml::Interop;
class App : public ApplicationT<App, IXamlMetadataProvider>
{
public:
void OnLaunched(LaunchActivatedEventArgs const&)
{
Resources().MergedDictionaries().Append(XamlControlsResources());
...
}
IXamlType GetXamlType(TypeName const& type)
{
return provider.GetXamlType(type);
}
IXamlType GetXamlType(hstring const& fullname)
{
return provider.GetXamlType(fullname);
}
com_array<XmlnsDefinition> GetXmlnsDefinitions()
{
return provider.GetXmlnsDefinitions();
}
private:
...
XamlControlsXamlMetaDataProvider provider;
};
IXamlMetadataProvider
interface provides XAML type information with following three methods;
GetXamlType(TypeName)
GetXamlType(String)
GetXmlnsDefinitions()
Implement these methods by passing arguments to XamlControlsXamlMetaDataProvider
class. XamlControlsXamlMetaDataProvider
class provides XAML type information for WinUI 3. Finally, add WinUI 3 theme to the applicarion by appending WinUI 3 XAML resource to Resources
property. XamlControlsResources()
returns default styles for the controls in WinUI 3.
Here is the complete code for the program:
#include <Windows.h>
#undef GetCurrentTime
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.UI.Xaml.Interop.h>
#include <winrt/Microsoft.UI.Xaml.Controls.h>
#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h>
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
#include <winrt/Microsoft.UI.Xaml.Markup.h>
using namespace winrt;
using namespace Microsoft::UI::Xaml;
using namespace Microsoft::UI::Xaml::Controls;
using namespace Microsoft::UI::Xaml::XamlTypeInfo;
using namespace Microsoft::UI::Xaml::Markup;
using namespace Windows::UI::Xaml::Interop;
class App : public ApplicationT<App, IXamlMetadataProvider>
{
public:
void OnLaunched(LaunchActivatedEventArgs const&)
{
Resources().MergedDictionaries().Append(XamlControlsResources());
window = Window();
StackPanel stackPanel;
stackPanel.HorizontalAlignment(HorizontalAlignment::Center);
stackPanel.VerticalAlignment(VerticalAlignment::Center);
Button button;
button.Content(box_value(L"WinUI 3 Without XAML!"));
window.Content(stackPanel);
stackPanel.Children().Append(button);
window.Activate();
}
IXamlType GetXamlType(TypeName const& type)
{
return provider.GetXamlType(type);
}
IXamlType GetXamlType(hstring const& fullname)
{
return provider.GetXamlType(fullname);
}
com_array<XmlnsDefinition> GetXmlnsDefinitions()
{
return provider.GetXmlnsDefinitions();
}
private:
Window window{ nullptr };
XamlControlsXamlMetaDataProvider provider;
};
int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
init_apartment();
Application::Start([](auto&&) {make<App>(); });
return 0;
}
Build and run the app. You can see a beautiful WinUI 3 button.
Now you can build WinUI 3 app in C++ without XAML. Here, I will show you some examples to help you to use WinUI 3 controls.
void OnLaunched(LaunchActivatedEventArgs const&)
{
Resources().MergedDictionaries().Append(XamlControlsResources());
window = Window();
StackPanel stackPanel;
stackPanel.HorizontalAlignment(HorizontalAlignment::Center);
stackPanel.VerticalAlignment(VerticalAlignment::Center);
TextBlock title;
title.Style(Application::Current().Resources().Lookup(box_value(L"TitleTextBlockStyle")).as<Style>());
title.Text(L"WinUI 3 in C++ Without XAML!");
title.HorizontalAlignment(HorizontalAlignment::Center);
HyperlinkButton project;
project.Content(box_value(L"Github Project Repository"));
project.NavigateUri(Uri(L"https://github.com/sotanakamura/winui3-without-xaml"));
project.HorizontalAlignment(HorizontalAlignment::Center);
Button button;
button.Content(box_value(L"Click"));
button.Click([&](IInspectable const &sender, RoutedEventArgs) { sender.as<Button>().Content(box_value(L"Thank You!")); });
button.HorizontalAlignment(HorizontalAlignment::Center);
button.Margin(ThicknessHelper::FromUniformLength(20));
window.Content(stackPanel);
stackPanel.Children().Append(title);
stackPanel.Children().Append(project);
stackPanel.Children().Append(button);
window.Activate();
}
C++/WinRT is header-only library and takes a lot of time to compile. You can reduce the time by precompiling. Any stable header files, for example Standard Library headers and C++/WinRT headers, can be precompiled cause it is not often changed.
- Add pch.h and pch.cpp files with following code.
- Right-click the project on Solution Explorer of Visual Studio and click "Properties".
- Set
Use
in "C/C++->Precompiled Headers->Precompiled Header". - Set
pch.h
in "C/C++->Precompiled Headers->Precompiled Header File". - Right-click pch.cpp and click "Properties".
- Set
Create
in "C/C++->Precompiled Headers->Precompiled Header". - Move including header files to
pch.h
and includepch.h
//pch.h
#include <Windows.h>
#undef GetCurrentTime
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.UI.Xaml.Interop.h>
#include <winrt/Microsoft.UI.Xaml.Controls.h>
#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h>
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
#include <winrt/Microsoft.UI.Xaml.Markup.h>
//pch.cpp
#include "pch.h"
//main.cpp
#include "pch.h"
using naespace ...
This method is an unsupported scenario; it is strongly recommended to use XAML when creating Winui 3 apps. Please refer to the official documents to get started with Windows App SDK. If you are not familiar with C++/WinRT and XAML, I highly recommend you to learn them before developing. C++ is not primary language for WinUI 3 apps, so this senario is not documented well. You need to refer to API reference many times. Windows App SDK APIs are mainly for WinUI 3. You can access modern Windows features through Windows SDK APIs.
- Windows App SDK: The Windows UI Library (WinUI) 3 is the latest and recommended user interface (UI) framework for Windows desktop apps. By incorporating the Fluent Design System into all experiences, controls, and styles, WinUI provides consistent, intuitive, and accessible experiences using the latest UI patterns. WinUI 3 is available as part of the Windows App SDK.
- C++/WinRT: C++/WinRT is an entirely standard modern C++17 language projection for Windows Runtime (WinRT) APIs, implemented as a header-file-based library, and designed to provide you with first-class access to the modern Windows API.
- XAML: Extensible Application Markup Language (XAML) is a declarative language. Specifically, XAML can initialize objects and set properties of objects using a language structure that shows hierarchical relationships between multiple objects and a backing type convention that supports extension of types. You can create visible UI elements in the declarative XAML markup.
- Windows App SDK APIs Reference: APIs for WinUI 3.
- Windows SDK APIs Reference: APIs for modern Windows features.
Some text in this document is cited from Microsoft documentation. https://learn.microsoft.com