无法在 UWP 中创建 CompositionTarget

Can't create a CompositionTarget in UWP

我正在使用通过

提取的CreateTargetForCurrentView API by invoking it on a UIElement's Compositor对象
auto visual = ElementCompositionPreview::GetElementVisual(elem);
auto compositor = visual->Compositor;

在此之后,因为我想创建一棵 Visuals with a sense of ordering amongst them so that I can position the visual one above the other as per my use case, I created a ContainerVisual 树来承载这棵视觉树。

auto containerVisual = compositor->CreateContainerVisual();

现在因为这个 ContainerVisual 需要附加到 CompositionTarget 的 root,我这样做了(参考 here):

auto compositionTarget = compositor->CreateTargetForCurrentView();

但这会导致 DCOMPOSITION_ERROR_WINDOW_ALREADY_COMPOSED,根据 docs 暗示:

The IDCompositionDevice::CreateTargetForHwnd method was called with hwnd and topmost parameters for which a visual tree already exists.

如果我没理解错的话,意思是IDCompositionDevice::CreateTargetForHwnd has been called somehow during the lifecycle of my UWP app. This API is a Win32 API that isn't being used directly in the app. Now my question is, in a UWP app, what API under Windows.UI.Composition namespace or any other, under the C++/Cx projection, should I be looking for that might be internally invoking IDCompositionDevice::CreateTargetForHwnd that's leading to this exception? Or better still, is there any API that can be used to extract a CompositionTarget from a View or Window? I see that there was a property CompositionRootVisual in the CoreApplicationView class that could be used to attach the ContainerVisual directly but it was removed in one of the API updates as per this

| public class Windows.ApplicationModel.Core.CoreApplicationView {
-   public Visual CompositionRootVisual { get; set; }
| }

奇怪的是,甚至 docs 也提到了这种将 ContainerVisual 附加到 View 的方法,但文档显然已经过时了。

如果要在XAML和Visual Layer之间互操作,我们不应该纠缠在CompositionTarget。让我们回到如何使用 ContainerVisual 创建可视化树。 请参阅 document, we could use ElementCompositionPreview.GetElementVisual(UIElement) method to grab the backing visual for any page element, and use ElementCompositionPreview.SetElementChildVisual(UIElement, Visual) method to take a created visual to a UIElement’s visual tree. There are some code in the above document and this document Using the Visual Layer with XAML,展示如何从 UIElement 抓取视觉对象并将视觉对象设置为 UIElement

您可以参考以下示例代码:

添加命名空间和 header:

#include "windowsnumerics.h"

using namespace Windows::UI::Xaml::Hosting;
using namespace Windows::UI::Composition;
using namespace Windows::Foundation::Numerics;
using namespace Windows::UI;

声明一些成员:

private:
    Compositor^ _compositor;
    ContainerVisual^ _root;
    Visual^ CreateChildElement();

获取视觉对象和设置视觉对象的代码:

void App2::MainPage::Button_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
    _compositor = ElementCompositionPreview::GetElementVisual(this)->Compositor;
    _root = _compositor->CreateContainerVisual();
    for (int i = 0; i < 5; i++)
    {
        _root->Children->InsertAtTop(CreateChildElement());
    }
    ElementCompositionPreview::SetElementChildVisual(this, _root);

}

Visual^ App2::MainPage::CreateChildElement()
{
    auto element = _compositor->CreateContainerVisual();
    element->Size = float2(100.0f, 100.0f);

    //
    // Position this visual randomly within our window
    //
    element->Offset = float3(((float)rand() / RAND_MAX) * 400, ((float)rand() / RAND_MAX) * 400, 0.0f);

    //
    // The outer rectangle is always white
    //
    auto visual = _compositor->CreateSpriteVisual();
    element->Children->InsertAtTop(visual);
    visual->Brush = _compositor->CreateColorBrush(ColorHelper::FromArgb(0xFF, 0xFF, 0x11, 0xFF));
    visual->Size = float2(100.0f, 100.0f);

    //
    // The inner rectangle is inset from the outer by three pixels all around
    //
    auto child = _compositor->CreateSpriteVisual();
    visual->Children->InsertAtTop(child);
    child->Offset = float3(3.0f, 3.0f, 0.0f);
    child->Size = float2(94.0f, 94.0f);

    //
    // Pick a random color for every rectangle
    //
    byte red = (byte)(0xFF * (0.2f + (((float)rand()/RAND_MAX) / 0.8f)));
    byte green = (byte)(0xFF * (0.2f + (((float)rand() / RAND_MAX) / 0.8f)));
    byte blue = (byte)(0xFF * (0.2f + (((float)rand() / RAND_MAX) / 0.8f)));
    child->Brush = _compositor->CreateColorBrush(ColorHelper::FromArgb(0xFF, red, green, blue));

    //
    // Make the subtree root visual partially transparent. This will cause each visual in the subtree
    // to render partially transparent, since a visual's opacity is multiplied with its parent's
    // opacity
    //
    element->Opacity = 0.8f;

    return element;

}