Menghosting kontrol WinRT XAML standar di aplikasi desktop C++ (Win32)

Penting

Topik ini menggunakan atau menyebutkan jenis dari repositori GitHub CommunityToolkit/Microsoft.Toolkit.Win32 . Untuk informasi penting tentang dukungan Kepulauan XAML, silakan lihat Pemberitahuan Kepulauan XAML di repositori tersebut.

Artikel ini menunjukkan cara menggunakan API hosting WinRT XAML untuk menghosting kontrol WinRT XAML standar (yaitu, kontrol yang disediakan oleh Windows SDK) di aplikasi desktop C++ baru. Kode didasarkan pada sampel Pulau XAML sederhana, dan bagian ini membahas beberapa bagian terpenting dari kode. Jika Anda memiliki proyek aplikasi desktop C++ yang sudah ada, Anda dapat menyesuaikan langkah-langkah dan contoh kode ini untuk proyek Anda.

Catatan

Skenario yang ditunjukkan dalam artikel ini tidak mendukung pengeditan markup XAML secara langsung untuk kontrol WinRT XAML yang dihosting di aplikasi Anda. Skenario ini membatasi Anda untuk memodifikasi tampilan dan perilaku kontrol yang dihosting melalui kode. Untuk instruksi yang memungkinkan Anda mengedit markup XAML secara langsung saat menghosting kontrol WinRT XAML, lihat Menghosting kontrol WinRT XAML kustom di aplikasi desktop C++.

Membuat proyek aplikasi desktop

  1. Di Visual Studio 2019 dengan Windows 10, versi 1903 SDK (build 10.0.18362) atau rilis yang lebih baru diinstal, buat proyek Aplikasi Desktop Windows baru dan beri nama MyDesktopWin32App. Jenis proyek ini tersedia di bawah filter proyek C++, Windows, dan Desktop .

  2. Di Penjelajah Solusi, klik kanan simpul solusi, klik Solusi penargetan ulang, pilih rilis SDK 10.0.18362.0 atau yang lebih baru, lalu klik OK.

  3. Instal paket NuGet Microsoft.Windows.CppWinRT untuk mengaktifkan dukungan untuk C++/WinRT di proyek Anda:

    1. Klik kanan proyek Anda di Penjelajah Solusi lalu pilih Kelola Paket NuGet.
    2. Pilih tab Telusuri , cari paket Microsoft.Windows.CppWinRT , dan instal versi terbaru paket ini.

    Catatan

    Untuk proyek baru, Anda dapat menginstal C++/WinRT Visual Studio Extension (VSIX) dan menggunakan salah satu templat proyek C++/WinRT yang disertakan dalam ekstensi tersebut. Untuk detail selengkapnya, lihat Dukungan Visual Studio untuk C++/WinRT, dan VSIX.

  4. Pada tab Telusuri jendela Manajer Paket NuGet, cari paket NuGet Microsoft.Toolkit.Win32.UI.SDK dan instal versi stabil terbaru paket ini. Paket ini menyediakan beberapa aset build dan run time yang memungkinkan Kepulauan XAML berfungsi di aplikasi Anda.

  5. Atur maxversiontested nilai dalam manifes aplikasi Anda untuk menentukan bahwa aplikasi Anda kompatibel dengan Windows 10, versi 1903.

    1. Jika Anda belum memiliki manifes aplikasi di proyek Anda, tambahkan file XML baru ke proyek Anda dan beri nama app.manifest.

    2. Dalam manifes aplikasi Anda, sertakan elemen kompatibilitas dan elemen turunan yang ditunjukkan dalam contoh berikut. Ganti atribut Id dari elemen maxversiontested dengan nomor versi Windows yang Anda targetkan (ini harus 10.0.18362.0 atau rilis yang lebih baru). Perhatikan bahwa mengatur nilai yang lebih tinggi berarti versi Windows yang lebih lama tidak akan menjalankan aplikasi dengan benar karena setiap rilis Windows hanya mengetahui versi sebelumnya. Jika Anda ingin aplikasi berjalan pada Windows 10, versi 1903 (build 10.0.18362), Anda harus meninggalkan nilai 10.0.18362.0 apa adanya, atau menambahkan beberapa elemen maxversiontested untuk berbagai nilai yang didukung aplikasi.

      <?xml version="1.0" encoding="UTF-8"?>
      <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
          <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
              <application>
                  <!-- Windows 10 -->
                  <maxversiontested Id="10.0.18362.0"/>
                  <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
              </application>
          </compatibility>
      </assembly>
      
  6. Tambahkan referensi ke metadata Windows Runtime:

    1. Di Penjelajah Solusi, klik kanan pada simpul Referensi proyek Anda dan pilih Tambahkan Referensi.
    2. Klik tombol Telusuri di bagian bawah halaman dan navigasikan ke folder UnionMetadata di jalur penginstalan SDK Anda. Secara default, SDK akan diinstal ke C:\Program Files (x86)\Windows Kits\10\UnionMetadata.
    3. Kemudian, pilih folder bernama sesuai versi Windows yang Anda targetkan (misalnya 10.0.18362.0) dan di dalam folder tersebut Windows.winmd pilih file.
    4. Klik OK untuk menutup dialog Tambahkan Referensi .

