diff --git a/WinUIGallery/SampleSupport/Data/ControlInfoData.json b/WinUIGallery/SampleSupport/Data/ControlInfoData.json
index cbd7107e2..acb97af8c 100644
--- a/WinUIGallery/SampleSupport/Data/ControlInfoData.json
+++ b/WinUIGallery/SampleSupport/Data/ControlInfoData.json
@@ -4057,6 +4057,39 @@
"EasingFunction"
]
},
+ {
+ "UniqueId": "Composition",
+ "Tags": [
+ "composition",
+ "Visual layer",
+ "ElementCompositionPreview",
+ "transform",
+ "clip"
+ ],
+ "Title": "Composition",
+ "ApiNamespace": "Microsoft.UI.Composition",
+ "Subtitle": "Connect XAML elements to the Visual layer and apply transforms and clips.",
+ "ImagePath": "ms-appx:///Assets/ControlImages/AnimationInterop.png",
+ "Description": "Explore XAML and Composition interop by retrieving and attaching visuals, working with coordinate systems, applying transform matrices, and clipping content.",
+ "IsNew": true,
+ "Docs": [
+ {
+ "Title": "Composition overview",
+ "Uri": "https://learn.microsoft.com/windows/apps/windows-app-sdk/composition"
+ },
+ {
+ "Title": "ElementCompositionPreview - API",
+ "Uri": "https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.hosting.elementcompositionpreview"
+ },
+ {
+ "Title": "Visual - API",
+ "Uri": "https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.composition.visual"
+ }
+ ],
+ "RelatedControls": [
+ "XamlCompInterop"
+ ]
+ },
{
"UniqueId": "ConnectedAnimation",
"Tags": [
diff --git a/WinUIGallery/Samples/Composition/CompositionChildVisual.txt b/WinUIGallery/Samples/Composition/CompositionChildVisual.txt
new file mode 100644
index 000000000..b563d6548
--- /dev/null
+++ b/WinUIGallery/Samples/Composition/CompositionChildVisual.txt
@@ -0,0 +1,33 @@
+--- header
+Attach a Composition child Visual and position it with AnchorPoint
+--- xaml
+
+--- c#
+private SpriteVisual? _childVisual;
+
+private void ChildVisualExample_Loaded(object sender, RoutedEventArgs e)
+{
+ Visual hostVisual = ElementCompositionPreview.GetElementVisual(ChildVisualHost);
+ _childVisual = hostVisual.Compositor.CreateSpriteVisual();
+ _childVisual.Size = new Vector2(80);
+ _childVisual.Brush = hostVisual.Compositor.CreateColorBrush(Microsoft.UI.Colors.DodgerBlue);
+
+ ElementCompositionPreview.SetElementChildVisual(ChildVisualHost, _childVisual);
+ UpdateChildVisual();
+}
+
+private void UpdateChildVisual()
+{
+ // AnchorPoint is normalized: (0,0) is the top-left and (1,1) is the bottom-right.
+ _childVisual.AnchorPoint = new Vector2(anchorX, anchorY);
+ _childVisual.Offset = new Vector3(ChildVisualHost.ActualSize / 2, 0);
+}
+
+private void Page_Unloaded(object sender, RoutedEventArgs e)
+{
+ ElementCompositionPreview.SetElementChildVisual(ChildVisualHost, null);
+ _childVisual?.Dispose();
+}
\ No newline at end of file
diff --git a/WinUIGallery/Samples/Composition/CompositionClip.txt b/WinUIGallery/Samples/Composition/CompositionClip.txt
new file mode 100644
index 000000000..54dbf78b6
--- /dev/null
+++ b/WinUIGallery/Samples/Composition/CompositionClip.txt
@@ -0,0 +1,26 @@
+--- header
+Clip a XAML element through its Composition Visual
+--- xaml
+
+
+
+--- c#
+private InsetClip? _insetClip;
+
+private void ClipExample_Loaded(object sender, RoutedEventArgs e)
+{
+ Visual visual = ElementCompositionPreview.GetElementVisual(ClipTarget);
+ _insetClip = visual.Compositor.CreateInsetClip();
+ visual.Clip = _insetClip;
+}
+
+private void UpdateClip(float horizontalInset, float verticalInset)
+{
+ _insetClip.LeftInset = horizontalInset;
+ _insetClip.RightInset = horizontalInset;
+ _insetClip.TopInset = verticalInset;
+ _insetClip.BottomInset = verticalInset;
+}
\ No newline at end of file
diff --git a/WinUIGallery/Samples/Composition/CompositionGetElementVisual.txt b/WinUIGallery/Samples/Composition/CompositionGetElementVisual.txt
new file mode 100644
index 000000000..60c65ba30
--- /dev/null
+++ b/WinUIGallery/Samples/Composition/CompositionGetElementVisual.txt
@@ -0,0 +1,32 @@
+--- header
+Get and transform the Visual backing a XAML element
+--- xaml
+
+
+
+--- c#
+private Visual? _elementVisual;
+
+private void ElementVisualExample_Loaded(object sender, RoutedEventArgs e)
+{
+ _elementVisual = ElementCompositionPreview.GetElementVisual(ElementVisualTarget);
+ UpdateVisual();
+}
+
+private void UpdateVisual()
+{
+ if (_elementVisual is null)
+ {
+ return;
+ }
+
+ // CenterPoint uses pixels in the Visual's coordinate space.
+ _elementVisual.CenterPoint = new Vector3(ElementVisualTarget.ActualSize / 2, 0);
+ // Translation is also exposed directly on UIElement as a Composition facade.
+ ElementVisualTarget.Translation = new Vector3(translationX, translationY, 0);
+ _elementVisual.RotationAngleInDegrees = rotation;
+}
\ No newline at end of file
diff --git a/WinUIGallery/Samples/Composition/CompositionPage.xaml b/WinUIGallery/Samples/Composition/CompositionPage.xaml
new file mode 100644
index 000000000..23fc3ac42
--- /dev/null
+++ b/WinUIGallery/Samples/Composition/CompositionPage.xaml
@@ -0,0 +1,166 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/WinUIGallery/Samples/Composition/CompositionPage.xaml.cs b/WinUIGallery/Samples/Composition/CompositionPage.xaml.cs
new file mode 100644
index 000000000..776e87267
--- /dev/null
+++ b/WinUIGallery/Samples/Composition/CompositionPage.xaml.cs
@@ -0,0 +1,180 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.UI.Composition;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Hosting;
+using System;
+using System.Numerics;
+
+namespace WinUIGallery.ControlPages;
+
+public sealed partial class CompositionPage : Page
+{
+ private Visual? _elementVisual;
+ private SpriteVisual? _childVisual;
+ private Visual? _transformMatrixVisual;
+ private InsetClip? _insetClip;
+
+ public CompositionPage()
+ {
+ InitializeComponent();
+ Unloaded += CompositionPage_Unloaded;
+ }
+
+ private void ElementVisualExample_Loaded(object sender, RoutedEventArgs e)
+ {
+ _elementVisual = ElementCompositionPreview.GetElementVisual(ElementVisualTarget);
+ UpdateElementVisualCenterPoint();
+ UpdateElementVisualTransform();
+ }
+
+ private void ElementVisualTarget_SizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ UpdateElementVisualCenterPoint();
+ }
+
+ private void ElementVisualTransform_ValueChanged(object sender, Microsoft.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
+ {
+ UpdateElementVisualTransform();
+ }
+
+ private void UpdateElementVisualCenterPoint()
+ {
+ if (_elementVisual is null)
+ {
+ return;
+ }
+
+ _elementVisual.CenterPoint = new Vector3(ElementVisualTarget.ActualSize / 2, 0);
+ }
+
+ private void UpdateElementVisualTransform()
+ {
+ if (_elementVisual is null)
+ {
+ return;
+ }
+
+ ElementVisualTarget.Translation = new Vector3(
+ (float)TranslationXSlider.Value,
+ (float)TranslationYSlider.Value,
+ 0);
+ _elementVisual.RotationAngleInDegrees = (float)RotationSlider.Value;
+ }
+
+ private void ChildVisualExample_Loaded(object sender, RoutedEventArgs e)
+ {
+ if (_childVisual is null)
+ {
+ Compositor compositor = ElementCompositionPreview.GetElementVisual(ChildVisualHost).Compositor;
+ _childVisual = compositor.CreateSpriteVisual();
+ _childVisual.Size = new Vector2(80);
+ _childVisual.Brush = compositor.CreateColorBrush(Microsoft.UI.ColorHelper.FromArgb(255, 0, 120, 212));
+ ElementCompositionPreview.SetElementChildVisual(ChildVisualHost, _childVisual);
+ }
+
+ UpdateChildVisual();
+ }
+
+ private void ChildVisualHost_SizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ UpdateChildVisual();
+ }
+
+ private void AnchorPointSlider_ValueChanged(object sender, Microsoft.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
+ {
+ UpdateChildVisual();
+ }
+
+ private void UpdateChildVisual()
+ {
+ if (_childVisual is null)
+ {
+ return;
+ }
+
+ _childVisual.AnchorPoint = new Vector2(
+ (float)AnchorXSlider.Value,
+ (float)AnchorYSlider.Value);
+ _childVisual.Offset = new Vector3(ChildVisualHost.ActualSize / 2, 0);
+ }
+
+ private void TransformMatrixExample_Loaded(object sender, RoutedEventArgs e)
+ {
+ _transformMatrixVisual = ElementCompositionPreview.GetElementVisual(TransformMatrixTarget);
+ UpdateTransformMatrixCenterPoint();
+ UpdateTransformMatrix();
+ }
+
+ private void TransformMatrixTarget_SizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ UpdateTransformMatrixCenterPoint();
+ }
+
+ private void TransformMatrixSlider_ValueChanged(object sender, Microsoft.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
+ {
+ UpdateTransformMatrix();
+ }
+
+ private void UpdateTransformMatrixCenterPoint()
+ {
+ if (_transformMatrixVisual is null)
+ {
+ return;
+ }
+
+ _transformMatrixVisual.CenterPoint = new Vector3(TransformMatrixTarget.ActualSize / 2, 0);
+ }
+
+ private void UpdateTransformMatrix()
+ {
+ if (_transformMatrixVisual is null)
+ {
+ return;
+ }
+
+ float skewRadians = (float)(SkewXSlider.Value * Math.PI / 180);
+ Matrix4x4 skewMatrix = Matrix4x4.Identity;
+ skewMatrix.M21 = MathF.Tan(skewRadians);
+ _transformMatrixVisual.TransformMatrix =
+ Matrix4x4.CreateScale((float)ScaleXSlider.Value, 1, 1) *
+ skewMatrix;
+ }
+
+ private void ClipExample_Loaded(object sender, RoutedEventArgs e)
+ {
+ Visual visual = ElementCompositionPreview.GetElementVisual(ClipTarget);
+ _insetClip ??= visual.Compositor.CreateInsetClip();
+ visual.Clip = _insetClip;
+ UpdateClip();
+ }
+
+ private void ClipSlider_ValueChanged(object sender, Microsoft.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
+ {
+ UpdateClip();
+ }
+
+ private void UpdateClip()
+ {
+ if (_insetClip is null)
+ {
+ return;
+ }
+
+ float horizontalInset = (float)HorizontalClipSlider.Value;
+ float verticalInset = (float)VerticalClipSlider.Value;
+ _insetClip.LeftInset = horizontalInset;
+ _insetClip.RightInset = horizontalInset;
+ _insetClip.TopInset = verticalInset;
+ _insetClip.BottomInset = verticalInset;
+ }
+
+ private void CompositionPage_Unloaded(object sender, RoutedEventArgs e)
+ {
+ ElementCompositionPreview.SetElementChildVisual(ChildVisualHost, null);
+ _childVisual?.Dispose();
+ _childVisual = null;
+ }
+}
diff --git a/WinUIGallery/Samples/Composition/CompositionTransformMatrix.txt b/WinUIGallery/Samples/Composition/CompositionTransformMatrix.txt
new file mode 100644
index 000000000..bb485d41b
--- /dev/null
+++ b/WinUIGallery/Samples/Composition/CompositionTransformMatrix.txt
@@ -0,0 +1,25 @@
+--- header
+Apply multiple transforms with TransformMatrix
+--- xaml
+
+
+
+--- c#
+private Visual? _visual;
+
+private void UpdateTransform(float horizontalScale, float skewInDegrees)
+{
+ _visual ??= ElementCompositionPreview.GetElementVisual(TransformMatrixTarget);
+ _visual.CenterPoint = new Vector3(TransformMatrixTarget.ActualSize / 2, 0);
+
+ float skewInRadians = skewInDegrees * MathF.PI / 180;
+ Matrix4x4 skewMatrix = Matrix4x4.Identity;
+ skewMatrix.M21 = MathF.Tan(skewInRadians);
+ _visual.TransformMatrix =
+ Matrix4x4.CreateScale(horizontalScale, 1, 1) *
+ skewMatrix;
+}
\ No newline at end of file
diff --git a/catalog/windows-samples.json b/catalog/windows-samples.json
index 804db1456..7ad36fe99 100644
--- a/catalog/windows-samples.json
+++ b/catalog/windows-samples.json
@@ -9,7 +9,7 @@
"defaultBranch": "main",
"license": "MIT"
},
- "controlCount": 126,
+ "controlCount": 127,
"controls": [
{
"id": "accessibilitycolorcontrast",
@@ -2920,6 +2920,106 @@
}
]
},
+ {
+ "id": "composition",
+ "name": "Composition",
+ "description": "Connect XAML elements to the Visual layer and apply transforms and clips.",
+ "details": "Explore XAML and Composition interop by retrieving and attaching visuals, working with coordinate systems, applying transform matrices, and clipping content.",
+ "apiNamespace": "Microsoft.UI.Composition",
+ "relatedControls": [
+ "Animation interop"
+ ],
+ "usings": [
+ "Microsoft.UI.Composition",
+ "Microsoft.UI.Xaml",
+ "Microsoft.UI.Xaml.Controls",
+ "Microsoft.UI.Xaml.Hosting",
+ "System",
+ "System.Numerics"
+ ],
+ "curatedKeywords": [
+ "composition",
+ "Visual layer",
+ "ElementCompositionPreview",
+ "transform",
+ "clip"
+ ],
+ "docs": [
+ {
+ "title": "Composition overview",
+ "uri": "https://learn.microsoft.com/windows/apps/windows-app-sdk/composition"
+ },
+ {
+ "title": "ElementCompositionPreview - API",
+ "uri": "https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.hosting.elementcompositionpreview"
+ },
+ {
+ "title": "Visual - API",
+ "uri": "https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.composition.visual"
+ }
+ ],
+ "gallery": {
+ "uniqueId": "Composition",
+ "group": {
+ "id": "Motion",
+ "title": "Motion"
+ },
+ "page": "WinUIGallery/Samples/Composition/CompositionPage.xaml",
+ "codeBehind": "WinUIGallery/Samples/Composition/CompositionPage.xaml.cs",
+ "badges": [
+ "New"
+ ],
+ "relatedSamples": [
+ "microsoft/WinUI-Gallery#XamlCompInterop"
+ ]
+ },
+ "samples": [
+ {
+ "header": "Get and transform the Visual backing a XAML element",
+ "xaml": "\u003CBorder\n x:Name=\u0022ElementVisualTarget\u0022\n Width=\u0022160\u0022\n Height=\u0022100\u0022\n SizeChanged=\u0022ElementVisualTarget_SizeChanged\u0022\u003E\n \u003CTextBlock Text=\u0022XAML element\u0022 /\u003E\n\u003C/Border\u003E",
+ "code": "private Visual? _elementVisual;\n\nprivate void ElementVisualExample_Loaded(object sender, RoutedEventArgs e)\n{\n _elementVisual = ElementCompositionPreview.GetElementVisual(ElementVisualTarget);\n UpdateVisual();\n}\n\nprivate void UpdateVisual()\n{\n if (_elementVisual is null)\n {\n return;\n }\n\n // CenterPoint uses pixels in the Visual\u0027s coordinate space.\n _elementVisual.CenterPoint = new Vector3(ElementVisualTarget.ActualSize / 2, 0);\n // Translation is also exposed directly on UIElement as a Composition facade.\n ElementVisualTarget.Translation = new Vector3(translationX, translationY, 0);\n _elementVisual.RotationAngleInDegrees = rotation;\n}",
+ "language": "csharp",
+ "gallery": {
+ "snippet": "CompositionGetElementVisual.txt",
+ "source": "WinUIGallery/Samples/Composition/CompositionGetElementVisual.txt",
+ "name": "Get Element Visual"
+ }
+ },
+ {
+ "header": "Attach a Composition child Visual and position it with AnchorPoint",
+ "xaml": "\u003CGrid\n x:Name=\u0022ChildVisualHost\u0022\n Height=\u0022220\u0022\n SizeChanged=\u0022ChildVisualHost_SizeChanged\u0022 /\u003E",
+ "code": "private SpriteVisual? _childVisual;\n\nprivate void ChildVisualExample_Loaded(object sender, RoutedEventArgs e)\n{\n Visual hostVisual = ElementCompositionPreview.GetElementVisual(ChildVisualHost);\n _childVisual = hostVisual.Compositor.CreateSpriteVisual();\n _childVisual.Size = new Vector2(80);\n _childVisual.Brush = hostVisual.Compositor.CreateColorBrush(Microsoft.UI.Colors.DodgerBlue);\n\n ElementCompositionPreview.SetElementChildVisual(ChildVisualHost, _childVisual);\n UpdateChildVisual();\n}\n\nprivate void UpdateChildVisual()\n{\n // AnchorPoint is normalized: (0,0) is the top-left and (1,1) is the bottom-right.\n _childVisual.AnchorPoint = new Vector2(anchorX, anchorY);\n _childVisual.Offset = new Vector3(ChildVisualHost.ActualSize / 2, 0);\n}\n\nprivate void Page_Unloaded(object sender, RoutedEventArgs e)\n{\n ElementCompositionPreview.SetElementChildVisual(ChildVisualHost, null);\n _childVisual?.Dispose();\n}",
+ "language": "csharp",
+ "gallery": {
+ "snippet": "CompositionChildVisual.txt",
+ "source": "WinUIGallery/Samples/Composition/CompositionChildVisual.txt",
+ "name": "Child Visual"
+ }
+ },
+ {
+ "header": "Apply multiple transforms with TransformMatrix",
+ "xaml": "\u003CBorder\n x:Name=\u0022TransformMatrixTarget\u0022\n Width=\u0022170\u0022\n Height=\u0022100\u0022\n SizeChanged=\u0022TransformMatrixTarget_SizeChanged\u0022\u003E\n \u003CTextBlock Text=\u0022TransformMatrix\u0022 /\u003E\n\u003C/Border\u003E",
+ "code": "private Visual? _visual;\n\nprivate void UpdateTransform(float horizontalScale, float skewInDegrees)\n{\n _visual ??= ElementCompositionPreview.GetElementVisual(TransformMatrixTarget);\n _visual.CenterPoint = new Vector3(TransformMatrixTarget.ActualSize / 2, 0);\n\n float skewInRadians = skewInDegrees * MathF.PI / 180;\n Matrix4x4 skewMatrix = Matrix4x4.Identity;\n skewMatrix.M21 = MathF.Tan(skewInRadians);\n _visual.TransformMatrix =\n Matrix4x4.CreateScale(horizontalScale, 1, 1) *\n skewMatrix;\n}",
+ "language": "csharp",
+ "gallery": {
+ "snippet": "CompositionTransformMatrix.txt",
+ "source": "WinUIGallery/Samples/Composition/CompositionTransformMatrix.txt",
+ "name": "Transform Matrix"
+ }
+ },
+ {
+ "header": "Clip a XAML element through its Composition Visual",
+ "xaml": "\u003CGrid\n x:Name=\u0022ClipTarget\u0022\n Width=\u0022240\u0022\n Height=\u0022140\u0022\u003E\n \u003C!-- Sample content --\u003E\n\u003C/Grid\u003E",
+ "code": "private InsetClip? _insetClip;\n\nprivate void ClipExample_Loaded(object sender, RoutedEventArgs e)\n{\n Visual visual = ElementCompositionPreview.GetElementVisual(ClipTarget);\n _insetClip = visual.Compositor.CreateInsetClip();\n visual.Clip = _insetClip;\n}\n\nprivate void UpdateClip(float horizontalInset, float verticalInset)\n{\n _insetClip.LeftInset = horizontalInset;\n _insetClip.RightInset = horizontalInset;\n _insetClip.TopInset = verticalInset;\n _insetClip.BottomInset = verticalInset;\n}",
+ "language": "csharp",
+ "gallery": {
+ "snippet": "CompositionClip.txt",
+ "source": "WinUIGallery/Samples/Composition/CompositionClip.txt",
+ "name": "Clip"
+ }
+ }
+ ]
+ },
{
"id": "connectedanimation",
"name": "Connected Animation",