Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Você pode usar as APIs de composição do Tempo de Execução do Windows (também chamadas de camada Visual) nos seus aplicativos Win32 para criar experiências modernas que encantam os utilizadores do Windows.
O código completo para este tutorial está disponível no GitHub: Win32 HelloComposition sample.
Os aplicativos universais do Windows que precisam de controle preciso sobre a composição da interface do usuário têm acesso ao namespace
Pré-requisitos
A API de hospedagem UWP tem esses pré-requisitos.
- Supomos que você tenha alguma familiaridade com o desenvolvimento de aplicativos usando Win32 e UWP. Para mais informações, consulte:
- Windows 10 versão 1803 ou posterior
- Windows 10 SDK 17134 ou posterior
Como usar APIs de composição de um aplicativo de área de trabalho Win32
Neste tutorial, você cria um aplicativo Win32 C++ simples e adiciona elementos de composição UWP a ele. O foco está em configurar corretamente o projeto, criar o código de interoperabilidade e desenhar algo simples usando APIs de composição do Windows. O aplicativo concluído tem esta aparência.
Criar um projeto C++ Win32 no Visual Studio
A primeira etapa é criar o projeto de aplicativo Win32 no Visual Studio.
Para criar um novo projeto de aplicativo Win32 em C++ chamado HelloComposition:
Abra o Visual Studio e selecione Arquivo>Novo Projeto>.
Abre-se a caixa de diálogo New Project.
Na categoria Instalado, expanda a opção Visual C++ e selecione Windows Desktop.
Selecione o modelo Aplicativo da Área de Trabalho do Windows.
Introduza o nome HelloCompositione, em seguida, clique em OK.
O Visual Studio cria o projeto e abre o editor para o arquivo principal do aplicativo.
Configurar o projeto para usar APIs do Tempo de Execução do Windows
Para usar APIs do Tempo de Execução do Windows (WinRT) em seu aplicativo Win32, usamos C++/WinRT. Você precisa configurar seu projeto do Visual Studio para adicionar suporte a C++/WinRT.
(Para obter detalhes, consulte Introdução ao C++/WinRT - Modificar um projeto de aplicativo da área de trabalho do Windows para adicionar suporte a C++/WinRT).
No menu Project, abra as propriedades do projeto (HelloComposition Properties) e verifique se as seguintes configurações estão definidas para os valores especificados:
- Para a configuração , selecione todas as configurações . Para Plataforma, selecione Todas as Plataformas.
- Propriedades de Configuração>Geral>Versão do Windows SDK = 10.0.17763.0 ou superior
- Linguagem>C/C++>Padrão da Linguagem C++ = Padrão ISO C++ 17 (/stf:c++17)
- vinculador>entrada>dependências adicionais deve incluir "windowsapp.lib". Se não estiver incluído na lista, adicione-o.
Atualizar o cabeçalho pré-compilado
Renomeie
stdafx.hestdafx.cppparapch.hepch.cpp, respectivamente.Defina a propriedade do projeto C/C++>cabeçalhos pré-compilados> de arquivo de cabeçalho pré-compilado para pch.h.
Localize e substitua
#include "stdafx.h"por#include "pch.h"em todos os ficheiros.(Editar>Localizar e substituir>Localizar em Ficheiros)
Em
pch.h, incluawinrt/base.heunknwn.h.// reference additional headers your program requires here #include <unknwn.h> #include <winrt/base.h>
É uma boa ideia construir o projeto neste momento para garantir que não haja erros antes de continuar.
Criar uma classe para hospedar elementos de composição
Para hospedar o conteúdo criado com a camada visual, crie uma classe (CompositionHost) para gerenciar a interoperabilidade e criar elementos de composição. É aqui que você faz a maior parte da configuração para hospedar APIs de composição, incluindo:
- obter um Compositor, que cria e gere objetos no namespace Windows.UI.Composition.
- criando um DispatcherQueueController/ DispatcherQueue para gerir tarefas para as APIs do WinRT.
- criando um DesktopWindowTarget e um contêiner Composition para exibir os objetos de composição.
Tornamos esta classe um singleton para evitar problemas de threading. Por exemplo, você só pode criar uma fila de dispatcher por thread, portanto, instanciar uma segunda instância de CompositionHost no mesmo thread causaria um erro.
Sugestão
Se você precisar, verifique o código completo no final do tutorial para se certificar de que todo o código está nos lugares certos enquanto você trabalha no tutorial.
Adicione um novo arquivo de classe ao seu projeto.
- No Explorador de Soluções , clique com o botão direito do mouse no projeto HelloComposition.
- No menu de contexto, selecione Adicionar>classe....
- Na caixa de diálogo Adicionar de classe, nomeie a classe CompositionHost.cse, em seguida, clique em Adicionar.
Inclua cabeçalhos e usos necessários para a interoperabilidade de composição.
- Em CompositionHost.h, adicione estes inclui na parte superior do arquivo.
#pragma once #include <winrt/Windows.UI.Composition.Desktop.h> #include <windows.ui.composition.interop.h> #include <DispatcherQueue.h>- Em CompositionHost.cpp, adicione esses usings na parte superior do arquivo, depois que qualquer incluir.
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;Edite a classe para usar o padrão singleton.
- Em CompositionHost.h, torne o construtor privado.
- Declare um método estático público GetInstance.
class CompositionHost { public: ~CompositionHost(); static CompositionHost* GetInstance(); private: CompositionHost(); };- Em CompositionHost.cpp, adicione a definição do GetInstance método.
CompositionHost* CompositionHost::GetInstance() { static CompositionHost instance; return &instance; }Em CompositionHost.h, declare variáveis de membro privado para Compositor, DispatcherQueueController e 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 };Adicione um método público para inicializar os objetos de interoperabilidade de composição.
Observação
No Initialize, você chama os métodos EnsureDispatcherQueue, CreateDesktopWindowTargete CreateCompositionRoot. Você cria esses métodos nas próximas etapas.
- Em CompositionHost.h, declare um método público chamado Initialize que usa um HWND como argumento.
void Initialize(HWND hwnd);- Em CompositionHost.cpp, adicione a definição do Initialize método.
void CompositionHost::Initialize(HWND hwnd) { EnsureDispatcherQueue(); if (m_dispatcherQueueController) m_compositor = Compositor(); CreateDesktopWindowTarget(hwnd); CreateCompositionRoot(); }Crie uma fila de dispatcher no thread que usará a Composição do Windows.
Um Compositor deve ser criado em um thread que tenha uma fila de dispatcher, portanto, esse método é chamado primeiro durante a inicialização.
- Em CompositionHost.h, declare um método privado chamado EnsureDispatcherQueue.
void EnsureDispatcherQueue();- Em CompositionHost.cpp, adicione a definição do EnsureDispatcherQueue método.
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; } }Registre a janela do seu aplicativo como um destino de composição.
- Em CompositionHost.h, declare um método privado chamado CreateDesktopWindowTarget que usa um HWND como argumento.
void CreateDesktopWindowTarget(HWND window);- Em CompositionHost.cpp, adicione a definição do CreateDesktopWindowTarget método.
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; }Crie um contêiner visual raiz para armazenar objetos visuais.
- Em CompositionHost.h, declare um método privado chamado CreateCompositionRoot.
void CreateCompositionRoot();- Em CompositionHost.cpp, adicione a definição do CreateCompositionRoot método.
void CompositionHost::CreateCompositionRoot() { auto root = m_compositor.CreateContainerVisual(); root.RelativeSizeAdjustment({ 1.0f, 1.0f }); root.Offset({ 124, 12, 0 }); m_target.Root(root); }
Crie o projeto agora para garantir que não haja erros.
Esses métodos configuram os componentes necessários para a interoperabilidade entre a camada visual UWP e as APIs do Win32. Agora você pode adicionar conteúdo ao seu aplicativo.
Adicionar elementos de composição
Com a infraestrutura instalada, agora você pode gerar o conteúdo de composição que deseja mostrar.
Neste exemplo, adiciona-se código que cria um quadrado colorido aleatoriamente SpriteVisual com uma animação que faz com que ele caia após um pequeno atraso.
Adicione um elemento de composição.
- Em CompositionHost.h, declare um método público chamado AddElement que recebe 3 float valores como parâmetros.
void AddElement(float size, float x, float y);- Em CompositionHost.cpp, adicione a definição do AddElement método.
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); } }
Criar e mostrar a janela
Agora, você pode adicionar um botão e o conteúdo de composição UWP à sua interface do usuário do Win32.
Em HelloComposition.cpp, na parte superior do arquivo, inclua CompositionHost.h, defina BTN_ADD e obtenha uma instância de CompositionHost.
#include "CompositionHost.h" // #define MAX_LOADSTRING 100 // This is already in the file. #define BTN_ADD 1000 CompositionHost* compHost = CompositionHost::GetInstance();No método
InitInstance, altere o tamanho da janela criada. (Nesta linha, altereCW_USEDEFAULT, 0para900, 672.)HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 900, 672, nullptr, nullptr, hInstance, nullptr);Na função WndProc, adicione
case WM_CREATEà mensagem no bloco de comutação. Nesse caso, você inicializa o CompositionHost e cria o botão.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;Também na função WndProc, manipule o clique do botão para adicionar um elemento de composição à interface do usuário.
Adicione
case BTN_ADDao bloco de comutador wmId dentro do bloco 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; }
Agora você pode criar e executar seu aplicativo. Se precisar, verifique o código completo no final do tutorial para se certificar de que todo o código está nos lugares certos.
Ao executar o aplicativo e clicar no botão, você verá quadrados animados adicionados à interface do usuário.
Recursos adicionais
- Exemplo do Win32 HelloComposition (GitHub)
- Introdução ao Win32 e C++
- Introdução às aplicações Windows (UWP)
- Aprimore o seu aplicativo de desktop para Windows (UWP)
- Espaço de nomes Windows.UI.Composition (UWP)
Código completo
Aqui está o código completo para a classe CompositionHost e o método InitInstance.
ComposiçãoHost.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 (parcial)
#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