Gunakan API hosting XAML untuk menghosting kontrol WinRT XAML

Proses dasar menggunakan API hosting XAML untuk menghosting kontrol WinRT XAML mengikuti langkah-langkah umum berikut:

  1. Inisialisasi kerangka kerja WinRT XAML untuk utas saat ini sebelum aplikasi Anda membuat objek Windows.UI.Xaml.UIElement yang akan dihosting. Ada beberapa cara untuk melakukan ini, tergantung pada kapan Anda berencana untuk membuat objek DesktopWindowXamlSource yang akan menghosting kontrol.

    • Jika aplikasi Anda membuat objek DesktopWindowXamlSource sebelum membuat objek Windows.UI.Xaml.UIElement yang akan dihosting , kerangka kerja ini akan diinisialisasi untuk Anda saat Anda membuat instans objek DesktopWindowXamlSource . Dalam skenario ini, Anda tidak perlu menambahkan kode anda sendiri untuk menginisialisasi kerangka kerja.

    • Namun, jika aplikasi Anda membuat objek Windows.UI.Xaml.UIElement sebelum membuat objek DesktopWindowXamlSource yang akan menghostingnya, aplikasi Anda harus memanggil metode WindowsXamlManager.InitializeForCurrentThread statis untuk menginisialisasi kerangka kerja WinRT XAML secara eksplisit sebelum objek Windows.UI.Xaml.UIElement diinisialisasi. Aplikasi Anda biasanya harus memanggil metode ini ketika elemen UI induk yang menghosting DesktopWindowXamlSource dibuat.

    Catatan

    Metode ini mengembalikan objek WindowsXamlManager yang berisi referensi ke kerangka kerja WinRT XAML. Anda dapat membuat objek WindowsXamlManager sebanyak yang Anda inginkan pada utas tertentu. Namun, karena setiap objek menyimpan referensi ke kerangka kerja WinRT XAML, Anda harus membuang objek untuk memastikan bahwa sumber daya XAML akhirnya dirilis.

  2. Buat objek DesktopWindowXamlSource dan lampirkan ke elemen UI induk di aplikasi Anda yang terkait dengan handel jendela.

    Untuk melakukan ini, Anda harus mengikuti langkah-langkah berikut:

    1. Buat objek DesktopWindowXamlSource dan transmisikan ke antarmuka COM IDesktopWindowXamlSourceNative atau IDesktopWindowXamlSourceNative2 .

    2. Panggil metode AttachToWindow dari antarmuka IDesktopWindowXamlSourceNative atau IDesktopWindowXamlSourceNative2, dan berikan handel jendela elemen UI induk di aplikasi Anda.

      Penting

      Pastikan kode Anda memanggil metode AttachToWindow hanya sekali per objek DesktopWindowXamlSource . Memanggil metode ini lebih dari sekali untuk objek DesktopWindowXamlSource dapat mengakibatkan kebocoran memori.

    3. Atur ukuran awal jendela anak internal yang terkandung dalam DesktopWindowXamlSource. Secara default, jendela anak internal ini diatur ke lebar dan tinggi 0. Jika Anda tidak mengatur ukuran jendela, kontrol WinRT XAML apa pun yang Anda tambahkan ke DesktopWindowXamlSource tidak akan terlihat. Untuk mengakses jendela anak internal di DesktopWindowXamlSource, gunakan properti WindowHandle dari antarmuka IDesktopWindowXamlSourceNative atau IDesktopWindowXamlSourceNative2 .

  3. Terakhir, tetapkan Windows.UI.Xaml.UIElement yang ingin Anda host ke properti Konten objek DesktopWindowXamlSource Anda.

