Kontrol kustom XAML (templat) dengan C++/WinRT

Penting

Untuk konsep dan istilah penting yang mendukung pemahaman Anda tentang cara menggunakan dan menulis kelas runtime dengan C++/WinRT, lihat Menggunakan API dengan C++/WinRT dan API Penulis dengan C++/WinRT.

Salah satu fitur paling kuat dari Platform Windows Universal (UWP) adalah fleksibilitas yang disediakan tumpukan antarmuka pengguna (UI) untuk membuat kontrol kustom berdasarkan jenis Kontrol XAML. Kerangka kerja UI XAML menyediakan fitur seperti properti dependensi kustom dan properti terlampir, dan templat kontrol, yang memudahkan untuk membuat kontrol yang kaya fitur dan dapat disesuaikan. Topik ini memancang Anda melalui langkah-langkah membuat kontrol kustom (templat) dengan C++/WinRT.

Membuat Aplikasi Kosong (BgLabelControlApp)

Mulailah dengan membuat proyek baru di Microsoft Visual Studio. Buat proyek Aplikasi Kosong (C++/WinRT), atur namanya ke BgLabelControlApp, dan (sehingga struktur folder Anda akan cocok dengan panduan) pastikan bahwa Tempatkan solusi dan proyek dalam direktori yang sama tidak dicentang. Targetkan versi terbaru yang tersedia secara umum (yaitu, bukan pratinjau) dari Windows SDK.

Di bagian selanjutnya dari topik ini, Anda akan diarahkan untuk membangun proyek Anda (tetapi jangan membangun sampai saat itu).

Catatan

Untuk informasi tentang menyiapkan Visual Studio untuk pengembangan C++/WinRT—termasuk menginstal dan menggunakan C++/WinRT Visual Studio Extension (VSIX) dan paket NuGet (yang bersama-sama menyediakan templat proyek dan dukungan build)—lihat Dukungan Visual Studio untuk C++/WinRT.

Kita akan menulis kelas baru untuk mewakili kontrol kustom (templat). Kami menulis dan mengonsumsi kelas dalam unit kompilasi yang sama. Tetapi kita ingin dapat membuat instans kelas ini dari markup XAML, dan untuk alasan itu itu akan menjadi kelas runtime. Dan kita akan menggunakan C++/WinRT untuk penulis dan mengonsumsinya.

Langkah pertama dalam menulis kelas runtime baru adalah menambahkan item Midl File (.idl) baru ke proyek. Beri nama BgLabelControl.idl. Hapus konten BgLabelControl.idldefault , dan tempelkan dalam deklarasi kelas runtime ini.

// BgLabelControl.idl
namespace BgLabelControlApp
{
    runtimeclass BgLabelControl : Windows.UI.Xaml.Controls.Control
    {
        BgLabelControl();
        static Windows.UI.Xaml.DependencyProperty LabelProperty{ get; };
        String Label;
    }
}

Daftar di atas menunjukkan pola yang Anda ikuti saat mendeklarasikan properti dependensi (DP). Ada dua bagian untuk setiap DP. Pertama, Anda mendeklarasikan properti statis baca-saja dari jenis DependencyProperty. Ini memiliki nama DP Plus Properti Anda. Anda akan menggunakan properti statis ini dalam implementasi Anda. Kedua, Anda mendeklarasikan properti instans baca-tulis dengan jenis dan nama DP Anda. Jika Anda ingin menulis properti terlampir (bukan DP), maka lihat contoh kode di Properti terlampir kustom.

Catatan

Jika Anda menginginkan DP dengan jenis floating-point, buatlah double (Double di MIDL 3.0). Mendeklarasikan dan menerapkan DP jenis float (Single di MIDL), lalu mengatur nilai untuk DP tersebut dalam markup XAML, menghasilkan kesalahan Gagal membuat 'Windows.Foundation.Single' dari teks '<NUMBER>'.

Simpan file. Proyek tidak akan dibangun hingga selesai saat ini, tetapi membangun sekarang adalah hal yang berguna untuk dilakukan karena menghasilkan file kode sumber tempat Anda akan mengimplementasikan kelas runtime BgLabelControl . Jadi lanjutkan dan bangun sekarang (kesalahan build yang dapat Anda harapkan untuk dilihat pada tahap ini ada hubungannya dengan "simbol eksternal yang belum terselesaikan").

