diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue30403.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue30403.xaml new file mode 100644 index 000000000000..878334a64cac --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue30403.xaml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue30403.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue30403.xaml.cs new file mode 100644 index 000000000000..876dbfa5113a --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue30403.xaml.cs @@ -0,0 +1,13 @@ +using Microsoft.Maui.Controls; + +namespace Maui.Controls.Sample.Issues +{ + [Issue(IssueTracker.Github, 30403, "Image under WinUI does not respect VerticalOptions and HorizontalOptions", PlatformAffected.UWP)] + public partial class Issue30403 : ContentPage + { + public Issue30403() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30403.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30403.cs new file mode 100644 index 000000000000..477737906520 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30403.cs @@ -0,0 +1,32 @@ +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues +{ + public class Issue30403 : _IssuesUITest + { + public Issue30403(TestDevice device) + : base(device) + { } + + public override string Issue => "Image under WinUI does not respect VerticalOptions and HorizontalOptions"; + + [Test] + [Category(UITestCategories.Layout)] + [Category(UITestCategories.Image)] + public void ImageShouldRespectLayoutOptions() + { + // Wait for images to load + App.WaitForElement("CenteredImage"); + App.WaitForElement("StartImage"); + App.WaitForElement("EndImage"); + App.WaitForElement("FillImage"); + App.WaitForElement("WideImage"); + + // On Windows, images should be positioned according to their alignment options + // instead of stretching to fill the container + VerifyScreenshot(); + } + } +} \ No newline at end of file diff --git a/src/Core/src/Handlers/Image/ImageHandler.Windows.cs b/src/Core/src/Handlers/Image/ImageHandler.Windows.cs index ee3a6ddbd533..a939371f402c 100644 --- a/src/Core/src/Handlers/Image/ImageHandler.Windows.cs +++ b/src/Core/src/Handlers/Image/ImageHandler.Windows.cs @@ -44,6 +44,8 @@ protected override void SetupContainer() UpdateValue(nameof(IView.Height)); UpdateValue(nameof(IView.Width)); + UpdateValue(nameof(IView.HorizontalLayoutAlignment)); + UpdateValue(nameof(IView.VerticalLayoutAlignment)); } /// @@ -138,6 +140,42 @@ public static void MapSource(IImageHandler handler, IImage image) => public static Task MapSourceAsync(IImageHandler handler, IImage image) => handler.SourceLoader.UpdateImageSourceAsync(); + /// + /// Maps the abstract property to the platform-specific implementations. + /// + /// The associated handler. + /// The associated instance. + internal static void MapHorizontalLayoutAlignment(IImageHandler handler, IImage image) + { + // Apply alignment to container if it exists, otherwise to the platform view + if (handler.ContainerView is FrameworkElement container) + { + container.UpdateHorizontalLayoutAlignment(image); + } + else + { + handler.PlatformView?.UpdateHorizontalLayoutAlignment(image); + } + } + + /// + /// Maps the abstract property to the platform-specific implementations. + /// + /// The associated handler. + /// The associated instance. + internal static void MapVerticalLayoutAlignment(IImageHandler handler, IImage image) + { + // Apply alignment to container if it exists, otherwise to the platform view + if (handler.ContainerView is FrameworkElement container) + { + container.UpdateVerticalLayoutAlignment(image); + } + else + { + handler.PlatformView?.UpdateVerticalLayoutAlignment(image); + } + } + void OnImageOpened(object sender, RoutedEventArgs e) { // Because this resolves from a task we should validate that the diff --git a/src/Core/src/Handlers/Image/ImageHandler.cs b/src/Core/src/Handlers/Image/ImageHandler.cs index cfcc20d83514..47fc788f1bbf 100644 --- a/src/Core/src/Handlers/Image/ImageHandler.cs +++ b/src/Core/src/Handlers/Image/ImageHandler.cs @@ -24,6 +24,8 @@ public partial class ImageHandler : IImageHandler #if WINDOWS [nameof(IImage.Height)] = MapHeight, [nameof(IImage.Width)] = MapWidth, + [nameof(IView.HorizontalLayoutAlignment)] = MapHorizontalLayoutAlignment, + [nameof(IView.VerticalLayoutAlignment)] = MapVerticalLayoutAlignment, #endif [nameof(IImage.Aspect)] = MapAspect, [nameof(IImage.IsAnimationPlaying)] = MapIsAnimationPlaying, diff --git a/src/Core/src/Platform/Windows/ImageViewExtensions.cs b/src/Core/src/Platform/Windows/ImageViewExtensions.cs index fbcd994371c5..80e7280e3981 100644 --- a/src/Core/src/Platform/Windows/ImageViewExtensions.cs +++ b/src/Core/src/Platform/Windows/ImageViewExtensions.cs @@ -16,11 +16,45 @@ public static void UpdateAspect(this WImage imageView, IImage image) { imageView.Stretch = image.Aspect.ToStretch(); + // For AspectFill, we still want center alignment on the image itself + // but for other aspects, the layout alignment should be controlled by + // the container or the layout alignment properties if (image.Aspect == Aspect.AspectFill) { imageView.VerticalAlignment = VerticalAlignment.Center; imageView.HorizontalAlignment = HorizontalAlignment.Center; } + else + { + // Clear any previous alignment settings when not AspectFill + // The layout alignment will be handled by the container or layout alignment properties + imageView.ClearValue(FrameworkElement.VerticalAlignmentProperty); + imageView.ClearValue(FrameworkElement.HorizontalAlignmentProperty); + } + } + + public static void UpdateHorizontalLayoutAlignment(this FrameworkElement platformView, IView view) + { + platformView.HorizontalAlignment = view.HorizontalLayoutAlignment switch + { + Primitives.LayoutAlignment.Start => HorizontalAlignment.Left, + Primitives.LayoutAlignment.Center => HorizontalAlignment.Center, + Primitives.LayoutAlignment.End => HorizontalAlignment.Right, + Primitives.LayoutAlignment.Fill => HorizontalAlignment.Stretch, + _ => HorizontalAlignment.Stretch + }; + } + + public static void UpdateVerticalLayoutAlignment(this FrameworkElement platformView, IView view) + { + platformView.VerticalAlignment = view.VerticalLayoutAlignment switch + { + Primitives.LayoutAlignment.Start => VerticalAlignment.Top, + Primitives.LayoutAlignment.Center => VerticalAlignment.Center, + Primitives.LayoutAlignment.End => VerticalAlignment.Bottom, + Primitives.LayoutAlignment.Fill => VerticalAlignment.Stretch, + _ => VerticalAlignment.Stretch + }; } public static void UpdateIsAnimationPlaying(this WImage imageView, IImageSourcePart image)