diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
index 98a6a3600e..38d9b34937 100644
--- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
+++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
@@ -146,8 +146,18 @@ public class CompositingRenderer : IRendererWithCompositor
return result == 0 ? lhs.index.CompareTo(rhs.index) : result;
});
}
-
- if (compositionChildren.Count == visualChildren.Count)
+
+ var childVisual = v.ChildCompositionVisual;
+
+ // Check if the current visual somehow got migrated to another compositor
+ if (childVisual != null && childVisual.Compositor != v.CompositionVisual.Compositor)
+ childVisual = null;
+
+ var expectedCount = visualChildren.Count;
+ if (childVisual != null)
+ expectedCount++;
+
+ if (compositionChildren.Count == expectedCount)
{
bool mismatch = false;
if (sortedChildren != null)
@@ -167,6 +177,9 @@ public class CompositingRenderer : IRendererWithCompositor
break;
}
+ if (childVisual != null &&
+ !ReferenceEquals(compositionChildren[compositionChildren.Count - 1], childVisual))
+ mismatch = true;
if (!mismatch)
{
@@ -193,6 +206,9 @@ public class CompositingRenderer : IRendererWithCompositor
if (compositionChild != null)
compositionChildren.Add(compositionChild);
}
+
+ if (childVisual != null)
+ compositionChildren.Add(childVisual);
}
private void UpdateCore()
diff --git a/src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs b/src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs
index 1a13d23acd..00fa7b3315 100644
--- a/src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs
@@ -29,4 +29,7 @@ public partial class Compositor
public ImplicitAnimationCollection CreateImplicitAnimationCollection() => new ImplicitAnimationCollection(this);
public CompositionAnimationGroup CreateAnimationGroup() => new CompositionAnimationGroup(this);
+
+ public CompositionSolidColorVisual CreateSolidColorVisual() =>
+ new(this, new ServerCompositionSolidColorVisual(Server));
}
\ No newline at end of file
diff --git a/src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs b/src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs
index 5bd8e4a4d3..b01321edd8 100644
--- a/src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs
+++ b/src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs
@@ -1,5 +1,8 @@
// Special license applies License.md
+using System;
+using Avalonia.VisualTree;
+
namespace Avalonia.Rendering.Composition;
///
@@ -13,4 +16,22 @@ public static class ElementComposition
///
///
public static CompositionVisual? GetElementVisual(Visual visual) => visual.CompositionVisual;
+
+ ///
+ /// Sets a custom as the last child of the element’s visual tree.
+ ///
+ public static void SetElementChildVisual(Visual visual, CompositionVisual? compositionVisual)
+ {
+ if (compositionVisual != null && visual.CompositionVisual != null &&
+ compositionVisual.Compositor != visual.CompositionVisual.Compositor)
+ throw new InvalidOperationException("Composition visuals belong to different compositor instances");
+
+ visual.ChildCompositionVisual = compositionVisual;
+ visual.GetVisualRoot()?.Renderer.RecalculateChildren(visual);
+ }
+
+ ///
+ /// Retrieves a object previously set by a call to .
+ ///
+ public static CompositionVisual? GetElementChildVisual(Visual visual) => visual.ChildCompositionVisual;
}
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionSolidColorVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionSolidColorVisual.cs
new file mode 100644
index 0000000000..79abd7ee17
--- /dev/null
+++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionSolidColorVisual.cs
@@ -0,0 +1,11 @@
+using Avalonia.Media.Immutable;
+
+namespace Avalonia.Rendering.Composition.Server;
+
+internal partial class ServerCompositionSolidColorVisual
+{
+ protected override void RenderCore(CompositorDrawingContextProxy canvas, Rect currentTransformedClip)
+ {
+ canvas.DrawRectangle(new ImmutableSolidColorBrush(Color), null, new Rect(0, 0, Size.X, Size.Y));
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs
index 29559a8618..d12eb1d138 100644
--- a/src/Avalonia.Base/Visual.cs
+++ b/src/Avalonia.Base/Visual.cs
@@ -292,6 +292,7 @@ namespace Avalonia
protected IRenderRoot? VisualRoot => _visualRoot ?? (this as IRenderRoot);
internal CompositionDrawListVisual? CompositionVisual { get; private set; }
+ internal CompositionVisual? ChildCompositionVisual { get; set; }
public bool HasNonUniformZIndexChildren { get; private set; }
diff --git a/src/Avalonia.Base/composition-schema.xml b/src/Avalonia.Base/composition-schema.xml
index e0e177da44..6dfcb2e74d 100644
--- a/src/Avalonia.Base/composition-schema.xml
+++ b/src/Avalonia.Base/composition-schema.xml
@@ -27,6 +27,9 @@
+