Dela via


Vara värd för en Standard WinRT XAML-kontroll i en C++-skrivbordsapp (Win32)

Viktigt!

Det här avsnittet använder eller nämner typer från CommunityToolkit/Microsoft.Toolkit.Win32 GitHub-lagringsplats. Viktig information om stöd för XAML Islands finns i XAML Islands-meddelande i den lagringsplatsen.

Den här artikeln visar hur du använder WinRT XAML-värd-API:et som värd för en Standard WinRT XAML-kontroll (dvs. en kontroll som tillhandahålls av Windows SDK) i en ny C++-skrivbordsapp. Koden baseras på enkla XAML Island-exempel, och i det här avsnittet beskrivs några av de viktigaste delarna i koden. Om du har ett befintligt C++-skrivbordsappprojekt kan du anpassa de här stegen och kodexemplen för projektet.

Anmärkning

Scenariot som visas i den här artikeln stöder inte direkt redigering av XAML-markering för WinRT XAML-kontroller som finns i din app. Det här scenariot begränsar dig till att ändra utseendet och beteendet för värdbaserade kontroller via kod. Anvisningar som gör att du kan redigera XAML-markering direkt när du är värd för WinRT XAML-kontroller finns i Värd för en anpassad WinRT XAML-kontroll i en C++-skrivbordsapp.

Skapa ett skrivbordsprogramprojekt

  1. I Visual Studio 2019 med Windows 10, version 1903 SDK (version 10.0.18362) eller en senare version installerad skapar du ett nytt Windows Desktop-program projekt och ger det namnet MyDesktopWin32App. Den här projekttypen är tillgänglig under projektfiltren C++, Windowsoch Desktop.

  2. I Solution Explorerhögerklickar du på lösningsnoden, klickar på Retarget-lösning, väljer 10.0.18362.0 eller en senare SDK-version och klickar sedan på OK.

  3. Installera nuget-paketet Microsoft.Windows.CppWinRT för att aktivera stöd för C++/WinRT- i projektet:

    1. Högerklicka på projektet i Solution Explorer och välj Hantera NuGet-paket.
    2. Välj fliken Bläddra, sök efter paketet Microsoft.Windows.CppWinRT och installera den senaste versionen av det här paketet.

    Anmärkning

    För nya projekt kan du också installera C++/WinRT Visual Studio Extension (VSIX) och använda en av C++/WinRT-projektmallarna som ingår i tillägget. Mer information finns i Visual Studio-stöd för C++/WinRT och VSIX-.

  4. På fliken Bläddra i fönstret NuGet Package Manager söker du efter Microsoft.Toolkit.Win32.UI.SDK NuGet-paketet och installerar den senaste stabila versionen av paketet. Det här paketet innehåller flera bygg- och körtidstillgångar som möjliggör XAML Islets att fungera i din app.

  5. Ange värdet maxversiontested i programmanifestet för att ange att programmet är kompatibelt med Windows 10 version 1903.

    1. Om du inte redan har ett programmanifest i projektet lägger du till en ny XML-fil i projektet och ger den namnet app.manifest.

    2. I applikationsmanifestet inkluderar du -kompatibilitetselementet och de underordnade element som visas i följande exempel. Ersätt attributet ID för det maxversiontestade-elementet med versionsnumret för Windows som du riktar in dig på (detta måste vara 10.0.18362.0 eller en senare version). Observera att inställningen av ett högre värde innebär att äldre versioner av Windows inte kör appen korrekt eftersom varje Windows-version bara känner till versioner före den. Om du vill att appen ska köras på Windows 10 version 1903 (version 10.0.18362) bör du antingen lämna värdet 10.0.18362.0 som det är eller lägga till flera maxversiontestade element för de olika värden som appen stöder.

      <?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. Lägg till en referens till Windows Runtime-metadata:

    1. I Solution Explorerhögerklickar du på projektnoden Referenser och väljer Lägg till referens.
    2. Klicka på knappen Bläddra längst ned på sidan och gå till mappen UnionMetadata i din SDK-installationssökväg. Som standardläge kommer SDK:et att installeras till C:\Program Files (x86)\Windows Kits\10\UnionMetadata.
    3. Välj sedan mappen med namnet efter den Windows-version som du riktar in dig på (t.ex. 10.0.18362.0) och välj filen i mappen Windows.winmd .
    4. Klicka på OK för att stänga dialogrutan Lägg till referens.