Langkah-langkah dan contoh kode berikut menunjukkan cara menerapkan proses di atas:

  1. Di folder File Sumber proyek, buka file MyDesktopWin32App.cpp default. Hapus seluruh konten file dan tambahkan pernyataan dan using berikut iniinclude. Selain header dan namespace layanan C++ dan UWP standar, pernyataan ini mencakup beberapa item khusus untuk Kepulauan XAML.

    #include <windows.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include <winrt/Windows.Foundation.Collections.h>
    #include <winrt/Windows.system.h>
    #include <winrt/windows.ui.xaml.hosting.h>
    #include <windows.ui.xaml.hosting.desktopwindowxamlsource.h>
    #include <winrt/windows.ui.xaml.controls.h>
    #include <winrt/Windows.ui.xaml.media.h>
    
    using namespace winrt;
    using namespace Windows::UI;
    using namespace Windows::UI::Composition;
    using namespace Windows::UI::Xaml::Hosting;
    using namespace Windows::Foundation::Numerics;
    
  2. Salin kode berikut setelah bagian sebelumnya. Kode ini mendefinisikan fungsi WinMain untuk aplikasi. Fungsi ini menginisialisasi jendela dasar dan menggunakan API hosting XAML untuk menghosting kontrol TextBlock UWP sederhana di jendela.

    LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
    
    HWND _hWnd;
    HWND _childhWnd;
    HINSTANCE _hInstance;
    
    int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)
    {
        _hInstance = hInstance;
    
        // The main window class name.
        const wchar_t szWindowClass[] = L"Win32DesktopApp";
        WNDCLASSEX windowClass = { };
    
        windowClass.cbSize = sizeof(WNDCLASSEX);
        windowClass.lpfnWndProc = WindowProc;
        windowClass.hInstance = hInstance;
        windowClass.lpszClassName = szWindowClass;
        windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    
        windowClass.hIconSm = LoadIcon(windowClass.hInstance, IDI_APPLICATION);
    
        if (RegisterClassEx(&windowClass) == NULL)
        {
            MessageBox(NULL, L"Windows registration failed!", L"Error", NULL);
            return 0;
        }
    
        _hWnd = CreateWindow(
            szWindowClass,
            L"Windows c++ Win32 Desktop App",
            WS_OVERLAPPEDWINDOW | WS_VISIBLE,
            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
            NULL,
            NULL,
            hInstance,
            NULL
        );
        if (_hWnd == NULL)
        {
            MessageBox(NULL, L"Call to CreateWindow failed!", L"Error", NULL);
            return 0;
        }
    
    
        // Begin XAML Island section.
    
        // The call to winrt::init_apartment initializes COM; by default, in a multithreaded apartment.
        winrt::init_apartment(apartment_type::single_threaded);
    
        // Initialize the XAML framework's core window for the current thread.
        WindowsXamlManager winxamlmanager = WindowsXamlManager::InitializeForCurrentThread();
    
        // This DesktopWindowXamlSource is the object that enables a non-UWP desktop application 
        // to host WinRT XAML controls in any UI element that is associated with a window handle (HWND).
        DesktopWindowXamlSource desktopSource;
    
        // Get handle to the core window.
        auto interop = desktopSource.as<IDesktopWindowXamlSourceNative>();
    
        // Parent the DesktopWindowXamlSource object to the current window.
        check_hresult(interop->AttachToWindow(_hWnd));
    
        // This HWND will be the window handler for the XAML Island: A child window that contains XAML.  
        HWND hWndXamlIsland = nullptr;
    
        // Get the new child window's HWND. 
        interop->get_WindowHandle(&hWndXamlIsland);
    
        // Update the XAML Island window size because initially it is 0,0.
        SetWindowPos(hWndXamlIsland, 0, 200, 100, 800, 200, SWP_SHOWWINDOW);
    
        // Create the XAML content.
        Windows::UI::Xaml::Controls::StackPanel xamlContainer;
        xamlContainer.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
    
        Windows::UI::Xaml::Controls::TextBlock tb;
        tb.Text(L"Hello World from Xaml Islands!");
        tb.VerticalAlignment(Windows::UI::Xaml::VerticalAlignment::Center);
        tb.HorizontalAlignment(Windows::UI::Xaml::HorizontalAlignment::Center);
        tb.FontSize(48);
    
        xamlContainer.Children().Append(tb);
        xamlContainer.UpdateLayout();
        desktopSource.Content(xamlContainer);
    
        // End XAML Island section.
    
        ShowWindow(_hWnd, nCmdShow);
        UpdateWindow(_hWnd);
    
        //Message loop:
        MSG msg = { };
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        return 0;
    }
    
  3. Salin kode berikut setelah bagian sebelumnya. Kode ini menentukan prosedur jendela untuk jendela.

    LRESULT CALLBACK WindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
    {
        PAINTSTRUCT ps;
        HDC hdc;
        wchar_t greeting[] = L"Hello World in Win32!";
        RECT rcClient;
    
        switch (messageCode)
        {
            case WM_PAINT:
                if (hWnd == _hWnd)
                {
                    hdc = BeginPaint(hWnd, &ps);
                    TextOut(hdc, 300, 5, greeting, wcslen(greeting));
                    EndPaint(hWnd, &ps);
                }
                break;
            case WM_DESTROY:
                PostQuitMessage(0);
                break;
    
            // Create main window
            case WM_CREATE:
                _childhWnd = CreateWindowEx(0, L"ChildWClass", NULL, WS_CHILD | WS_BORDER, 0, 0, 0, 0, hWnd, NULL, _hInstance, NULL);
                return 0;
    
            // Main window changed size
            case WM_SIZE:
                // Get the dimensions of the main window's client
                // area, and enumerate the child windows. Pass the
                // dimensions to the child windows during enumeration.
                GetClientRect(hWnd, &rcClient);
                MoveWindow(_childhWnd, 200, 200, 400, 500, TRUE);
                ShowWindow(_childhWnd, SW_SHOW);
    
                return 0;
    
                // Process other messages.
    
            default:
                return DefWindowProc(hWnd, messageCode, wParam, lParam);
                break;
        }
    
        return 0;
    }
    
  4. Simpan file kode, dan buat dan jalankan aplikasi. Konfirmasikan bahwa Anda melihat kontrol UWP TextBlock di jendela aplikasi.

    Catatan

    Anda mungkin melihat beberapa peringatan build, termasuk warning C4002: too many arguments for function-like macro invocation 'GetCurrentTime' dan manifest authoring warning 81010002: Unrecognized Element "maxversiontested" in namespace "urn:schemas-microsoft-com:compatibility.v1". Peringatan ini diketahui masalah dengan alat saat ini dan paket NuGet, dan dapat diabaikan.

