Memindahkan sampel Clipboard ke C++/WinRT dari C#—studi kasus

Topik ini menyajikan studi kasus porting salah satu sampel aplikasi Platform Windows Universal (UWP) dari C# ke C++/WinRT. Anda dapat memperoleh praktik dan pengalaman porting dengan mengikuti panduan dan memindahkan sampel untuk diri Anda sendiri saat Anda pergi.

Untuk katalog komprehensif detail teknis yang terlibat dalam porting ke C++/WinRT dari C#, lihat topik pendamping Pindahkan ke C++/WinRT dari C#.

Preface singkat tentang file kode sumber C# dan C++

Dalam proyek C#, file kode sumber Anda terutama .cs adalah file. Saat Anda pindah ke C++, Anda akan melihat bahwa ada lebih banyak jenis file kode sumber untuk membiasakan diri. Alasannya adalah untuk melakukan perbedaan antara pengkompilasi, cara kode sumber C++ digunakan kembali, dan gagasan mendeklarasikan dan menentukan jenis dan fungsinya (metodenya).

Deklarasi fungsi hanya menjelaskan tanda tangan fungsi (jenis pengembaliannya, namanya, serta jenis dan nama parameternya). Definisi fungsi mencakup isi fungsi (implementasinya).

Ini sedikit berbeda ketika datang ke jenis. Anda menentukan jenis dengan memberikan namanya dan dengan (minimal) hanya mendeklarasikan semua fungsi anggotanya (dan anggota lainnya). Itu benar, Anda dapat menentukan jenis bahkan jika Anda tidak menentukan fungsi anggotanya.

  • File kode sumber C++ umum adalah .h (aitch titik) dan .cpp file. File .h adalah file header , dan mendefinisikan satu atau beberapa jenis. Meskipun Anda dapat menentukan fungsi anggota di header, biasanya itulah tujuan .cpp file. Jadi untuk jenis C++ hipotetis MyClass, Anda akan menentukan MyClass di MyClass.h, dan Anda akan menentukan fungsi anggotanya di MyClass.cpp. Agar pengembang lain dapat menggunakan kembali kelas Anda, Anda hanya .h akan berbagi file dan kode objek. Anda akan merahasiakan file Anda .cpp , karena implementasinya merupakan kekayaan intelektual Anda.
  • Header yang telah dikompresi (pch.h). Biasanya ada sekumpulan file header yang Anda sertakan dalam aplikasi Anda, dan Anda tidak sering mengubah file tersebut. Jadi alih-alih memproses konten kumpulan header tersebut setiap kali Anda mengkompilasi, Anda dapat menggabungkan header tersebut ke dalam satu file, mengkompilasinya sekali, lalu menggunakan output langkah prakompilasi tersebut setiap kali Anda membangun. Anda melakukannya melalui file header yang telah dikommpilasikan sebelumnya (biasanya bernama pch.h).
  • .idl File. File-file ini berisi Bahasa Definisi Antarmuka (IDL). Anda dapat menganggap IDL sebagai file header untuk jenis Windows Runtime. Kita akan berbicara lebih lanjut tentang IDL di bagian IDL untuk jenis MainPage.

Mengunduh dan menguji sampel Clipboard

Kunjungi halaman web sampel Clipboard, dan klik Unduh ZIP. Unzip file yang diunduh, dan lihat struktur folder.

  • Versi C# dari kode sumber sampel terkandung dalam folder bernama cs.
  • Versi C++/WinRT dari kode sumber sampel terkandung dalam folder bernama cppwinrt.
  • File lain—digunakan oleh versi C# dan versi C++/WinRT—dapat ditemukan di shared folder dan SharedContent .

Panduan dalam topik ini menunjukkan bagaimana Anda dapat membuat ulang versi C++/WinRT dari sampel Clipboard dengan memindahkannya dari kode sumber C#. Dengan begitu, Anda dapat melihat bagaimana Anda dapat memindahkan proyek C# Anda sendiri ke C++/WinRT.

Untuk merasakan apa yang dilakukan sampel, buka solusi C# (\Clipboard_sample\cs\Clipboard.sln), ubah konfigurasi yang sesuai (mungkin ke x64), bangun, dan jalankan. Antarmuka pengguna (UI) sampel sendiri memandu Anda melalui berbagai fiturnya, langkah demi langkah.

Tip

Folder akar sampel yang Anda unduh mungkin dinamai Clipboard bukan Clipboard_sample. Tetapi kami akan terus merujuk ke folder Clipboard_sample tersebut untuk membedakannya dari versi C++/WinRT yang akan Anda buat di langkah selanjutnya.

Buat Aplikasi Kosong (C++/WinRT), bernama Clipboard

Catatan

Untuk informasi tentang 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.

Mulai proses porting dengan membuat proyek C++/WinRT baru di Microsoft Visual Studio. Buat proyek baru menggunakan templat proyek Blank App (C++/WinRT). Atur namanya ke Clipboard, dan (sehingga struktur folder Anda akan cocok dengan penelusuran) pastikan bahwa Tempatkan solusi dan proyek di direktori yang sama tidak dicentang.

Hanya untuk mendapatkan garis besar, pastikan proyek baru ini kosong, dibangun dan dijalankan.

File package.appxmanifest, dan aset

Jika versi C# dan C++/WinRT dari sampel tidak perlu diinstal berdampingan pada komputer yang sama, maka file sumber manifes paket aplikasi dua proyek (Package.appxmanifest) dapat identik. Dalam hal ini, Anda hanya dapat menyalin Package.appxmanifest dari proyek C# ke proyek C++/WinRT, dan Anda selesai.

Agar dua versi sampel berdampingan, mereka membutuhkan pengidentifikasi yang berbeda. Dalam hal ini, dalam proyek C++/WinRT, buka Package.appxmanifest file di editor XML, dan catat ketiga nilai ini.

  • Di dalam elemen /Package/Identity, perhatikan nilai atribut Name. Ini adalah nama paket. Untuk proyek yang baru dibuat, proyek akan memberinya nilai awal GUID unik.
  • Di dalam elemen /Package/Applications/Application, perhatikan nilai atribut Id. Ini adalah id aplikasi.
  • Di dalam elemen /Package/mp:Telepon Identity, perhatikan nilai atribut Telepon ProductId. Sekali lagi, untuk proyek yang baru dibuat, ini akan diatur ke GUID yang sama dengan nama paket diatur ke.

Kemudian salin Package.appxmanifest dari proyek C# ke proyek C++/WinRT. Terakhir, Anda dapat memulihkan tiga nilai yang Anda catat. Atau Anda dapat mengedit nilai yang disalin untuk membuatnya unik dan/atau sesuai untuk aplikasi dan untuk organisasi Anda (seperti yang biasanya Anda lakukan untuk proyek baru). Misalnya, dalam hal ini alih-alih memulihkan nilai nama paket, kita hanya dapat mengubah nilai yang disalin dari Microsoft.SDKSamples.Clipboard.CS ke Microsoft.SDKSamples.Clipboard.CppWinRT. Dan kita dapat membiarkan id aplikasi diatur ke Aplikasi. Selama nama paket atau id aplikasi berbeda, kedua aplikasi akan memiliki ID Model Pengguna Aplikasi (AUMID) yang berbeda. Dan itulah yang diperlukan agar dua aplikasi diinstal berdampingan pada komputer yang sama.

Untuk tujuan panduan ini, masuk akal untuk membuat beberapa perubahan lain di Package.appxmanifest. Ada tiga kemunculan string Clipboard C# Sample. Ubah itu menjadi Sampel Clipboard C++/WinRT.

Dalam proyek C++/WinRT, Package.appxmanifest file dan proyek sekarang tidak sinkron sehubungan dengan file aset yang mereka referensikan. Untuk memperbaikinya, pertama-tama hapus aset dari proyek C++/WinRT dengan memilih semua file di Assets folder (dalam Penjelajah Solusi di Visual Studio) dan menghapusnya (pilih Hapus dalam dialog).

Proyek C# mereferensikan file aset dari folder bersama. Anda dapat melakukan hal yang sama dalam proyek C++/WinRT, atau Anda dapat menyalin file seperti yang akan kita lakukan dalam panduan ini.

Navigasi ke folder \Clipboard_sample\SharedContent\media. Pilih tujuh file yang disertakan proyek C# (microsoft-sdk.png, , smalltile-sdk.png, splash-sdk.png, squaretile-sdk.pngstorelogo-sdk.png, tile-sdk.png, dan windows-sdk.png), salin, dan tempelkan ke \Clipboard\Clipboard\Assets folder dalam proyek baru.

Assets Klik kanan folder (dalam Penjelajah Solusi di proyek C++/WinRT) >Tambahkan>item yang Ada... dan navigasi ke .\Clipboard\Clipboard\Assets Di pemilih file, pilih tujuh file dan klik Tambahkan.

Package.appxmanifest sekarang kembali sinkron dengan file aset proyek.

MainPage, termasuk fungsionalitas yang mengonfigurasi sampel

Sampel Clipboard—seperti semua sampel aplikasi Platform Windows Universal (UWP) —terdiri dari kumpulan skenario yang dapat dilalui pengguna satu per satu. Kumpulan skenario dalam sampel tertentu dikonfigurasi dalam kode sumber sampel. Setiap skenario dalam koleksi adalah item data yang menyimpan judul, serta jenis kelas dalam proyek yang mengimplementasikan skenario.

Dalam versi C# sampel, jika Anda melihat dalam SampleConfiguration.cs file kode sumber, Anda akan melihat dua kelas. Sebagian besar logika konfigurasi berada di kelas MainPage , yang merupakan kelas parsial (membentuk kelas lengkap ketika dikombinasikan dengan markup di MainPage.xaml dan kode imperatif di MainPage.xaml.cs). Kelas lain dalam file kode sumber ini adalah Skenario, dengan properti Judul dan ClassType-nya.

Selama beberapa subbagian berikutnya, kita akan melihat cara mem-port MainPage dan Skenario.

IDL untuk jenis MainPage

Mari kita mulai bagian ini dengan berbicara secara singkat tentang Bahasa Definisi Antarmuka (IDL), dan bagaimana hal itu membantu kita ketika kita memprogram dengan C++/WinRT. IDL adalah semacam kode sumber yang menjelaskan permukaan yang dapat dipanggil dari jenis Windows Runtime. Permukaan jenis yang dapat dipanggil (atau publik) diproyeksikan ke seluruh dunia, sehingga jenisnya dapat dikonsumsi. Bagian yang diproyeksikan dari jenis kontras dengan implementasi internal aktual dari jenis tersebut, yang tentu saja tidak dapat dipanggil, dan bukan publik. Ini hanya bagian yang diproyeksikan yang kita tentukan dalam IDL.

Setelah menulis kode sumber IDL (dalam .idl file), Anda kemudian dapat mengkompilasi IDL ke dalam file metadata yang dapat dibaca mesin (juga dikenal sebagai Metadata Windows). File metadata tersebut memiliki ekstensi .winmd, dan berikut adalah beberapa kegunaannya.

  • Dapat .winmd menjelaskan jenis Windows Runtime dalam komponen. Ketika Anda mereferensikan Komponen Runtime Windows (WRC) dari proyek aplikasi, proyek aplikasi membaca Metadata Windows milik WRC (metadata tersebut mungkin berada dalam file terpisah, atau mungkin dimas ke dalam file yang sama dengan WRC itu sendiri) sehingga Anda dapat menggunakan jenis WRC dari dalam aplikasi.
  • Dapat .winmd menjelaskan jenis Windows Runtime di salah satu bagian aplikasi Anda sehingga dapat dikonsumsi oleh bagian yang berbeda dari aplikasi yang sama. Misalnya, jenis Windows Runtime yang digunakan oleh halaman XAML di aplikasi yang sama.
  • Untuk memudahkan Anda menggunakan jenis Windows Runtime (bawaan atau pihak ketiga), sistem build C++/WinRT menggunakan .winmd file untuk menghasilkan jenis pembungkus untuk mewakili bagian yang diproyeksikan dari jenis Windows Runtime tersebut.
  • Untuk mempermudah Anda menerapkan jenis Windows Runtime Anda sendiri, sistem build C++/WinRT mengubah IDL Anda menjadi .winmd file, lalu menggunakannya untuk menghasilkan pembungkus untuk proyeksi Anda, serta stub untuk mendasarkan implementasi Anda (kita akan berbicara lebih banyak tentang bongkahan ini nanti dalam topik ini).

