Win32 ile Görsel Katmanı Kullanma

Windows kullanıcıları için aydınlatan modern deneyimler oluşturmak için Win32 uygulamalarınızda Windows Çalışma Zamanı Oluşturma API'lerini ( Görsel katmanı olarak da adlandırılır) kullanabilirsiniz.

Bu öğreticinin tam kodu GitHub'da kullanılabilir: Win32 HelloComposition örneği.

Kullanıcı arabirimi oluşturması üzerinde hassas denetime ihtiyaç duyan Evrensel Windows Uygulamaları, kullanıcı arabiriminin nasıl oluşturulup işlendiği üzerinde ayrıntılı denetim sağlamak için Windows.UI.Composition ad alanına erişebilir. Ancak bu oluşturma API'si UWP uygulamalarıyla sınırlı değildir. Win32 masaüstü uygulamaları, UWP ve Windows'taki modern bileşim sistemlerinden yararlanabilir.

Prerequisites

UWP barındırma API'sinin bu önkoşulları vardır.

Win32 masaüstü uygulamasından Oluşturma API'lerini kullanma

Bu öğreticide basit bir Win32 C++ uygulaması oluşturacak ve buna UWP Oluşturma öğeleri ekleyebilirsiniz. Odak, projeyi doğru şekilde yapılandırmak, birlikte çalışma kodu oluşturmak ve Windows Oluşturma API'lerini kullanarak basit bir şey çizmektir. Tamamlanmış uygulama şöyle görünür.

Çalışan uygulama kullanıcı arabirimi

Visual Studio'da C++ Win32 projesi oluşturma

İlk adım, Visual Studio'da Win32 uygulama projesi oluşturmaktır.

C++ içinde HelloComposition adlı yeni bir Win32 Uygulaması projesi oluşturmak için:

  1. Visual Studio'yu açın ve Dosya>Yeni>Projeöğesini seçin.

    Yeni Proje iletişim kutusu açılır.

  2. yüklü kategorisinin altında Visual C++ düğümünü genişletin ve ardından windows masaüstüseçin.

  3. Windows Masaüstü Uygulaması şablonunu seçin.

  4. HelloCompositionadını girin, ardından OK'e tıklayın.

    Visual Studio projeyi oluşturur ve ana uygulama dosyasının düzenleyicisini açar.

Projeyi Windows Çalışma Zamanı API'lerini kullanacak şekilde yapılandırma

Win32 uygulamanızda Windows Çalışma Zamanı (WinRT) API'lerini kullanmak için C++/WinRT kullanırız. C++/WinRT desteği eklemek için Visual Studio projenizi yapılandırmanız gerekir.

(Ayrıntılar için bkz. C++/WinRT kullanmaya başlama - C++/WinRT desteği eklemek için Windows Masaüstü uygulama projesini değiştirme).

  1. Proje menüsünden proje özelliklerini (HelloComposition Özellikleri) açın ve aşağıdaki ayarların belirtilen değerlere ayarlandığından emin olun:

    • Yapılandırma için Tüm Yapılandırmalar'ı seçin. Platformiçin Tüm Platformlarseçin.
    • Yapılandırma Özellikleri>Genel>Windows SDK Sürümü = 10.0.17763.0 veya üzeri

    SDK sürümünü ayarlama

    • C/C++>Dil>C++ Dil Standardı = ISO C++ 17 Standart (/stf:c++17)

    Dil standardı ayarlama

    • Bağlayıcı>Girdi>Ek Bağımlılıklar "windowsapp.lib" içermelidir. Listede yoksa ekleyin.

    Bağlayıcı bağımlılığını ekle

  2. Önceden derlenmiş üst bilgiyi güncelleştirme

    • stdafx.h ve stdafx.cpp öğelerini sırasıyla pch.h ve pch.cpp olarak yeniden adlandırın.

    • C/C++Önceden Derlenmiş Üst BilgilerÖnceden Derlenmiş Üst Bilgi Dosyası için proje özelliğini pch.holarak ayarlayın.

    • Tüm dosyalarda #include "stdafx.h" öğesini bulun ve #include "pch.h" ile değiştirin.

      (Düzenle>Bul ve Değiştir>Dosyalarda Bul)

      stdafx.h bulma ve değiştirme

    • pch.h içinde, winrt/base.h ve unknwn.h dahil edin.

      // reference additional headers your program requires here
      #include <unknwn.h>
      #include <winrt/base.h>
      