Untuk contoh lengkap yang menunjukkan penggunaan API hosting XAML untuk menghosting kontrol WinRT XAML, lihat file kode berikut:

Mengemas aplikasi

Anda dapat secara opsional mengemas aplikasi dalam paket MSIX untuk penyebaran. MSIX adalah teknologi pengemasan aplikasi modern untuk Windows, dan didasarkan pada kombinasi teknologi penginstalan MSI, .appx, App-V, dan ClickOnce.

Instruksi berikut menunjukkan kepada Anda cara mengemas semua komponen dalam solusi dalam paket MSIX dengan menggunakan Proyek Pengemasan Aplikasi Windows di Visual Studio 2019. Langkah-langkah ini diperlukan hanya jika Anda ingin mengemas aplikasi dalam paket MSIX.

Catatan

Jika Anda memilih untuk tidak mengemas aplikasi Anda dalam paket MSIX untuk penyebaran, komputer yang menjalankan aplikasi Anda harus menginstal Visual C++ Runtime .

  1. Tambahkan Proyek Pengemasan Aplikasi Windows baru ke solusi Anda. Saat Anda membuat proyek, pilih Windows 10, versi 1903 (10.0; Build 18362) untuk versi Target dan Versi minimum.

  2. Dalam proyek pengemasan, klik kanan simpul Aplikasi dan pilih Tambahkan referensi. Dalam daftar proyek, pilih proyek aplikasi desktop C++ dalam solusi Anda dan klik OK.

  3. Bangun dan jalankan proyek pengemasan. Konfirmasikan bahwa aplikasi berjalan dan menampilkan kontrol WinRT XAML seperti yang diharapkan.

  4. Untuk informasi tentang mendistribusikan/menyebarkan paket, lihat Mengelola penyebaran MSIX Anda.

Langkah berikutnya

Contoh kode dalam artikel ini membuat Anda mulai dengan skenario dasar menghosting kontrol WinRT XAML standar di aplikasi desktop C++. Bagian berikut memperkenalkan skenario tambahan yang mungkin perlu didukung aplikasi Anda.

Menghosting kontrol WinRT XAML kustom

Untuk banyak skenario, Anda mungkin perlu menghosting kontrol WinRT XAML kustom yang berisi beberapa kontrol individual yang bekerja sama. Proses untuk menghosting kontrol kustom (baik kontrol yang Anda tentukan sendiri atau kontrol yang disediakan oleh pihak ke-3) di aplikasi desktop C++ lebih kompleks daripada menghosting kontrol standar, dan memerlukan kode tambahan.

Untuk panduan lengkap, lihat Menghosting kontrol WinRT XAML kustom di aplikasi desktop C++ menggunakan API Hosting XAML.

Skenario tingkat lanjut

Banyak aplikasi desktop yang menghosting Kepulauan XAML harus menangani skenario tambahan untuk memberikan pengalaman pengguna yang lancar. Misalnya, aplikasi desktop mungkin perlu menangani input keyboard di Kepulauan XAML, navigasi fokus antara Kepulauan XAML dan elemen UI lainnya, dan perubahan tata letak.

Untuk informasi selengkapnya tentang menangani skenario dan penunjuk ini ke sampel kode terkait, lihat Skenario tingkat lanjut untuk Kepulauan XAML di aplikasi desktop C++.