Versi spesifik IDL yang kami gunakan dengan C++/WinRT adalah Microsoft Interface Definition Language 3.0. Di sisa bagian topik ini, kita akan memeriksa jenis C# MainPage dalam beberapa detail. Kita akan memutuskan bagian mana yang perlu berada dalam proyeksi jenis C++/WinRT MainPage (yaitu, dalam panggilannya, atau publik, permukaan), dan yang hanya dapat menjadi bagian dari implementasinya. Perbedaan itu penting karena ketika kita datang untuk menulis IDL kita (yang akan kita lakukan di bagian setelah ini), kita hanya akan menentukan bagian yang dapat dipanggil di sana.

File kode sumber C# yang bersama-sama mengimplementasikan jenis MainPage adalah: MainPage.xaml (yang akan segera kita port, dengan menyalinnya), MainPage.xaml.cs, dan SampleConfiguration.cs.

Dalam versi C++/WinRT, kita akan memperhitungkan jenis MainPage kita ke dalam file kode sumber dengan cara yang sama. Kami akan mengambil logika dan MainPage.xaml.cs menerjemahkannya untuk sebagian besar ke MainPage.h dan MainPage.cpp. Dan untuk logika di SampleConfiguration.cs, kita akan menerjemahkannya ke SampleConfiguration.h dan SampleConfiguration.cpp.

Kelas dalam aplikasi C# Platform Windows Universal (UWP) tentu saja jenis Windows Runtime. Tetapi ketika Anda menulis jenis dalam aplikasi C++/WinRT, Anda dapat memilih apakah jenis tersebut adalah jenis Windows Runtime, atau kelas/struktur/enumerasi C++ biasa.

Halaman XAML apa pun dalam proyek kami harus merupakan jenis Windows Runtime, sehingga MainPage harus menjadi jenis Windows Runtime. Dalam proyek C++/WinRT, MainPage sudah merupakan jenis Windows Runtime, jadi kita tidak perlu mengubah aspek itu. Secara khusus, ini adalah kelas runtime.

  • Untuk detail selengkapnya tentang apakah Anda harus menulis kelas runtime untuk jenis tertentu atau tidak, lihat topik API Penulis dengan C++/WinRT.
  • Di C++/WinRT, implementasi internal kelas runtime, dan bagian yang diproyeksikan (publik), ada dalam bentuk dua kelas yang berbeda. Ini dikenal sebagai jenis implementasi dan jenis yang diproyeksikan. Anda dapat mempelajari lebih lanjut tentang mereka dalam topik yang disebutkan di poin sebelumnya, dan juga di Menggunakan API dengan C++/WinRT.
  • Untuk informasi selengkapnya tentang koneksi antara kelas runtime dan IDL (.idl file), Anda dapat membaca dan mengikuti bersama dengan topik kontrol XAML; mengikat ke properti C++/WinRT. Topik itu menjelaskan proses penulisan kelas runtime baru, langkah pertama adalah menambahkan item File Midl (.idl) baru ke proyek.

Untuk MainPage, kami sebenarnya sudah memiliki file yang diperlukan MainPage.idl dalam proyek C++/WinRT. Itu karena templat proyek membuatnya untuk kita. Tetapi nanti dalam panduan ini kita akan menambahkan file lebih lanjut .idl ke proyek.

Kita akan segera melihat daftar IDL apa yang perlu kita tambahkan ke file yang ada MainPage.idl . Sebelum itu, kita memiliki beberapa alasan untuk melakukan tentang apa yang dilakukan, dan apa yang tidak, perlu masuk ke IDL.

Untuk menentukan anggota MainPage mana yang perlu kami deklarasikan MainPage.idl (sehingga mereka menjadi bagian dari kelas runtime MainPage ), dan yang hanya dapat menjadi anggota jenis implementasi MainPage , mari kita buat daftar anggota kelas C# MainPage . Kami menemukan anggota tersebut dengan melihat masuk MainPage.xaml.cs dan masuk SampleConfiguration.cs.

Kami menemukan total dua belas protected bidang dan private metode. Dan kami menemukan anggota berikut public .

  • Konstruktor MainPage()default .
  • Bidang statis Saat ini dan FEATURE_NAME.
  • Properti IsClipboardContentChangedEnabled dan Skenario.
  • Metode BuildClipboardFormatsOutputString, DisplayToast, EnableClipboardContentChangedNotifications, dan NotifyUser.

Ini adalah anggota yang public merupakan kandidat untuk mendeklarasikan di MainPage.idl. Jadi mari kita periksa masing-masing dan lihat apakah mereka perlu menjadi bagian dari kelas runtime MainPage , atau apakah mereka hanya perlu menjadi bagian dari implementasinya.

  • Konstruktor MainPage()default . Untuk Halaman XAML, biasanya mendeklarasikan konstruktor default dalam IDL-nya. Dengan begitu, kerangka kerja UI XAML dapat mengaktifkan jenisnya.
  • Bidang statis Saat ini digunakan dari dalam skenario individual halaman XAML untuk mengakses instans Aplikasi MainPage. Karena Saat ini tidak digunakan untuk beroperasi dengan kerangka kerja XAML (juga tidak digunakan di seluruh unit kompilasi), kami dapat mencadangkannya hanya menjadi anggota jenis implementasi. Dengan proyek Anda sendiri dalam kasus seperti ini, Anda dapat memilih untuk melakukannya. Tetapi karena bidang adalah instans dari jenis yang diproyeksikan, rasanya logis untuk mendeklarasikannya di IDL. Jadi itulah yang akan kita lakukan di sini (dan melakukannya juga membuat kode sedikit lebih bersih).
  • Ini adalah kasus serupa untuk bidang FEATURE_NAME statis, yang diakses dalam jenis MainPage. Sekali lagi, memilih untuk mendeklarasikannya di IDL membuat kode kita sedikit lebih bersih.
  • Properti IsClipboardContentChangedEnabled hanya digunakan di kelas OtherScenarios . Jadi selama port, kita akan menyederhanakan hal-hal sedikit, dan menjadikannya bidang privat dari kelas runtime OtherScenarios . Jadi yang satu tidak akan masuk ke IDL.
  • Skenario properti adalah kumpulan objek jenis Skenario (jenis yang kami sebutkan sebelumnya). Kita akan berbicara tentang Skenario di sub-bagian berikutnya, jadi mari kita tinggalkan properti Skenario sampai saat itu juga.
  • Metode BuildClipboardFormatsOutputString, DisplayToast, dan EnableClipboardContentChangedNotifications adalah fungsi utilitas yang terasa lebih berkaitan dengan status umum sampel daripada tentang halaman utama. Jadi selama port, kita akan merefaktor ketiga metode ini ke jenis utilitas baru bernama SampleState (yang tidak perlu menjadi jenis Windows Runtime). Untuk alasan itu, ketiga metode ini tidak akan masuk ke IDL.
  • Metode NotifyUser dipanggil dari dalam skenario individual halaman XAML pada instans MainPage yang dikembalikan dari bidang Current statis. Karena (seperti yang telah disebutkan) Saat ini adalah instans dari jenis yang diproyeksikan, kita perlu mendeklarasikan NotifyUser di IDL. NotifyUser mengambil parameter jenis NotifyType. Kita akan membicarakannya di sub-bagian berikutnya.

Setiap anggota yang ingin Anda ikat datanya juga perlu dideklarasikan dalam IDL (baik Anda menggunakan {x:Bind} atau {Binding}). Untuk informasi selengkapnya, lihat Pengikatan data.

Kami membuat kemajuan: kami mengembangkan daftar anggota mana yang akan ditambahkan, dan yang tidak akan ditambahkan, ke MainPage.idl file. Tetapi kita masih harus membahas properti Skenario , dan jenis NotifyType . Jadi mari kita lakukan berikutnya.

IDL untuk jenis Skenario dan NotifyType

Kelas Skenario didefinisikan dalam SampleConfiguration.cs. Kami memiliki keputusan untuk membuat tentang cara memindahkan kelas tersebut ke C++/WinRT. Secara default, kita mungkin akan menjadikannya C++ structbiasa. Tetapi jika Skenario digunakan di seluruh biner, atau untuk beroperasi dengan kerangka kerja XAML, maka skenario tersebut perlu dideklarasikan dalam IDL sebagai jenis Windows Runtime.

Mempelajari kode sumber C#, kami menemukan bahwa Skenario digunakan dalam konteks ini.

<ListBox x:Name="ScenarioControl" ... >
var itemCollection = new List<Scenario>();
int i = 1;
foreach (Scenario s in scenarios)
{
    itemCollection.Add(new Scenario { Title = $"{i++}) {s.Title}", ClassType = s.ClassType });
}
ScenarioControl.ItemsSource = itemCollection;

Kumpulan objek Skenario sedang ditetapkan ke properti ItemsSource dari ListBox (yang merupakan kontrol item). Karena Skenariomemang perlu dioperasikan dengan XAML, skenario harus berupa jenis Windows Runtime. Jadi perlu didefinisikan dalam IDL. Menentukan jenis Skenario di IDL menyebabkan sistem build C++/WinRT menghasilkan definisi kode sumber Skenario untuk Anda dalam file header di balik layar (nama dan lokasi yang tidak penting untuk panduan ini).

Dan Anda akan ingat bahwa MainPage.Scenarios adalah kumpulan objek Skenario , yang baru saja kami katakan perlu berada di IDL. Untuk itu, MainPage.Scenarios sendiri juga perlu dideklarasikan dalam IDL.

NotifyType adalah yang enum dideklarasikan dalam C#'s MainPage.xaml.cs. Karena kami meneruskan NotifyType ke metode milik kelas runtime MainPage , NotifyType juga perlu menjadi jenis Windows Runtime; dan perlu didefinisikan dalam MainPage.idl.

Sekarang mari kita tambahkan ke MainPage.idl file jenis baru dan anggota baru Mainpage yang telah kami putuskan untuk dinyatakan dalam IDL. Pada saat yang sama, kami akan menghapus dari IDL anggota tempat penampung Mainpage yang diberikan templat proyek Visual Studio kepada kami.

Jadi, dalam proyek C++/WinRT Anda, buka MainPage.idl, dan edit sehingga terlihat seperti daftar di bawah ini. Perhatikan bahwa salah satu pengeditannya adalah mengubah nama namespace layanan dari Clipboard ke SDKTemplate. Jika mau, Anda cukup mengganti seluruh konten MainPage.idl dengan kode berikut. Tweak lain yang perlu diperhatikan adalah kami mengubah nama Skenario::ClassType menjadi Skenario::ClassName.

// MainPage.idl
namespace SDKTemplate
{
    struct Scenario
    {
        String Title;
        Windows.UI.Xaml.Interop.TypeName ClassName;
    };

    enum NotifyType
    {
        StatusMessage,
        ErrorMessage
    };

    [default_interface]
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        MainPage();

        static MainPage Current{ get; };
        static String FEATURE_NAME{ get; };

        static Windows.Foundation.Collections.IVector<Scenario> scenarios{ get; };

        void NotifyUser(String strMessage, NotifyType type);
    };
}

Catatan

Untuk informasi selengkapnya tentang konten .idl file dalam proyek C++/WinRT, lihat Microsoft Interface Definition Language 3.0.

Dengan pekerjaan porting Anda sendiri, Anda mungkin tidak ingin atau perlu mengubah nama namespace seperti yang kami lakukan di atas. Kami melakukannya di sini hanya karena namespace default proyek C# yang kami porting adalah SDKTemplate; sementara nama proyek dan perakitannya adalah Clipboard.

Tapi, saat kita melanjutkan dengan port dalam panduan ini, kita akan mengubah setiap kemunculan dalam kode sumber nama namespace Clipboard menjadi SDKTemplate. Ada juga tempat di properti proyek C++/WinRT tempat nama namespace Clipboard muncul, jadi kita akan mengambil kesempatan untuk mengubahnya sekarang.

Di Visual Studio, untuk proyek C++/WinRT, atur properti proyek Properti>Umum C++/Namespace Akar WinRT>ke nilai SDKTemplate.

Simpan IDL, dan buat ulang file stub

Topik kontrol XAML; mengikat properti C++/WinRT memperkenalkan gagasan file stub, dan menunjukkan kepada Anda panduan tentang mereka dalam tindakan. Kami juga menyebutkan stub sebelumnya dalam topik ini ketika kami menyebutkan bahwa sistem build C++/WinRT mengubah konten file Anda .idl menjadi Metadata Windows, dan kemudian dari metadata tersebut alat bernama cppwinrt.exe menghasilkan stub tempat Anda dapat mendasarkan implementasi Anda.

Setiap kali Anda menambahkan, menghapus, atau mengubah sesuatu di IDL Anda, dan membangun, sistem build memperbarui implementasi stub dalam file stub tersebut. Jadi setiap kali Anda mengubah IDL dan build, kami sarankan Anda melihat file stub tersebut, menyalin tanda tangan yang diubah, dan menempelkannya ke dalam proyek Anda. Kami akan memberikan lebih spesifik dan contoh tentang cara melakukannya dalam sesaat. Tetapi keuntungan melakukan ini adalah memberi Anda cara bebas kesalahan untuk mengetahui setiap saat seperti apa bentuk jenis implementasi Anda, dan apa tanda tangan metodenya.