Använd XAML-hosting-API:t för att hantera en WinRT XAML-kontroll

Den grundläggande processen för att använda XAML-värd-API:et som värd för en WinRT XAML-kontroll följer dessa allmänna steg:

  1. Initiera WinRT XAML-ramverket för den aktuella tråden innan appen skapar något av Windows.UI.Xaml.UIElement objekt som den ska vara värd för. Det finns flera sätt att göra detta, beroende på när du planerar att skapa DesktopWindowXamlSource objekt som ska vara värd för kontrollerna.

    • Om ditt program skapar DesktopWindowXamlSource--objektet innan det skapar något av Windows.UI.Xaml.UIElement objekt som det ska vara värd för, initieras det här ramverket åt dig när du instansierar DesktopWindowXamlSource--objektet. I det här scenariot behöver du inte lägga till någon egen kod för att initiera ramverket.

    • Men om ditt program skapar Windows.UI.Xaml.UIElement objekt innan det skapar DesktopWindowXamlSource- objekt som ska vara värd för dem, ditt program måste anropa metoden static WindowsXamlManager.InitializeForCurrentThread för att uttryckligen initiera WinRT XAML-ramverket innan Windows.UI.Xaml.UIElement objekt instansieras. Programmet bör vanligtvis anropa den här metoden när det överordnade användargränssnittselementet som är värd för DesktopWindowXamlSource- instansieras.

    Anmärkning

    Den här metoden returnerar ett WindowsXamlManager- objekt som innehåller en referens till WinRT XAML-ramverket. Du kan skapa så många WindowsXamlManager objekt som du vill i en viss tråd. Men eftersom varje objekt innehåller en referens till WinRT XAML-ramverket bör du ta bort objekten för att säkerställa att XAML-resurser släpps så småningom.

  2. Skapa ett DesktopWindowXamlSource- objekt och koppla det till ett överordnat användargränssnittselement i ditt program som är associerat med ett fönsterhandtag.

    För att göra detta måste du följa dessa steg:

    1. Skapa ett DesktopWindowXamlSource- objekt och skicka det till IDesktopWindowXamlSourceNative eller IDesktopWindowXamlSourceNative2 COM-gränssnittet.

    2. Anropa metoden AttachToWindow för IDesktopWindowXamlSourceNative eller IDesktopWindowXamlSourceNative2-gränssnittet och skicka in fönsterhandtaget för det överordnade användargränssnittselementet i ditt program.

      Viktigt!

      Kontrollera att koden bara anropar metoden AttachToWindow en gång per DesktopWindowXamlSource-objekt. Om du anropar den här metoden mer än en gång för en DesktopWindowXamlSource kan objekt resultera i en minnesläcka.

    3. Ange den ursprungliga storleken på det interna barnfönstret i DesktopWindowXamlSource. Som standardinställning är det här interna underordnade fönstret inställt på en bredd och höjd på 0. Om du inte anger fönstrets storlek visas inte alla WinRT XAML-kontroller som du lägger till i DesktopWindowXamlSource-. Om du vill få åtkomst till det interna underfönstret i DesktopWindowXamlSource, använder du egenskapen WindowHandle för IDesktopWindowXamlSourceNative eller IDesktopWindowXamlSourceNative2-gränssnittet.

  3. Tilldela slutligen Windows.UI.Xaml.UIElement du vill vara värd för egenskapen Content för ditt DesktopWindowXamlSource--objekt.