Devam etmeden önce hata olmadığından emin olmak için bu noktada projeyi oluşturmak iyi bir fikirdir.

Oluşturma öğelerini barındırmak için bir sınıf oluşturma

Görsel katmanla oluşturduğunuz içeriği barındırmak için, birlikte çalışmayı yönetip oluşturma öğelerini yaratacak bir sınıf (CompositionHost) oluşturun. Oluşturma API'lerini barındırmak için yapılandırmanın çoğunu burada yaparsınız, örneğin:

İş parçacığı sorunlarını önlemek için bu sınıfı tekil yaparız. Örneğin, iş parçacığı başına yalnızca bir dağıtıcı kuyruğu oluşturabilirsiniz, bu nedenle aynı iş parçacığında CompositionHost'un ikinci bir örneğinin örneğini oluşturmak hataya neden olur.

Tavsiye

Gerekirse, öğreticiyi takip ederken tüm kodun doğru yerlerde olduğundan emin olmak için, öğreticinin sonundaki tüm kodu kontrol edin.

  1. Projenize yeni bir sınıf dosyası ekleyin.

    • Çözüm Gezginipenceresinde, HelloComposition projesine sağ tıklayın.
    • Bağlam menüsündeSınıf Ekle...'> seçin.
    • Sınıf Ekle iletişim kutusunda, sınıfı CompositionHost olarak adlandırın ve Ekle'ye tıklayın.
  2. Birleştirme aralarındaki birlikte çalışabilirlik için gereken üst bilgileri ve using bildirimlerini ekleyin.

    • CompositionHost.h dosyasında, dosyanın en üstüne bu iç içe geçmiş olan'i ekleyin.
    #pragma once
    #include <winrt/Windows.UI.Composition.Desktop.h>
    #include <windows.ui.composition.interop.h>
    #include <DispatcherQueue.h>
    
    • CompositionHost.cpp'da, ekledikten sonra dosyanın en üstündeki bu kullanımı ekleyin.
    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;
    
  3. Sınıfı, singleton tasarımını kullanacak şekilde değiştirin.

    • CompositionHost.h dosyasında oluşturucuyu özel hale getirin.
    • Bir genel statik GetInstance yöntemi bildirin.
    class CompositionHost
    {
    public:
        ~CompositionHost();
        static CompositionHost* GetInstance();
    
    private:
        CompositionHost();
    };
    
    • CompositionHost.cpp getInstance yönteminin tanımını ekleyin.
    CompositionHost* CompositionHost::GetInstance()
    {
        static CompositionHost instance;
        return &instance;
    }
    
  4. CompositionHost.h dosyasında Compositor, DispatcherQueueController ve DesktopWindowTarget için özel üye değişkenleri bildirin.

    winrt::Windows::UI::Composition::Compositor m_compositor{ nullptr };
    winrt::Windows::System::DispatcherQueueController m_dispatcherQueueController{ nullptr };
    winrt::Windows::UI::Composition::Desktop::DesktopWindowTarget m_target{ nullptr };
    
  5. Birlikte çalışma nesnelerini oluşturma işlemini başlatmak için genel bir yöntem ekleyin.

    Note

    Initializeiçinde EnsureDispatcherQueue, CreateDesktopWindowTargetve CreateCompositionRoot yöntemlerini çağırırsınız. Bu yöntemleri sonraki adımlarda oluşturacaksınız.

    • CompositionHost.h dosyasında, bir HWND'yi bağımsız değişken olarak alan Initialize adlı bir genel yöntem bildirin.
    void Initialize(HWND hwnd);
    
    • CompositionHost.cpp'da Initialize yönteminin tanımını ekleyin.
    void CompositionHost::Initialize(HWND hwnd)
    {
        EnsureDispatcherQueue();
        if (m_dispatcherQueueController) m_compositor = Compositor();
    
        CreateDesktopWindowTarget(hwnd);
        CreateCompositionRoot();
    }
    
  6. Windows Composition sisteminin kullanacağı iş parçacığında bir dağıtıcı kuyruğu oluşturun.

    Bir Compositor, dağıtıcı kuyruğu olan bir iş parçacığında oluşturulmalıdır, bu nedenle başlatma sırasında ilk olarak bu yöntem çağrılır.

    • CompositionHost.h dosyasında EnsureDispatcherQueue adlı özel bir yöntem bildirin.
    void EnsureDispatcherQueue();
    
    • CompositionHost.cpp,EnsureDispatcherQueue yönteminin tanımını ekleyin.
    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;
        }
    }
    
  7. Uygulamanızın penceresini oluşturma hedefi olarak kaydedin.

    • CompositionHost.h dosyasında, bağımsız değişken olarak HWND alan CreateDesktopWindowTarget adlı özel bir yöntem bildirin.
    void CreateDesktopWindowTarget(HWND window);
    
    • CompositionHost.cpp'da CreateDesktopWindowTarget yönteminin tanımını ekleyin.
    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;
    }
    
  8. Görsel nesneleri tutmak için bir kök görsel kapsayıcısı oluşturun.

    • CompositionHost.h dosyasında CreateCompositionRoot adlı özel bir yöntem bildirin.
    void CreateCompositionRoot();
    
    • CompositionHost.cpp'da CreateCompositionRoot yönteminin tanımını ekleyin.
    void CompositionHost::CreateCompositionRoot()
    {
        auto root = m_compositor.CreateContainerVisual();
        root.RelativeSizeAdjustment({ 1.0f, 1.0f });
        root.Offset({ 124, 12, 0 });
        m_target.Root(root);
    }
    