Pada titik ini dalam panduan, kami sudah selesai mengedit MainPage.idl file untuk saat ini, jadi Anda harus menyimpannya sekarang. Proyek ini tidak akan dibangun hingga selesai saat ini, tetapi melakukan build sekarang adalah hal yang berguna untuk dilakukan karena meregenerasi file stub untuk MainPage. Jadi bangun proyek sekarang, dan abaikan kesalahan build apa pun.

Untuk proyek C++/WinRT ini, file stub dihasilkan di \Clipboard\Clipboard\Generated Files\sources folder . Anda akan menemukannya di sana setelah build parsial berakhir (sekali lagi, seperti yang diharapkan, build tidak akan berhasil sepenuhnya. Tetapi langkah yang kami minati—menghasilkan ganja—akan berhasil). File yang kami minati adalah MainPage.h dan MainPage.cpp.

Dalam dua file stub tersebut, Anda akan melihat implementasi stub baru dari anggota MainPage yang kami tambahkan ke IDL (Saat ini dan FEATURE_NAME, misalnya). Anda mungkin ingin menyalin implementasi stub tersebut MainPage.h ke dalam file dan MainPage.cpp yang sudah ada dalam proyek. Pada saat yang sama, seperti yang kami lakukan dengan IDL, kami akan menghapus dari file yang ada anggota tempat penampung Mainpage yang diberikan templat proyek Visual Studio (properti dummy bernama MyProperty, dan penanganan aktivitas bernama ClickHandler).

Bahkan, satu-satunya anggota versi MainPage saat ini yang ingin kita simpan adalah konstruktor.

Setelah Anda menyalin anggota baru dari file stub, menghapus anggota yang tidak kami inginkan, dan memperbarui namespace layanan, MainPage.h file dan MainPage.cpp dalam proyek Anda akan terlihat seperti daftar kode di bawah ini. Perhatikan bahwa ada dua jenis MainPage . Satu di namespace implementasi , dan yang kedua di namespace factory_implementation . Satu-satunya perubahan yang telah kita lakukan pada factory_implementation adalah menambahkan SDKTemplate ke namespace layanannya.

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

namespace winrt::SDKTemplate::implementation
{
    struct MainPage : MainPageT<MainPage>
    {
        MainPage();

        static SDKTemplate::MainPage Current();
        static hstring FEATURE_NAME();
        static Windows::Foundation::Collections::IVector<SDKTemplate::Scenario> scenarios();
        void NotifyUser(hstring const& strMessage, SDKTemplate::NotifyType const& type);
    };
}
namespace winrt::SDKTemplate::factory_implementation
{
    struct MainPage : MainPageT<MainPage, implementation::MainPage>
    {
    };
}
// MainPage.cpp
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"

namespace winrt::SDKTemplate::implementation
{
    MainPage::MainPage()
    {
        InitializeComponent();
    }
    SDKTemplate::MainPage MainPage::Current()
    {
        throw hresult_not_implemented();
    }
    hstring MainPage::FEATURE_NAME()
    {
        throw hresult_not_implemented();
    }
    Windows::Foundation::Collections::IVector<SDKTemplate::Scenario> MainPage::scenarios()
    {
        throw hresult_not_implemented();
    }
    void MainPage::NotifyUser(hstring const& strMessage, SDKTemplate::NotifyType const& type)
    {
        throw hresult_not_implemented();
    }
}

Untuk string, C# menggunakan System.String. Lihat metode MainPage.NotifyUser misalnya. Dalam IDL kami, kami mendeklarasikan string dengan String, dan ketika cppwinrt.exe alat menghasilkan kode C++/WinRT untuk kami, alat ini menggunakan jenis winrt::hstring. Setiap kali kita menemukan string dalam kode C#, kita akan port itu ke winrt::hstring. Untuk informasi selengkapnya, lihat Penanganan string di C++/WinRT.

Untuk penjelasan const& tentang parameter dalam tanda tangan metode, lihat Parameter-passing.

Perbarui semua deklarasi/referensi namespace yang tersisa, dan buat

Sebelum membangun proyek C++/WinRT, temukan deklarasi (dan referensi ke) namespace Clipboard , dan ubah menjadi SDKTemplate.

  • MainPage.xaml dan App.xaml. Namespace muncul dalam nilai x:Class atribut dan xmlns:local .
  • App.idl.
  • App.h.
  • App.cpp. Ada dua using namespace arahan (cari substring using namespace Clipboard), dan dua kualifikasi dari jenis MainPage (cari Clipboard::MainPage). Mereka perlu berubah.

Karena kami menghapus penanganan aktivitas dari MainPage, juga masuk ke MainPage.xaml dan hapus elemen Tombol dari markup.

Simpan semua file. Bersihkan solusi (Bangun>Solusi Bersih), lalu bangun. Setelah mengikuti semua perubahan sejauh ini, persis seperti yang ditulis, build diharapkan berhasil.

Menerapkan anggota MainPage yang kami deklarasikan di IDL

Konstruktor, Saat Ini, dan FEATURE_NAME

