Los lectores pueden actualizar Visual Studio 2022 a la versión 17.9.1 o superior. El proyecto WinUI3 en C++ creado con la plantilla predeterminada ya incluye un botón; al pulsarlo se pueden ejecutar múltiples operaciones en segundo plano.
Vamos a crear un nuevo proyecto


En la lección anterior explicamos cada archivo del proyecto y sus funciones correspondientes. En este apartado crearemos una nueva ventana vacía, añadiremos un botón sobre ella y gestionaremos la acción de pulsación del botón.
Como comentamos anteriormente, nuestra clase de punto de entrada es App, aquí mismo.
void App::OnLaunched([[maybe_unused]] LaunchActivatedEventArgs const& e)
{
window = make<MainWindow>();
window.Activate();
}Lenguaje del código: C++ (cpp)
Aquí se instancia la ventana principal, que es la ventana que aparece al abrir el programa. A continuación analizaremos nuestra ventana principal: una ventana está compuesta por cuatro archivos en total.

Ventana
MainWindow.xaml (archivo de descripción de interfaz)
Se trata de un lenguaje de marcado XML puro, diseñado exclusivamente para definir diseños de interfaz de usuario. Solo se encarga del aspecto visual de la interfaz y no alberga lógica de negocio.
Aquí puedes declarar controles de disposición y todo tipo de componentes de ventana como Button, TextBox, entre otros. Este archivo de definición de interfaz se utiliza desde WPF y es fundamentalmente un XML. Debe acompañarse de archivos .h y .cpp con el mismo nombre para gestionar eventos en segundo plano.
En el ejemplo oficial predeterminado, el archivo XAML está vacío y no contiene ningún control.
<?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>Lenguaje del código: YAML (yaml)
MainWindow.idl
Archivo de definición de interfaces, componente central exclusivo de C++/WinRT
IDL = Interface Definition Language (lenguaje de definición de interfaces). Define las API, propiedades y eventos que la ventana expone al exterior y actúa como puente para la interoperabilidad entre lenguajes en C++/WinRT.
- Especifica los miembros públicos de la clase ventana: propiedades personalizadas, eventos propios y métodos invocables
- El compilador genera automáticamente código de interfaz ABI estándar de WinRT a partir del IDL, para que C++, C#, JS puedan llamar a esta ventana
- Los enlaces de datos y controles personalizados de la interfaz XAML solo funcionan si las propiedades se declaran en el IDL
¿Por qué es obligatorio el archivo .idl en WinUI3 C++/WinRT?
Porque WinRT es un estándar ABI binario compatible con varios lenguajes
Windows Runtime (WinRT) no es simplemente una librería C++, sino una especificación de interfaz binaria universal multiplataforma. C++, C#, Python, JS pueden invocar el mismo componente WinRT gracias a una descripción de interfaz unificada; no es posible la comunicación mutua solo mediante archivos de cabecera C++.
Los archivos de cabecera C++ solo los entiende el compilador de C++ y son ilegibles para C#/JS. En cambio, IDL es un lenguaje de interfaz neutro que el compilador traduce a código de enlace ABI compatible con todos los lenguajes.
A continuación se muestra el contenido del archivo IDL de nuestro ejemplo
namespace WinuiCppDemo
{
[default_interface]
runtimeclass MainWindow : Microsoft.UI.Xaml.Window
{
MainWindow();
Int32 MyProperty;
}
}
Ya dijimos que IDL es un lenguaje descriptivo. En este archivo IDL encontramos primero el espacio de nombres namespace WinuiCppDemo, seguido del atributo [default_interface]
Este atributo asigna una interfaz principal predeterminada a la runtimeclass y es un requisito indispensable al crear ventanas o controles personalizados con C++/WinRT
El compilador MIDL genera automáticamente una interfaz virtual pura IMainWindow que contiene todos los miembros de la clase (constructor, propiedades, métodos)
[default_interface] indica que esta IMainWindow es la interfaz ABI principal que la clase expone al exterior
La línea MainWindow(); entre llaves es la declaración del constructor sin parámetros, que hay que escribir explícitamente. El motor XAML llama a este constructor predeterminado al instanciar la ventana
Esto corresponde a la implementación del constructor en el archivo .xaml.h.
Int32 MyProperty;
Declara una propiedad de dependencia WinRT (propiedad enlazable); aquí se aprecia el valor fundamental del IDL:
Int32: tipo base estándar de WinRT (equivalente al tipo C++ int32_t) ;
MyProperty: genera automáticamente un sistema completo de propiedades:
- Getter:
int32_t MyProperty(); - Setter:
void MyProperty(int32_t value); - Evento de cambio de propiedad:
MyPropertyChanged(se activa automáticamente al modificar la propiedad, permite quex:Bindde XAML actualice la interfaz)
Solo los miembros declarados en el IDL se pueden enlazar en XAML con {x:Bind MyProperty}; si solo defines una variable miembro normal en el archivo .h, XAML no la detectará y el enlace generará un error.
Puedes hacer clic derecho sobre el archivo IDL para compilarlo por separado, la herramienta encargada es midl.exe. Genera tanto archivos de metadatos para C# como archivos de cabecera de interfaz para C++.
En resumen, el IDL sirve para generar interfaces que permiten llamadas entre lenguajes y proporciona los metadatos necesarios para los enlaces de datos de XAML. ¿Por qué WinUI 3 en C# no necesita archivos IDL? Porque C# incorpora reflexión y metadatos de forma nativa, es un lenguaje gestionado. La versión C++ de WinRT no cuenta con mecanismos nativos de reflexión ni metadatos.
Si solo declaras una variable miembro en el archivo .h, el motor XAML no la verá en absoluto;
Solo al declarar Int32 MyProperty; en el IDL, el compilador genera lo siguiente:
- Interfaces Get/Set para la propiedad
- Evento de notificación de cambio de propiedad
MyPropertyChanged - Metadatos que permiten la validación de enlaces estáticos en XAML
Incluso si tu proyecto usa exclusivamente C++ y no interactúa con C#/JS ni otros lenguajes, es obligatorio escribir un IDL en cuanto uses XAML
WinRT se diseñó con una ABI unificada para que C++ / C# / JS / Python puedan invocar componentes mutuamente:
IDL es un lenguaje descriptivo neutro, no vinculado a ningún lenguaje de programación. Los compiladores de cada lenguaje leen los archivos de metadatos .winmd generados por el IDL para reconocer tus clases, propiedades y métodos;
Haz clic derecho en el archivo IDL para compilarlo