Hata olmadığından emin olmak için projeyi şimdi derleyin.

Bu yöntemler, UWP görsel katmanı ile Win32 API'leri arasında birlikte çalışma için gereken bileşenleri ayarlar. Artık uygulamanıza içerik ekleyebilirsiniz.

Oluşturma öğeleri ekleme

Altyapı yerleşik olarak, artık göstermek istediğiniz Oluşturma içeriğini oluşturabilirsiniz.

Bu örnekte, SpriteVisual rastgele renkli bir kare oluşturan ve kısa bir gecikmeden sonra düşmesine neden olan bir animasyon içeren bir kod eklersiniz.

  1. Bir bileşim öğesi ekleyin.

    • CompositionHost.h dosyasında, bağımsız değişken olarak 3 kayan değer alan AddElement adlı bir genel yöntem bildirin.
    void AddElement(float size, float x, float y);
    
    • CompositionHost.cpp AddElement yönteminin tanımını ekleyin.
    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);
        }
    }
    

Pencereyi oluşturma ve gösterme

Artık Win32 kullanıcı arabiriminize bir düğme ve UWP oluşturma içeriği ekleyebilirsiniz.

  1. HelloComposition.cpp dosyasının en üstünde CompositionHost.h dosyasını ekleyin, BTN_ADD tanımlayın ve CompositionHost örneğini alın.

    #include "CompositionHost.h"
    
    // #define MAX_LOADSTRING 100 // This is already in the file.
    #define BTN_ADD 1000
    
    CompositionHost* compHost = CompositionHost::GetInstance();
    
  2. yönteminde InitInstance , oluşturulan pencerenin boyutunu değiştirin. (Bu satırda CW_USEDEFAULT, 0900, 672 olarak değiştirin.)

    HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, 900, 672, nullptr, nullptr, hInstance, nullptr);
    
  3. WndProc işlevinde, case WM_CREATE anahtar bloğuna ekleyin. Bu durumda, CompositionHost'u başlatır ve düğmeyi oluşturursunuz.

    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;
    
  4. Ayrıca WndProc işlevinde, düğme tıklamasını işleyip kullanıcı arabirimine bir oluşturma öğesi ekleyin.

    case BTN_ADD'i WM_COMMAND bloğunun içindeki wmId anahtar bloğuna ekleyin.

    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;
    }
    

Artık uygulamanızı derleyebilir ve çalıştırabilirsiniz. Gerekirse, tüm kodun doğru yerlerde olduğundan emin olmak için öğreticinin sonundaki tüm kodu denetleyin.

Uygulamayı çalıştırıp düğmeye tıkladığınızda, kullanıcı arabirimine eklenmiş animasyonlu kareler görmeniz gerekir.

Ek kaynaklar

Kodu tamamlama

Burada CompositionHost sınıfının ve InitInstance yönteminin tam kodu yer alır.

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 (Kısmi)

#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 ...
// ...