首页 > 解决方案 > 无法在 UWP 中创建 CompositionTarget

问题描述

我正在使用CreateTargetForCurrentView API,方法是 通过

auto visual = ElementCompositionPreview::GetElementVisual(elem);
auto compositor = visual->Compositor;

在此之后,由于我想创建一个Visual的树,它们之间有一种排序感,以便我可以根据我的用例将视觉放在另一个之上,因此我创建了一个ContainerVisual来托管这棵视觉树。

auto containerVisual = compositor->CreateContainerVisual();

现在因为这ContainerVisual需要附加到rootCompositionTarget 的,所以我这样做了(从这里获取参考):

auto compositionTarget = compositor->CreateTargetForCurrentView();

但这会导致DCOMPOSITION_ERROR_WINDOW_ALREADY_COMPOSED根据文档暗示:

IDCompositionDevice::CreateTargetForHwnd 方法是使用 hwnd 和 topmost 参数调用的,其可视化树已经存在。

如果我理解正确,这意味着IDCompositionDevice::CreateTargetForHwnd在我的 UWP 应用程序的生命周期中以某种方式被调用。此 APIWin32是未在应用程序中直接使用的 API。现在我的问题是,在 UWP 应用程序中,Windows.UI.Composition命名空间下的 API 或投影下的任何其他API C++/Cx,我应该寻找可能在内部调用IDCompositionDevice::CreateTargetForHwnd导致此异常的 API 吗?或者更好的是,是否有任何 API 可用于CompositionTarget从 aView或中提取 a Window?我看到类中有一个属性CompositionRootVisualCoreApplicationView用于直接附加,但它已在其中一个 API 更新ContainerVisual被删除。

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

奇怪的是,即使是文档也提到了将 a 附加ContainerVisual到 a的这条路线,View但文档显然已经过时了。

标签: uwpc++-cx

解决方案


如果要在 XAML 和可视层之间进行互操作,我们不应该纠缠于CompositionTarget. 让我们回到如何使用ContainerVisual来创建可视化树。参考文档,我们可以使用ElementCompositionPreview.GetElementVisual(UIElement)方法来获取任何页面元素的背景视觉,并使用ElementCompositionPreview.SetElementChildVisual(UIElement, Visual)方法将创建的视觉带到 UIElement 的视觉树中。上述文档和本文档Using the Visual Layer with XAML中有一些代码,展示了如何从 a 中获取视觉对象UIElement并将视觉对象设置为 a UIElement

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

添加命名空间和标题:

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

}

推荐阅读