Pembaca dapat memperbarui Visual Studio 2022 ke versi 17.9.1 atau lebih tinggi. Proyek WinUI3 berbasis C++ yang dibuat dengan templat bawaan sudah memiliki tombol; saat tombol diklik, berbagai operasi latar belakang dapat dijalankan.
Kita akan membuat proyek baru


Pada pelajaran sebelumnya, kita sudah menjelaskan setiap berkas dalam proyek beserta fungsinya masing-masing. Di bagian ini, kita akan membuat jendela kosong baru, menambahkan satu tombol ke jendela, lalu menangani peristiwa klik tombol tersebut.
Sebagaimana yang kita bahas sebelumnya, kelas titik masuk program kita adalah App, ada di sini.
void App::OnLaunched([[maybe_unused]] LaunchActivatedEventArgs const& e)
{
window = make<MainWindow>();
window.Activate();
}Code language: C++ (cpp)
Di sinilah jendela utama diinstansiasi, jendela ini yang akan muncul saat program dibuka. Selanjutnya kita analisis jendela utama kita: satu jendela dibentuk dari empat berkas secara keseluruhan.

Jendela
MainWindow.xaml (berkas deskripsi antarmuka)
Ini adalah bahasa markup XML murni yang dirancang khusus untuk merancang tata letak antarmuka pengguna. Hanya bertanggung jawab atas tampilan visual antarmuka dan tidak mengandung logika bisnis sama sekali.
Kamu dapat menuliskan kontrol tata letak serta berbagai komponen jendela seperti Button, TextBox, dan lainnya di dalamnya. Berkas definisi antarmuka ini sudah digunakan sejak era WPF dan pada dasarnya merupakan berkas XML. Berkas ini harus dipasangkan dengan berkas .h dan .cpp bernama sama untuk menangani peristiwa yang berjalan di latar belakang.
Dalam contoh resmi bawaan, berkas XAML berisi konten kosong dan tidak memiliki kontrol apapun.
<?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
Berkas definisi antarmuka, komponen inti eksklusif milik C++/WinRT
IDL = Interface Definition Language (bahasa definisi antarmuka). Mendefinisikan API, properti, dan peristiwa yang diekspos jendela ke kode luar serta berperan sebagai jembatan interoperabilitas antar bahasa pada C++/WinRT.
- Menentukan anggota publik dari kelas jendela: properti kustom, peristiwa buatan, dan metode yang dapat dipanggil
- Kompilator akan menghasilkan kode antarmuka ABI standar WinRT secara otomatis dari berkas IDL, sehingga C++, C#, JS dapat memanggil jendela ini
- Pengikatan data dan kontrol kustom pada antarmuka XAML hanya akan berfungsi jika properti dideklarasikan di dalam IDL
Mengapa berkas .idl wajib digunakan di WinUI3 C++/WinRT?
Karena WinRT merupakan standar ABI biner yang kompatibel dengan berbagai bahasa pemrograman
Windows Runtime (WinRT) bukan hanya pustaka C++, melainkan spesifikasi antarmuka biner universal lintas bahasa. C++, C#, Python, JS dapat memanggil komponen WinRT yang sama berkat deskripsi antarmuka yang terpadu; komunikasi timbal balik hanya melalui berkas header C++ tidak dapat dilakukan.
Berkas header C++ hanya dapat dipahami oleh kompilator C++ dan tidak dapat dibaca oleh C#/JS. Sementara IDL adalah bahasa antarmuka netral yang akan diterjemahkan kompilator menjadi kode pengikat ABI yang dapat dibaca semua bahasa.
Berikut isi berkas IDL dari contoh proyek kita
namespace WinuiCppDemo
{
[default_interface]
runtimeclass MainWindow : Microsoft.UI.Xaml.Window
{
MainWindow();
Int32 MyProperty;
}
}
Sebagaimana sudah dijelaskan, IDL adalah bahasa deskriptif. Di dalam berkas IDL ini terdapat namespace WinuiCppDemo terlebih dahulu, kemudian atribut [default_interface]
Atribut ini menandai antarmuka utama bawaan untuk runtimeclass dan merupakan syarat wajib saat membuat jendela atau kontrol kustom menggunakan C++/WinRT
Kompilator MIDL akan membuat antarmuka virtual murni IMainWindow secara otomatis yang berisi seluruh anggota kelas (konstruktor, properti, metode)
[default_interface] menentukan bahwa IMainWindow ini adalah antarmuka ABI utama yang diekspos kelas ke kode luar
Baris MainWindow(); di dalam kurung kurawal adalah deklarasi konstruktor tanpa parameter yang harus ditulis secara eksplisit. Mesin XAML akan memanggil konstruktor bawaan ini saat menginstansiasi jendela
Ini sesuai dengan implementasi konstruktor di berkas .xaml.h.
Int32 MyProperty;
Mendeklarasikan properti ketergantungan WinRT (properti yang dapat diikat), di sini terlihat manfaat utama IDL:
Int32: tipe dasar standar WinRT (setara dengan tipe C++ int32_t) ;
MyProperty: kompilator akan menghasilkan sistem properti lengkap secara otomatis:
- Getter:
int32_t MyProperty(); - Setter:
void MyProperty(int32_t value); - Peristiwa perubahan properti:
MyPropertyChanged(terpicu otomatis saat nilai properti berubah, digunakan sintaksx:BindXAML untuk memperbarui antarmuka secara otomatis)
Hanya anggota yang dideklarasikan di IDL yang dapat diikat di XAML dengan sintaks {x:Bind MyProperty}; jika kamu hanya mendeklarasikan variabel anggota biasa di berkas .h, XAML tidak akan mengenalinya dan pengikatan akan menghasilkan galat.
Kamu bisa klik kanan berkas IDL untuk mengkompilasinya secara terpisah, alat yang bertugas menangani proses ini adalah midl.exe. Alat ini menghasilkan berkas metadata untuk C# serta berkas header antarmuka untuk C++.
Singkatnya, IDL berfungsi menghasilkan antarmuka untuk panggilan antar bahasa sekaligus menyediakan metadata yang dibutuhkan pengikatan data XAML. Mengapa WinUI 3 berbasis C# tidak memerlukan IDL? Karena C# sudah memiliki refleksi dan metadata bawaan, merupakan bahasa terkelola. Versi C++ WinRT tidak memiliki mekanisme refleksi dan metadata asli.
Jika kamu hanya mendeklarasikan variabel anggota di berkas .h, mesin XAML tidak akan melihatnya sama sekali;
Hanya dengan mendeklarasikan Int32 MyProperty; di IDL, kompilator akan menghasilkan hal berikut:
- Antarmuka Get/Set untuk properti
- Peristiwa pemberitahuan perubahan properti
MyPropertyChanged - Metadata yang memungkinkan validasi pengikatan statis di XAML
Meskipun proyekmu hanya menggunakan C++ tanpa interaksi dengan C#/JS atau bahasa lain, IDL tetap wajib ada jika kamu menggunakan XAML
WinRT dirancang dengan ABI terpadu agar C++ / C# / JS / Python dapat saling memanggil komponen satu sama lain:
IDL adalah bahasa deskriptif netral, tidak terikat pada satu bahasa pemrograman. Kompilator setiap bahasa akan membaca berkas metadata .winmd hasil generate IDL untuk mengenali kelas, properti, dan metode milikmu;
Klik kanan berkas IDL untuk mengkompilasinya

