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",