Selama proses build, midl.exe alat dijalankan untuk membuat file metadata Windows Runtime (\BgLabelControlApp\Debug\BgLabelControlApp\Unmerged\BgLabelControl.winmd) yang menjelaskan kelas runtime. Kemudian, cppwinrt.exe alat ini dijalankan untuk menghasilkan file kode sumber untuk mendukung Anda dalam menulis dan mengonsumsi kelas runtime Anda. File-file ini termasuk stub untuk membuat Anda mulai menerapkan kelas runtime BgLabelControl yang Anda deklarasikan di IDL Anda. Stub-stub itu adalah \BgLabelControlApp\BgLabelControlApp\Generated Files\sources\BgLabelControl.h dan BgLabelControl.cpp.

Salin file BgLabelControl.h stub dan BgLabelControl.cpp dari \BgLabelControlApp\BgLabelControlApp\Generated Files\sources\ ke folder proyek, yaitu \BgLabelControlApp\BgLabelControlApp\. Di Penjelajah Solusi, pastikan Tampilkan Semua File diaktifkan. Klik kanan file stub yang Anda salin, dan klik Sertakan Dalam Proyek.

Anda akan melihat static_assert di bagian BgLabelControl.h atas dan BgLabelControl.cpp, yang harus Anda hapus. Sekarang proyek akan dibangun.

Menerapkan kelas kontrol kustom BgLabelControl

Sekarang, mari kita buka \BgLabelControlApp\BgLabelControlApp\BgLabelControl.h dan BgLabelControl.cpp terapkan kelas runtime kita. Di BgLabelControl.h, ubah konstruktor untuk mengatur kunci gaya default, terapkan Label dan LabelProperty, tambahkan penanganan aktivitas statis bernama OnLabelChanged untuk memproses perubahan pada nilai properti dependensi, dan tambahkan anggota privat untuk menyimpan bidang pendukung untuk LabelProperty.

Setelah menambahkannya, Anda BgLabelControl.h terlihat seperti ini. Anda dapat menyalin dan menempelkan daftar kode ini untuk menggantikan konten BgLabelControl.h.

// BgLabelControl.h
#pragma once
#include "BgLabelControl.g.h"

namespace winrt::BgLabelControlApp::implementation
{
    struct BgLabelControl : BgLabelControlT<BgLabelControl>
    {
        BgLabelControl() { DefaultStyleKey(winrt::box_value(L"BgLabelControlApp.BgLabelControl")); }

        winrt::hstring Label()
        {
            return winrt::unbox_value<winrt::hstring>(GetValue(m_labelProperty));
        }

        void Label(winrt::hstring const& value)
        {
            SetValue(m_labelProperty, winrt::box_value(value));
        }

        static Windows::UI::Xaml::DependencyProperty LabelProperty() { return m_labelProperty; }

        static void OnLabelChanged(Windows::UI::Xaml::DependencyObject const&, Windows::UI::Xaml::DependencyPropertyChangedEventArgs const&);

    private:
        static Windows::UI::Xaml::DependencyProperty m_labelProperty;
    };
}
namespace winrt::BgLabelControlApp::factory_implementation
{
    struct BgLabelControl : BgLabelControlT<BgLabelControl, implementation::BgLabelControl>
    {
    };
}

Dalam BgLabelControl.cpp, tentukan anggota statis seperti ini. Anda dapat menyalin dan menempelkan daftar kode ini untuk menggantikan konten BgLabelControl.cpp.

// BgLabelControl.cpp
#include "pch.h"
#include "BgLabelControl.h"
#include "BgLabelControl.g.cpp"

namespace winrt::BgLabelControlApp::implementation
{
    Windows::UI::Xaml::DependencyProperty BgLabelControl::m_labelProperty =
        Windows::UI::Xaml::DependencyProperty::Register(
            L"Label",
            winrt::xaml_typename<winrt::hstring>(),
            winrt::xaml_typename<BgLabelControlApp::BgLabelControl>(),
            Windows::UI::Xaml::PropertyMetadata{ winrt::box_value(L"default label"), Windows::UI::Xaml::PropertyChangedCallback{ &BgLabelControl::OnLabelChanged } }
    );

    void BgLabelControl::OnLabelChanged(Windows::UI::Xaml::DependencyObject const& d, Windows::UI::Xaml::DependencyPropertyChangedEventArgs const& /* e */)
    {
        if (BgLabelControlApp::BgLabelControl theControl{ d.try_as<BgLabelControlApp::BgLabelControl>() })
        {
            // Call members of the projected type via theControl.

            BgLabelControlApp::implementation::BgLabelControl* ptr{ winrt::get_self<BgLabelControlApp::implementation::BgLabelControl>(theControl) };
            // Call members of the implementation type via ptr.
        }
    }
}