Tugas utama IDL adalah menyediakan metadata bagi XAML untuk mengenali pengikatan antarmuka, sekaligus menerapkan interoperabilitas antar bahasa.
Saat IDL dikompilasi, akan dihasilkan berkas berakhiran xxxx.g.h dan xxxx.g.cpp yang disimpan di folder Generated Files dalam direktori proyek

Generated Files adalah direktori tempat kode dihasilkan secara otomatis
- Semua berkas yang dibuat otomatis oleh midl.exe, cppwinrt.exe dan kompilator XAML saat proses build proyek.
- Jangan mengedit berkas di folder ini secara manual, semua konten akan tertimpa sepenuhnya saat build ulang proyek
- Konfigurasi proyek Visual Studio menambahkan folder ini ke jalur pencarian header secara bawaan, itulah sebabnya sintaks #include “MainWindow.g.h” dapat menemukan berkas tersebut
MainWindow.xaml.h
Berkas header standar C++ yang berisi deklarasi kelas MainWindow.
Secara otomatis menyertakan antarmuka dasar hasil generate dari IDL;
Bidang kelas, variabel privat, dan deklarasi maju dituliskan di sini, sama seperti berkas .h biasa dengan sintaks C++ tradisional.
Mencantumkan semua variabel dan fungsi tanpa menulis implementasinya; kelas lain dapat mengaksesnya melalui direktif include.
// Perlindungan header: mencegah penyertaan berkas berulang dan galat pendekatan ulang saat kompilasi
#pragma once
// Menyertakan berkas header penghubung yang dihasilkan otomatis saat kompilasi MainWindow.idl
// .g.h berisi antarmuka IMainWindow dari IDL, kelas dasar templat CRTP MainWindowT<> serta definisi metadata WinRT
// Semua properti MyProperty dan antarmuka konstruktor yang dideklarasikan di IDL didefinisikan di sini
#include "MainWindow.g.h"
// 【Namespace implementasi】Menampung kelas implementasi logika bisnis jendela, di sini kita buat properti dan fungsi buatan sendiri
namespace winrt::WinuiCppDemo::implementation
{
// MainWindowT<MainWindow> adalah kelas dasar CRTP yang disediakan berkas .g.h
// 1. Mewarisi secara otomatis antarmuka IMainWindow yang didefinisikan IDL dan mewajibkan implementasi seluruh anggota yang dideklarasikan IDL
// 2. Membungkus logika dasar jendela WinUI, inisialisasi komponen XAML dan penerusan antarmuka ABI
struct MainWindow : MainWindowT<MainWindow>
{
// Konstruktor bawaan jendela, sesuai dengan MainWindow(); yang dideklarasikan di IDL
MainWindow()
{
// Aturan resmi: dilarang memanggil InitializeComponent() di dalam konstruktor
// Inisialisasi kontrol XAML dijalankan secara tertunda oleh framework; panggilan di konstruktor akan menyebabkan kerusakan siklus hidup
// Lihat dokumentasi resmi cppwinrt Microsoft
// See https://github.com/microsoft/cppwinrt/tree/master/nuget#initializecomponent
}
// Antarmuka baca properti Int32 MyProperty; hasil generate otomatis dari IDL
// Mengembalikan nilai numerik int32_t milik properti
int32_t MyProperty();
// Antarmuka tulis properti Int32 MyProperty; hasil generate otomatis dari IDL
// Mengubah nilai properti dan memicu secara internal peristiwa perubahan MyPropertyChanged agar x:Bind memperbarui antarmuka
void MyProperty(int32_t value);
};
}
// 【Namespace pabrik】Kelas pabrik objek WinRT yang bertugas mengekspos dan menginstansiasi objek jendela MainWindow ke luar kode
namespace winrt::WinuiCppDemo::factory_implementation
{
// Parameter kedua MainWindowT menghubungkan kelas implementasi implementation::MainWindow di atas
// Tujuan: menyediakan titik pembuatan ABI terpadu agar mesin XAML dan kode luar (C# / komponen lain) dapat membuat instansi jendela
struct MainWindow : MainWindowT<MainWindow, implementation::MainWindow>
{
};
}Code language: C++ (cpp)
MainWindow.xaml.cpp
Berkas implementasi C++, tempat seluruh kode logika bisnis ditulis.
- Mengimplementasikan semua fungsi yang dideklarasikan di MainWindow.xaml.h
- Penanganan peristiwa kontrol (klik tombol, muat jendela, tanggapan input pengguna)
- Pemrosesan data, akses jaringan, pengelolaan berkas, perhitungan bisnis
- Logika interaksi: memanipulasi kontrol UI, mengubah properti terikat, menampilkan dialog, navigasi antar halaman
// Menyertakan header prakompilasi, mengelompokkan header umum proyek dan proyeksi dasar WinRT untuk mempercepat kompilasi
#include "pch.h"
// Menyertakan header kelas jendela saat ini, berisi deklarasi kelas implementasi MainWindow
#include "MainWindow.xaml.h"
// Kompilasi bersyarat: memeriksa apakah ada berkas penghubung implementasi tingkat bawah MainWindow.g.cpp hasil generate IDL
// .g.cpp dibuat oleh alat midl+cppwinrt dari berkas .idl, membungkus peristiwa perubahan properti dan logika penerusan ABI tingkat bawah
// Jika ada, kita sertakan agar pemberitahuan perubahan properti (MyPropertyChanged) dan pengikatan runtime XAML berfungsi dengan benar
#if __has_include("MainWindow.g.cpp")
#include "MainWindow.g.cpp"
#endif
// Sederhanakan namespace global, tidak perlu mengulang awalan winrt:: di seluruh kode
using namespace winrt;
// Namespace API utama kontrol, jendela dan peristiwa rute WinUI Xaml
using namespace Microsoft::UI::Xaml;
// Catatan resmi: tautan dokumentasi struktur proyek dan templat 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: berisi implementasi logika bisnis jendela MainWindow, sesuai dengan deklarasi kelas di header
namespace winrt::WinuiCppDemo::implementation
{
// Implementasi metode baca properti MyProperty yang dideklarasikan di IDL
// Mengembalikan nilai properti tipe Int32; XAML x:Bind memanggil fungsi ini untuk membaca data
int32_t MainWindow::MyProperty()
{
// Templat bawaan melempar galat fungsi belum diimplementasikan, ganti kode ini dengan logika pengembalian variabel penyimpanan asli milikmu
throw hresult_not_implemented();
}
// Implementasi metode tulis properti MyProperty yang dideklarasikan di IDL
// value: nilai baru yang akan diberikan ke properti; setelah perubahan, subsistem secara otomatis memicu peristiwa MyPropertyChanged untuk memperbarui antarmuka yang terikat
void MainWindow::MyProperty(int32_t /* value */)
{
// Templat bawaan melempar galat fungsi belum diimplementasikan, tambahkan sendiri logika pemberian nilai variabel
throw hresult_not_implemented();
}
}Code language: PHP (php)
Selanjutnya kita akan menambahkan tombol di jendela utama ini. Saat tombol diklik, properti MyProperty akan bertambah 1 secara otomatis, lalu akan muncul jendela dialog yang menampilkan nilai terkini MyProperty.
Pertama kita tambahkan tombol di bagian XAML
<Grid>
<Button Content="Click Me" HorizontalAlignment="Center" VerticalAlignment="Center" Click="OnButtonClick"/>
</Grid>Code language: C++ (cpp)
Teks yang terlihat di tombol adalah «Click Me», selain itu peristiwa klik OnButtonClick sudah terhubung ke tombol tersebut.
Arahkan kursor mouse ke tulisan OnButtonClick lalu tekan tombol F12: Visual Studio akan membuat fungsi pelaksana peristiwa ini secara otomatis.
Kemudian kamu akan melihat penambahan konten di berkas MainWindow.xaml.h dan MainWindow.xaml.cpp
void OnButtonClick(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);Code language: C++ (cpp)

Sebuah deklarasi fungsi baru sudah ditambahkan.
Implementasi fungsi ini terletak di berkas MainWindow.xaml.cpp

Setelah itu kita akan mengedit kode di berkas cpp
Sampai sini pelajaran ini selesai. Di pelajaran berikutnya kita akan mengimplementasikan fungsi klik tombol dan mempelajari cara kerja pengikatan properti.