问题描述
我通过在 CreateTargetForCurrentView UIElement's 对象上调用 Compositor API 来使用它
auto visual = ElementCompositionPreview::GetElementVisual(elem);
auto compositor = visual->Compositor;
在此之后,由于我想创建一棵 Visual 树,它们之间具有排序感,以便我可以根据我的用例将视觉放在另一个之上,因此我创建了一个 {{3 }} 来托管这个视觉树。
auto containerVisual = compositor->CreateContainerVisual();
现在,由于此 ContainerVisual
需要附加到 CompositionTarget 的 root
,因此我这样做了(从 ContainerVisual 获取参考):
auto compositionTarget = compositor->CreateTargetForCurrentView();
但这会导致 DCOMPOSITION_ERROR_WINDOW_ALREADY_COMPOSED
,根据 here 暗示:
IDCompositionDevice::CreateTargetForHwnd 方法被调用 已存在可视化树的 hwnd 和最顶层参数。
如果我理解正确,这意味着 docs 在我的 UWP 应用的生命周期中以某种方式被调用。此 API 是未直接在应用中使用的 Win32
API。现在我的问题是,在 UWP 应用程序中,Windows.UI.Composition
命名空间或任何其他命名空间下的什么 API,在 C++/Cx
投影下,我应该寻找可能在内部调用 IDCompositionDevice::CreateTargetForHwnd
导致这个例外?或者更好的是,是否有任何 API 可用于从 CompositionTarget
或 View
中提取 Window
?我看到 CompositionRootVisual
类中有一个属性 CoreApplicationView
可用于直接附加 ContainerVisual
,但根据 IDCompositionDevice::CreateTargetForHwnd 在其中一个 API 更新中将其删除.
| public class Windows.ApplicationModel.Core.CoreApplicationView {
- public Visual CompositionRootVisual { get; set; }
| }
奇怪的是,即使 this 指的是这种将 ContainerVisual
附加到 View
的方法,但文档显然已经过时了。
解决方法
如果要在 XAML 和可视层之间进行互操作,我们不应该纠结于 CompositionTarget
。让我们回到如何使用 ContainerVisual
创建可视化树。
参考 document,我们可以使用 ElementCompositionPreview.GetElementVisual(UIElement)
方法获取任何页面元素的背景视觉,并使用 ElementCompositionPreview.SetElementChildVisual(UIElement,Visual) 方法将创建的视觉效果带到 UIElement 的视觉树中。上述文档和本文档 Using the Visual Layer with XAML 中有一些代码,展示了如何从 UIElement
中获取视觉对象并将视觉对象设置为 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;
}