Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
V aplikacích Win32 můžete použít rozhraní API prostředí Windows Runtime Composition (označovaná také jako vizuální vrstva) a vytvářet tak moderní prostředí, která zaujmou uživatele Windows.
Kompletní kód pro tento kurz je k dispozici na GitHubu: ukázka Win32 HelloComposition.
Univerzální aplikace pro Windows, které potřebují přesnou kontrolu nad složením uživatelského rozhraní, mají přístup k oboru názvů Windows.UI.Composition , aby mohly provádět jemně odstupňovanou kontrolu nad tím, jak se jejich uživatelské rozhraní skládá a vykresluje. Toto rozhraní API pro složení se však neomezuje jen na aplikace pro UWP. Desktopové aplikace Win32 mohou využívat výhod moderních kompozičních systémů v UWP a Windows.
Předpoklady
Rozhraní API pro hostování UWP má tyto požadavky.
- Předpokládáme, že máte určitou znalost vývoje aplikací pomocí Win32 a UPW. Další informace najdete tady:
- Windows 10 verze 1803 nebo novější
- Windows 10 SDK 17134 nebo novější
Jak používat rozhraní API composition z desktopové aplikace Win32
V tomto kurzu vytvoříte jednoduchou aplikaci Win32 C++ a přidáte do ní elementy upW Composition. Fokus je na správné konfiguraci projektu, vytvoření kódu vzájemné spolupráce a nakreslení něčeho jednoduchého pomocí rozhraní WINDOWS Composition API. Hotová aplikace vypadá takto.
Vytvoření projektu C++ Win32 v sadě Visual Studio
Prvním krokem je vytvoření projektu aplikace Win32 v sadě Visual Studio.
Vytvoření nového projektu aplikace Win32 v jazyce C++ s názvem HelloComposition:
Otevřete Visual Studio a vyberte Soubor>Nový>Projekt.
Otevře se dialogové okno Nový projekt .
V kategorii Nainstalováno rozbalte uzel Visual C++ a pak vyberte Windows Desktop.
Vyberte šablonu desktopové aplikace systému Windows.
Zadejte název HelloComposition a klikněte na tlačítko OK.
Visual Studio vytvoří projekt a otevře editor hlavního souboru aplikace.
Konfigurace projektu pro použití rozhraní API prostředí prostředí Windows Runtime
K používání rozhraní API prostředí prostředí Windows Runtime (WinRT) ve vaší aplikaci Win32 používáme C++/WinRT. Abyste mohli přidat podporu C++/WinRT, musíte nakonfigurovat projekt sady Visual Studio.
(Podrobnosti najdete v tématu Začínáme s C++/WinRT – Úprava projektu desktopové aplikace windows pro přidání podpory C++/WinRT).
V nabídce Projekt otevřete vlastnosti projektu (HelloComposition Properties) a ujistěte se, že jsou následující nastavení nastavená na zadané hodnoty:
- Ve konfiguracivyberte Všechny konfigurace. Pro platformuvyberte všechny platformy.
- Vlastnosti konfigurace>Obecné>Verze sady Windows SDK = 10.0.17763.0 nebo vyšší
- cs-CZ: Jazyk C/C++>Jazyk>Standard jazyka C++ = ISO C++ 17 Standard (/stf:c++17)
- Linker>vstup>Další závislosti musí obsahovat "windowsapp.lib". Pokud to není v seznamu, přidejte to.
Aktualizace předkompilované hlavičky
Přejmenujte
stdafx.hastdafx.cppnapch.hapch.cpppodle pořadí.Nastavte vlastnost projektu C/C++>Předkompilované hlavičky>Předkompilovaný soubor hlaviček na pch.h.
Najděte a nahraďte
#include "stdafx.h"za#include "pch.h"ve všech souborech.(Upravit>Najít a nahradit>Najít v souborech)
Ve
pch.hzahrňtewinrt/base.haunknwn.h.// reference additional headers your program requires here #include <unknwn.h> #include <winrt/base.h>
V tomto okamžiku je vhodné vytvořit projekt, abyste se ujistili, že před zahájením nedochází k žádným chybám.
Vytvoření třídy pro hostování prvků složení
Pokud chcete hostovat obsah, který vytvoříte pomocí vizuální vrstvy, vytvořte třídu (CompositionHost) pro správu interoperability a vytváření prvků kompozice. Tady provádíte většinu konfigurace pro hostování rozhraní API composition, mezi které patří:
- získání Compositor, který vytváří a spravuje objekty v Windows.UI.Composition oboru názvů.
- vytvoření DispatcherQueueController/DispatcherQueue pro správu úloh pro rozhraní API WinRT.
- vytvoření DesktopWindowTarget a kontejneru Composition pro zobrazení kompozičních objektů.
Tuto třídu vytvoříme jako singleton, abychom se vyhnuli problémům s vlákny. Například můžete vytvořit pouze jednu frontu dispečera na vlákno, takže vytvoření druhého instance CompositionHost ve stejném vlákně by způsobilo chybu.
Tip
Pokud potřebujete, zkontrolujte kompletní kód na konci kurzu a ujistěte se, že je veškerý kód na správných místech při procházení kurzu.
Přidejte do projektu nový soubor třídy.
- V Průzkumníku řešeníklikněte pravým tlačítkem na projekt HelloComposition.
- V místní nabídce vyberte Přidat třídu>....
- V dialogovém okně Přidat třídu pojmenujte třídu CompositionHost a klepněte na tlačítko Přidat.
Zahrňte záhlaví a použití vyžadované pro složení pro interoperabilitu.
- Přidejte do souboru CompositionHost.h tyto include v horní části souboru.
#pragma once #include <winrt/Windows.UI.Composition.Desktop.h> #include <windows.ui.composition.interop.h> #include <DispatcherQueue.h>- V CompositionHost.cpp přidejte tyto pomocí v horní části souboru za zahrnuje.
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;Upravte třídu tak, aby používala jednotonový vzor.
- V Souboru CompositionHost.h nastavte konstruktor jako soukromý.
- Deklarujte veřejnou statickou metodu GetInstance .
class CompositionHost { public: ~CompositionHost(); static CompositionHost* GetInstance(); private: CompositionHost(); };- V CompositionHost.cpp přidejte definici GetInstance metody.
CompositionHost* CompositionHost::GetInstance() { static CompositionHost instance; return &instance; }Ve CompositionHost.h deklarujte proměnné soukromého člena pro Compositor, DispatcherQueueController a 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 };Přidejte veřejnou metodu pro inicializaci kompozičních interop objektů.
Note
V Initializevoláte metody EnsureDispatcherQueue, CreateDesktopWindowTargeta CreateCompositionRoot. Tyto metody vytvoříte v dalších krocích.
- Ve CompositionHost.h deklarujte veřejnou metodu s názvem Initialize , která přebírá HWND jako argument.
void Initialize(HWND hwnd);- Do CompositionHost.cpp přidejte definici metody Initialize .
void CompositionHost::Initialize(HWND hwnd) { EnsureDispatcherQueue(); if (m_dispatcherQueueController) m_compositor = Compositor(); CreateDesktopWindowTarget(hwnd); CreateCompositionRoot(); }Vytvořte frontu dispečera v rámci vlákna, které bude používat Windows Composition.
Kompozitor musí být vytvořen ve vlákně, které má frontu dispečera, a proto je tato metoda volána jako první během inicializace.
- Ve CompositionHost.h deklarujte privátní metodu s názvem EnsureDispatcherQueue.
void EnsureDispatcherQueue();- V CompositionHost.cpp přidejte definici Metody EnsureDispatcherQueue .
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; } }Zaregistrujte okno aplikace jako cílovou plochu kompozice.
- Ve CompositionHost.h deklarujte privátní metodu CreateDesktopWindowTarget , která přebírá HWND jako argument.
void CreateDesktopWindowTarget(HWND window);- V CompositionHost.cpp přidejte definici CreateDesktopWindowTarget metody.
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; }Vytvořte kořenový vizuální kontejner k uchování vizuálních objektů.
- Ve CompositionHost.h deklarujte privátní metodu s názvem CreateCompositionRoot.
void CreateCompositionRoot();- V CompositionHost.cpp přidejte definici CreateCompositionRoot metoda.
void CompositionHost::CreateCompositionRoot() { auto root = m_compositor.CreateContainerVisual(); root.RelativeSizeAdjustment({ 1.0f, 1.0f }); root.Offset({ 124, 12, 0 }); m_target.Root(root); }
Sestavte projekt, abyste měli jistotu, že nedošlo k žádným chybám.
Tyto metody nastaví komponenty potřebné pro interoperabilitu mezi vizuální vrstvou UPW a rozhraními API Win32. Teď můžete do aplikace přidat obsah.
Přidejte kompoziční prvky
Jakmile je infrastruktura připravena, můžete vytvořit obsah kompozice, který chcete zobrazit.
V tomto příkladu přidáte kód, který vytvoří náhodně barevný čtverec SpriteVisual s animací, která způsobí, že se po krátké prodlevě zahodí.
Přidejte prvek složení.
- Ve CompositionHost.h deklarujte veřejnou metodu s názvem AddElement , která přijímá 3 hodnoty float jako argumenty.
void AddElement(float size, float x, float y);- V CompositionHost.cpp přidejte definici AddElement metody.
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); } }
Vytvoření a zobrazení okna
Nyní můžete do uživatelského rozhraní Win32 přidat tlačítko a obsah složení UWP.
V HelloComposition.cpp v horní části souboru zahrňte CompositionHost.h, definujte BTN_ADD a získejte instanci CompositionHost.
#include "CompositionHost.h" // #define MAX_LOADSTRING 100 // This is already in the file. #define BTN_ADD 1000 CompositionHost* compHost = CompositionHost::GetInstance();InitInstanceV metodě změňte velikost vytvořeného okna. (V tomto řádku přejděteCW_USEDEFAULT, 0na900, 672.)HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 900, 672, nullptr, nullptr, hInstance, nullptr);Ve funkci WndProc přidejte zprávu
do bloku přepínače . V tomto případě inicializujete CompositionHost a vytvoříte tlačítko. 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;Ve funkci WndProc také zpracujte kliknutí na tlačítko a přidejte kompoziční prvek do uživatelského rozhraní.
Přidejte
case BTN_ADDdo bloku přepínače wmId uvnitř bloku WM_COMMAND.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; }
Teď můžete sestavit a spustit aplikaci. Pokud potřebujete, zkontrolujte kompletní kód na konci kurzu a ujistěte se, že je veškerý kód na správných místech.
Když spustíte aplikaci a kliknete na tlačítko, měli byste vidět animované čtverce přidané do uživatelského rozhraní.
Dodatečné zdroje
-
Ukázka Win32 HelloComposition (GitHub) - Začněte s Win32 a C++
- Začínáme s aplikacemi pro Windows (UPW)
- Vylepšení desktopové aplikace pro Windows (UPW)
- Jmenný prostor Windows.UI.Composition (UWP)
Kompletní kód
Zde je kompletní kód pro CompositionHost třídy a InitInstance metoda.
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 (částečná)
#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 ...
// ...