Les lecteurs peuvent mettre à jour Visual Studio 2022 vers la version 17.9.1 ou supérieure. Le projet WinUI3 C++ créé avec le modèle par défaut contient un bouton ; en cliquant dessus, on peut exécuter plusieurs traitements en arrière-plan.
Créons un nouveau projet


Au cours précédent, nous avons détaillé chaque fichier du projet et leurs fonctions. Dans cette leçon, nous allons créer une nouvelle fenêtre vide, ajouter un bouton sur la fenêtre et gérer l’action du clic sur ce bouton.
Comme nous l’avons vu précédemment, notre classe de point d’entrée est App ici.
void App::OnLaunched([[maybe_unused]] LaunchActivatedEventArgs const& e)
{
window = make<MainWindow>();
window.Activate();
}Langage du code : C++ (cpp)
C’est à cet endroit que la fenêtre principale est instanciée ; c’est la fenêtre qui s’affiche au lancement du programme. Voici notre fenêtre principale, analysons-la : une fenêtre est composée de quatre fichiers au total.

Fenêtre
MainWindow.xaml (fichier de description de l’interface)
C’est un langage de balisage XML pur dédié à la mise en page de l’interface utilisateur. Il ne gère que l’affichage visuel de l’interface et ne contient aucune logique métier.
On y écrit des conteneurs de mise en page et divers contrôles de fenêtre tels que Button, TextBox, etc. Ce format de description d’interface est utilisé depuis WPF ; il s’agit fondamentalement d’un fichier XML. Il doit être associé à des fichiers .h et .cpp de même nom pour traiter les événements en arrière-plan.
Dans l’exemple officiel par défaut, le fichier XAML est vide et ne contient aucun contrôle.
<?xml version="1.0" encoding="utf-8"?>
<Window
x:Class="WinuiCppDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinuiCppDemo"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="WinuiCppDemo">
<Grid>
</Grid>
</Window>Langage du code : YAML (yaml)
MainWindow.idl
Fichier de définition d’interface, élément central propre à C++/WinRT
IDL = Interface Definition Language (langage de définition d’interface). Il définit les API, propriétés et événements exposés par la fenêtre et constitue le pont d’interopérabilité multi-langages de C++/WinRT.
- Définit les membres publics de la classe fenêtre : propriétés personnalisées, événements personnalisés et méthodes appelables
- Le compilateur génère automatiquement le code d’interface ABI standard WinRT à partir du fichier IDL, afin que C++, C#, JS puissent appeler cette fenêtre
- Les liaisons de données et contrôles personnalisés de l’interface XAML ne fonctionnent que si les propriétés sont déclarées dans l’IDL
Pourquoi le fichier .idl est indispensable avec WinUI3 C++/WinRT ?
Car WinRT est une norme ABI binaire inter-langages
Windows Runtime (WinRT) n’est pas simplement une bibliothèque C++ : c’est une spécification d’interface binaire universelle compatible avec plusieurs langages. C++, C#, Python, JS peuvent appeler le même composant WinRT grâce à une description d’interface unifiée ; les seuls fichiers d’en-tête C++ ne permettent pas cette interopérabilité.
Les en-têtes C++ ne sont interprétés que par le compilateur C++ et ne sont pas compréhensibles par C#/JS. L’IDL est quant à lui un langage d’interface neutre que le compilateur convertit en code de liaison ABI lisible par tous les langages.
Voici le contenu du fichier IDL de notre exemple
namespace WinuiCppDemo
{
[default_interface]
runtimeclass MainWindow : Microsoft.UI.Xaml.Window
{
MainWindow();
Int32 MyProperty;
}
}
Nous l’avons vu, l’IDL est un langage de description. Dans ce fichier IDL, on trouve d’abord l’espace de noms namespace WinuiCppDemo, puis l’attribut [default_interface]
Cet attribut attribue une interface principale par défaut au runtimeclass ; c’est un élément obligatoire pour concevoir des fenêtres ou contrôles personnalisés en C++/WinRT
Le compilateur MIDL génère automatiquement une interface virtuelle pure IMainWindow regroupant tous les membres de la classe (constructeur, propriétés, méthodes)
[default_interface] indique que cette IMainWindow est l’interface ABI principale exposée par la classe
La ligne MainWindow(); entre accolades est la déclaration du constructeur sans paramètre, qu’il faut écrire explicitement. Le moteur XAML invoque ce constructeur par défaut lors de l’instanciation de la fenêtre
Cela correspond à l’implémentation du constructeur dans le fichier .xaml.h.
Int32 MyProperty;
Déclare une propriété de dépendance WinRT (propriété liaisonsable) ; c’est là que l’on voit tout l’intérêt de l’IDL :
Int32 : type de base standard WinRT (correspond au type C++ int32_t) ;
MyProperty : génère automatiquement un ensemble complet de mécanismes de propriété :
- Getter :
int32_t MyProperty(); - Setter :
void MyProperty(int32_t value); - Événement de modification de propriété :
MyPropertyChanged(déclenché automatiquement quand la propriété change, permet àx:BindXAML de rafraîchir l’interface)
Seuls les membres déclarés dans l’IDL peuvent être liés dans XAML avec {x:Bind MyProperty} ; si vous ne déclarez qu’une variable membre ordinaire dans le fichier .h, XAML ne la détectera pas et la liaison produira une erreur.
Un clic droit sur le fichier IDL permet de le compiler séparément, c’est l’outil midl.exe qui s’en charge. Il génère à la fois un fichier de métadonnées destiné à C# et des fichiers d’en-tête d’interface pour C++.
En résumé, l’IDL sert à générer des interfaces pour permettre les appels inter-langages et fournir les métadonnées nécessaires aux liaisons de données XAML. Pourquoi WinUI 3 en C# n’a pas besoin de fichier IDL ? Car C# intègre nativement la réflexion et les métadonnées : c’est un langage managé. La version C++ de WinRT ne dispose pas de mécanismes natifs de réflexion ni de métadonnées.
Si vous ne déclarez une variable membre que dans le fichier .h, le moteur XAML ne la verra absolument pas ;
Ce n’est qu’en déclarant Int32 MyProperty; dans l’IDL que le compilateur génère :
- Interface Get/Set pour la propriété
- Événement de notification de modification de propriété
MyPropertyChanged - Métadonnées permettant la validation des liaisons statiques XAML
Même si votre projet n’utilise exclusivement que du C++ sans aucune interaction avec C#/JS ou d’autres langages, l’IDL reste obligatoire dès que vous utilisez XAML
WinRT a été conçu autour d’une ABI unifiée afin que C++ / C# / JS / Python puissent appeler mutuellement des composants :
L’IDL est un langage de description neutre indépendant de tout langage de programmation. Les compilateurs de chaque langage lisent les métadonnées .winmd générées par l’IDL pour reconnaître vos classes, propriétés et méthodes ;
Faites un clic droit sur le fichier IDL pour le compiler

