Os leitores podem atualizar o Visual Studio 2022 para a versão 17.9.1 ou superior. O projeto padrão de WinUI3 em C++ já vem com um botão; ao clicar nele, é possível executar diversas operações em segundo plano.
Vamos criar um novo projeto


Na aula anterior, explicamos cada arquivo do projeto e suas funções. Nesta seção, criaremos uma nova janela vazia, adicionaremos um botão na janela e trataremos o evento de clique do botão.
Como já comentamos antes, nossa classe de ponto de entrada é a App, aqui mesmo.
void App::OnLaunched([[maybe_unused]] LaunchActivatedEventArgs const& e)
{
window = make<MainWindow>();
window.Activate();
}Code language: C++ (cpp)
Neste ponto a janela principal é instanciada, ela é a janela exibida ao abrir o programa. Vamos analisar nossa janela principal: uma janela é composta por quatro arquivos no total.

Janela
MainWindow.xaml (arquivo de descrição da interface)
Trata-se de uma linguagem de marcação XML pura, desenvolvida exclusivamente para montar layouts de interface do usuário. Ela só cuida da aparência visual da tela e não contém lógica de negócio.
Nela você pode declarar controles de layout e vários componentes de janela como Button, TextBox e outros. Esse arquivo de definição de interface é usado desde o WPF e é basicamente um XML. Ele precisa ser acompanhado de arquivos .h e .cpp com o mesmo nome para tratar eventos em segundo plano.
No exemplo oficial padrão, o arquivo XAML está vazio e não contém nenhum controle.
<?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>Code language: YAML (yaml)
MainWindow.idl
Arquivo de definição de interface, componente central exclusivo do C++/WinRT
IDL = Interface Definition Language (Linguagem de Definição de Interfaces). Ela define as APIs, propriedades e eventos expostos pela janela e funciona como ponte para interoperabilidade entre linguagens no C++/WinRT.
- Especifica os membros públicos da classe janela: propriedades personalizadas, eventos customizados e métodos invocáveis
- O compilador gera automaticamente código de interface ABI padrão do WinRT a partir do IDL, permitindo que C++, C# e JS chamem essa janela
- Vínculos de dados e controles personalizados da interface XAML só funcionam se as propriedades forem declaradas no IDL
Por que o arquivo .idl é obrigatório no WinUI3 C++/WinRT?
Porque o WinRT é um padrão ABI binário compatível com múltiplas linguagens
O Windows Runtime (WinRT) não é apenas uma biblioteca C++, e sim uma especificação de interface binária universal para diversas linguagens. C++, C#, Python e JS conseguem chamar o mesmo componente WinRT graças a uma descrição de interface unificada; não há comunicação apenas com arquivos de cabeçalho C++.
Arquivos de cabeçalho C++ só são interpretados pelo compilador C++ e são ilegíveis para C#/JS. Já o IDL é uma linguagem de interface neutra, que o compilador converte em código de ligação ABI reconhecível por todas as linguagens.
Abaixo o conteúdo do arquivo IDL do nosso exemplo
namespace WinuiCppDemo
{
[default_interface]
runtimeclass MainWindow : Microsoft.UI.Xaml.Window
{
MainWindow();
Int32 MyProperty;
}
}
Como explicado, o IDL é uma linguagem descritiva. Nesse arquivo IDL temos primeiro o namespace WinuiCppDemo, seguido do atributo [default_interface]
Esse atributo marca a interface principal padrão da runtimeclass e é um requisito obrigatório ao criar janelas ou controles personalizados com C++/WinRT
O compilador MIDL cria automaticamente uma interface virtual pura IMainWindow, contendo todos os membros da classe (construtor, propriedades, métodos)
[default_interface] determina que essa IMainWindow é a interface ABI principal exposta pela classe
A linha MainWindow(); dentro das chaves é a declaração do construtor sem parâmetros, que deve ser escrita explicitamente. O motor XAML chama esse construtor padrão ao instanciar a janela
Isso corresponde à implementação do construtor no arquivo .xaml.h.
Int32 MyProperty;
Declara uma propriedade de dependência WinRT (propriedade vinculável), aqui vemos o principal diferencial do IDL:
Int32: tipo base padrão do WinRT (equivalente ao tipo C++ int32_t) ;
MyProperty: gera automaticamente um sistema completo de propriedades:
- Getter:
int32_t MyProperty(); - Setter:
void MyProperty(int32_t value); - Evento de alteração de propriedade:
MyPropertyChanged(disparado automaticamente ao mudar o valor da propriedade, usado pelox:Binddo XAML para atualizar a interface)
Apenas membros declarados no IDL podem ser vinculados no XAML com {x:Bind MyProperty}; se você declarar apenas uma variável comum no arquivo .h, o XAML não a reconhecerá e o vínculo gerará erro.
Clique com o botão direito no arquivo IDL para compilá-lo separadamente, a ferramenta responsável é o midl.exe. Ele gera arquivos de metadados para C# e arquivos de cabeçalho de interface para C++.
Resumindo, o IDL serve para gerar interfaces para chamadas entre linguagens e fornecer os metadados necessários para os vínculos de dados do XAML. Por que o WinUI 3 em C# não precisa de IDL? Porque o C# já traz reflexão e metadados nativos, é uma linguagem gerenciada. A versão C++ do WinRT não tem mecanismos nativos de reflexão nem metadados.
Se você só declarar uma variável no arquivo .h, o motor XAML não conseguirá visualizá-la;
Só ao declarar Int32 MyProperty; no IDL, o compilador gera:
- Interfaces Get/Set da propriedade
- Evento de aviso de alteração de propriedade
MyPropertyChanged - Metadados que validam os vínculos estáticos no XAML
Mesmo que seu projeto use somente C++ sem interagir com C#/JS ou outras linguagens, o IDL é obrigatório se você utilizar XAML
O WinRT foi projetado com uma ABI unificada para que C++ / C# / JS / Python consigam chamar componentes uns dos outros:
O IDL é uma linguagem descritiva neutra, não vinculada a nenhuma linguagem de programação. Compiladores de cada linguagem leem os metadados .winmd gerados pelo IDL para identificar suas classes, propriedades e métodos;
Clique com o botão direito no arquivo IDL para compilá-lo