Följande steg och kodexempel visar hur du implementerar ovanstående process:

  1. I mappen Source Files i projektet öppnar du standardfilen MyDesktopWin32App.cpp. Ta bort hela innehållet i filen och lägg till följande include- och using-instruktioner. Förutom standardrubriker och namnområden för C++ och UWP innehåller dessa instruktioner flera objekt som är specifika för XAML-öar.

    #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. Kopiera följande kod efter föregående avsnitt. Den här koden definierar funktionen WinMain för appen. Den här funktionen initierar ett grundläggande fönster och använder XAML-värd-API:et för att köra en enkel UWP-TextBlock--kontroll i fönstret.

    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. Kopiera följande kod efter föregående avsnitt. Den här koden definierar fönsterproceduren för fönstret.

    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. Spara kodfilen och skapa och kör appen. Bekräfta att du ser kontrollen UWP TextBlock i appfönstret.

    Anmärkning

    Du kan komma att se flera byggvarningar, inklusive warning C4002: too many arguments for function-like macro invocation 'GetCurrentTime' och manifest authoring warning 81010002: Unrecognized Element "maxversiontested" in namespace "urn:schemas-microsoft-com:compatibility.v1". Dessa varningar är kända problem med de aktuella verktygen och NuGet-paketen, och de kan ignoreras.

Fullständiga exempel som visar hur du använder XAML-värd-API:et som värd för en WinRT XAML-kontroll finns i följande kodfiler:

Förpacka appen

Du kan också paketera appen i ett MSIX-paket för distribution. MSIX är den moderna apppaketeringstekniken för Windows och baseras på en kombination av MSI, .appx, App-V och ClickOnce-installationstekniker.

Följande instruktioner visar hur du paketerar alla komponenter i lösningen i ett MSIX-paket med hjälp av Windows Application Packaging Project i Visual Studio 2019. De här stegen är bara nödvändiga om du vill paketera appen i ett MSIX-paket.

Anmärkning

Om du väljer att inte paketera programmet i ett MSIX-paket för distribution måste datorer som kör appen ha Visual C++ Runtime installerat.

  1. Lägg till ett nytt Windows Application Packaging Project till din lösning. När du skapar projektet väljer du Windows 10, version 1903 (10.0; Build 18362) för både Målversionen och Minimiversion.

  2. Högerklicka på noden Program i paketeringsprojektet och välj Lägg till referens. I listan över projekt väljer du C++-skrivbordsprogramprojektet i din lösning och klickar på OK.

  3. Skapa och kör paketeringsprojektet. Bekräfta att appen körs och visar WinRT XAML-kontrollerna som förväntat.

  4. Information om hur du distribuerar paketet finns i Hantera msix-distributionen.

Nästa steg

Kodexemplen i den här artikeln hjälper dig att komma igång med det grundläggande scenariot att värda en standard WinRT XAML-kontroll i en C++-skrivbordsapplikation. I följande avsnitt beskrivs ytterligare scenarier som programmet kan behöva stödja.

Skapa en anpassad WinRT XAML-kontroll

I många scenarier kan du behöva vara värd för en anpassad WinRT XAML-kontroll som innehåller flera enskilda kontroller som fungerar tillsammans. Processen för att vara värd för en anpassad kontroll (antingen en kontroll som du definierar själv eller en kontroll som tillhandahålls av en tredje part) i en C++-skrivbordsapp är mer komplex än att vara värd för en standardkontroll och kräver ytterligare kod.

En fullständig genomgång finns i Värda en anpassad WinRT XAML-kontroll i en C++-skrivbordsapp med XAML-värd-API.

Avancerade scenarier

Många skrivbordsprogram som är värdar för XAML-öar måste hantera ytterligare scenarier för att ge en smidig användarupplevelse. Till exempel kan skrivbordsprogram behöva hantera tangentbordsindata på XAML-öarna, fokusnavigering mellan XAML-öar och andra gränssnittselement och layoutändringar.

Mer information om hur du hanterar dessa scenarier och pekare på relaterade kodexempel finns i Avancerade scenarier för XAML-öar i C++-skrivbordsappar.