Berikut adalah kode yang relevan (dari proyek C#) yang perlu kita porting.

<!-- MainPage.xaml -->
...
<TextBlock x:Name="SampleTitle" ... />
...
// MainPage.xaml.cs
...
public sealed partial class MainPage : Page
{
    public static MainPage Current;

    public MainPage()
    {
        InitializeComponent();
        Current = this;
        SampleTitle.Text = FEATURE_NAME;
    }
...
}
...

// SampleConfiguration.cs
...
public partial class MainPage : Page
{
    public const string FEATURE_NAME = "Clipboard C# sample";
...
}
...

Segera, kita akan menggunakan MainPage.xaml kembali secara keseluruhan (dengan menyalinnya). Untuk saat ini (di bawah), kami akan menambahkan elemen TextBlock untuk sementara, dengan nama yang sesuai, ke dalam MainPage.xaml proyek C++/WinRT.

FEATURE_NAME adalah bidang statis MainPage(bidang C# const pada dasarnya statis dalam perilakunya), didefinisikan dalam SampleConfiguration.cs. Untuk C++/WinRT, alih-alih bidang (statis), kita akan menjadikannya ekspresi C++/WinRT dari properti baca-saja (statis). Cara C++/WinRT untuk mengekspresikan getter properti adalah sebagai fungsi yang mengembalikan nilai properti, dan tidak mengambil parameter (pengakses). Jadi bidang statis C# FEATURE_NAME menjadi fungsi aksesor statis C++/WinRT FEATURE_NAME (dalam hal ini, mengembalikan string literal).

Kebetulan, kami akan melakukan hal yang sama jika kami memindahkan properti baca-saja C#. Untuk properti C# yang dapat ditulis, cara C++/WinRT untuk mengekspresikan setter properti adalah sebagai void fungsi yang mengambil nilai properti sebagai parameter (mutator). Dalam kedua kasus, jika bidang atau properti C# statis, begitu juga aksesor C++/WinRT dan/atau mutator.

Saat ini adalah bidang statis (bukan konstanta) dari MainPage. Sekali lagi, kita akan membuatnya (ekspresi C++/WinRT dari) properti baca-saja, dan sekali lagi membuatnya statis. Jika FEATURE_NAME konstan, Saat ini tidak. Jadi di C++/WinRT kita akan membutuhkan bidang dukungan, dan aksesor kita akan mengembalikannya. Jadi, dalam proyek C++/WinRT, kami akan mendeklarasikan di MainPage.h bidang statis privat bernama saat ini, kami akan menentukan/menginisialisasi saat ini di MainPage.cpp (karena memiliki durasi penyimpanan statis), dan kami akan mengaksesnya melalui fungsi aksesor statis publik bernama Current.

Konstruktor itu sendiri melakukan beberapa tugas, yang mudah untuk port.

Dalam proyek C++/WinRT, tambahkan item Visual C++>Code>C++ File (.cpp) baru dengan nama .SampleConfiguration.cpp

Edit MainPage.xaml, MainPage.h, MainPage.cpp, dan SampleConfiguration.cpp agar sesuai dengan daftar di bawah ini.

<!-- MainPage.xaml -->
...
<StackPanel ...>
    <TextBlock x:Name="SampleTitle" />
</StackPanel>
...
// MainPage.h
...
namespace winrt::SDKTemplate::implementation
{
    struct MainPage : MainPageT<MainPage>
    {
...
        static SDKTemplate::MainPage Current() { return current; }
...
    private:
        static SDKTemplate::MainPage current;
...
    };
...
}

// MainPage.cpp
...
namespace winrt::SDKTemplate::implementation
{
    SDKTemplate::MainPage MainPage::current{ nullptr };

    MainPage::MainPage()
    {
        InitializeComponent();
        MainPage::current = *this;
        SampleTitle().Text(FEATURE_NAME());
    }
...
}

// SampleConfiguration.cpp
#include "pch.h"
#include "MainPage.h"

using namespace winrt;
using namespace SDKTemplate;

hstring implementation::MainPage::FEATURE_NAME()
{
    return L"Clipboard C++/WinRT Sample";
}

Selain itu, pastikan untuk menghapus badan fungsi yang ada dari MainPage.cpp untuk MainPage::Current() dan MainPage::FEATURE_NAME(), karena kita sekarang mendefinisikan metode tersebut di tempat lain.

Seperti yang Anda lihat, MainPage::current dinyatakan sebagai jenis SDKTemplate::MainPage, yang merupakan jenis yang diproyeksikan. Ini bukan jenis SDKTemplate::implementation::MainPage, yang merupakan jenis implementasi. Jenis yang diproyeksikan adalah jenis yang dirancang untuk dikonsumsi baik dalam proyek untuk interoperatasi XAML, atau di seluruh biner. Jenis implementasi adalah apa yang Anda gunakan untuk mengimplementasikan fasilitas yang telah Anda ekspos pada jenis yang diproyeksikan. Karena deklarasi MainPage::current (in ) muncul dalam MainPage.hnamespace implementasi (winrt::SDKTemplate::implementation), MainPage yang tidak memenuhi syarat akan mengacu pada jenis implementasi. Jadi, kami memenuhi syarat dengan SDKTemplate:: agar lebih jelas bahwa kami ingin MainPage::current menjadi instans dari jenis yang diproyeksikan winrt::SDKTemplate::MainPage.

Dalam konstruktor, ada beberapa poin yang terkait dengan MainPage::current = *this; itu layak mendapatkan penjelasan.

  • Saat Anda menggunakan penunjuk this di dalam anggota jenis implementasi, this penunjuk tentu saja merupakan penunjuk ke jenis implementasi.
  • Untuk mengonversi this penunjuk ke jenis proyeksi yang sesuai, dereferensikannya. Asalkan Anda menghasilkan jenis implementasi dari IDL (seperti yang kami miliki di sini), jenis implementasi memiliki operator konversi yang dikonversi ke jenis yang diproyeksikan. Itu sebabnya tugas di sini berfungsi.

Untuk informasi selengkapnya tentang detail tersebut, lihat Membuat instans dan mengembalikan jenis dan antarmuka implementasi.

Juga dalam konstruktor adalah SampleTitle().Text(FEATURE_NAME());. Bagian ini SampleTitle() adalah panggilan ke fungsi aksesor sederhana bernama SampleTitle, yang mengembalikan TextBlock yang kami tambahkan ke XAML. Setiap kali Anda x:Name elemen XAML, pengkompilasi XAML menghasilkan aksesor untuk Anda yang dinamai untuk elemen . Bagian .Text(...) memanggil fungsi Mutator Teks pada objek TextBlock yang dikembalikan oleh pengakses SampleTitle . Dan FEATURE_NAME() memanggil fungsi aksesor MainPage statis ::FEATURE_NAME kami untuk mengembalikan string literal. Sama sekali, baris kode tersebut mengatur properti Teks dari TextBlock bernama SampleTitle.

Perhatikan bahwa karena string lebar di Windows Runtime, untuk memindahkan string secara harfiah, kami awali dengan awalan pengodean Lwide-char . Jadi kita mengubah (misalnya) "string literal" menjadi L"string literal". Lihat juga Literal string lebar.

Skenario

Berikut adalah kode C# relevan yang perlu kita porting.

// MainPage.xaml.cs
...
public sealed partial class MainPage : Page
{
...
    public List<Scenario> Scenarios
    {
        get { return this.scenarios; }
    }
...
}
...

// SampleConfiguration.cs
...
public partial class MainPage : Page
{
...
    List<Scenario> scenarios = new List<Scenario>
    {
        new Scenario() { Title = "Copy and paste text", ClassType = typeof(CopyText) },
        new Scenario() { Title = "Copy and paste an image", ClassType = typeof(CopyImage) },
        new Scenario() { Title = "Copy and paste files", ClassType = typeof(CopyFiles) },
        new Scenario() { Title = "Other Clipboard operations", ClassType = typeof(OtherScenarios) }
    };
...
}
...

Dari penyelidikan kami sebelumnya, kami tahu bahwa kumpulan objek Skenario ini ditampilkan di ListBox. Di C++/WinRT, ada batasan jenis koleksi yang dapat kami tetapkan ke properti ItemsSource kontrol item. Koleksi harus berupa vektor atau vektor yang dapat diamati, dan elemennya harus salah satu dari yang berikut:

Untuk kasus IInspectable, jika elemen bukan sendiri kelas runtime, maka elemen-elemen tersebut harus berupa jenis yang dapat dikotak dan dikotak ke dan dari IInspectable. Dan itu berarti bahwa mereka harus menjadi jenis Windows Runtime (lihat Nilai Tinju dan membuka kotak ke IInspectable).

Untuk studi kasus ini, kami tidak menjadikan Skenario sebagai kelas runtime. Itu masih pilihan yang masuk akal, meskipun. Dan akan ada kasus dalam pekerjaan porting Anda sendiri di mana kelas runtime pasti akan menjadi cara untuk pergi. Misalnya, jika Anda perlu membuat jenis elemen dapat diamati (lihat kontrol XAML; mengikat properti C++/WinRT), atau jika elemen perlu memiliki metode karena alasan lain, dan itu lebih dari sekadar sekumpulan anggota data.

Karena, dalam panduan ini, kita tidak akan pergi dengan kelas runtime untuk jenis Skenario, maka kita perlu berpikir tentang tinju. Jika kita membuat Skenario sebagai C++ structbiasa, maka kita tidak akan dapat mengkotaknya. Tapi kami mendeklarasikan Skenario sebagai struct di IDL, dan agar kami dapat kotak itu.

Kami dibiarkan dengan pilihan tinju Skenario sebelumnya, atau menunggu sampai kami akan menetapkan ke ItemsSource, dan mengetikkannya tepat waktu. Berikut adalah beberapa pertimbangan mengenai dua opsi tersebut.

  • Tinju sebelumnya. Untuk opsi ini, anggota data kami adalah kumpulan IInspectable yang siap ditetapkan ke UI. Pada inisialisasi, kami mengemas objek Skenario ke dalam anggota data tersebut. Kita hanya perlu satu salinan koleksi itu, tetapi kita harus membuka kotak elemen setiap kali kita perlu membaca bidangnya.
  • Tinju tepat pada waktunya. Untuk opsi ini, anggota data kami adalah kumpulan Skenario. Ketika waktunya tiba untuk menetapkan ke UI, kami mengetik objek Skenario dari anggota data ke dalam koleksi baru IInspectable. Kita dapat membaca bidang elemen di anggota data tanpa membuka kotak, tetapi kita memerlukan dua salinan koleksi.

Seperti yang Anda lihat, untuk koleksi kecil seperti ini, pro dan kontra menjadikannya sesuatu dari cuci. Jadi, untuk studi kasus ini, kita akan pergi dengan opsi just-in-time.

Anggota skenario adalah bidang MainPage, yang ditentukan dan diinisialisasi dalam SampleConfiguration.cs . Dan Skenario adalah properti baca-saja dari MainPage, yang didefinisikan dalam MainPage.xaml.cs (dan diimplementasikan untuk hanya mengembalikan bidang skenario ). Kita akan melakukan sesuatu yang serupa dalam proyek C++/WinRT; tetapi kita akan membuat dua anggota statis (karena kita hanya membutuhkan satu instans di seluruh aplikasi; dan sehingga kita dapat mengaksesnya tanpa memerlukan instans kelas). Dan kita akan menamai mereka skenarioInner dan skenario, masing-masing. Kami akan mendeklarasikan skenarioInner di MainPage.h. Dan, karena memiliki durasi penyimpanan statis, kami akan menentukan/menginisialisasinya dalam .cpp file (SampleConfiguration.cpp, dalam hal ini).

Edit MainPage.h dan SampleConfiguration.cpp agar sesuai dengan daftar di bawah ini.

// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
...
    static Windows::Foundation::Collections::IVector<Scenario> scenarios() { return scenariosInner; }
...
private:
    static winrt::Windows::Foundation::Collections::IVector<Scenario> scenariosInner;
...
};

// SampleConfiguration.cpp
...
using namespace Windows::Foundation::Collections;
...
IVector<Scenario> implementation::MainPage::scenariosInner = winrt::single_threaded_observable_vector<Scenario>(
{
    Scenario{ L"Copy and paste text", xaml_typename<SDKTemplate::CopyText>() },
    Scenario{ L"Copy and paste an image", xaml_typename<SDKTemplate::CopyImage>() },
    Scenario{ L"Copy and paste files", xaml_typename<SDKTemplate::CopyFiles>() },
    Scenario{ L"History and roaming", xaml_typename<SDKTemplate::HistoryAndRoaming>() },
    Scenario{ L"Other Clipboard operations", xaml_typename<SDKTemplate::OtherScenarios>() },
});

Selain itu, pastikan untuk menghapus isi fungsi yang ada dari MainPage.cpp untuk MainPage::scenarios(), karena kita sekarang mendefinisikan metode tersebut dalam file header.

Seperti yang Anda lihat, dalam SampleConfiguration.cpp, kami menginisialisasi skenario anggota data statisInner dengan memanggil fungsi pembantu C++/WinRT bernama winrt::single_threaded_observable_vector. Fungsi itu membuat objek koleksi Windows Runtime baru untuk kami, dan mengembalikannya sebagai antarmuka IObservableVector. Karena, dalam sampel ini, koleksi tidak dapat diamati (tidak perlu, karena tidak menambahkan atau menghapus elemen setelah inisialisasi), kita bisa memilih untuk memanggil winrt::single_threaded_vector. Fungsi tersebut mengembalikan koleksi sebagai antarmuka IVector.

Untuk informasi selengkapnya tentang koleksi, dan mengikatnya, lihat kontrol item XAML; mengikat koleksi C++/WinRT, dan Koleksi dengan C++/WinRT.

Kode inisialisasi yang baru saja Anda tambahkan jenis referensi yang belum ada dalam proyek (misalnya, winrt::SDKTemplate::CopyText. Untuk memperbaikinya, mari kita lanjutkan dan tambahkan lima halaman XAML kosong baru ke proyek.

Tambahkan lima halaman XAML kosong baru

Tambahkan item Visual C++>Blank Page (C++/WinRT) baru ke proyek (pastikan itu adalah templat item Halaman Kosong (C++/WinRT), dan bukan Halaman Kosong satu). Beri nama CopyText. Halaman XAML baru didefinisikan dalam namespace layanan SDKTemplate , yang merupakan apa yang kita inginkan.

Ulangi proses di atas empat kali lagi, dan beri nama halaman CopyImageXAML , , CopyFilesHistoryAndRoaming, dan OtherScenarios.

Anda sekarang dapat membangun lagi, jika mau.

NotifyUser

Dalam proyek C#, Anda akan menemukan implementasi metode MainPage.NotifyUser di MainPage.xaml.cs. MainPage.NotifyUser memiliki dependensi pada MainPage.UpdateStatus, dan metode tersebut pada gilirannya memiliki dependensi pada elemen XAML yang belum kami porting. Jadi untuk saat ini kita hanya akan membagi metode UpdateStatus dalam proyek C++/WinRT, dan kita akan memindahkannya nanti.

Berikut adalah kode C# relevan yang perlu kita porting.

// MainPage.xaml.cs
...
public void NotifyUser(string strMessage, NotifyType type)
if (Dispatcher.HasThreadAccess)
{
    UpdateStatus(strMessage, type);
}
else
{
    var task = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => UpdateStatus(strMessage, type));
}
private void UpdateStatus(string strMessage, NotifyType type) { ... }{
...

NotifyUser menggunakan enum Windows.UI.Core.CoreDispatcherPriority. Di C++/WinRT, setiap kali Anda ingin menggunakan jenis dari namespace Windows, Anda perlu menyertakan file header namespace layanan Windows C++/WinRT yang sesuai (untuk informasi selengkapnya tentang itu, lihat Mulai menggunakan C++/WinRT). Dalam hal ini, seperti yang akan Anda lihat dalam daftar kode di bawah ini, headernya adalah winrt/Windows.UI.Core.h, dan kita akan menyertakannya dalam pch.h.

UpdateStatus bersifat pribadi. Jadi kita akan menjadikannya metode privat pada jenis implementasi MainPage kita. UpdateStatus tidak dimaksudkan untuk dipanggil pada kelas runtime, jadi kami tidak akan mendeklarasikannya di IDL.

Setelah porting MainPage.NotifyUser, dan stubbing out MainPage.UpdateStatus, ini adalah apa yang kita miliki dalam proyek C++/WinRT. Setelah daftar kode ini, kami akan memeriksa beberapa detailnya.

// pch.h
...
#include <winrt/Windows.UI.Core.h>
...

// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
...
    void NotifyUser(hstring const& strMessage, SDKTemplate::NotifyType const& type);
...
private:
    void UpdateStatus(hstring const& strMessage, SDKTemplate::NotifyType const& type);
...
};

// MainPage.cpp
...
void MainPage::NotifyUser(hstring const& strMessage, SDKTemplate::NotifyType const& type)
{
    if (Dispatcher().HasThreadAccess())
    {
        UpdateStatus(strMessage, type);
    }
    else
    {
        Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [strMessage, type, this]()
            {
                UpdateStatus(strMessage, type);
            });
    }
}
void MainPage::UpdateStatus(hstring const& strMessage, SDKTemplate::NotifyType const& type)
{
    throw hresult_not_implemented();
}
...

Di C#, Anda dapat menggunakan notasi titik untuk melakukan titik ke properti berlapis. Jadi, jenis C# MainPage dapat mengakses properti Dispatcher sendiri dengan sintaks Dispatcher. Dan C# dapat melanjutkan ke nilai tersebut dengan sintaks seperti Dispatcher.HasThreadAccess. Di C++/WinRT, properti diimplementasikan sebagai fungsi pengakses, sehingga sintaksisnya hanya berbeda karena Anda menambahkan tanda kurung untuk setiap panggilan fungsi.

C# C++/WinRT
Dispatcher.HasThreadAccess Dispatcher().HasThreadAccess()

Ketika versi C# notifyUser memanggil CoreDispatcher.RunAsync, ia mengimplementasikan delegasi callback asinkron sebagai fungsi lambda. Versi C++/WinRT melakukan hal yang sama, tetapi sintaksnya sedikit berbeda. Di C++/WinRT, kita menangkap dua parameter yang akan kita gunakan, serta this pointer (karena kita akan memanggil fungsi anggota). Ada info selengkapnya tentang menerapkan delegasi sebagai lambda, dan contoh kode, dalam topik Menangani peristiwa dengan menggunakan delegasi di C++/WinRT. Juga, kita dapat mengacuhkan var task = bagian dalam kasus khusus ini. Kami tidak menunggu objek asinkron yang dikembalikan, jadi tidak perlu menyimpannya.

Menerapkan anggota MainPage yang tersisa

Mari kita buat daftar lengkap anggota MainPage (diimplementasikan di seluruh MainPage.xaml.cs dan SampleConfiguration.cs) sehingga kita dapat melihat mana yang telah kita port sejauh ini, dan mana yang belum dilakukan.

Anggota Access Keadaan
Konstruktor MainPage public Porting
Properti saat ini public Porting
properti FEATURE_NAME public Porting
Properti IsClipboardContentChangedEnabled public Belum dimulai
Properti skenario public Porting
Metode BuildClipboardFormatsOutputString public Belum dimulai
Metode DisplayToast public Belum dimulai
Metode EnableClipboardContentChangedNotifications public Belum dimulai
Metode NotifyUser public Porting
Metode OnNavigatedTo protected Belum dimulai
bidang isApplicationWindowActive private Belum dimulai
bidang needToPrintClipboardFormat private Belum dimulai
bidang skenario private Porting
metode Button_Click private Belum dimulai
Metode DisplayChangedFormats private Belum dimulai
metode Footer_Click private Belum dimulai
Metode HandleClipboardChanged private Belum dimulai
Metode OnClipboardChanged private Belum dimulai
Metode OnWindowActivated private Belum dimulai
metode ScenarioControl_SelectionChanged private Belum dimulai
Metode UpdateStatus private Tersungkur

Kita akan berbicara tentang anggota yang belum dilaporkan dalam beberapa subbagian berikutnya, kemudian.

Catatan