A principal função do IDL é fornecer metadados ao XAML para reconhecer vínculos de interface, além de implementar a interoperabilidade entre linguagens.
A compilação do IDL gera arquivos xxxx.g.h e xxxx.g.cpp, armazenados na pasta Generated Files dentro do diretório do projeto

Generated Files é o diretório de código gerado automaticamente
- Todos os arquivos gerados automaticamente por midl.exe, cppwinrt.exe e o compilador XAML durante a compilação do projeto.
- Não edite manualmente nenhum arquivo dessa pasta, todos serão sobrescritos na próxima compilação
- A configuração padrão do projeto VS adiciona essa pasta ao caminho de inclusão de cabeçalhos, por isso o #include “MainWindow.g.h” consegue localizar o arquivo
MainWindow.xaml.h
Arquivo de cabeçalho C++ padrão, contendo as declarações da classe MainWindow.
Inclui automaticamente as interfaces base geradas pelo IDL;
Campos da classe, variáveis privadas e declarações antecipadas são escritos aqui, igual em qualquer arquivo .++ tradicional de sintaxe C++.
Lista todas as variáveis e funções sem implementá-las; outras classes podem acessá-lo via diretiva include.
// Proteção de cabeçalho: evita inclusão múltipla do arquivo e erros de redefinição na compilação
#pragma once
// Importa o arquivo de cabeçalho ponte gerado automaticamente ao compilar MainWindow.idl
// O .g.h contém a interface IMainWindow do IDL, a classe base template CRTP MainWindowT<> e definições de metadados WinRT
// Todas as propriedades MyProperty e interfaces de construtor declaradas no IDL estão definidas aqui
#include "MainWindow.g.h"
// 【Namespace de implementação】Armazena a classe de implementação da lógica de negócio da janela, onde criamos nossas próprias propriedades e funções
namespace winrt::WinuiCppDemo::implementation
{
// MainWindowT<MainWindow> é a classe base CRTP disponibilizada pelo arquivo .g.h
// 1. Herda automaticamente a interface IMainWindow definida no IDL e força a implementação de todos os membros declarados no IDL
// 2. Encapsula a lógica interna das janelas WinUI, inicialização de componentes XAML e encaminhamento de interfaces ABI
struct MainWindow : MainWindowT<MainWindow>
{
// Construtor padrão da janela, corresponde ao MainWindow(); declarado no IDL
MainWindow()
{
// Regra oficial: proibido chamar InitializeComponent() dentro do construtor
// A inicialização dos controles XAML é executada de forma atrasada pelo framework; chamá-la no construtor causa falhas no ciclo de vida
// Consulte a documentação oficial do cppwinrt da Microsoft
// See https://github.com/microsoft/cppwinrt/tree/master/nuget#initializecomponent
}
// Interface de leitura da propriedade Int32 MyProperty; gerada automaticamente pelo IDL
// Retorna o valor numérico int32_t da propriedade
int32_t MyProperty();
// Interface de escrita da propriedade Int32 MyProperty; gerada automaticamente pelo IDL
// Altera o valor da propriedade e dispara internamente o evento MyPropertyChanged para o x:Bind atualizar a interface
void MyProperty(int32_t value);
};
}
// 【Namespace fábrica】Classe fábrica de objetos WinRT responsável por expor e instanciar o objeto da janela MainWindow
namespace winrt::WinuiCppDemo::factory_implementation
{
// O segundo parâmetro do MainWindowT vincula a classe de implementação implementation::MainWindow acima
// Objetivo: fornecer um ponto de criação ABI unificado para que o motor XAML e códigos externos (C# / outros componentes) criem instâncias de janela
struct MainWindow : MainWindowT<MainWindow, implementation::MainWindow>
{
};
}Code language: C++ (cpp)
MainWindow.xaml.cpp
Arquivo de implementação C++, onde todo o código de lógica de negócio é escrito.
- Implementar todas as funções declaradas no MainWindow.xaml.h
- Tratamento de eventos de controle (clique em botão, carregamento da janela, resposta a entradas)
- Processamento de dados, rede, arquivos e cálculos de negócio
- Lógicas de interação: manipular controles UI, alterar propriedades vinculáveis, exibir caixas de diálogo, navegar entre telas
// Importa o cabeçalho pré-compilado, agrupa cabeçalhos comuns do projeto e projeções base do WinRT para acelerar a compilação
#include "pch.h"
// Importa o cabeçalho da classe da janela atual, contendo a declaração da classe de implementação MainWindow
#include "MainWindow.xaml.h"
// Compilação condicional: verifica se existe o arquivo ponte de implementação de baixo nível MainWindow.g.cpp gerado pelo IDL
// O .g.cpp é criado pelas ferramentas midl+cppwinrt a partir do .idl, encapsula eventos de alteração de propriedade e lógica de encaminhamento ABI
// Se existir, importamos para que os avisos de alteração de propriedade (MyPropertyChanged) e os vínculos em tempo de execução XAML funcionem corretamente
#if __has_include("MainWindow.g.cpp")
#include "MainWindow.g.cpp"
#endif
// Simplifica namespaces globais, não precisa repetir o prefixo winrt:: em todo o código
using namespace winrt;
// Namespace das APIs centrais de controles, janelas e eventos de roteamento do WinUI Xaml
using namespace Microsoft::UI::Xaml;
// Comentário oficial: link da documentação sobre estrutura de projetos e templates WinUI
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
// Namespace implementation: contém a implementação da lógica de negócio da janela MainWindow, correspondente às declarações de classe no cabeçalho
namespace winrt::WinuiCppDemo::implementation
{
// Implementação do método de leitura da propriedade MyProperty declarada no IDL
// Retorna o valor da propriedade do tipo Int32; o x:Bind do XAML chama essa função para ler dados
int32_t MainWindow::MyProperty()
{
// O template padrão lança exceção de não implementado, substitua esse código pela lógica de retorno da sua variável de armazenamento real
throw hresult_not_implemented();
}
// Implementação do método de escrita da propriedade MyProperty declarada no IDL
// value: novo valor a ser atribuído à propriedade; após alteração, o subsistema dispara automaticamente o evento MyPropertyChanged para atualizar a interface vinculada
void MainWindow::MyProperty(int32_t /* value */)
{
// O template padrão lança exceção de não implementado, adicione você mesmo a lógica de atribuição de variáveis
throw hresult_not_implemented();
}
}Code language: PHP (php)
Agora vamos adicionar um botão nessa janela principal. Ao clicar no botão, a propriedade MyProperty incrementa automaticamente em 1, depois uma caixa de diálogo abre mostrando o valor atual de MyProperty.
Primeiro adicionamos um botão no XAML
<Grid>
<Button Content="Click Me" HorizontalAlignment="Center" VerticalAlignment="Center" Click="OnButtonClick"/>
</Grid>Code language: C++ (cpp)
O texto exibido no botão é “Click Me”, e o evento de clique OnButtonClick está vinculado a ele.
Posicione o mouse sobre OnButtonClick e aperte F12: o Visual Studio cria automaticamente a função executora desse evento.
Depois você verá adições no MainWindow.xaml.h e MainWindow.xaml.cpp
void OnButtonClick(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);Code language: C++ (cpp)

Uma nova declaração de função foi adicionada.
A implementação dessa função fica no MainWindow.xaml.cpp

Depois vamos editar o código do arquivo cpp
Isso é tudo para essa aula. Na próxima aula implementaremos a funcionalidade do clique no botão e entenderemos como funcionam os vínculos de propriedades.