La función principal del IDL es proporcionar a XAML los metadatos necesarios para reconocer los enlaces de interfaz, además de implementar la interoperabilidad entre lenguajes.
Al compilar el IDL se generan archivos xxxx.g.h y xxxx.g.cpp, que se guardan en la carpeta Generated Files del directorio del proyecto

Generated Files es el directorio de código generado automáticamente
- Todos los archivos generados automáticamente por midl.exe, cppwinrt.exe y el compilador XAML al compilar el proyecto.
- No modifiques manualmente ningún archivo de esta carpeta, se sobrescribirán completamente al volver a compilar
- La configuración del proyecto VS añade esta carpeta por defecto a la ruta de inclusión de cabeceras, por eso #include «MainWindow.g.h» puede localizar el archivo
MainWindow.xaml.h
Archivo de cabecera estándar de C++ que contiene las declaraciones de la clase MainWindow.
Incluye automáticamente las interfaces base generadas por el IDL;
Aquí se escriben los campos de clase, variables privadas y declaraciones anticipadas, igual que en cualquier archivo .h de sintaxis C++ tradicional.
Enumera las variables y funciones sin escribir su implementación; otras clases pueden acceder a él mediante una directiva include.
// Protección de cabecera: evita incluir el archivo varias veces y previene errores de redefinición en compilación
#pragma once
// Importar el archivo de cabecera puente generado automáticamente al compilar MainWindow.idl
// .g.h contiene la interfaz IMainWindow generada por IDL, la clase base plantilla MainWindowT<> y definiciones relacionadas con metadatos WinRT
// Las propiedades MyProperty e interfaces de constructor declaradas en el IDL se definen todas aquí
#include "MainWindow.g.h"
// 【Espacio de nombres de implementación】Almacena la clase de implementación de la lógica de negocio de la ventana, aquí implementamos nuestras propias propiedades y funciones
namespace winrt::WinuiCppDemo::implementation
{
// MainWindowT<MainWindow> es la clase base CRTP proporcionada por el archivo .g.h
// 1. Hereda automáticamente la interfaz IMainWindow definida en el IDL y obliga a implementar todos los miembros declarados en el IDL
// 2. Encapsula la lógica subyacente de ventanas WinUI, inicialización de componentes XAML y lógica de reenvío de interfaces ABI
struct MainWindow : MainWindowT<MainWindow>
{
// Constructor predeterminado de la ventana, corresponde a MainWindow(); declarado en el IDL
MainWindow()
{
// Norma oficial: prohibido llamar a InitializeComponent() dentro del constructor
// El framework ejecuta la inicialización de controles XAML de forma diferida; llamarlo en el constructor causa fallos del ciclo de vida
// Consulta la documentación oficial de cppwinrt de Microsoft
// See https://github.com/microsoft/cppwinrt/tree/master/nuget#initializecomponent
}
// Interfaz de lectura de propiedad generada automáticamente para Int32 MyProperty; del IDL
// Devuelve el valor numérico int32_t de la propiedad
int32_t MyProperty();
// Interfaz de escritura de propiedad generada automáticamente para Int32 MyProperty; del IDL
// Modifica el valor de la propiedad y activa internamente el evento de cambio MyPropertyChanged para que x:Bind actualice la interfaz
void MyProperty(int32_t value);
};
}
// 【Espacio de nombres fábrica】Clase fábrica de objetos WinRT encargada de exponer e instanciar el objeto ventana MainWindow al exterior
namespace winrt::WinuiCppDemo::factory_implementation
{
// El segundo parámetro de MainWindowT enlaza la clase de implementación implementation::MainWindow de arriba
// Propósito: ofrecer un punto de creación ABI unificado para que el motor XAML y el código externo (C# / otros componentes) puedan crear instancias de ventana
struct MainWindow : MainWindowT<MainWindow, implementation::MainWindow>
{
};
}Lenguaje del código: C++ (cpp)
MainWindow.xaml.cpp
Archivo de implementación C++ donde se escribe todo el código de lógica de negocio.
- Implementar todas las funciones declaradas en MainWindow.xaml.h
- Manejo de eventos de controles (pulsación de botón, carga de ventana, respuesta a entradas)
- Procesamiento de datos, red, archivos y cálculos de negocio
- Lógicas de interacción: manipular controles UI, modificar propiedades enlazadas, mostrar diálogos, navegación entre páginas
// Importar archivo de cabecera precompilado, agrupa cabeceras comunes del proyecto y proyecciones base de WinRT para acelerar la compilación
#include "pch.h"
// Importar la cabecera de la clase de ventana actual, contiene la declaración de la clase de implementación MainWindow
#include "MainWindow.xaml.h"
// Compilación condicional: comprobar si existe el archivo puente de implementación subyacente MainWindow.g.cpp generado por IDL
// .g.cpp lo generan las herramientas midl+cppwinrt a partir del archivo .idl, encapsula eventos de cambio de propiedad y lógica de reenvío ABI de bajo nivel
// Si existe, lo importamos para que las notificaciones de cambio de propiedad (MyPropertyChanged) y los enlaces en tiempo de ejecución XAML funcionen correctamente
#if __has_include("MainWindow.g.cpp")
#include "MainWindow.g.cpp"
#endif
// Simplificar espacios de nombres globales, no hace falta repetir el prefijo winrt:: en todo el código
using namespace winrt;
// Espacio de nombres de las API principales de controles, ventanas y eventos de enrutamiento de WinUI Xaml
using namespace Microsoft::UI::Xaml;
// Comentario oficial: enlace a la documentación sobre estructura de proyectos WinUI y plantillas
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
// Espacio de nombres implementation: contiene la implementación de la lógica de negocio de la ventana MainWindow, corresponde a las declaraciones de clase de la cabecera
namespace winrt::WinuiCppDemo::implementation
{
// Implementación del método de lectura de la propiedad MyProperty declarada en el IDL
// Devuelve el valor de propiedad de tipo Int32; XAML x:Bind llama a esta función para leer datos
int32_t MainWindow::MyProperty()
{
// La plantilla predeterminada lanza una excepción de no implementado, debes sustituir este código por la lógica que devuelva tu variable de almacenamiento real
throw hresult_not_implemented();
}
// Implementación del método de escritura de la propiedad MyProperty declarada en el IDL
// value: nuevo valor que se asigna a la propiedad; tras modificarlo, el subsistema activa automáticamente el evento MyPropertyChanged para actualizar la interfaz enlazada
void MainWindow::MyProperty(int32_t /* value */)
{
// La plantilla predeterminada lanza una excepción de no implementado, debes añadir tú mismo la lógica de asignación de variables
throw hresult_not_implemented();
}
}Lenguaje del código: PHP (php)
A continuación añadiremos un botón en esta ventana principal. Al pulsar el botón, la propiedad MyProperty se incrementará automáticamente en 1 y luego se abrirá un cuadro de diálogo que mostrará el valor actual de MyProperty.
Primero añadimos un botón en el XAML
<Grid>
<Button Content="Click Me" HorizontalAlignment="Center" VerticalAlignment="Center" Click="OnButtonClick"/>
</Grid>Lenguaje del código: C++ (cpp)
El texto mostrado en el botón es «Click Me», además el evento de pulsación OnButtonClick está enlazado.
Coloca el cursor del ratón sobre OnButtonClick y pulsa F12: Visual Studio crea automáticamente la función de ejecución para este evento.
A continuación verás adiciones en MainWindow.xaml.h y MainWindow.xaml.cpp
void OnButtonClick(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);Lenguaje del código: C++ (cpp)

Se ha añadido una nueva declaración de función.
La implementación de esta función se encuentra en MainWindow.xaml.cpp

Después editaremos el código del archivo cpp
Eso es todo para esta lección. En la próxima lección implementaremos la funcionalidad de pulsación del botón y conoceremos el funcionamiento de los enlaces de propiedades.