Dari waktu ke waktu, kita akan menemukan referensi dalam kode sumber ke elemen UI dalam markup XAML (dalam MainPage.xaml). Saat kami datang ke referensi ini, kami akan sementara mengerjakannya dengan menambahkan elemen tempat penampung sederhana ke XAML. Dengan begitu, proyek akan terus dibangun setelah setiap subbagian. Alternatifnya adalah menyelesaikan referensi dengan menyalin seluruh konten dari MainPage.xaml proyek C# ke proyek C++/WinRT sekarang. Tetapi jika kita melakukan itu maka itu akan menjadi waktu yang lama sebelum kita dapat datang ke pit stop dan membangun lagi (sehingga berpotensi mengaburkan kesalahan ketik atau kesalahan lain yang kita buat di sepanjang jalan).

Setelah selesai melakukan porting kode imperatif untuk kelas MainPage , maka kita akan menyalin konten file XAML dan yakin bahwa proyek akan tetap dibangun.

IsClipboardContentChangedEnabled

Ini adalah properti C# get-set yang default ke false. Ini adalah anggota MainPage, dan didefinisikan dalam SampleConfiguration.cs.

Untuk C++/WinRT, kita memerlukan fungsi aksesor, fungsi mutator, dan anggota data cadangan sebagai bidang. Karena IsClipboardContentChangedEnabled mewakili status salah satu skenario dalam sampel, daripada status MainPage itu sendiri, kita akan membuat anggota baru pada jenis utilitas baru yang disebut SampleState. Dan kita akan menerapkannya dalam file kode sumber, SampleConfiguration.cpp dan kita akan membuat anggota static (karena kita hanya membutuhkan satu instans di seluruh aplikasi; dan sehingga kita dapat mengaksesnya tanpa memerlukan instans kelas).

Untuk menemani kami SampleConfiguration.cpp dalam proyek C++/WinRT, tambahkan item Visual C++>Code>Header File (.h) baru dengan nama .SampleConfiguration.h Edit SampleConfiguration.h dan SampleConfiguration.cpp agar sesuai dengan daftar di bawah ini.

// SampleConfiguration.h
#pragma once 
#include "pch.h"

namespace winrt::SDKTemplate
{
    struct SampleState
    {
        static bool IsClipboardContentChangedEnabled();
        static void IsClipboardContentChangedEnabled(bool checked);
    private:
        static bool isClipboardContentChangedEnabled;
    };
}

// SampleConfiguration.cpp
...
#include "SampleConfiguration.h"
...
bool SampleState::isClipboardContentChangedEnabled = false;
...
bool SampleState::IsClipboardContentChangedEnabled()
{
    return isClipboardContentChangedEnabled;
}
void SampleState::IsClipboardContentChangedEnabled(bool checked)
{
    if (isClipboardContentChangedEnabled != checked)
    {
        isClipboardContentChangedEnabled = checked;
    }
}

Sekali lagi, bidang dengan static penyimpanan (seperti SampleState::isClipboardContentChangedEnabled) harus ditentukan sekali dalam aplikasi, dan .cpp file adalah tempat yang baik untuk itu (SampleConfiguration.cpp dalam hal ini).

BuildClipboardFormatsOutputString

Metode ini adalah anggota publik MainPage, dan didefinisikan dalam SampleConfiguration.cs.

// SampleConfiguration.cs
...
public string BuildClipboardFormatsOutputString()
{
    DataPackageView clipboardContent = Windows.ApplicationModel.DataTransfer.Clipboard.GetContent();
    StringBuilder output = new StringBuilder();

    if (clipboardContent != null && clipboardContent.AvailableFormats.Count > 0)
    {
        output.Append("Available formats in the clipboard:");
        foreach (var format in clipboardContent.AvailableFormats)
        {
            output.Append(Environment.NewLine + " * " + format);
        }
    }
    else
    {
        output.Append("The clipboard is empty");
    }
    return output.ToString();
}
...

Di C++/WinRT, kita akan menjadikan BuildClipboardFormatsOutputString sebagai metode statis publik SampleState. Kita dapat membuatnya static karena tidak mengakses anggota instans apa pun.

Untuk menggunakan jenis Clipboard dan DataPackageView di C++/WinRT, kita harus menyertakan file winrt/Windows.ApplicationModel.DataTransfer.hheader namespace layanan Windows C++/WinRT .

Di C#, properti DataPackageView.AvailableFormats adalah IReadOnlyList, sehingga kita dapat mengakses properti Count dari properti tersebut. Di C++/WinRT, fungsi aksesor DataPackageView::AvailableFormats mengembalikan IVectorView, yang memiliki fungsi aksesor Ukuran yang dapat kita panggil.

Untuk mem-port penggunaan jenis C# System.Text.StringBuilder, kita akan menggunakan tipe C++ standar std::wostringstream. Jenis itu adalah aliran output untuk string lebar (dan untuk menggunakannya kita harus menyertakan sstream file header). Alih-alih menggunakan metode Tambahkan seperti yang Anda lakukan dengan StringBuilder, Anda menggunakan operator penyisipan (<<) dengan aliran output seperti wostringstream. Untuk informasi selengkapnya, lihat pemrograman iostream, dan Memformat string C++/WinRT.

Kode C# membuat StringBuilder dengan new kata kunci. Di C#, objek adalah jenis referensi secara default, dinyatakan pada heap dengan new. Dalam C++standar modern, objek adalah jenis nilai secara default, dideklarasikan pada tumpukan (tanpa menggunakan new). Jadi kami port StringBuilder output = new StringBuilder(); ke C++/WinRT hanya std::wostringstream output;sebagai .

Kata kunci C# var meminta pengkompilasi untuk menyimpulkan jenis. Anda melakukan port var ke auto di C++/WinRT. Tetapi dalam C++/WinRT, ada kasus di mana (untuk menghindari salinan) Anda ingin referensi ke jenis yang disimpulkan (atau disimpulkan), dan Anda mengekspresikan referensi lvalue ke jenis yang disimpulkan dengan auto&. Ada juga kasus di mana Anda menginginkan jenis referensi khusus yang mengikat dengan benar apakah itu diinisialisasi dengan lvalue atau dengan rvalue. Dan anda mengekspresikan itu dengan auto&&. Itulah formulir yang Anda lihat digunakan dalam perulangan for dalam kode port di bawah ini. Untuk pengenalan lvalues dan rvalues, lihat Kategori nilai, dan referensinya.

Edit pch.h, SampleConfiguration.h, dan SampleConfiguration.cpp agar sesuai dengan daftar di bawah ini.

// pch.h
...
#include <sstream>
#include "winrt/Windows.ApplicationModel.DataTransfer.h"
...

// SampleConfiguration.h
...
struct SampleState
{
    static hstring BuildClipboardFormatsOutputString();
    ...
}
...

// SampleConfiguration.cpp
...
using namespace Windows::ApplicationModel::DataTransfer;
...
hstring SampleState::BuildClipboardFormatsOutputString()
{
    DataPackageView clipboardContent{ Clipboard::GetContent() };
    std::wostringstream output;

    if (clipboardContent && clipboardContent.AvailableFormats().Size() > 0)
    {
        output << L"Available formats in the clipboard:";
        for (auto&& format : clipboardContent.AvailableFormats())
        {
            output << std::endl << L" * " << std::wstring_view(format);
        }
    }
    else
    {
        output << L"The clipboard is empty";
    }

    return hstring{ output.str() };
}

Catatan

Sintaks dalam baris kode DataPackageView clipboardContent{ Clipboard::GetContent() }; menggunakan fitur C++ standar modern yang disebut inisialisasi seragam, dengan penggunaan karakteristik kurung kurawal alih-alih = tanda. Sintaksis itu memperjelas bahwa inisialisasi, bukan penugasan, sedang berlangsung. Jika Anda lebih suka bentuk sintaksis yang terlihat seperti penugasan (tetapi sebenarnya tidak), maka Anda dapat mengganti sintaks di atas dengan yang setara DataPackageView clipboardContent = Clipboard::GetContent();. Ada baiknya untuk menjadi nyaman dengan kedua cara mengekspresikan inisialisasi, karena Anda cenderung sering melihat keduanya digunakan dalam kode yang Anda temui.

DisplayToast

DisplayToast adalah metode statis publik dari kelas C# MainPage , dan Anda akan menemukannya didefinisikan dalam SampleConfiguration.cs. Di C++/WinRT, kita akan menjadikannya metode statis publik SampleState.

Kami telah menemukan sebagian besar detail dan teknik yang relevan dengan porting metode ini. Salah satu item baru yang perlu diperhatikan adalah Anda memindahkan string verbatim C# literal (@) ke literal string mentah C++ standar (LR).

Selain itu, saat Mereferensikan jenis ToastNotification dan XmlDocument di C++/WinRT, Anda dapat memenuhi syarat dengan nama namespace layanan, atau Anda dapat mengedit SampleConfiguration.cpp dan menambahkan using namespace direktif seperti contoh berikut.

using namespace Windows::UI::Notifications;

Anda memiliki pilihan yang sama saat mereferensikan jenis XmlDocument , dan setiap kali Anda mereferensikan jenis Windows Runtime lainnya.

Selain item tersebut, cukup ikuti panduan yang sama dengan yang Sebelumnya Anda lakukan untuk menyelesaikan langkah-langkah berikut.

  • Deklarasikan metode dalam SampleConfiguration.h, dan tentukan dalam SampleConfiguration.cpp.
  • Edit pch.h untuk menyertakan file header namespace layanan Windows C++/WinRT yang diperlukan.
  • Buat objek C++/WinRT pada tumpukan, bukan pada tumpukan.
  • Ganti panggilan ke properti dapatkan aksesor dengan sintaks panggilan fungsi (()).

Penyebab kesalahan kompilator/linker yang sangat umum adalah lupa menyertakan file header namespace C++/WinRT Windows yang Anda butuhkan. Untuk informasi selengkapnya tentang satu kemungkinan kesalahan, lihat C3779: Mengapa kompilator memberi saya "consume_Something: fungsi yang mengembalikan 'otomatis' tidak dapat digunakan sebelum ditentukan" kesalahan?.

Jika Anda ingin mengikuti panduan dan port DisplayToast sendiri, maka Anda dapat membandingkan hasil Anda dengan kode dalam versi C++/WinRT di ZIP kode sumber sampel Clipboard yang Anda unduh.

EnableClipboardContentChangedNotifications

EnableClipboardContentChangedNotifications adalah metode statis publik dari kelas C# MainPage , dan didefinisikan dalam SampleConfiguration.cs.

// SampleConfiguration.cs
...
public bool EnableClipboardContentChangedNotifications(bool enable)
{
    if (IsClipboardContentChangedEnabled == enable)
    {
        return false;
    }

    IsClipboardContentChangedEnabled = enable;
    if (enable)
    {
        Clipboard.ContentChanged += OnClipboardChanged;
        Window.Current.Activated += OnWindowActivated;
    }
    else
    {
        Clipboard.ContentChanged -= OnClipboardChanged;
        Window.Current.Activated -= OnWindowActivated;
    }
    return true;
}
...
private void OnClipboardChanged(object sender, object e) { ... }
private void OnWindowActivated(object sender, WindowActivatedEventArgs e) { ... }
...

Di C++/WinRT, kita akan menjadikannya metode statis publik SampleState.

Di C#, Anda menggunakan += sintaks operator dan -= untuk mendaftarkan dan mencabut delegasi penanganan peristiwa. Di C++/WinRT, Anda memiliki beberapa opsi sindikat untuk mendaftarkan/mencabut delegasi, seperti yang dijelaskan dalam Menangani peristiwa dengan menggunakan delegasi di C++/WinRT. Tetapi bentuk umumnya adalah Anda mendaftar dan mencabut dengan panggilan ke sepasang fungsi yang dinamai untuk peristiwa tersebut. Untuk mendaftar, Anda meneruskan delegasi Anda ke fungsi pendaftaran, dan Anda mengambil token pencabutan sebagai imbalan (winrt ::event_token). Untuk mencabut, Anda meneruskan token tersebut ke fungsi pencabutan. Dalam hal ini, hander bersifat statis dan (seperti yang Anda lihat dalam daftar kode berikut) sintaks panggilan fungsi mudah.

Token serupa sebenarnya digunakan, di belakang layar, di C#. Tetapi bahasa membuat detail itu implisit. C++/WinRT membuatnya eksplisit.

Jenis objek muncul di tanda tangan penanganan aktivitas C#. Dalam bahasa C#, objek adalah alias untuk jenis .NET System.Object. Yang setara dalam C++/WinRT adalah winrt::Windows::Foundation::IInspectable. Jadi, Anda akan melihat IInspectable di penanganan aktivitas C++/WinRT.

Edit SampleConfiguration.h dan SampleConfiguration.cpp agar sesuai dengan daftar di bawah ini.

