I was using the CreateTargetForCurrentView API by invoking it on a UIElement's Compositor object extracted via
auto visual = ElementCompositionPreview::GetElementVisual(elem);
auto compositor = visual->Compositor;
After this, since I want to create a tree of 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 to host this tree of visuals.
auto containerVisual = compositor->CreateContainerVisual();
Now since this ContainerVisual
needs to be attached to the root
of a CompositionTarget, I did so (taking the reference from here):
auto compositionTarget = compositor->CreateTargetForCurrentView();
but this leads to a DCOMPOSITION_ERROR_WINDOW_ALREADY_COMPOSED
which as per the docs implies:
The IDCompositionDevice::CreateTargetForHwnd method was called with hwnd and topmost parameters for which a visual tree already exists.
If I understand correctly, this means 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; }
| }
Strangely even the docs refer to this route of attaching a ContainerVisual
to a View
but the documentation is apparently outdated.
If you want to interop between XAML and the Visual Layer, we shouldn’t be entangled in CompositionTarget
. Let’s back to how to use ContainerVisual
to create a visual tree.
Refer to the 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, showing how to grab a visual from a UIElement
and set a visual to a UIElement
.
Here is the sample code you could refer to:
Add the namespaces and a header:
#include "windowsnumerics.h"
using namespace Windows::UI::Xaml::Hosting;
using namespace Windows::UI::Composition;
using namespace Windows::Foundation::Numerics;
using namespace Windows::UI;
Declare some members:
private:
Compositor^ _compositor;
ContainerVisual^ _root;
Visual^ CreateChildElement();
The code to grab a visual and set a visual:
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;
}