L’intérêt principal de l’IDL est de fournir à XAML les métadonnées requises pour reconnaître les liaisons UI, tout en gérant par la même occasion l’interopérabilité multi-langages.
La compilation de l’IDL génère des fichiers xxxx.g.h et xxxx.g.cpp qui sont stockés dans le dossier Generated Files du répertoire du projet

Generated Files est le répertoire dédié aux codes générés automatiquement
- Tous les fichiers générés automatiquement par midl.exe, cppwinrt.exe et le compilateur XAML lors de la compilation du projet.
- Ne modifiez manuellement aucun fichier de ce dossier : ils seront entièrement écrasés à la prochaine compilation
- La configuration du projet VS ajoute par défaut ce répertoire au chemin d’inclusion des en-têtes, c’est pourquoi #include « MainWindow.g.h » permet de retrouver le fichier
MainWindow.xaml.h
Fichier d’en-tête C++ standard qui contient les déclarations de la classe MainWindow.
Inclut automatiquement les interfaces de base générées par l’IDL ;
Les champs de classe, variables privées et déclarations anticipées y sont écrits, comme dans n’importe quel fichier .h de syntaxe C++ classique.
Il liste les variables et fonctions sans en écrire l’implémentation ; d’autres classes peuvent y accéder via une directive include.
// Protection d'en-tête : empêche l'inclusion multiple du fichier et évite les erreurs de redéfinition à la compilation
#pragma once
// Inclusion du fichier d'en-tête de liaison généré automatiquement par la compilation de MainWindow.idl
// .g.h contient l'interface IMainWindow issue de l'IDL, la classe de base template MainWindowT<>, ainsi que les définitions liées aux métadonnées WinRT
// Les propriétés MyProperty et interfaces de constructeur déclarées dans l'IDL sont toutes définies ici
#include "MainWindow.g.h"
// 【Espace de noms d'implémentation】Contient la classe d'implémentation de la logique métier de la fenêtre, où nous implémentons nos propres propriétés et fonctions
namespace winrt::WinuiCppDemo::implementation
{
// MainWindowT<MainWindow> est la classe de base CRTP fournie par le fichier .g.h
// 1. Hérite automatiquement de l'interface IMainWindow définie dans l'IDL et impose l'implémentation de tous les membres déclarés dans l'IDL
// 2. Encapsule la logique basique des fenêtres WinUI, l'initialisation des composants XAML et la transmission des interfaces ABI
struct MainWindow : MainWindowT<MainWindow>
{
// Constructeur par défaut de la fenêtre, correspond à MainWindow(); déclaré dans l'IDL
MainWindow()
{
// Règle officielle : interdiction d'appeler InitializeComponent() dans le constructeur
// L'initialisation des contrôles XAML est exécutée en différé par le framework ; un appel dans le constructeur provoque un crash du cycle de vie
// Référence : documentation officielle cppwinrt Microsoft
// See https://github.com/microsoft/cppwinrt/tree/master/nuget#initializecomponent
}
// Interface de lecture de propriété générée automatiquement pour Int32 MyProperty; dans l'IDL
// Retourne la valeur numérique int32_t de la propriété
int32_t MyProperty();
// Interface d'écriture de propriété générée automatiquement pour Int32 MyProperty; dans l'IDL
// Modifie la valeur de la propriété et déclenche automatiquement l'événement de modification MyPropertyChanged pour rafraîchir l'interface via x:Bind
void MyProperty(int32_t value);
};
}
// 【Espace de noms usine】Classe usine d'objets WinRT chargée d'exposer et d'instancier l'objet fenêtre MainWindow vers l'extérieur
namespace winrt::WinuiCppDemo::factory_implementation
{
// Le second paramètre de MainWindowT lie la classe d'implémentation implementation::MainWindow ci-dessus
// Rôle : fournir un point d'entrée de création ABI unifié permettant au moteur XAML et au code externe (C# / autres composants) d'instancier la fenêtre
struct MainWindow : MainWindowT<MainWindow, implementation::MainWindow>
{
};
}Langage du code : C++ (cpp)
MainWindow.xaml.cpp
Fichier d’implémentation C++ où tout le code de logique métier est écrit.
- Implémenter toutes les fonctions déclarées dans MainWindow.xaml.h
- Gestion des événements des contrôles (clic de bouton, chargement de fenêtre, réponse aux saisies)
- Traitement des données, réseau, fichiers et calculs métier
- Logiques d’interaction : manipulation des contrôles UI, modification des propriétés liées, affichage de boîtes de dialogue, navigation entre pages
// Inclusion du fichier d'en-tête précompilé, regroupe les en-têtes communs du projet et les projections de base WinRT pour accélérer la compilation
#include "pch.h"
// Inclusion de l'en-tête de la classe de fenêtre actuelle, contenant la déclaration de la classe d'implémentation MainWindow
#include "MainWindow.xaml.h"
// Compilation conditionnelle : vérifie l'existence du fichier de liaison d'implémentation bas niveau MainWindow.g.cpp généré par l'IDL
// .g.cpp est généré par les outils midl+cppwinrt à partir du fichier .idl, il encapsule les événements de modification de propriété et la logique de transmission ABI bas niveau
// S'il existe, on l'inclut pour que les notifications de modification de propriété (MyPropertyChanged) et les liaisons runtime XAML fonctionnent correctement
#if __has_include("MainWindow.g.cpp")
#include "MainWindow.g.cpp"
#endif
// Simplification des espaces de noms globaux, évite de répéter le préfixe winrt:: dans tout le code
using namespace winrt;
// Espace de noms des API des contrôles principaux WinUI Xaml, fenêtres et événements de routage
using namespace Microsoft::UI::Xaml;
// Commentaire officiel : adresse de la documentation sur la structure de projet WinUI et les modèles
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
// Espace de noms implementation : contient l'implémentation de la logique métier de la fenêtre MainWindow, correspond aux déclarations de classe dans l'en-tête
namespace winrt::WinuiCppDemo::implementation
{
// Implémentation de la méthode de lecture de la propriété MyProperty déclarée dans l'IDL
// Retourne la valeur de propriété de type Int32 ; XAML x:Bind appelle cette fonction pour lire les données
int32_t MainWindow::MyProperty()
{
// Le modèle par défaut lève une exception non implémentée, vous devez remplacer ce code par la logique de retour de votre variable de stockage réelle
throw hresult_not_implemented();
}
// Implémentation de la méthode d'écriture de la propriété MyProperty déclarée dans l'IDL
// value : nouvelle valeur à attribuer à la propriété ; après modification, le sous-système déclenche automatiquement l'événement MyPropertyChanged pour rafraîchir l'interface liée
void MainWindow::MyProperty(int32_t /* value */)
{
// Le modèle par défaut lève une exception non implémentée, ajoutez vous-même la logique d'affectation de variable
throw hresult_not_implemented();
}
}
Nous allons maintenant ajouter un bouton sur cette fenêtre principale. Au clic sur le bouton, la propriété MyProperty s’incrémente automatiquement de 1, puis une boîte de dialogue s’ouvre pour afficher la valeur actuelle de MyProperty.
Tout d’abord, ajoutons un bouton dans le fichier XAML
<Grid>
<Button Content="Click Me" HorizontalAlignment="Center" VerticalAlignment="Center" Click="OnButtonClick"/>
</Grid>Langage du code : C++ (cpp)
Le texte affiché sur le bouton est « Click Me », et l’événement de clic OnButtonClick y est lié.
Placez votre curseur sur OnButtonClick et appuyez sur F12 : Visual Studio crée automatiquement la fonction de gestion de cet événement.
Des ajouts apparaissent alors dans MainWindow.xaml.h et MainWindow.xaml.cpp
void OnButtonClick(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);Langage du code : C++ (cpp)

Une définition de fonction a été ajoutée.
L’implémentation de cette fonction se trouve dans MainWindow.xaml.cpp

Nous allons ensuite modifier le code du fichier cpp
C’est tout pour cette leçon. Dans la prochaine partie, nous implémenterons la fonctionnalité du clic sur le bouton et découvrirons le fonctionnement des liaisons de propriétés.