// SampleConfiguration.h
...
    static bool EnableClipboardContentChangedNotifications(bool enable);
    ...
private:
    ...
    static event_token clipboardContentChangedToken;
    static event_token activatedToken;
    static void OnClipboardChanged(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
    static void OnWindowActivated(Windows::Foundation::IInspectable const& sender, Windows::UI::Core::WindowActivatedEventArgs const& e);
...

// SampleConfiguration.cpp
...
using namespace Windows::Foundation;
using namespace Windows::UI::Core;
using namespace Windows::UI::Xaml;
...
event_token SampleState::clipboardContentChangedToken;
event_token SampleState::activatedToken;
...
bool SampleState::EnableClipboardContentChangedNotifications(bool enable)
{
    if (isClipboardContentChangedEnabled == enable)
    {
        return false;
    }

    IsClipboardContentChangedEnabled(enable);
    if (enable)
    {
        clipboardContentChangedToken = Clipboard::ContentChanged(OnClipboardChanged);
        activatedToken = Window::Current().Activated(OnWindowActivated);
    }
    else
    {
        Clipboard::ContentChanged(clipboardContentChangedToken);
        Window::Current().Activated(activatedToken);
    }
    return true;
}
void SampleState::OnClipboardChanged(IInspectable const&, IInspectable const&){}
void SampleState::OnWindowActivated(IInspectable const&, WindowActivatedEventArgs const& e){}

Biarkan delegasi penanganan peristiwa itu sendiri (OnClipboardChanged dan OnWindowActivated) sebagai stub untuk saat ini. Mereka sudah ada dalam daftar anggota kami ke port, jadi kami akan sampai ke mereka di sub bagian selanjutnya.

OnNavigatedTo

OnNavigatedTo adalah metode yang dilindungi dari kelas C# MainPage , dan didefinisikan dalam MainPage.xaml.cs. Ini dia, bersama dengan XAML ListBox yang dirujuknya.

<!-- MainPage.xaml -->
...
<ListBox x:Name="ScenarioControl" ... />
...
// MainPage.xaml.cs
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    // Populate the scenario list from the SampleConfiguration.cs file
    var itemCollection = new List<Scenario>();
    int i = 1;
    foreach (Scenario s in scenarios)
    {
        itemCollection.Add(new Scenario { Title = $"{i++}) {s.Title}", ClassType = s.ClassType });
    }
    ScenarioControl.ItemsSource = itemCollection;

    if (Window.Current.Bounds.Width < 640)
    {
        ScenarioControl.SelectedIndex = -1;
    }
    else
    {
        ScenarioControl.SelectedIndex = 0;
    }
}

Ini adalah metode penting dan menarik, karena di sinilah kumpulan objek Skenario kami ditetapkan ke UI. Kode C# membangun objek System.Collections.Generic.List of Scenario , dan menetapkannya ke properti ItemsSource dari ListBox (yang merupakan kontrol item). Dan, di C#, kami menggunakan interpolasi string untuk membangun judul untuk setiap objek Skenario (perhatikan penggunaan $ karakter khusus).

Di C++/WinRT, kita akan menjadikan OnNavigatedTo sebagai metode publik MainPage. Dan kita akan menambahkan elemen ListBox stub ke XAML sehingga build akan berhasil. Setelah daftar kode, kami akan memeriksa beberapa detailnya.

<!-- MainPage.xaml -->
...
<StackPanel ...>
    ...
    <ListBox x:Name="ScenarioControl" />
</StackPanel>
...
// MainPage.h
...
void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs const& e);
...

// MainPage.cpp
...
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Navigation;
...
void MainPage::OnNavigatedTo(NavigationEventArgs const& /* e */)
{
    auto itemCollection = winrt::single_threaded_observable_vector<IInspectable>();
    int i = 1;
    for (auto s : MainPage::scenarios())
    {
        s.Title = winrt::to_hstring(i++) + L") " + s.Title;
        itemCollection.Append(winrt::box_value(s));
    }
    ScenarioControl().ItemsSource(itemCollection);

    if (Window::Current().Bounds().Width < 640)
    {
        ScenarioControl().SelectedIndex(-1);
    }
    else
    {
        ScenarioControl().SelectedIndex(0);
    }
}
...

Sekali lagi, kami memanggil fungsi winrt::single_threaded_observable_vector , tetapi kali ini untuk membuat koleksi IInspectable. Itu adalah bagian dari keputusan yang kami buat untuk melakukan tinju objek Skenario kami secara tepat waktu.

Dan, sebagai ganti dari penggunaan interpolasi string C# di sini, kami menggunakan kombinasi fungsi to_hstring dan operator perangkaian winrt::hstring.

isApplicationWindowActive

Di C#, isApplicationWindowActive adalah bidang privat bool sederhana milik kelas MainPage , dan didefinisikan dalam SampleConfiguration.cs. Ini default ke false. Di C++/WinRT, kita akan menjadikannya bidang statis privat SampleState (karena alasan yang telah kami jelaskan) dalam SampleConfiguration.h file dan SampleConfiguration.cpp , dengan default yang sama.

Kami telah melihat cara mendeklarasikan, menentukan, dan menginisialisasi bidang statis. Untuk penyegaran, lihat kembali apa yang kami lakukan dengan bidang isClipboardContentChangedEnabled , dan lakukan hal yang sama dengan isApplicationWindowActive.

needToPrintClipboardFormat

Pola yang sama dengan isApplicationWindowActive (lihat judul segera sebelum yang satu ini).

Button_Click

Button_Click adalah metode privat (penanganan peristiwa) dari kelas C# MainPage, dan didefinisikan dalam MainPage.xaml.cs. Ini dia, bersama dengan XAML SplitView yang direferensikannya, dan ToggleButton yang mendaftarkannya.

<!-- MainPage.xaml -->
...
<SplitView x:Name="Splitter" ... />
...
<ToggleButton Click="Button_Click" .../>
...
private void Button_Click(object sender, RoutedEventArgs e)
{
    Splitter.IsPaneOpen = !Splitter.IsPaneOpen;
}

Dan inilah yang setara, di-port ke C++/WinRT. Perhatikan bahwa dalam versi C++/WinRT, penanganan aktivitas adalah public (seperti yang Anda lihat, Anda mendeklarasikannya sebelumprivate:deklarasi). Ini karena penanganan aktivitas yang terdaftar di markup XAML, seperti ini, harus berada public di C++/WinRT agar markup XAML dapat mengaksesnya. Di sisi lain, jika Anda mendaftarkan penanganan aktivitas dalam kode imperatif (seperti yang kami lakukan di MainPage::EnableClipboardContentChangedNotifications sebelumnya), maka penanganan aktivitas tidak perlu .public

<!-- MainPage.xaml -->
...
<StackPanel ...>
    ...
    <SplitView x:Name="Splitter" />
</StackPanel>
...
// MainPage.h
...
    void Button_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e);
private:
...

// MainPage.cpp
void MainPage::Button_Click(Windows::Foundation::IInspectable const& /* sender */, Windows::UI::Xaml::RoutedEventArgs const& /* e */)
{
    Splitter().IsPaneOpen(!Splitter().IsPaneOpen());
}

DisplayChangedFormats

Dalam C#, DisplayChangedFormats adalah metode privat milik kelas MainPage , dan didefinisikan dalam SampleConfiguration.cs.

private void DisplayChangedFormats()
{
    string output = "Clipboard content has changed!" + Environment.NewLine;
    output += BuildClipboardFormatsOutputString();
    NotifyUser(output, NotifyType.StatusMessage);
}

Di C++/WinRT, kita akan menjadikannya bidang statis privat SampleState (tidak mengakses anggota instans apa pun), dalam SampleConfiguration.h file dan SampleConfiguration.cpp . Kode C# untuk metode ini tidak menggunakan System.Text.StringBuilder; tetapi cukup memformat string yang untuk versi C++/WinRT ini adalah tempat lain yang baik untuk menggunakan std::wostringstream.

Alih-alih properti System.Environment.NewLine statis, yang digunakan dalam kode C#, kita akan menyisipkan C++ std::endl standar (karakter baris baru) ke dalam aliran output.

// SampleConfiguration.h
...
private:
    static void DisplayChangedFormats();
...

// SampleConfiguration.cpp
void SampleState::DisplayChangedFormats()
{
    std::wostringstream output;
    output << L"Clipboard content has changed!" << std::endl;
    output << BuildClipboardFormatsOutputString().c_str();
    MainPage::Current().NotifyUser(output.str(), NotifyType::StatusMessage);
}

Ada ketidakefisienan kecil dalam desain versi C++/WinRT di atas. Pertama, kita membuat std::wostringstream. Tetapi kami juga memanggil metode BuildClipboardFormatsOutputString (yang kami port sebelumnya). Metode itu menciptakan std sendiri::wostringstream. Dan mengubah alirannya menjadi winrt::hstring dan mengembalikannya. Kami memanggil fungsi hstring::c_str untuk mengubah hstring yang dikembalikan kembali menjadi string gaya C, dan kemudian kami memasukkannya ke dalam aliran kami. Akan lebih efisien untuk membuat hanya satu std::wostringstream, dan meneruskan (referensi ke) di sekitarnya, sehingga metode dapat memasukkan string ke dalamnya secara langsung.

Itulah yang kami lakukan dalam versi C++/WinRT dari kode sumber sampel Clipboard (di ZIP yang Anda unduh). Dalam kode sumber tersebut, ada metode statis privat baru bernama SampleState::AddClipboardFormatsOutputString, yang mengambil dan beroperasi pada referensi ke aliran output. Dan kemudian metode SampleState::D isplayChangedFormats dan SampleState::BuildClipboardFormatsOutputString direfaktor untuk memanggil metode baru tersebut. Ini secara fungsional setara dengan daftar kode dalam topik ini, tetapi lebih efisien.

Footer_Click adalah penanganan aktivitas asinkron milik kelas C# MainPage, dan didefinisikan dalam MainPage.xaml.cs. Kode yang tercantum di bawah ini secara fungsional setara dengan metode dalam kode sumber yang Anda unduh. Tapi di sini saya telah membongkarnya dari satu baris ke empat, untuk membuatnya lebih mudah untuk melihat apa yang dilakukannya, dan akibatnya bagaimana kita harus memindahkannya.

async void Footer_Click(object sender, RoutedEventArgs e)
{
    var hyperlinkButton = (HyperlinkButton)sender;
    string tagUrl = hyperlinkButton.Tag.ToString();
    Uri uri = new Uri(tagUrl);
    await Windows.System.Launcher.LaunchUriAsync(uri);
}

Sementara, secara teknis, metode ini asinkron, tidak melakukan apa pun setelah await, sehingga tidak memerlukan await (atau async kata kunci). Ini mungkin menggunakannya untuk menghindari pesan IntelliSense di Visual Studio.

Metode C++/WinRT yang setara juga akan asinkron (karena memanggil Launcher.LaunchUriAsync). Tetapi tidak perlu co_await, juga tidak mengembalikan objek asinkron. Untuk informasi tentang co_await dan objek asinkron, lihat Operasi konkurensi dan asinkron dengan C++/WinRT.

Sekarang mari kita bicara tentang apa metode yang dilakukan. Karena ini adalah penanganan aktivitas untuk peristiwa Klik HyperlinkButton, objek bernama pengirim sebenarnya adalah HyperlinkButton. Jadi konversi jenis aman (kita bisa saja mengekspresikan konversi ini sebagai sender as HyperlinkButton). Selanjutnya, kami mengambil nilai properti Tag (jika Anda melihat markup XAML di proyek C#, Anda akan melihat bahwa ini diatur ke string yang mewakili url web). Meskipun properti FrameworkElement.Tag (HyperlinkButton adalah FrameworkElement) berjenis objek, di C# kita dapat merangkainya dengan Object.ToString. Dari string yang dihasilkan, kami membuat objek Uri . Dan akhirnya (dengan bantuan Shell) kami meluncurkan browser dan menavigasi ke url.

Berikut adalah metode yang di-port ke C++/WinRT (sekali lagi, diperluas untuk kejelasan), setelah itu adalah deskripsi detailnya.

// pch.h
...
#include "winrt/Windows.System.h"
...

// MainPage.h
...
    void Footer_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e);
private:
...

// MainPage.cpp
...
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::UI::Xaml::Controls;
...
void MainPage::Footer_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const&)
{
    auto hyperlinkButton{ sender.as<HyperlinkButton>() };
    hstring tagUrl{ winrt::unbox_value<hstring>(hyperlinkButton.Tag()) };
    Uri uri{ tagUrl };
    Windows::System::Launcher::LaunchUriAsync(uri);
}