Dalam panduan ini, kita tidak akan menggunakan OnLabelChanged. Tetapi ada di sana sehingga Anda dapat melihat cara mendaftarkan properti dependensi dengan panggilan balik yang diubah properti. Implementasi OnLabelChanged juga menunjukkan cara mendapatkan jenis proyeksi turunan dari jenis proyeksi dasar (jenis dasar yang diproyeksikan adalah DependencyObject, dalam hal ini). Dan itu menunjukkan cara kemudian mendapatkan penunjuk ke jenis yang mengimplementasikan jenis yang diproyeksikan. Operasi kedua itu secara alami hanya akan dimungkinkan dalam proyek yang mengimplementasikan jenis yang diproyeksikan (yaitu, proyek yang mengimplementasikan kelas runtime).

Catatan

Jika Anda belum menginstal Windows SDK versi 10.0.17763.0 (Windows 10, versi 1809), atau yang lebih baru, maka Anda perlu memanggil winrt::from_abi di properti dependensi mengubah penanganan aktivitas di atas, alih-alih winrt::get_self.

Mendesain gaya default untuk BgLabelControl

Dalam konstruktornya, BgLabelControl mengatur kunci gaya default untuk dirinya sendiri. Tapi apa itu gaya default? Kontrol kustom (templat) harus memiliki gaya default—yang berisi templat kontrol default—yang dapat digunakan untuk merender dirinya sendiri jika konsumen kontrol tidak mengatur gaya dan/atau templat. Di bagian ini kita akan menambahkan file markup ke proyek yang berisi gaya default kita.

Pastikan Perlihatkan Semua File masih diaktifkan (dalam Penjelajah Solusi). Di bawah simpul proyek Anda, buat folder baru (bukan filter, tetapi folder) dan beri nama "Tema". Di bawah Themes, tambahkan item baru jenis Visual C++>XAML XAML>View, dan beri nama "Generic.xaml". Nama folder dan file harus seperti ini agar kerangka kerja XAML menemukan gaya default untuk kontrol kustom. Hapus konten Generic.xamldefault , dan tempelkan markup di bawah ini.

<!-- \Themes\Generic.xaml -->
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:BgLabelControlApp">

    <Style TargetType="local:BgLabelControl" >
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:BgLabelControl">
                    <Grid Width="100" Height="100" Background="{TemplateBinding Background}">
                        <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{TemplateBinding Label}"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Dalam hal ini, satu-satunya properti yang ditetapkan gaya default adalah templat kontrol. Templat terdiri dari persegi (yang latar belakangnya terikat ke properti Latar Belakang yang dimiliki semua instans jenis Kontrol XAML), dan elemen teks (yang teksnya terikat ke properti dependensi BgLabelControl::Label).

Menambahkan instans BgLabelControl ke halaman UI utama

Buka MainPage.xaml, yang berisi markup XAML untuk halaman UI utama kami. Segera setelah elemen Tombol (di dalam StackPanel), tambahkan markup berikut.

<local:BgLabelControl Background="Red" Label="Hello, World!"/>

Selain itu, tambahkan direktif berikut sehingga MainPage.hjenis MainPage (kombinasi mengkompilasi markup XAML dan kode imperatif) mengetahui jenis kontrol kustom BgLabelControl . Jika Anda ingin menggunakan BgLabelControl dari halaman XAML lain, tambahkan hal yang sama ini termasuk direktif ke file header untuk halaman tersebut juga. Atau, atau, cukup letakkan satu direktif termasuk dalam file header Anda yang telah dikomprelasi.

// MainPage.h
...
#include "BgLabelControl.h"
...

Sekarang bangun dan jalankan proyek. Anda akan melihat bahwa templat kontrol default mengikat ke kuas latar belakang, dan ke label, instans BgLabelControl dalam markup.

Panduan ini menunjukkan contoh sederhana kontrol kustom (templat) di C++/WinRT. Anda dapat membuat kontrol kustom Anda sendiri yang kaya sewenang-wenang dan berfungsi penuh. Misalnya, kontrol kustom dapat mengambil bentuk sesuatu yang rumit sebagai kisi data yang dapat diedit, pemutar video, atau visualizer geometri 3D.

Menerapkan metode yang dapat diganti, seperti MeasureOverride dan OnApplyTemplate

Lihat bagian di Memanggil dan menimpa jenis dasar Anda dengan C++/WinRT.

API penting