Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
U kunt Windows Runtime Composition-API's (ook wel de Visual Layergenoemd) gebruiken in uw Win32-apps om moderne functies te creëren die oplichten voor Windows-gebruikers.
De volledige code voor deze zelfstudie is beschikbaar op GitHub: Win32 HelloComposition-voorbeeld.
Universele Windows-toepassingen die nauwkeurige controle over hun UI-samenstelling nodig hebben, hebben toegang tot de naamruimte Windows.UI.Composition om nauwkeurige controle uit te oefenen over hoe hun gebruikersinterface is samengesteld en weergegeven. Deze samenstellings-API is echter niet beperkt tot UWP-apps. Win32-desktoptoepassingen kunnen profiteren van de moderne samenstellingssystemen in UWP en Windows.
Vereiste voorwaarden
De UWP-hosting-API heeft deze vereisten.
- We gaan ervan uit dat u bekend bent met app-ontwikkeling met Behulp van Win32 en UWP. Zie voor meer informatie:
- Windows 10 versie 1803 of hoger
- Windows 10 SDK 17134 of hoger
Samenstellings-API's gebruiken vanuit een Win32-bureaubladtoepassing
In deze zelfstudie maakt u een eenvoudige Win32 C++-app en voegt u UWP-samenstellingselementen eraan toe. De focus ligt op het correct configureren van het project, het maken van de interoperabiliteitscode en het tekenen van iets eenvoudigs met behulp van Windows Composition-API's. De voltooide app ziet er als volgt uit.
Een C++ Win32-project maken in Visual Studio
De eerste stap is het maken van het Win32-app-project in Visual Studio.
Een nieuw Win32-toepassingsproject maken in C++ met de naam HelloComposition:
Open Visual Studio en vervolgens selecteer Bestand>Nieuw>Project.
Het dialoogvenster Nieuw project wordt geopend.
Vouw onder de categorie Geïnstalleerd het knooppunt Visual C++ uit en selecteer vervolgens Windows Desktop.
Selecteer de sjabloon Windows-bureaubladtoepassing.
Voer de naam HelloComposition in en klik op OK.
Visual Studio maakt het project en opent de editor voor het hoofd-app-bestand.
Het project configureren voor het gebruik van Windows Runtime-API's
Als u Windows Runtime-API's (WinRT) wilt gebruiken in uw Win32-app, gebruiken we C++/WinRT. U moet uw Visual Studio-project configureren om C++/WinRT-ondersteuning toe te voegen.
Open in het menu Project de projecteigenschappen (HelloComposition Properties) en zorg ervoor dat de volgende instellingen zijn ingesteld op de opgegeven waarden:
- Selecteer voor -configuratieAlle Configuraties. Selecteer Alle platformsvoor Platform.
- Configuratie-eigenschappen>Algemeen>Windows SDK-versie = 10.0.17763.0 of hoger
- C/C++>Taal>C++ Taalstandaard = ISO C++ 17 Standaard (/stf:c++17)
- Linker>Invoer>Extra Afhankelijkheden moet bevatten "windowsapp.lib". Als deze niet is opgenomen in de lijst, voegt u deze toe.
De vooraf gecompileerde header bijwerken
Wijzig de naam van
stdafx.henstdafx.cpprespectievelijk inpch.henpch.cpp.Stel de projecteigenschap in C/C++>Vooraf gecompileerde Headers>Vooraf gecompileerd Headerbestand op pch.h.
Zoeken en vervangen
#include "stdafx.h"door#include "pch.h"in alle bestanden.(Bewerken>Zoeken en vervangen>Zoeken in bestanden)
Neem in
pch.hwinrt/base.henunknwn.hop.// reference additional headers your program requires here #include <unknwn.h> #include <winrt/base.h>
Het is een goed idee om het project op dit moment te bouwen om ervoor te zorgen dat er geen fouten zijn voordat u aan de slag gaat.
Een klasse maken om samenstellingselementen te hosten
Als u inhoud wilt hosten die u maakt met de visuele laag, maakt u een klasse (CompositionHost) voor het beheren van interop- en samenstellingselementen. Hier voert u de meeste configuratie uit voor het hosten van samenstellings-API's, waaronder:
- Een Compositorophalen, dat objecten creëert en beheert in de Windows.UI.Composition naamruimte.
- een DispatcherQueueController/DispatcherQueue maken om taken voor de WinRT-API's te beheren.
- het maken van een DesktopWindowTarget en een samenstellingscontainer om de samenstellingsobjecten weer te geven.
We maken deze klasse een singleton om problemen met threading te voorkomen. U kunt bijvoorbeeld slechts één dispatcherwachtrij per thread maken, dus het instantiëren van een tweede exemplaar van CompositionHost op dezelfde thread zou een fout veroorzaken.
Aanbeveling
Als dat nodig is, controleert u de volledige code aan het einde van de zelfstudie om ervoor te zorgen dat alle code zich op de juiste plaatsen bevindt terwijl u de zelfstudie doorloopt.
Voeg een nieuw klassebestand toe aan uw project.
- Klik in Solution Explorer-met de rechtermuisknop op het project HelloComposition.
- Selecteer Klasse toevoegen>... in het contextmenu.
- Geef in het dialoogvenster Klasse toevoegen de klasse een naam CompositionHost.cs en klik vervolgens op Toevoegen.
Voeg kopteksten en toe met behulp van vereist voor samenstellingsinteroperabiliteit.
- Voeg in CompositionHost.h deze includes bovenaan het bestand toe.
#pragma once #include <winrt/Windows.UI.Composition.Desktop.h> #include <windows.ui.composition.interop.h> #include <DispatcherQueue.h>- Voeg in CompositionHost.cpp deze toe met behulp van boven aan het bestand, nadat bevat.
using namespace winrt; using namespace Windows::System; using namespace Windows::UI; using namespace Windows::UI::Composition; using namespace Windows::UI::Composition::Desktop; using namespace Windows::Foundation::Numerics;Bewerk de klasse om het singleton-patroon te gebruiken.
- Maak in CompositionHost.h de constructor privé.
- Declareer een openbare statische GetInstance-methode .
class CompositionHost { public: ~CompositionHost(); static CompositionHost* GetInstance(); private: CompositionHost(); };- Voeg in CompositionHost.cpp de definitie van de Methode GetInstance toe.
CompositionHost* CompositionHost::GetInstance() { static CompositionHost instance; return &instance; }Declareer in CompositionHost.h privélidvariabelen voor de Compositor, DispatcherQueueController en DesktopWindowTarget.
winrt::Windows::UI::Composition::Compositor m_compositor{ nullptr }; winrt::Windows::System::DispatcherQueueController m_dispatcherQueueController{ nullptr }; winrt::Windows::UI::Composition::Desktop::DesktopWindowTarget m_target{ nullptr };Voeg een openbare methode toe om de samenstellingsinteroperabiliteitsobjecten te initialiseren.
Opmerking
In Initialize roept u de methoden EnsureDispatcherQueue, CreateDesktopWindowTarget en CreateCompositionRoot aan. U maakt deze methoden in de volgende stappen.
- Declareer in CompositionHost.h een openbare methode met de naam Initialize die een HWND als argument gebruikt.
void Initialize(HWND hwnd);- Voeg in CompositionHost.cpp de definitie van de methode Initialiseren toe.
void CompositionHost::Initialize(HWND hwnd) { EnsureDispatcherQueue(); if (m_dispatcherQueueController) m_compositor = Compositor(); CreateDesktopWindowTarget(hwnd); CreateCompositionRoot(); }Maak een dispatcherwachtrij op de thread die Gebruikmaakt van Windows Composition.
Een compositor moet worden gemaakt op een thread met een dispatcherwachtrij, dus deze methode wordt als eerste aangeroepen tijdens het initialisatieproces.
- Declareer in CompositionHost.h een privémethode met de naam EnsureDispatcherQueue.
void EnsureDispatcherQueue();- Voeg in CompositionHost.cpp de definitie van de methode EnsureDispatcherQueue toe.
void CompositionHost::EnsureDispatcherQueue() { namespace abi = ABI::Windows::System; if (m_dispatcherQueueController == nullptr) { DispatcherQueueOptions options { sizeof(DispatcherQueueOptions), /* dwSize */ DQTYPE_THREAD_CURRENT, /* threadType */ DQTAT_COM_ASTA /* apartmentType */ }; Windows::System::DispatcherQueueController controller{ nullptr }; check_hresult(CreateDispatcherQueueController(options, reinterpret_cast<abi::IDispatcherQueueController**>(put_abi(controller)))); m_dispatcherQueueController = controller; } }Registreer het venster van uw app als een samenstellingsdoel.
- Declareer in CompositionHost.h een privémethode met de naam CreateDesktopWindowTarget die een HWND als argument gebruikt.
void CreateDesktopWindowTarget(HWND window);- Voeg in CompositionHost.cpp de definitie van de methode CreateDesktopWindowTarget toe.
void CompositionHost::CreateDesktopWindowTarget(HWND window) { namespace abi = ABI::Windows::UI::Composition::Desktop; auto interop = m_compositor.as<abi::ICompositorDesktopInterop>(); DesktopWindowTarget target{ nullptr }; check_hresult(interop->CreateDesktopWindowTarget(window, false, reinterpret_cast<abi::IDesktopWindowTarget**>(put_abi(target)))); m_target = target; }Maak een hoofdvisualcontainer voor het opslaan van visuele objecten.
- Declareer in CompositionHost.h een privémethode met de naam CreateCompositionRoot.
void CreateCompositionRoot();- Voeg in CompositionHost.cpp de definitie van de methode CreateCompositionRoot toe.
void CompositionHost::CreateCompositionRoot() { auto root = m_compositor.CreateContainerVisual(); root.RelativeSizeAdjustment({ 1.0f, 1.0f }); root.Offset({ 124, 12, 0 }); m_target.Root(root); }
Bouw het project nu om ervoor te zorgen dat er geen fouten zijn.
Met deze methoden worden de onderdelen ingesteld die nodig zijn voor interoperabiliteit tussen de UWP-visuallaag en Win32-API's. U kunt nu inhoud toevoegen aan uw app.
Samenstellingselementen toevoegen
Nu de infrastructuur is ingesteld, kunt u nu de samenstellingsinhoud genereren die u wilt weergeven.
In dit voorbeeld voegt u code toe waarmee een willekeurig gekleurd vierkant SpriteVisual wordt gemaakt met een animatie die ervoor zorgt dat het na een korte vertraging valt.
Voeg een samenstellingselement toe.
- Declareer in CompositionHost.h een openbare methode met de naam AddElement die 3 floatwaarden als argumenten accepteert.
void AddElement(float size, float x, float y);- Voeg in CompositionHost.cpp de definitie van de methode AddElement toe.
void CompositionHost::AddElement(float size, float x, float y) { if (m_target.Root()) { auto visuals = m_target.Root().as<ContainerVisual>().Children(); auto visual = m_compositor.CreateSpriteVisual(); auto element = m_compositor.CreateSpriteVisual(); uint8_t r = (double)(double)(rand() % 255);; uint8_t g = (double)(double)(rand() % 255);; uint8_t b = (double)(double)(rand() % 255);; element.Brush(m_compositor.CreateColorBrush({ 255, r, g, b })); element.Size({ size, size }); element.Offset({ x, y, 0.0f, }); auto animation = m_compositor.CreateVector3KeyFrameAnimation(); auto bottom = (float)600 - element.Size().y; animation.InsertKeyFrame(1, { element.Offset().x, bottom, 0 }); using timeSpan = std::chrono::duration<int, std::ratio<1, 1>>; std::chrono::seconds duration(2); std::chrono::seconds delay(3); animation.Duration(timeSpan(duration)); animation.DelayTime(timeSpan(delay)); element.StartAnimation(L"Offset", animation); visuals.InsertAtTop(element); visuals.InsertAtTop(visual); } }
Het venster maken en weergeven
U kunt nu een knop en de INHOUD van uw UWP-samenstelling toevoegen aan uw Win32-gebruikersinterface.
Voeg in HelloComposition.cpp boven aan het bestand CompositionHost.h toe, definieer BTN_ADD en haal een exemplaar van CompositionHost op.
#include "CompositionHost.h" // #define MAX_LOADSTRING 100 // This is already in the file. #define BTN_ADD 1000 CompositionHost* compHost = CompositionHost::GetInstance();Wijzig in de
InitInstancemethode de grootte van het venster dat is gemaakt. (Wijzig in deze regelCW_USEDEFAULT, 0in900, 672.)HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 900, 672, nullptr, nullptr, hInstance, nullptr);Voeg in de functie WndProc
case WM_CREATEtoe aan het bericht schakelblok. In dit geval initialiseert u de CompositionHost en maakt u de knop.case WM_CREATE: { compHost->Initialize(hWnd); srand(time(nullptr)); CreateWindow(TEXT("button"), TEXT("Add element"), WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 12, 12, 100, 50, hWnd, (HMENU)BTN_ADD, nullptr, nullptr); } break;Ook in de WndProc-functie, behandel de knopklik om een compositie-element toe te voegen aan de gebruikersinterface.
Voeg
case BTN_ADDtoe aan het wmId-switchblok binnen het WM_COMMAND-blok.case BTN_ADD: // addButton click { double size = (double)(rand() % 150 + 50); double x = (double)(rand() % 600); double y = (double)(rand() % 200); compHost->AddElement(size, x, y); break; }
U kunt nu uw app bouwen en uitvoeren. Als dat nodig is, controleert u de volledige code aan het einde van de zelfstudie om ervoor te zorgen dat alle code zich op de juiste plaatsen bevindt.
Wanneer u de app uitvoert en op de knop klikt, ziet u geanimeerde vierkantjes die zijn toegevoegd aan de gebruikersinterface.
Aanvullende bronnen
- Win32 HelloComposition-voorbeeld (GitHub)
- Aan de slag met Win32 en C++
- Aan de slag met Windows-apps (UWP)
- Uw bureaubladtoepassing verbeteren voor Windows (UWP)
- Windows.UI.Composition-naamruimte (UWP)
De volledige code
Hier volgt de volledige code voor de klasse CompositionHost en de Methode InitInstance.
CompositionHost.h
#pragma once
#include <winrt/Windows.UI.Composition.Desktop.h>
#include <windows.ui.composition.interop.h>
#include <DispatcherQueue.h>
class CompositionHost
{
public:
~CompositionHost();
static CompositionHost* GetInstance();
void Initialize(HWND hwnd);
void AddElement(float size, float x, float y);
private:
CompositionHost();
void CreateDesktopWindowTarget(HWND window);
void EnsureDispatcherQueue();
void CreateCompositionRoot();
winrt::Windows::UI::Composition::Compositor m_compositor{ nullptr };
winrt::Windows::UI::Composition::Desktop::DesktopWindowTarget m_target{ nullptr };
winrt::Windows::System::DispatcherQueueController m_dispatcherQueueController{ nullptr };
};
CompositionHost.cpp
#include "pch.h"
#include "CompositionHost.h"
using namespace winrt;
using namespace Windows::System;
using namespace Windows::UI;
using namespace Windows::UI::Composition;
using namespace Windows::UI::Composition::Desktop;
using namespace Windows::Foundation::Numerics;
CompositionHost::CompositionHost()
{
}
CompositionHost* CompositionHost::GetInstance()
{
static CompositionHost instance;
return &instance;
}
CompositionHost::~CompositionHost()
{
}
void CompositionHost::Initialize(HWND hwnd)
{
EnsureDispatcherQueue();
if (m_dispatcherQueueController) m_compositor = Compositor();
if (m_compositor)
{
CreateDesktopWindowTarget(hwnd);
CreateCompositionRoot();
}
}
void CompositionHost::EnsureDispatcherQueue()
{
namespace abi = ABI::Windows::System;
if (m_dispatcherQueueController == nullptr)
{
DispatcherQueueOptions options
{
sizeof(DispatcherQueueOptions), /* dwSize */
DQTYPE_THREAD_CURRENT, /* threadType */
DQTAT_COM_ASTA /* apartmentType */
};
Windows::System::DispatcherQueueController controller{ nullptr };
check_hresult(CreateDispatcherQueueController(options, reinterpret_cast<abi::IDispatcherQueueController**>(put_abi(controller))));
m_dispatcherQueueController = controller;
}
}
void CompositionHost::CreateDesktopWindowTarget(HWND window)
{
namespace abi = ABI::Windows::UI::Composition::Desktop;
auto interop = m_compositor.as<abi::ICompositorDesktopInterop>();
DesktopWindowTarget target{ nullptr };
check_hresult(interop->CreateDesktopWindowTarget(window, false, reinterpret_cast<abi::IDesktopWindowTarget**>(put_abi(target))));
m_target = target;
}
void CompositionHost::CreateCompositionRoot()
{
auto root = m_compositor.CreateContainerVisual();
root.RelativeSizeAdjustment({ 1.0f, 1.0f });
root.Offset({ 124, 12, 0 });
m_target.Root(root);
}
void CompositionHost::AddElement(float size, float x, float y)
{
if (m_target.Root())
{
auto visuals = m_target.Root().as<ContainerVisual>().Children();
auto visual = m_compositor.CreateSpriteVisual();
auto element = m_compositor.CreateSpriteVisual();
uint8_t r = (double)(double)(rand() % 255);;
uint8_t g = (double)(double)(rand() % 255);;
uint8_t b = (double)(double)(rand() % 255);;
element.Brush(m_compositor.CreateColorBrush({ 255, r, g, b }));
element.Size({ size, size });
element.Offset({ x, y, 0.0f, });
auto animation = m_compositor.CreateVector3KeyFrameAnimation();
auto bottom = (float)600 - element.Size().y;
animation.InsertKeyFrame(1, { element.Offset().x, bottom, 0 });
using timeSpan = std::chrono::duration<int, std::ratio<1, 1>>;
std::chrono::seconds duration(2);
std::chrono::seconds delay(3);
animation.Duration(timeSpan(duration));
animation.DelayTime(timeSpan(delay));
element.StartAnimation(L"Offset", animation);
visuals.InsertAtTop(element);
visuals.InsertAtTop(visual);
}
}
HelloComposition.cpp (gedeeltelijk)
#include "pch.h"
#include "HelloComposition.h"
#include "CompositionHost.h"
#define MAX_LOADSTRING 100
#define BTN_ADD 1000
CompositionHost* compHost = CompositionHost::GetInstance();
// Global Variables:
// ...
// ... code not shown ...
// ...
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, 900, 672, nullptr, nullptr, hInstance, nullptr);
// ...
// ... code not shown ...
// ...
}
// ...
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
// Add this...
case WM_CREATE:
{
compHost->Initialize(hWnd);
srand(time(nullptr));
CreateWindow(TEXT("button"), TEXT("Add element"),
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
12, 12, 100, 50,
hWnd, (HMENU)BTN_ADD, nullptr, nullptr);
}
break;
// ...
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
// Add this...
case BTN_ADD: // addButton click
{
double size = (double)(rand() % 150 + 50);
double x = (double)(rand() % 600);
double y = (double)(rand() % 200);
compHost->AddElement(size, x, y);
break;
}
// ...
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// ...
// ... code not shown ...
// ...
Windows developer