Seperti biasa, kami membuat penanganan publicaktivitas . Kami menggunakan fungsi sebagai pada objek pengirim untuk mengonversinya menjadi HyperlinkButton. Di C++/WinRT, properti Tag adalah IInspectable (setara dengan Object). Tapi tidak ada Tostring di IInspectable. Sebaliknya, kita harus membuka kotak IInspectable ke nilai skalar (string, dalam hal ini). Sekali lagi, untuk informasi selengkapnya tentang tinju dan membuka kotak, lihat Nilai Tinju dan buka kotak ke IInspectable.

Dua baris terakhir mengulangi pola porting yang telah kita lihat sebelumnya, dan mereka cukup banyak menggemakan versi C#.

HandleClipboardChanged

Tidak ada yang baru terlibat dalam porting metode ini. Anda dapat membandingkan versi C# dan C++/WinRT di ZIP kode sumber sampel Clipboard yang Anda unduh.

OnClipboardChanged dan OnWindowActivated

Sejauh ini kami hanya memiliki stub kosong untuk dua penanganan aktivitas ini. Tetapi porting mereka mudah, dan tidak memunculkan sesuatu yang baru untuk dibahas.

ScenarioControl_SelectionChanged

Ini adalah penanganan aktivitas privat lain milik kelas C# MainPage , dan didefinisikan dalam MainPage.xaml.cs. Di C++/WinRT, kita akan membuatnya publik, dan mengimplementasikannya di MainPage.h dan MainPage.cpp.

Untuk metode ini, kita memerlukan MainPage::navigating, yang merupakan bidang Boolean privat, diinisialisasi ke false. Dan Anda akan memerlukan Bingkai di MainPage.xaml, bernama ScenarioFrame. Tetapi, selain detail tersebut, porting metode ini tidak mengungkapkan teknik baru.

Jika, alih-alih porting dengan tangan, Anda menyalin kode dari versi C++/WinRT di ZIP kode sumber sampel Clipboard yang Anda unduh, maka Anda akan melihat MainPage::NavigateUntuk digunakan di sana. Untuk saat ini, cukup refaktor konten NavigateTo ke ScenarioControl_SelectionChanged.

UpdateStatus

Kami hanya memiliki stub sejauh ini untuk MainPage.UpdateStatus. Porting implementasinya, sekali lagi, mencakup tanah yang sebagian besar tua. Satu poin baru yang perlu diperhatikan adalah bahwa sementara di C# kita dapat membandingkan string dengan String.Empty, Di C++/WinRT kita sebagai gantinya memanggil fungsi winrt::hstring::empty. Lainnya adalah bahwa nullptr adalah C++ standar yang setara dengan C#'s null.

Anda dapat melakukan sisa port dengan teknik yang telah kami bahas. Berikut adalah daftar jenis hal yang perlu Anda lakukan sebelum versi port metode ini akan dikompilasi.

  • Ke MainPage.xaml, tambahkan Batas bernama StatusBorder.
  • Ke MainPage.xaml, tambahkan TextBlock bernama StatusBlock.
  • Ke MainPage.xaml, tambahkan StackPanel bernama StatusPanel.
  • Ke pch.h, tambahkan #include "winrt/Windows.UI.Xaml.Media.h".
  • Ke pch.h, tambahkan #include "winrt/Windows.UI.Xaml.Automation.Peers.h".
  • Untuk MainPage.cpp menambahkan using namespace winrt::Windows::UI::Xaml::Media;.
  • Untuk MainPage.cpp menambahkan using namespace winrt::Windows::UI::Xaml::Automation::Peers;.

Salin XAML dan gaya yang diperlukan untuk menyelesaikan port MainPage

Untuk XAML, kasus idealnya adalah Anda dapat menggunakan markup XAML yang sama di seluruh C# dan proyek C++/WinRT. Dan sampel Clipboard adalah salah satu kasus tersebut.

Dalam filenya Styles.xaml , sampel Clipboard memiliki XAML ResourceDictionary gaya, yang diterapkan ke tombol, menu, dan elemen UI lainnya di seluruh UI aplikasi. Halaman Styles.xaml digabungkan ke dalam App.xaml. Dan kemudian ada titik awal standar MainPage.xaml untuk UI, yang telah kita lihat secara singkat. Kita sekarang dapat menggunakan kembali ketiga .xaml file tersebut, tidak berubah, dalam versi C++/WinRT proyek.

Seperti halnya file aset, Anda dapat memilih untuk mereferensikan file XAML bersama yang sama dari beberapa versi aplikasi Anda. Dalam panduan ini, hanya demi kesederhanaan, kita akan menyalin file ke dalam proyek C++/WinRT dan menambahkannya dengan cara itu.

Navigasi ke \Clipboard_sample\SharedContent\xaml folder, pilih dan salin App.xaml dan MainPage.xaml, lalu tempelkan kedua file tersebut \Clipboard\Clipboard ke folder di proyek C++/WinRT Anda, memilih untuk mengganti file saat diminta.

Di proyek C++/WinRT di Visual Studio, klik Perlihatkan Semua File untuk mengaktifkannya. Sekarang tambahkan folder baru, segera di bawah simpul proyek, dan beri nama Styles. Di File Explorer, navigasikan \Clipboard_sample\SharedContent\xaml ke folder, pilih dan salin Styles.xaml, dan tempelkan ke \Clipboard\Clipboard\Styles folder yang baru saja Anda buat. Kembali ke Penjelajah Solusi dalam proyek C++/WinRT, klik Styles kanan folder >Tambahkan>item yang Ada... dan navigasi ke .\Clipboard\Clipboard\Styles Di pemilih file, pilih Styles dan klik Tambahkan.

Tambahkan folder baru ke proyek C++/WinRT, segera di bawah simpul proyek, dan bernama Styles. Navigasi ke \Clipboard_sample\SharedContent\xaml folder, pilih dan salin Styles.xaml, dan tempelkan ke \Clipboard\Clipboard\Styles folder di proyek C++/WinRT Anda. Styles Klik kanan folder (dalam Penjelajah Solusi di proyek C++/WinRT) >Tambahkan>item yang Ada... dan navigasi ke .\Clipboard\Clipboard\Styles Di pemilih file, pilih Styles dan klik Tambahkan.

Klik Perlihatkan Semua File lagi untuk mengalihkannya.

Kami sekarang telah selesai memindahkan MainPage, dan jika Anda telah mengikuti langkah-langkahnya, proyek C++/WinRT Anda sekarang akan dibangun dan dijalankan.

Mengonsolidasikan file Anda .idl

Selain titik awal standar MainPage.xaml untuk UI, sampel Clipboard memiliki lima halaman XAML khusus skenario lainnya, bersama dengan file code-behind yang sesuai. Kita akan menggunakan kembali markup XAML aktual dari semua halaman ini, tidak berubah, dalam versi C++/WinRT proyek. Dan kita akan melihat cara mem-port kode di belakang di beberapa bagian utama berikutnya. Tapi sebelum itu, mari kita bicara tentang IDL.

Ada nilai dalam mengonsolidasikan IDL untuk kelas runtime Anda ke dalam satu file IDL. Untuk mempelajari tentang nilai tersebut, lihat Memperhitungkan kelas runtime ke dalam file Midl (.idl). Jadi selanjutnya kita akan mengonsolidasikan CopyFiles.idlkonten , , CopyImage.idl, CopyText.idlHistoryAndRoaming.idl, dan OtherScenarios.idl dengan memindahkan IDL tersebut ke dalam satu file bernama Project.idl (lalu menghapus file asli).

Saat kami melakukannya, mari kita juga menghapus properti dummy yang dihasilkan secara otomatis (Int32 MyProperty;, dan implementasinya) dari masing-masing dari lima jenis halaman XAML tersebut.

Pertama, tambahkan item Midl File (.idl) baru ke proyek C++/WinRT. Beri nama Project.idl. Ganti seluruh konten Project.idl dengan kode berikut.

// Project.idl
namespace SDKTemplate
{
    [default_interface]
    runtimeclass CopyFiles : Windows.UI.Xaml.Controls.Page
    {
        CopyFiles();
    }

    [default_interface]
    runtimeclass CopyImage : Windows.UI.Xaml.Controls.Page
    {
        CopyImage();
    }

    [default_interface]
    runtimeclass CopyText : Windows.UI.Xaml.Controls.Page
    {
        CopyText();
    }

    [default_interface]
    runtimeclass HistoryAndRoaming : Windows.UI.Xaml.Controls.Page
    {
        HistoryAndRoaming();
    }

    [default_interface]
    runtimeclass OtherScenarios : Windows.UI.Xaml.Controls.Page
    {
        OtherScenarios();
    }
}

Seperti yang Anda lihat, itu hanya salinan konten file individual .idl , semuanya di dalam satu namespace, dan dengan MyProperty dihapus dari setiap kelas runtime.

Di Penjelajah Solusi di Visual Studio, pilih beberapa file IDL asli (CopyFiles.idl, , CopyImage.idl, HistoryAndRoaming.idlCopyText.idl, dan OtherScenarios.idl) dan Edit>Hapus (pilih Hapus dalam dialog).

Terakhir—dan untuk menyelesaikan penghapusan MyProperty—dalam .h file dan .cpp untuk masing-masing dari lima jenis halaman XAML yang sama, hapus deklarasi dan definisi int32_t MyProperty() fungsi aksesor dan void MyProperty(int32_t) mutator.

Kebetulan, selalu ada baiknya untuk memiliki nama file XAML Anda yang cocok dengan nama kelas yang mereka wakili. Misalnya, jika Anda memiliki x:Class="MyNamespace.MyPage" dalam file markup XAML, maka file tersebut harus diberi nama MyPage.xaml. Meskipun ini bukan persyaratan teknis, tidak harus mengubah nama yang berbeda untuk artefak yang sama akan membuat proyek Anda lebih dapat dimengerti dan dipertahankan, dan lebih mudah dikerjakan.

CopyFiles

Dalam proyek C#, jenis halaman CopyFiles XAML diimplementasikan dalam CopyFiles.xaml file kode sumber dan CopyFiles.xaml.cs . Mari kita lihat masing-masing anggota CopyFiles secara bergantian.

rootPage

Ini adalah bidang privat.

// CopyFiles.xaml.cs
...
public sealed partial class CopyFiles : Page
{
    MainPage rootPage = MainPage.Current;
    ...
}
...

Di C++/WinRT, kita dapat menentukan dan menginisialisasinya seperti ini.

// CopyFiles.h
...
struct CopyFiles : CopyFilesT<CopyFiles>
{
    ...
private:
    SDKTemplate::MainPage rootPage{ MainPage::Current() };
};
...

Sekali lagi (sama seperti MainPage ::current), CopyFiles::rootPage dinyatakan sebagai jenis SDKTemplate::MainPage, yang merupakan jenis yang diproyeksikan, dan bukan jenis implementasi.

CopyFiles (konstruktor)

Dalam proyek C++/WinRT, jenis CopyFiles sudah memiliki konstruktor yang berisi kode yang kita inginkan (hanya memanggil InitializeComponent).

CopyButton_Click

Metode C# CopyButton_Click adalah penanganan aktivitas, dan dari async kata kunci dalam tanda tangannya kita dapat mengetahui bahwa metode melakukan pekerjaan asinkron. Di C++/WinRT, kami menerapkan metode asinkron sebagai coroutine. Untuk pengenalan konkurensi di C++/WinRT, bersama dengan deskripsi tentang apa itu coroutine , lihat Operasi konkurensi dan asinkron dengan C++/WinRT.

Adalah umum untuk ingin menjadwalkan pekerjaan lebih lanjut setelah koroutin selesai, dan untuk kasus seperti itu koroutine akan mengembalikan beberapa jenis objek asinkron yang dapat ditunggu, dan yang secara opsional melaporkan kemajuan. Tetapi pertimbangan tersebut biasanya tidak berlaku untuk penanganan aktivitas. Jadi ketika Anda memiliki penanganan aktivitas yang melakukan operasi asinkron, Anda dapat mengimplementasikannya sebagai coroutine yang mengembalikan winrt::fire_and_forget. Untuk informasi selengkapnya, lihat Api dan lupakan.

Meskipun ide coroutine api dan lupa adalah Anda tidak peduli ketika selesai, pekerjaan masih berlanjut (atau ditangguhkan, menunggu dimulainya kembali) di latar belakang. Anda dapat melihat dari implementasi C# yang CopyButton_Click tergantung pada this pointer (mengakses anggota rootPagedata instans ). Jadi kita harus yakin bahwa this penunjuk (penunjuk ke objek CopyFiles ) keluar dari CopyButton_Click coroutine. Dalam situasi seperti aplikasi sampel ini, di mana pengguna menavigasi antara halaman UI, kami tidak dapat langsung mengontrol masa pakai halaman tersebut. Jika halaman CopyFiles dihancurkan (dengan menavigasi jauh darinya) saat CopyButton_Click masih dalam penerbangan pada utas latar belakang, tidak akan aman untuk mengakses rootPage. Untuk membuat koroutine benar, ia perlu mendapatkan referensi yang kuat ke this penunjuk, dan menyimpan referensi tersebut selama durasi koroutin. Untuk informasi selengkapnya, lihat Referensi yang kuat dan lemah di C++/WinRT.

Jika Anda melihat versi C++/WinRT sampel, di CopyFiles::CopyButton_Click, Anda akan melihat bahwa itu dilakukan dengan deklarasi sederhana pada tumpukan.

fire_and_forget CopyFiles::CopyButton_Click(IInspectable const&, RoutedEventArgs const&)
{
    auto lifetime{ get_strong() };
    ...
}

Mari kita lihat aspek lain dari kode port yang patut diperhatikan.

Dalam kode, kita membuat instans objek FileOpenPicker, dan dua baris kemudian kita mengakses properti FileTypeFilter objek tersebut. Jenis pengembalian properti tersebut mengimplementasikan IVector string. Dan pada IVector itu, kami memanggil IVector<T>. Metode ReplaceAll(T[]). Aspek yang menarik adalah nilai yang kita teruskan ke metode itu, di mana array diharapkan. Berikut adalah baris kode.

filePicker.FileTypeFilter().ReplaceAll({ L"*" });

Nilai yang kita lewati ({ L"*" }) adalah daftar inisialisasi C++ standar. Ini berisi satu objek, dalam hal ini, tetapi daftar inisialisasi dapat berisi sejumlah objek yang dipisahkan koma. Potongan C++/WinRT yang memungkinkan Anda kenyamanan meneruskan daftar inisialisasi ke metode seperti ini dijelaskan dalam daftar inisialisasi Standar.

Kami memindahkan kata kunci C# await ke co_await di C++/WinRT. Berikut adalah contoh dari kode.

auto storageItems{ co_await filePicker.PickMultipleFilesAsync() };

Selanjutnya, pertimbangkan baris kode C# ini.

dataPackage.SetStorageItems(storageItems);

C# dapat secara implisit mengonversi IReadOnlyList StorageFile> yang diwakili oleh storageItems ke dalam IEnumerable IStorageItem> yang<diharapkan oleh DataPackage.SetStorageItems.< Tetapi di C++/WinRT kita perlu secara eksplisit mengonversi dari IVectorView<StorageFile> ke IStorageItem> yang dapat<diubah. Jadi kita memiliki contoh lain dari fungsi sebagai dalam tindakan.

dataPackage.SetStorageItems(storageItems.as<IVectorView<IStorageItem>>());

Di mana kita menggunakan null kata kunci di C# (misalnya, Clipboard.SetContentWithOptions(dataPackage, null)), kita gunakan nullptr di C++/WinRT (misalnya, Clipboard::SetContentWithOptions(dataPackage, nullptr)).

PasteButton_Click

Ini adalah penanganan aktivitas lain dalam bentuk koroutin api dan lupa. Mari kita lihat aspek kode port yang patut diperhatikan.

Dalam sampel versi C#, kita menangkap pengecualian dengan catch (Exception ex). Dalam kode C++/WinRT yang di-port, Anda akan melihat ekspresi catch (winrt::hresult_error const& ex). Untuk informasi selengkapnya tentang winrt::hresult_error dan cara bekerja dengannya, lihat Penanganan kesalahan dengan C++/WinRT.

Contoh pengujian apakah objek C# adalah null atau tidak adalah if (storageItems != null). Di C++/WinRT, kita dapat mengandalkan operator konversi ke bool, yang melakukan pengujian terhadap nullptr internal.

Berikut adalah versi yang sedikit disederhanakan dari fragmen kode dari sampel versi C++/WinRT yang di-port.

std::wostringstream output;
output << std::wstring_view(ApplicationData::Current().LocalFolder().Path());

Membangun std::wstring_view dari winrt::hstring seperti itu menggambarkan alternatif untuk memanggil fungsi hstring ::c_str (untuk mengubah winrt::hstring menjadi string gaya C). Alternatif ini berfungsi berkat operator konversi hstring ke std::wstring_view.

Pertimbangkan fragmen C#.

var file = storageItem as StorageFile;
if (file != null)
...

Untuk memindahkan kata kunci C# as ke C++/WinRT, sejauh ini kita telah melihat sebagai fungsi yang digunakan beberapa kali. Fungsi tersebut melemparkan pengecualian jika konversi jenis gagal. Tetapi jika kita ingin konversi kembali nullptr jika gagal (sehingga kita dapat menangani kondisi itu dalam kode), maka kita sebagai gantinya menggunakan fungsi try_as.

auto file{ storageItem.try_as<StorageFile>() };
if (file)
...

Salin XAML yang diperlukan untuk menyelesaikan port copyFiles

Anda sekarang dapat memilih seluruh konten CopyFiles.xaml file dari shared folder unduhan kode sumber sampel asli, dan menempelkannya ke dalam CopyFiles.xaml file di proyek C++/WinRT (menggantikan konten file yang ada di proyek C++/WinRT).

Terakhir, edit CopyFiles.h dan .cpp hapus fungsi ClickHandler dummy, karena kami hanya menimpa markup XAML yang sesuai.

Kami sekarang telah selesai memindahkan CopyFiles, dan jika Anda telah mengikuti langkah-langkahnya, proyek C++/WinRT Anda sekarang akan membangun dan menjalankan, dan skenario CopyFiles akan berfungsi.

CopyImage

Untuk memindahkan jenis halaman CopyImage XAML, Anda mengikuti proses yang sama seperti untuk CopyFiles. Saat memindahkan CopyImage, Anda akan menemukan penggunaan C# menggunakan pernyataan, yang memastikan bahwa objek yang mengimplementasikan antarmuka IDisposable dibuang dengan benar.

if (imageReceived != null)
{
    using (var imageStream = await imageReceived.OpenReadAsync())
    {
        ... // Pass imageStream to other APIs, and do other work.
    }
}

Antarmuka yang setara di C++/WinRT adalah IClosable, dengan satu metode Close-nya. Berikut adalah C++/WinRT yang setara dengan kode C# di atas.

if (imageReceived)
{
    auto imageStream{ co_await imageReceived.OpenReadAsync() };
    ... // Pass imageStream to other APIs, and do other work.
    imageStream.Close();
}

Objek C++/WinRT mengimplementasikan IClosable terutama untuk manfaat bahasa yang tidak memiliki finalisasi deterministik. C++/WinRT memiliki finalisasi deterministik, sehingga kami sering tidak perlu memanggil IClosable::Close saat kami menulis C++/WinRT. Tapi ada kalanya ada baiknya untuk menyebutnya, dan ini adalah salah satu waktu. Di sini, pengidentifikasi imageStream adalah pembungkus yang dihitung referensi di sekitar objek Windows Runtime yang mendasarinya (dalam hal ini, objek yang mengimplementasikan IRandomAccessStreamWithContentType). Meskipun kita dapat menentukan bahwa finalizer imageStream (destruktornya) akan berjalan di akhir cakupan penutup (kurung kurawal), kita tidak dapat yakin bahwa finalizer akan memanggil Tutup. Itu karena kami meneruskan imageStream ke API lain, dan mungkin masih berkontribusi pada jumlah referensi objek Windows Runtime yang mendasarinya. Jadi ini adalah kasus di mana ada baiknya untuk memanggil Close secara eksplisit. Untuk informasi selengkapnya, lihat Apakah saya perlu memanggil IClosable::Close pada kelas runtime yang saya gunakan?.

Selanjutnya, pertimbangkan ekspresi (uint)(imageDecoder.OrientedPixelWidth * 0.5)C# , yang akan Anda temukan di penanganan aktivitas OnDeferredImageRequestedHandler . Ekspresi tersebut mengalikan dengan uintdouble, menghasilkan double. Kemudian melemparkannya ke uint. Dalam C++/WinRT, kita dapat menggunakan pemeran gaya C yang terlihat mirip ((uint32_t)(imageDecoder.OrientedPixelWidth() * 0.5)), tetapi lebih baik untuk membuatnya jelas jenis cast apa yang kami niatkan, dan dalam hal ini kami akan melakukan itu dengan static_cast<uint32_t>(imageDecoder.OrientedPixelWidth() * 0.5).

CopyImage.OnDeferredImageRequestedHandler versi C# memiliki finally klausa, tetapi bukan catch klausa. Kami hanya sedikit lebih jauh dalam versi C++/WinRT, dan menerapkan catch klausul sehingga kami dapat melaporkan apakah penyajian yang tertunda berhasil atau tidak.

Porting sisa halaman XAML ini tidak menghasilkan sesuatu yang baru untuk dibahas. Ingatlah untuk menghapus fungsi ClickHandler dummy. Dan, sama seperti dengan CopyFiles, langkah terakhir di port adalah memilih seluruh konten CopyImage.xaml, dan menempelkannya ke dalam file yang sama di proyek C++/WinRT.

CopyText

Anda dapat melakukan port CopyText.xaml dan CopyText.xaml.cs menggunakan teknik yang telah kami bahas.

HistoryAndRoaming

Ada beberapa tempat menarik yang muncul saat memindahkan jenis halaman HistoryAndRoaming XAML.

Pertama, lihat kode sumber C#, dan ikuti alur kontrol dari OnNavigatedTo melalui penanganan aktivitas OnHistoryEnabledChanged , dan akhirnya ke fungsi asinkron CheckHistoryAndRoaming (yang tidak ditunggu, jadi pada dasarnya aktif dan lupakan). Karena CheckHistoryAndRoaming tidak sinkron, kita harus berhati-hati di C++/WinRT tentang masa this pakai pointer. Anda dapat melihat hasilnya jika Anda melihat implementasi dalam HistoryAndRoaming.cpp file kode sumber. Pertama, ketika kita melampirkan delegasi ke peristiwa Clipboard::HistoryEnabledChanged dan Clipboard::RoamingEnabledChanged , kita hanya mengambil referensi lemah ke objek halaman HistoryAndRoaming . Kami melakukannya dengan membuat delegasi dengan dependensi pada nilai yang dikembalikan dari winrt::get_weak, bukan dependensi pada this pointer. Yang berarti bahwa delegasi itu sendiri, yang akhirnya memanggil ke dalam kode asinkron, tidak menjaga halaman HistoryAndRoaming tetap hidup, harus kita menavigasi menjauh darinya.

Dan kedua, ketika kita akhirnya mencapai koroutine CheckHistoryAndRoaming, hal pertama yang kita lakukan adalah mengambil referensi yang kuat untuk this menjamin bahwa halaman HistoryAndRoaming hidup setidaknya sampai koroutine akhirnya selesai. Untuk informasi selengkapnya tentang kedua aspek yang baru saja dijelaskan, lihat Referensi yang kuat dan lemah di C++/WinRT.

Kami menemukan tempat menarik lain saat memindahkan CheckHistoryAndRoaming. Ini berisi kode untuk memperbarui UI; jadi kita harus yakin bahwa kita melakukan itu pada utas UI utama. Utas yang awalnya memanggil ke penanganan aktivitas adalah utas UI utama. Tetapi biasanya, metode asinkron dapat mengeksekusi dan/atau melanjutkan pada utas arbitrer apa pun. Di C#, solusinya adalah memanggil CoreDispatcher.RunAsync, dan memperbarui UI dari dalam fungsi lambda. Di C++/WinRT, kita dapat menggunakan fungsi winrt::resume_foreground bersama dengan this Dispatcher pointer untuk menangguhkan coroutine dan segera melanjutkan pada utas UI utama.

Ekspresi yang relevan adalah co_await winrt::resume_foreground(Dispatcher());. Atau, meskipun dengan lebih sedikit kejelasan, kita dapat mengekspresikan itu hanya sebagai co_await Dispatcher();. Versi yang lebih pendek dicapai berdasarkan operator konversi yang disediakan oleh C++/WinRT.

Porting sisa halaman XAML ini tidak menghasilkan sesuatu yang baru untuk dibahas. Ingatlah untuk menghapus fungsi ClickHandler dummy, dan untuk menyalin melalui markup XAML.

OtherScenarios

Anda dapat melakukan port OtherScenarios.xaml dan OtherScenarios.xaml.cs menggunakan teknik yang telah kami bahas.

Kesimpulan

Semoga panduan ini telah mempersenjatai Anda dengan info porting dan teknik yang cukup yang sekarang dapat Anda lanjutkan dan port aplikasi C# Anda sendiri ke C++/WinRT. Dengan cara penyegaran, Anda dapat terus merujuk kembali ke versi sebelum (C#) dan setelah (C++/WinRT) dari kode sumber dalam sampel Clipboard, dan membandingkannya secara berdampingan untuk melihat korespondensi.