diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index 934fe7623e1d..c2d342fc8af4 100644
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -200,20 +200,6 @@ git commit -m "Fix: Description of the change"
- `.github/instructions/android.instructions.md` - Android handler implementation
- `.github/instructions/xaml-unittests.instructions.md` - XAML unit test guidelines
-### Opening PRs
-
-All PRs are required to have this at the top of the description:
-
-```
-
-> [!NOTE]
-> Are you waiting for the changes in this PR to be merged?
-> It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you!
-```
-
-Always put that at the top, without the block quotes. Without it, users will NOT be able to try the PR and your work will have been in vain!
-
-
## Custom Agents and Skills
diff --git a/.github/skills/pr-finalize/SKILL.md b/.github/skills/pr-finalize/SKILL.md
index 28ba6916dd8b..9932ac6534c5 100644
--- a/.github/skills/pr-finalize/SKILL.md
+++ b/.github/skills/pr-finalize/SKILL.md
@@ -127,16 +127,10 @@ Examples:
## Description Requirements
PR description should:
-1. Start with the required NOTE block (so users can test PR artifacts)
-2. Include the base sections from `.github/PULL_REQUEST_TEMPLATE.md` ("Description of Change" and "Issues Fixed"). The skill adds additional structured fields (Root cause, Fix, Key insight, etc.) as recommended enhancements for better agent context.
-3. Match the actual implementation
+1. Include the base sections from `.github/PULL_REQUEST_TEMPLATE.md` ("Description of Change" and "Issues Fixed"). The skill adds additional structured fields (Root cause, Fix, Key insight, etc.) as recommended enhancements for better agent context.
+2. Match the actual implementation
```markdown
-
-> [!NOTE]
-> Are you waiting for the changes in this PR to be merged?
-> It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you!
-
### Description of Change
[Must match actual implementation]
@@ -229,11 +223,6 @@ Example: "Before: Safe area applied by default (opt-out). After: Only views impl
Use structured template only when existing description is inadequate:
```markdown
-
-> [!NOTE]
-> Are you waiting for the changes in this PR to be merged?
-> It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you!
-
### Root Cause
[Why the bug occurred - be specific about the code path]
diff --git a/.github/skills/pr-finalize/references/complete-example.md b/.github/skills/pr-finalize/references/complete-example.md
index 034da2e3d09d..6f8ff08d1006 100644
--- a/.github/skills/pr-finalize/references/complete-example.md
+++ b/.github/skills/pr-finalize/references/complete-example.md
@@ -9,10 +9,6 @@ This example shows a PR description optimized for future agent success.
## Description
```markdown
-> [!NOTE]
-> Are you waiting for the changes in this PR to be merged?
-> It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you!
-
### Root Cause
In `MauiView.GetAdjustedSafeAreaInsets()` on iOS, views that don't implement `ISafeAreaView` or `ISafeAreaView2` (such as `ContentPresenter`, `Border`) were falling through to return `baseSafeArea`. This applied full device safe area insets to views that never opted into safe area handling, causing double-padding when used inside ControlTemplates.
diff --git a/eng/pipelines/common/ui-tests-steps.yml b/eng/pipelines/common/ui-tests-steps.yml
index e3c1186e329c..b5b2a8f79a8a 100644
--- a/eng/pipelines/common/ui-tests-steps.yml
+++ b/eng/pipelines/common/ui-tests-steps.yml
@@ -186,6 +186,8 @@ steps:
- task: PublishBuildArtifacts@1
condition: always()
displayName: publish artifacts
+ inputs:
+ artifactName: drop-$(System.StageName)-$(System.JobName)-$(System.JobAttempt)
# Enable Notification Center re-enabled only for catalyst
- ${{ if eq(parameters.platform, 'catalyst')}}:
diff --git a/eng/pipelines/common/ui-tests.yml b/eng/pipelines/common/ui-tests.yml
index fe1231c2292c..258ab00de3e7 100644
--- a/eng/pipelines/common/ui-tests.yml
+++ b/eng/pipelines/common/ui-tests.yml
@@ -24,7 +24,7 @@ parameters:
- 'Cells,CheckBox,ContextActions,CustomRenderers,DatePicker,Dispatcher,DisplayAlert,DisplayPrompt,DragAndDrop'
- 'CollectionView'
- 'Entry'
- - 'Editor,Effects,FlyoutPage,Focus,Fonts,Frame,Gestures,GraphicsView'
+ - 'Editor,Effects,Essentials,FlyoutPage,Focus,Fonts,Frame,Gestures,GraphicsView'
- 'Image,ImageButton,IndicatorView,InputTransparent,IsEnabled,IsVisible'
- 'Label'
- 'Layout'
@@ -38,8 +38,8 @@ parameters:
- 'SearchBar,Shape,Slider'
- 'SoftInput,Stepper,Switch,SwipeView'
- 'Shell'
- - 'TabbedPage,TableView,TimePicker,TitleView,ToolbarItem'
- - 'ViewBaseTests,Window'
+ - 'TabbedPage,TableView,TimePicker,TitleView,ToolbarItem,Triggers'
+ - 'ViewBaseTests,VisualStateManager,Window'
- 'WebView'
projects:
diff --git a/src/BlazorWebView/src/Maui/BlazorWebView.cs b/src/BlazorWebView/src/Maui/BlazorWebView.cs
index 15922e86b364..d54ee207cf77 100644
--- a/src/BlazorWebView/src/Maui/BlazorWebView.cs
+++ b/src/BlazorWebView/src/Maui/BlazorWebView.cs
@@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
+using System.Runtime.Versioning;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Extensions.FileProviders;
using Microsoft.Maui;
@@ -10,8 +11,24 @@ namespace Microsoft.AspNetCore.Components.WebView.Maui
///
/// A that can render Blazor content.
///
+#if ANDROID
+ [SupportedOSPlatform(AndroidSupportedOSPlatformVersion)]
+#elif IOS
+ [SupportedOSPlatform(iOSSupportedOSPlatformVersion)]
+#elif MACCATALYST
+ [SupportedOSPlatform(MacCatalystSupportedOSPlatformVersion)]
+#endif
public partial class BlazorWebView : View, IBlazorWebView
{
+ // NOTE: keep these in *reasonably* in sync with:
+ // * src\BlazorWebView\src\Maui\Microsoft.AspNetCore.Components.WebView.Maui.csproj
+ // * src\Templates\src\templates\maui-blazor\MauiApp.1.csproj
+ // * src\Templates\src\templates\maui-blazor-solution\MauiApp.1\MauiApp.1.csproj
+ // * https://learn.microsoft.com/dotnet/maui/supported-platforms
+ internal const string AndroidSupportedOSPlatformVersion = "android24.0";
+ internal const string iOSSupportedOSPlatformVersion = "ios15.0";
+ internal const string MacCatalystSupportedOSPlatformVersion = "maccatalyst15.0";
+
internal static string AppHostAddress { get; } = HostAddressHelper.GetAppHostAddress();
private readonly JSComponentConfigurationStore _jSComponents = new();
@@ -80,9 +97,11 @@ public string StartPath
///
#if ANDROID
- [System.Runtime.Versioning.SupportedOSPlatform("android23.0")]
+ [System.Runtime.Versioning.SupportedOSPlatform(AndroidSupportedOSPlatformVersion)]
#elif IOS
- [System.Runtime.Versioning.SupportedOSPlatform("ios11.0")]
+ [System.Runtime.Versioning.SupportedOSPlatform(iOSSupportedOSPlatformVersion)]
+#elif MACCATALYST
+ [System.Runtime.Versioning.SupportedOSPlatform(MacCatalystSupportedOSPlatformVersion)]
#endif
public virtual IFileProvider CreateFileProvider(string contentRootDir)
{
@@ -97,7 +116,11 @@ public virtual IFileProvider CreateFileProvider(string contentRootDir)
/// Returns a representing true if the was called, or false if it was not called because Blazor is not currently running.
/// Thrown if is null .
#if ANDROID
- [System.Runtime.Versioning.SupportedOSPlatform("android23.0")]
+ [System.Runtime.Versioning.SupportedOSPlatform(AndroidSupportedOSPlatformVersion)]
+#elif IOS
+ [System.Runtime.Versioning.SupportedOSPlatform(iOSSupportedOSPlatformVersion)]
+#elif MACCATALYST
+ [System.Runtime.Versioning.SupportedOSPlatform(MacCatalystSupportedOSPlatformVersion)]
#endif
public virtual async Task TryDispatchAsync(Action workItem)
{
diff --git a/src/BlazorWebView/src/Maui/BlazorWebViewHandler.cs b/src/BlazorWebView/src/Maui/BlazorWebViewHandler.cs
index 052a96f5586f..df638d6d7cd1 100644
--- a/src/BlazorWebView/src/Maui/BlazorWebViewHandler.cs
+++ b/src/BlazorWebView/src/Maui/BlazorWebViewHandler.cs
@@ -9,7 +9,11 @@
namespace Microsoft.AspNetCore.Components.WebView.Maui
{
#if ANDROID
- [SupportedOSPlatform("android23.0")]
+ [SupportedOSPlatform(BlazorWebView.AndroidSupportedOSPlatformVersion)]
+#elif IOS
+ [SupportedOSPlatform(BlazorWebView.iOSSupportedOSPlatformVersion)]
+#elif MACCATALYST
+ [SupportedOSPlatform(BlazorWebView.MacCatalystSupportedOSPlatformVersion)]
#endif
public partial class BlazorWebViewHandler
{
diff --git a/src/BlazorWebView/src/Maui/Microsoft.AspNetCore.Components.WebView.Maui.csproj b/src/BlazorWebView/src/Maui/Microsoft.AspNetCore.Components.WebView.Maui.csproj
index d4268f64fa8f..115ba0586b46 100644
--- a/src/BlazorWebView/src/Maui/Microsoft.AspNetCore.Components.WebView.Maui.csproj
+++ b/src/BlazorWebView/src/Maui/Microsoft.AspNetCore.Components.WebView.Maui.csproj
@@ -23,6 +23,10 @@
$(DefaultPackageTags);blazor;webview;aspnet
$(MauiRootDirectory)Assets\aspnet-icon.png
Build .NET Multi-platform App UI (.NET MAUI) apps with Blazor web UI in the BlazorWebView control.
+
+ 15.0
+ 15.0
+ 24.0
diff --git a/src/BlazorWebView/src/SharedSource/BlazorWebViewServiceCollectionExtensions.cs b/src/BlazorWebView/src/SharedSource/BlazorWebViewServiceCollectionExtensions.cs
index b7b0c748177d..a83a5e187c3c 100644
--- a/src/BlazorWebView/src/SharedSource/BlazorWebViewServiceCollectionExtensions.cs
+++ b/src/BlazorWebView/src/SharedSource/BlazorWebViewServiceCollectionExtensions.cs
@@ -30,9 +30,11 @@ public static IWindowsFormsBlazorWebViewBuilder AddWindowsFormsBlazorWebView(thi
public static IWpfBlazorWebViewBuilder AddWpfBlazorWebView(this IServiceCollection services)
#elif WEBVIEW2_MAUI
#if ANDROID
- [System.Runtime.Versioning.SupportedOSPlatform("android23.0")]
+ [System.Runtime.Versioning.SupportedOSPlatform(BlazorWebView.AndroidSupportedOSPlatformVersion)]
#elif IOS
- [System.Runtime.Versioning.SupportedOSPlatform("ios11.0")]
+ [System.Runtime.Versioning.SupportedOSPlatform(BlazorWebView.iOSSupportedOSPlatformVersion)]
+#elif MACCATALYST
+ [System.Runtime.Versioning.SupportedOSPlatform(BlazorWebView.MacCatalystSupportedOSPlatformVersion)]
#endif
public static IMauiBlazorWebViewBuilder AddMauiBlazorWebView(this IServiceCollection services)
#else
diff --git a/src/Compatibility/Core/src/iOS/Renderers/NavigationRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/NavigationRenderer.cs
index ae911866e9ad..09e30a6a4fa8 100644
--- a/src/Compatibility/Core/src/iOS/Renderers/NavigationRenderer.cs
+++ b/src/Compatibility/Core/src/iOS/Renderers/NavigationRenderer.cs
@@ -762,6 +762,11 @@ void UpdateBarTextColor()
void SetStatusBarStyle()
{
+ if (NavPage is null)
+ {
+ return;
+ }
+
var barTextColor = NavPage.BarTextColor;
var statusBarColorMode = NavPage.OnThisPlatform().GetStatusBarTextColorMode();
diff --git a/src/Compatibility/Maps/src/Android/MapRenderer.cs b/src/Compatibility/Maps/src/Android/MapRenderer.cs
index fe526759bf13..e0a18f5b2c6d 100644
--- a/src/Compatibility/Maps/src/Android/MapRenderer.cs
+++ b/src/Compatibility/Maps/src/Android/MapRenderer.cs
@@ -46,6 +46,7 @@ public class MapRenderer : Handlers.Compatibility.ViewRenderer, Go
List _polylines;
List _polygons;
List _circles;
+ List _trackedMapElements;
public MapRenderer(Context context) : base(context)
{
@@ -581,6 +582,16 @@ void MapElementCollectionChanged(NotifyCollectionChangedEventArgs e)
AddMapElements(e.NewItems.Cast());
break;
case NotifyCollectionChangedAction.Reset:
+ // Clear MapElementId from tracked elements (not Element.MapElements,
+ // which is already empty after ObservableCollection.Clear())
+ if (_trackedMapElements != null)
+ {
+ foreach (var element in _trackedMapElements)
+ element.MapElementId = null;
+
+ _trackedMapElements = null;
+ }
+
if (_polylines != null)
{
for (int i = 0; i < _polylines.Count; i++)
@@ -612,8 +623,11 @@ void MapElementCollectionChanged(NotifyCollectionChangedEventArgs e)
void AddMapElements(IEnumerable mapElements)
{
+ _trackedMapElements ??= new List();
+
foreach (var element in mapElements)
{
+ _trackedMapElements.Add(element);
element.PropertyChanged += MapElementPropertyChanged;
switch (element)
diff --git a/src/Compatibility/Maps/src/iOS/MapRenderer.cs b/src/Compatibility/Maps/src/iOS/MapRenderer.cs
index c248128a451e..0bcdddc456a4 100644
--- a/src/Compatibility/Maps/src/iOS/MapRenderer.cs
+++ b/src/Compatibility/Maps/src/iOS/MapRenderer.cs
@@ -31,6 +31,7 @@ public class MapRenderer : Microsoft.Maui.Controls.Handlers.Compatibility.ViewRe
bool _shouldUpdateRegion;
object _lastTouchedView;
bool _disposed;
+ List _trackedMapElements;
#if __MOBILE__
UITapGestureRecognizer _mapClickedGestureRecognizer;
@@ -528,6 +529,16 @@ void MapElementCollectionChanged(NotifyCollectionChangedEventArgs e)
case NotifyCollectionChangedAction.Reset:
var mkMapView = (MKMapView)Control;
+ // Clear MapElementId from tracked elements (not Element.MapElements,
+ // which is already empty after ObservableCollection.Clear())
+ if (_trackedMapElements != null)
+ {
+ foreach (var mapElement in _trackedMapElements)
+ mapElement.MapElementId = null;
+
+ _trackedMapElements = null;
+ }
+
if (mkMapView.Overlays != null)
{
var overlays = mkMapView.Overlays;
@@ -544,8 +555,11 @@ void MapElementCollectionChanged(NotifyCollectionChangedEventArgs e)
void AddMapElements(IEnumerable mapElements)
{
+ _trackedMapElements ??= new List();
+
foreach (var element in mapElements)
{
+ _trackedMapElements.Add(element);
element.PropertyChanged += MapElementPropertyChanged;
IMKOverlay overlay = null;
diff --git a/src/Controls/samples/Controls.Sample/Pages/Others/LargeTitlesPageiOS.cs b/src/Controls/samples/Controls.Sample/Pages/Others/LargeTitlesPageiOS.cs
deleted file mode 100644
index e0ccaf46ba17..000000000000
--- a/src/Controls/samples/Controls.Sample/Pages/Others/LargeTitlesPageiOS.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using System.Linq;
-using System.Windows.Input;
-using Microsoft.Maui.Controls;
-using Microsoft.Maui.Controls.PlatformConfiguration;
-using Microsoft.Maui.Graphics;
-using static Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.NavigationPage;
-using static Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.Page;
-using LargeTitleDisplayMode = Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.LargeTitleDisplayMode;
-
-namespace Maui.Controls.Sample.Pages
-{
- public class LargeTitlesPageiOS : ContentPage
- {
- public LargeTitlesPageiOS()
- {
- Title = "Large Titles";
-
- var offscreenPageLimit = new Label();
- var content = new StackLayout
- {
- VerticalOptions = LayoutOptions.Fill,
- HorizontalOptions = LayoutOptions.Fill,
- Children =
- {
- new Button
- {
- Text = "LargeTitleDisplayMode.Never",
- Command = new Command(() => On().SetLargeTitleDisplay(LargeTitleDisplayMode.Never))
- },
- new Button
- {
- Text = "LargeTitleDisplayMode.Always",
- Command = new Command(() => On().SetLargeTitleDisplay(LargeTitleDisplayMode.Always))
- },
- new Button
- {
- Text = "LargeTitleDisplayMode.Automatic -> next page will have same LargeTitleDisplayMode as this one",
- Command = new Command(async () =>{
- var page = new ContentPage { Title = "Page Title" };
- page.On().SetLargeTitleDisplay(LargeTitleDisplayMode.Automatic);
- await Navigation.PushAsync(page);
- } )
- },
- new Button
- {
- Text = "Tooggle UseLargeTitles on Navigation",
- Command = new Command( () =>{
- var navPage = (NavigationPage)Parent;
- navPage.On().SetPrefersLargeTitles(!navPage.On().PrefersLargeTitles());
- } )
- },
-
- new Button
- {
- Text = "UseLargeTitles on Navigation with safe Area",
- Command = new Command( () =>{
- var navPage = (NavigationPage)Parent;
- navPage.On().SetPrefersLargeTitles(true);
- var page = new ContentPage { Title = "New Title", BackgroundColor = Colors.Red };
- page.SafeAreaEdges = Microsoft.Maui.SafeAreaEdges.All;
- var listView = new ListView(ListViewCachingStrategy.RecycleElementAndDataTemplate)
- {
- HasUnevenRows = true,
- VerticalOptions = LayoutOptions.Fill
- };
-
- listView.ItemTemplate = new DataTemplate(()=>{
- var cell = new ViewCell();
- cell.View = new Label { Text ="Hello", FontSize = 30};
- return cell;
- });
- listView.ItemsSource = Enumerable.Range(1, 40);
- listView.Header = new Label { BackgroundColor = Colors.Pink , Text = "I'm a header, background is red"};
- listView.Footer = new Label { BackgroundColor = Colors.Yellow , Text = "I'm a footer, you should see no white below me"};
- page.Content = listView;
- navPage.PushAsync(page);
- } )
- },
- offscreenPageLimit
- }
- };
-
- var restoreButton = new Button { Text = "Back To Gallery" };
- restoreButton.Clicked += async (sender, args) => await Navigation.PopAsync();
- content.Children.Add(restoreButton);
-
- Content = content;
- }
- }
-}
diff --git a/src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/iOS/iOSLargeTitlePage.cs b/src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/iOS/iOSLargeTitlePage.cs
new file mode 100644
index 000000000000..22f70954eaae
--- /dev/null
+++ b/src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/iOS/iOSLargeTitlePage.cs
@@ -0,0 +1,228 @@
+using System;
+using System.Linq;
+using Microsoft.Maui;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Controls.PlatformConfiguration;
+using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
+using Microsoft.Maui.Graphics;
+
+namespace Maui.Controls.Sample.Pages;
+
+public partial class iOSLargeTitlePage : ContentPage
+{
+ public iOSLargeTitlePage()
+ {
+ Title = "Large Titles";
+
+ // Set the parent NavigationPage's BarTextColor to black when this page appears
+ Appearing += (s, e) =>
+ {
+ if (Parent is Microsoft.Maui.Controls.NavigationPage navPage)
+ {
+ navPage.BarTextColor = Colors.Black;
+ }
+ };
+
+ var scrollView = new Microsoft.Maui.Controls.ScrollView
+ {
+ Content = new StackLayout
+ {
+ Padding = new Thickness(10),
+ Spacing = 15,
+ Children =
+ {
+ // Display Mode Section
+ new Label
+ {
+ Text = "Display Mode",
+ FontSize = 20,
+ FontAttributes = FontAttributes.Bold,
+ Margin = new Thickness(0, 10, 0, 0)
+ },
+ new Button
+ {
+ Text = "Never - Hide large title",
+ Command = new Command(() => On().SetLargeTitleDisplay(LargeTitleDisplayMode.Never))
+ },
+ new Button
+ {
+ Text = "Always - Show large title",
+ Command = new Command(() => On().SetLargeTitleDisplay(LargeTitleDisplayMode.Always))
+ },
+ new Button
+ {
+ Text = "Automatic - Inherit from previous page",
+ Command = new Command(async () =>
+ {
+ var page = new ContentPage { Title = "Automatic Mode" };
+ page.On().SetLargeTitleDisplay(LargeTitleDisplayMode.Automatic);
+ await Navigation.PushAsync(page);
+ })
+ },
+
+ // Navigation Configuration Section
+ new BoxView { HeightRequest = 1, Color = Colors.Gray, Margin = new Thickness(0, 10) },
+ new Label
+ {
+ Text = "Navigation Configuration",
+ FontSize = 20,
+ FontAttributes = FontAttributes.Bold
+ },
+ new Button
+ {
+ Text = "Toggle PrefersLargeTitles on NavigationPage",
+ Command = new Command(async () =>
+ {
+ var navPage = (Microsoft.Maui.Controls.NavigationPage)Parent;
+ navPage.On().SetPrefersLargeTitles(!navPage.On().PrefersLargeTitles());
+
+ // Refresh the page to show the change
+ Navigation.InsertPageBefore(new iOSLargeTitlePage(), this);
+ await Navigation.PopAsync(false);
+ })
+ },
+
+ // Scrolling Behavior Section
+ new BoxView { HeightRequest = 1, Color = Colors.Gray, Margin = new Thickness(0, 10) },
+ new Label
+ {
+ Text = "Scrolling Behavior",
+ FontSize = 20,
+ FontAttributes = FontAttributes.Bold
+ },
+ new Button
+ {
+ Text = "ScrollView - Watch title collapse on scroll",
+ Command = new Command(async () =>
+ {
+ var navPage = (Microsoft.Maui.Controls.NavigationPage)Parent;
+ navPage.On().SetPrefersLargeTitles(true);
+
+ var page = new ContentPage { Title = "Scroll Me" };
+ page.On().SetLargeTitleDisplay(LargeTitleDisplayMode.Always);
+
+ var scrollContent = new StackLayout { Padding = 20, Spacing = 10 };
+ for (int i = 1; i <= 50; i++)
+ {
+ scrollContent.Children.Add(new Label
+ {
+ Text = $"Item {i} - Scroll to see the large title collapse",
+ FontSize = 16,
+ Padding = new Thickness(0, 10)
+ });
+ }
+
+ page.Content = new Microsoft.Maui.Controls.ScrollView { Content = scrollContent };
+ await Navigation.PushAsync(page);
+ })
+ },
+ new Button
+ {
+ Text = "CollectionView - Large title with SafeArea",
+ Command = new Command(async () =>
+ {
+ var navPage = (Microsoft.Maui.Controls.NavigationPage)Parent;
+ navPage.On().SetPrefersLargeTitles(true);
+
+ var page = new ContentPage { Title = "CollectionView Demo", BackgroundColor = Colors.LightBlue };
+ page.On().SetLargeTitleDisplay(LargeTitleDisplayMode.Always);
+ page.SafeAreaEdges = Microsoft.Maui.SafeAreaEdges.All;
+
+ var collectionView = new Microsoft.Maui.Controls.CollectionView
+ {
+ VerticalOptions = LayoutOptions.Fill
+ };
+
+ collectionView.ItemTemplate = new DataTemplate(() =>
+ {
+ var label = new Label
+ {
+ Text = "Scroll to see title collapse",
+ FontSize = 18,
+ Padding = 15
+ };
+ return label;
+ });
+
+ collectionView.ItemsSource = Enumerable.Range(1, 40);
+ collectionView.Header = new Label { BackgroundColor = Colors.Pink, Text = "Header", Padding = 10, FontSize = 16 };
+ collectionView.Footer = new Label { BackgroundColor = Colors.Yellow, Text = "Footer", Padding = 10, FontSize = 16 };
+
+ page.Content = collectionView;
+ await navPage.PushAsync(page);
+ })
+ },
+ new Button
+ {
+ Text = "Transparent NavBar - Content shows through",
+ Command = new Command(async () =>
+ {
+ var navPage = (Microsoft.Maui.Controls.NavigationPage)Parent;
+ navPage.On().SetPrefersLargeTitles(true);
+ var previousBarColor = navPage.BarBackgroundColor;
+ navPage.BarBackgroundColor = Colors.Transparent;
+
+ var page = new ContentPage
+ {
+ Title = "Transparent Bar",
+ // Create a gradient background that shows through the nav bar
+ Background = new LinearGradientBrush
+ {
+ StartPoint = new Point(0, 0),
+ EndPoint = new Point(0, 1),
+ GradientStops = new GradientStopCollection
+ {
+ new GradientStop { Color = Color.FromArgb("#667eea"), Offset = 0.0f },
+ new GradientStop { Color = Color.FromArgb("#764ba2"), Offset = 1.0f }
+ }
+ }
+ };
+ page.On().SetLargeTitleDisplay(LargeTitleDisplayMode.Always);
+
+ var scrollContent = new StackLayout { Padding = 20, Spacing = 15 };
+ scrollContent.Children.Add(new Label
+ {
+ Text = "Notice how the gradient background shows through the navigation bar area. This follows Apple's HIG recommendation.",
+ FontSize = 16,
+ TextColor = Colors.White,
+ Margin = new Thickness(0, 20)
+ });
+
+ for (int i = 1; i <= 30; i++)
+ {
+ scrollContent.Children.Add(new Frame
+ {
+ BackgroundColor = Colors.White.WithAlpha(0.9f),
+ CornerRadius = 10,
+ Padding = 15,
+ Content = new Label
+ {
+ Text = $"Card {i} - Scroll to see the effect",
+ FontSize = 16
+ }
+ });
+ }
+
+ page.Content = new Microsoft.Maui.Controls.ScrollView { Content = scrollContent };
+ await Navigation.PushAsync(page);
+
+ // Restore opaque background when returning
+ page.Disappearing += (s, e) => navPage.BarBackgroundColor = previousBarColor;
+ })
+ },
+
+ // Back Button
+ new BoxView { HeightRequest = 1, Color = Colors.Gray, Margin = new Thickness(0, 10) },
+ new Button
+ {
+ Text = "← Back To Gallery",
+ Margin = new Thickness(0, 10, 0, 20),
+ Command = new Command(async () => await Navigation.PopAsync())
+ }
+ }
+ }
+ };
+
+ Content = scrollView;
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/iOS/iOSLargeTitlePage.xaml b/src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/iOS/iOSLargeTitlePage.xaml
deleted file mode 100644
index 20cb2334a915..000000000000
--- a/src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/iOS/iOSLargeTitlePage.xaml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/iOS/iOSLargeTitlePage.xaml.cs b/src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/iOS/iOSLargeTitlePage.xaml.cs
deleted file mode 100644
index 92cad19ebad7..000000000000
--- a/src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/iOS/iOSLargeTitlePage.xaml.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using System;
-using System.Windows.Input;
-using Microsoft.Maui.Controls;
-using Microsoft.Maui.Controls.PlatformConfiguration;
-using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
-
-namespace Maui.Controls.Sample.Pages
-{
- public partial class iOSLargeTitlePage : ContentPage
- {
- public iOSLargeTitlePage()
- {
- InitializeComponent();
- }
-
- void OnButtonClicked(object sender, EventArgs e)
- {
- switch (On().LargeTitleDisplay())
- {
- case LargeTitleDisplayMode.Always:
- On().SetLargeTitleDisplay(LargeTitleDisplayMode.Automatic);
- break;
- case LargeTitleDisplayMode.Automatic:
- On().SetLargeTitleDisplay(LargeTitleDisplayMode.Never);
- break;
- case LargeTitleDisplayMode.Never:
- On().SetLargeTitleDisplay(LargeTitleDisplayMode.Always);
- break;
- }
- }
-
- void OnReturnButtonClicked(object sender, EventArgs e)
- {
- Navigation.PopAsync();
- }
- }
-}
diff --git a/src/Controls/samples/Controls.Sample/ViewModels/OthersViewModel.cs b/src/Controls/samples/Controls.Sample/ViewModels/OthersViewModel.cs
index 9c2798e06a94..aec7338e9b94 100644
--- a/src/Controls/samples/Controls.Sample/ViewModels/OthersViewModel.cs
+++ b/src/Controls/samples/Controls.Sample/ViewModels/OthersViewModel.cs
@@ -12,9 +12,6 @@ protected override IEnumerable CreateItems() => new[]
new SectionModel(typeof(GraphicsViewPage), "GraphicsView",
"Allow to draw directly in a Canvas. You can combine a canvas and native Views on the same page."),
- new SectionModel(typeof(LargeTitlesPageiOS), "Large Titles - iOS",
- "This iOS platform-specific is used to display the page title as a large title on the navigation bar of a NavigationPage, for devices that use iOS 11 or greater."),
-
new SectionModel(typeof(StyleSheetsPage), "StyleSheets",
"Demonstrates the usage of CSS in XAML."),
diff --git a/src/Controls/src/Build.Tasks/XmlTypeExtensions.cs b/src/Controls/src/Build.Tasks/XmlTypeExtensions.cs
index 3d894400c417..e6c52e656e67 100644
--- a/src/Controls/src/Build.Tasks/XmlTypeExtensions.cs
+++ b/src/Controls/src/Build.Tasks/XmlTypeExtensions.cs
@@ -111,7 +111,14 @@ public static bool TryGetTypeReference(this XmlType xmlType, XamlCache cache, Mo
string typeName = typeInfo.typeName.Replace('+', '/'); //Nested types
var type = module.GetTypeDefinition(cache, (typeInfo.assemblyName, typeInfo.clrNamespace, typeName));
if (type is not null && type.IsPublicOrVisibleInternal(module))
+ {
+ // Skip static classes found via the "Extension" suffix expansion when they shadow
+ // the exact name (e.g. MyEnumExtension shadowing MyEnum). Static types cannot
+ // implement IMarkupExtension, so they are never valid markup extensions. (#34021)
+ if (typeInfo.typeName != xmlType.Name && type.IsAbstract && type.IsSealed)
+ return null;
return type;
+ }
return null;
}, expandToExtension: expandToExtension);
diff --git a/src/Controls/src/Core/BindingExpression.cs b/src/Controls/src/Core/BindingExpression.cs
index 75ee9aaec0fc..fc83ec7fdefa 100644
--- a/src/Controls/src/Core/BindingExpression.cs
+++ b/src/Controls/src/Core/BindingExpression.cs
@@ -68,8 +68,11 @@ internal void Apply(object sourceObject, BindableObject target, BindableProperty
{
if (Binding is Binding { Source: var source, DataType: Type dataType })
{
- // Do not check type mismatch if this is a binding with Source and compilation of bindings with Source is disabled
- bool skipTypeMismatchCheck = source is not null && !RuntimeFeature.IsXamlCBindingWithSourceCompilationEnabled;
+ // Skip the type-mismatch check when an explicit concrete Source is provided (e.g. Source={x:Reference}).
+ // In a DataTemplate, x:DataType describes the item context - not the type of the referenced element.
+ // RelativeBindingSource is excluded: a mismatch against its source type must still be reported.
+ bool skipTypeMismatchCheck = (source is not null && source is not RelativeBindingSource)
+ || !RuntimeFeature.IsXamlCBindingWithSourceCompilationEnabled;
if (!skipTypeMismatchCheck)
{
if (sourceObject != null && !dataType.IsAssignableFrom(sourceObject.GetType()))
diff --git a/src/Controls/src/Core/Button/Button.iOS.cs b/src/Controls/src/Core/Button/Button.iOS.cs
index c710d5a2a8de..4e9f1fa1f067 100644
--- a/src/Controls/src/Core/Button/Button.iOS.cs
+++ b/src/Controls/src/Core/Button/Button.iOS.cs
@@ -204,10 +204,19 @@ void LayoutButton(UIButton platformButton, Button button, Rect size)
// Which makes the later math easier to follow
if (layout.Position == ButtonContentLayout.ImagePosition.Left || layout.Position == ButtonContentLayout.ImagePosition.Right)
{
- imageInsets.Left += titleWidth / 2;
- imageInsets.Right -= titleWidth / 2;
- titleInsets.Left -= imageWidth / 2;
- titleInsets.Right += imageWidth / 2;
+ // In RTL mode, physical Left/Right offsets must be reversed.
+ // Use EffectiveFlowDirection to handle both explicit and inherited RTL.
+ nfloat dir = ((IVisualElementController)button).EffectiveFlowDirection.IsRightToLeft() ? -1 : 1;
+
+ imageInsets.Left += dir * (titleWidth / 2);
+ imageInsets.Right -= dir * (titleWidth / 2);
+ titleInsets.Left -= dir * (imageWidth / 2);
+ titleInsets.Right += dir * (imageWidth / 2);
+
+ imageInsets.Left -= dir * ((titleWidth / 2) + sharedSpacing);
+ imageInsets.Right += dir * ((titleWidth / 2) + sharedSpacing);
+ titleInsets.Left += dir * ((imageWidth / 2) + sharedSpacing);
+ titleInsets.Right -= dir * ((imageWidth / 2) + sharedSpacing);
}
if (layout.Position == ButtonContentLayout.ImagePosition.Top)
@@ -236,23 +245,6 @@ void LayoutButton(UIButton platformButton, Button button, Rect size)
titleInsets.Right += (nfloat)padding.Right;
}
- else if (layout.Position == ButtonContentLayout.ImagePosition.Left)
- {
- imageInsets.Left -= (titleWidth / 2) + sharedSpacing;
- imageInsets.Right += (titleWidth / 2) + sharedSpacing;
-
- titleInsets.Left += (imageWidth / 2) + sharedSpacing;
- titleInsets.Right -= (imageWidth / 2) + sharedSpacing;
-
- }
- else if (layout.Position == ButtonContentLayout.ImagePosition.Right)
- {
- imageInsets.Left += (titleWidth / 2) + sharedSpacing;
- imageInsets.Right -= (titleWidth / 2) + sharedSpacing;
-
- titleInsets.Left -= (imageWidth / 2) + sharedSpacing;
- titleInsets.Right += (imageWidth / 2) + sharedSpacing;
- }
}
#pragma warning disable CA1416, CA1422
@@ -409,7 +401,10 @@ bool ResizeImageIfNecessary(UIButton platformButton, Button button, UIImage imag
return false;
}
- image = image?.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
+ // Preserve AlwaysTemplate if explicitly set (e.g., by a tint behavior),
+ // otherwise use AlwaysOriginal to prevent unwanted default tinting
+ if (image?.RenderingMode != UIImageRenderingMode.AlwaysTemplate)
+ image = image?.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
platformButton.SetImage(image, UIControlState.Normal);
diff --git a/src/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cs
index 3c32df43d13e..9ec7e8447a56 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cs
@@ -399,7 +399,7 @@ void LayoutChildren(bool animated)
if (IsRTL && !FlyoutOverlapsDetailsInPopoverMode)
{
- flyoutFrame.X = (int)(flyoutFrame.Width * .25);
+ flyoutFrame.X = (int)(frame.Width - flyoutFrame.Width);
}
var detailsFrame = frame;
diff --git a/src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs
index f7928950539e..955508c8955a 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs
@@ -242,11 +242,28 @@ public override void ViewDidLoad()
Element.PropertyChanged += HandlePropertyChanged;
+ InteractivePopGestureRecognizer.Delegate = new GestureDelegate(() => _uiRequestedPop = true);
+
UpdateToolBarVisible();
UpdateBackgroundColor();
Current = navPage.CurrentPage;
}
+ class GestureDelegate : UIGestureRecognizerDelegate
+ {
+ readonly Func _shouldPop;
+
+ public GestureDelegate(Func shouldPop)
+ {
+ _shouldPop = shouldPop;
+ }
+
+ public override bool ShouldBegin(UIGestureRecognizer recognizer)
+ {
+ return _shouldPop();
+ }
+ }
+
protected override void Dispose(bool disposing)
{
if (_disposed)
@@ -265,6 +282,7 @@ protected override void Dispose(bool disposing)
_secondaryToolbar.RemoveFromSuperview();
_secondaryToolbar.Dispose();
_secondaryToolbar = null;
+ InteractivePopGestureRecognizer.Delegate = null;
if (_currentBarBackgroundBrush is GradientBrush gb)
gb.InvalidateGradientBrushRequested -= OnBarBackgroundChanged;
@@ -332,6 +350,7 @@ protected virtual async Task OnPopToRoot(Page page, bool animated)
}
UpdateToolBarVisible();
+ UpdateFlyoutMenuButton();
return success;
}
@@ -363,6 +382,7 @@ protected virtual async Task OnPopViewAsync(Page page, bool animated)
poppedViewController?.Dispose();
UpdateToolBarVisible();
+ UpdateFlyoutMenuButton();
return actuallyRemoved;
}
@@ -441,6 +461,12 @@ void FindParentFlyoutPage()
_parentFlyoutPage = flyoutDetail;
}
+ void UpdateFlyoutMenuButton(Page pageBeingRemoved = null)
+ {
+ var parentingViewController = GetParentingViewController();
+ parentingViewController?.UpdateLeftBarButtonItem(pageBeingRemoved);
+ }
+
TaskCompletionSource _pendingNavigationRequest;
ActionDisposable _removeLifecycleEvents;
@@ -470,6 +496,15 @@ Task GetAppearedOrDisappearedTask(Page page)
_ = page.ToPlatform(MauiContext);
var renderer = (IPlatformViewHandler)page.Handler;
+ // renderer or ViewController can be null if a rapid push/pop causes the handler
+ // to be torn down before this navigation completes (mirrors fix for ShellSectionRenderer #32426)
+ if (renderer?.ViewController == null)
+ {
+ var pendingTask = _pendingNavigationRequest.Task;
+ CompletePendingNavigation(false);
+ return pendingTask;
+ }
+
var parentViewController = renderer.ViewController.ParentViewController as ParentingViewController;
if (parentViewController == null)
throw new NotSupportedException("ParentingViewController parent could not be found. Please file a bug.");
@@ -659,7 +694,14 @@ void InsertPageBefore(Page page, Page before)
var pageContainer = CreateViewControllerForPage(page);
var target = nvh.ViewController.ParentViewController;
- ViewControllers = ViewControllers.Insert(ViewControllers.IndexOf(target), pageContainer);
+ var index = ViewControllers.IndexOf(target);
+ ViewControllers = ViewControllers.Insert(index, pageContainer);
+
+ // Update the flyout icon when the root page changes
+ if (index == 0)
+ {
+ (target as ParentingViewController)?.UpdateLeftBarButtonItem();
+ }
}
void OnInsertPageBeforeRequested(object sender, NavigationRequestedEventArgs e)
@@ -721,8 +763,7 @@ void RemovePage(Page page)
ViewControllers = _removeControllers;
}
target.Dispose();
- var parentingViewController = GetParentingViewController();
- parentingViewController?.UpdateLeftBarButtonItem(page);
+ UpdateFlyoutMenuButton(page);
}
void RemoveViewControllers(bool animated)
@@ -947,20 +988,56 @@ void UpdateBarTextColor()
? UINavigationBar.Appearance.TintColor
: iconColor.ToPlatform();
- if ((OperatingSystem.IsIOSVersionAtLeast(26) || OperatingSystem.IsMacCatalystVersionAtLeast(26)) && NavigationBar.TintColor is not null)
+ // iOS 26+ Liquid Glass ignores TintColor for the back button; apply via appearance instead.
+ if (OperatingSystem.IsIOSVersionAtLeast(26) || OperatingSystem.IsMacCatalystVersionAtLeast(26))
{
- if (VisibleViewController?.NavigationItem?.RightBarButtonItems is UIBarButtonItem[] items)
+ if (NavigationBar.TintColor is not null && VisibleViewController?.NavigationItem?.RightBarButtonItems is UIBarButtonItem[] items)
{
foreach (var item in items)
{
item.TintColor = NavigationBar.TintColor;
}
}
+
+ var useCustomColor = iconColor != null && NavPage.OnThisPlatform().GetStatusBarTextColorMode() != StatusBarTextColorMode.DoNotAdjust;
+ if (useCustomColor)
+ {
+ var backColor = iconColor.ToPlatform();
+ var colorAttributes = NSDictionary.FromObjectsAndKeys(
+ new NSObject[] { backColor }, new NSString[] { UIStringAttributeKey.ForegroundColor });
+ var appearance = new UIBarButtonItemAppearance(UIBarButtonItemStyle.Plain);
+ appearance.Normal.TitleTextAttributes = colorAttributes;
+ appearance.Highlighted.TitleTextAttributes = colorAttributes;
+ NavigationBar.CompactAppearance.BackButtonAppearance = appearance;
+ NavigationBar.StandardAppearance.BackButtonAppearance = appearance;
+ NavigationBar.ScrollEdgeAppearance.BackButtonAppearance = appearance;
+
+ var backimage = UIImage.GetSystemImage("chevron.backward");
+ if (backimage is not null)
+ {
+ var tinted = backimage.ApplyTintColor(backColor).ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
+ NavigationBar.BackIndicatorImage = tinted;
+ NavigationBar.BackIndicatorTransitionMaskImage = tinted;
+ }
+ }
+ else
+ {
+ NavigationBar.CompactAppearance.BackButtonAppearance = null;
+ NavigationBar.StandardAppearance.BackButtonAppearance = null;
+ NavigationBar.ScrollEdgeAppearance.BackButtonAppearance = null;
+ NavigationBar.BackIndicatorImage = null;
+ NavigationBar.BackIndicatorTransitionMaskImage = null;
+ }
}
}
void SetStatusBarStyle()
{
+ if (NavPage is null)
+ {
+ return;
+ }
+
var barTextColor = NavPage.BarTextColor;
var statusBarColorMode = NavPage.OnThisPlatform().GetStatusBarTextColorMode();
@@ -1026,6 +1103,8 @@ async Task UpdateFormsInnerNavigation(Page pageBeingRemoved)
if (Element.Navigation.NavigationStack.Contains(pageBeingRemoved))
{
await (NavPage as INavigationPageController)?.RemoveAsyncInner(pageBeingRemoved, false, true);
+ UpdateFlyoutMenuButton();
+
if (_uiRequestedPop)
{
NavPage?.SendNavigatedFromHandler(pageBeingRemoved, NavigationType.Pop);
@@ -1424,8 +1503,20 @@ public override void ViewDidLoad()
{
base.ViewDidLoad();
- _tracker.Target = Child;
- _tracker.AdditionalTargets = Child.GetParentPages();
+ var parentPages = Child.GetParentPages();
+ var flyoutPageWithToolbarItems = FindFlyoutPageWithToolbarItems(parentPages);
+ if (flyoutPageWithToolbarItems != null)
+ {
+ _tracker.Target = flyoutPageWithToolbarItems.Flyout;
+ var additionalTargets = new List(parentPages);
+ additionalTargets.Add(Child);
+ _tracker.AdditionalTargets = additionalTargets;
+ }
+ else
+ {
+ _tracker.Target = Child;
+ _tracker.AdditionalTargets = parentPages;
+ }
_tracker.CollectionChanged += TrackerOnCollectionChanged;
UpdateToolbarItems();
@@ -1676,8 +1767,7 @@ internal void UpdateLeftBarButtonItem(Page pageBeingRemoved = null)
return;
var currentChild = this.Child;
- var firstPage = n.NavPageController.Pages.FirstOrDefault();
-
+ var firstPage = (n.ViewControllers.FirstOrDefault() as ParentingViewController)?.Child;
if (n._parentFlyoutPage == null)
return;
@@ -1745,6 +1835,18 @@ internal void UpdateTitleArea(Page page)
}
}
+ FlyoutPage FindFlyoutPageWithToolbarItems(IEnumerable parentPages)
+ {
+ foreach (var page in parentPages)
+ {
+ if (page is FlyoutPage flyoutPage && flyoutPage.Flyout?.ToolbarItems?.Count > 0)
+ {
+ return flyoutPage;
+ }
+ }
+ return null;
+ }
+
///
/// Creates a Container with the appropriate configuration for the current iOS version.
/// For iOS 26+, uses autoresizing masks and sets frame from navigation bar to prevent layout issues.
@@ -2330,10 +2432,34 @@ public override CGRect Frame
value.Width = (value.X - xSpace) + value.Width;
value.X = xSpace;
}
+
+ if (_child?.VirtualView is IView view)
+ {
+ var margin = view.Margin;
+
+ // Apply margins AFTER back button spacing calculations
+ // Margins push the view inward to keep it within the nav bar bounds
+ var newWidth = value.Width - (nfloat)(margin.Left + margin.Right);
+ if (newWidth < 0)
+ newWidth = 0;
+
+ value = new RectangleF(
+ value.X + (nfloat)margin.Left,
+ value.Y + (nfloat)margin.Top,
+ newWidth,
+ value.Height
+ );
+ }
}
- ;
value.Height = ToolbarHeight;
+
+ // Reduce height by vertical margins so the view stays within the nav bar
+ if (_child?.VirtualView is IView marginView)
+ {
+ var verticalMargin = (nfloat)(marginView.Margin.Top + marginView.Margin.Bottom);
+ value.Height = (nfloat)Math.Max(0, value.Height - verticalMargin);
+ }
}
base.Frame = value;
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellBottomNavViewAppearanceTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellBottomNavViewAppearanceTracker.cs
index 1f9d682d41d9..b7718728d990 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellBottomNavViewAppearanceTracker.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellBottomNavViewAppearanceTracker.cs
@@ -131,7 +131,7 @@ static ColorStateList MakeDefaultColorStateList(Context context)
return null;
var baseCSL = AppCompatResources.GetColorStateList(context, mTypedValue.ResourceId);
- var colorPrimary = (ShellRenderer.IsDarkTheme) ? AColor.White : ShellRenderer.DefaultBackgroundColor.ToPlatform();
+ var colorPrimary = (ShellRenderer.IsDarkTheme) ? AColor.White : RuntimeFeature.IsMaterial3Enabled ? Color.FromArgb("#625B71").ToPlatform() : ShellRenderer.DefaultBackgroundColor.ToPlatform();
int defaultColor = baseCSL.DefaultColor;
var disabledcolor = baseCSL.GetColorForState(new[] { -R.Attribute.StateEnabled }, AColor.Gray);
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutTemplatedContentRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutTemplatedContentRenderer.cs
index 75c19f1ffb26..d2311ce85709 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutTemplatedContentRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutTemplatedContentRenderer.cs
@@ -53,7 +53,7 @@ public class ShellFlyoutTemplatedContentRenderer : Java.Lang.Object, IShellFlyou
protected IShellContext ShellContext => _shellContext;
protected AView FooterView => _footerView?.PlatformView;
protected AView View => _rootView;
- WindowsListener _windowsListener;
+ ShellFlyoutWindowInsetListener _shellFlyoutListener;
public ShellFlyoutTemplatedContentRenderer(IShellContext shellContext)
@@ -85,7 +85,7 @@ void OnFlyoutStateChanging(object sender, AndroidX.DrawerLayout.Widget.DrawerLay
// - Keep this minimal.
// - Will be replaced by the planned comprehensive window insets solution.
// - Do not extend; add new logic to the forthcoming implementation instead.
- internal class WindowsListener : MauiWindowInsetListener, IOnApplyWindowInsetsListener
+ internal class ShellFlyoutWindowInsetListener : MauiWindowInsetListener
{
private WeakReference _bgImageRef;
private WeakReference _flyoutViewRef;
@@ -120,7 +120,7 @@ public AView FooterView
}
}
- public WindowsListener(ImageView bgImage)
+ public ShellFlyoutWindowInsetListener(ImageView bgImage)
{
_bgImageRef = new WeakReference(bgImage);
}
@@ -207,8 +207,8 @@ protected virtual void LoadView(IShellContext shellContext)
LayoutParameters = new LP(coordinator.LayoutParameters)
};
- _windowsListener = new WindowsListener(_bgImage);
- MauiWindowInsetListener.SetupViewWithLocalListener(coordinator, _windowsListener);
+ _shellFlyoutListener = new ShellFlyoutWindowInsetListener(_bgImage);
+ MauiWindowInsetListener.SetupViewWithLocalListener(coordinator, _shellFlyoutListener);
UpdateFlyoutHeaderBehavior();
_shellContext.Shell.PropertyChanged += OnShellPropertyChanged;
@@ -304,7 +304,7 @@ protected virtual void UpdateFlyoutContent()
}
_flyoutContentView = CreateFlyoutContent(_rootView);
- _windowsListener.FlyoutView = _flyoutContentView;
+ _shellFlyoutListener.FlyoutView = _flyoutContentView;
if (_flyoutContentView == null)
return;
@@ -421,7 +421,7 @@ protected virtual void UpdateFlyoutFooter()
var oldFooterView = _footerView;
_rootView.RemoveView(_footerView);
_footerView = null;
- _windowsListener.FooterView = null;
+ _shellFlyoutListener.FooterView = null;
oldFooterView.View = null;
}
@@ -441,7 +441,7 @@ protected virtual void UpdateFlyoutFooter()
MatchWidth = true
};
- _windowsListener.FooterView = _footerView;
+ _shellFlyoutListener.FooterView = _footerView;
var footerViewLP = new CoordinatorLayout.LayoutParams(0, 0)
{
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs
index 650b3fabb113..d0c1dc495539 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs
@@ -13,6 +13,7 @@
using Google.Android.Material.Navigation;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
+using Microsoft.Maui.Controls.Handlers.Compatibility;
using Microsoft.Maui.Graphics;
using AColor = Android.Graphics.Color;
using AView = Android.Views.View;
@@ -141,6 +142,11 @@ protected virtual void SetAppearance(ShellAppearance appearance)
return;
}
+ // Apply background color from appearance, fallback to default if unavailable
+ if (_bottomView.Background is ColorDrawable background && appearance is IShellAppearanceElement appearanceElement)
+ {
+ background.Color = appearanceElement.EffectiveTabBarBackgroundColor?.ToPlatform() ?? ShellRenderer.DefaultBottomNavigationViewBackgroundColor.ToPlatform();
+ }
_appearanceSet = true;
_appearanceTracker.SetAppearance(_bottomView, appearance);
}
@@ -456,7 +462,7 @@ _updateMenuItemSource is not null &&
}
}
- if (DisplayedPage is null)
+ if (DisplayedPage is null && !_menuSetup)
return;
if (ShellItemController.ShowTabs)
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellPageContainer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellPageContainer.cs
index c58fcf794e9f..408eec70fc87 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellPageContainer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellPageContainer.cs
@@ -14,8 +14,6 @@ internal class ShellPageContainer : ViewGroup
{
static int? DarkBackground;
static int? LightBackground;
-
-
public IViewHandler Child { get; set; }
public bool IsInFragment { get; set; }
@@ -24,13 +22,13 @@ public ShellPageContainer(Context context, IPlatformViewHandler child, bool inFr
{
Child = child;
IsInFragment = inFragment;
- if (child.VirtualView.Background == null)
+ if (child.VirtualView.Background is null)
{
- int color;
- if (ShellRenderer.IsDarkTheme)
- color = DarkBackground ??= ContextCompat.GetColor(context, AColorRes.BackgroundDark);
- else
- color = LightBackground ??= ContextCompat.GetColor(context, AColorRes.BackgroundLight);
+ bool isDark = ShellRenderer.IsDarkTheme;
+
+ int color = RuntimeFeature.IsMaterial3Enabled
+ ? GetMaterial3Background(context)
+ : GetResourceBackground(context, isDark);
child.PlatformView.SetBackgroundColor(new AColor(color));
}
@@ -38,6 +36,27 @@ public ShellPageContainer(Context context, IPlatformViewHandler child, bool inFr
AddView(child.PlatformView);
}
+ int GetMaterial3Background(Context context)
+ {
+ // Material 3 colorSurface automatically adapts to light/dark theme
+ // The theme resolution happens in GetThemeAttrColor based on the active theme
+ return ContextExtensions.GetThemeAttrColor(context, Resource.Attribute.colorSurface);
+ }
+
+ int GetResourceBackground(Context context, bool isDark)
+ {
+ int color;
+ if (isDark)
+ {
+ color = DarkBackground ??= ContextCompat.GetColor(context, AColorRes.BackgroundDark);
+ }
+ else
+ {
+ color = LightBackground ??= ContextCompat.GetColor(context, AColorRes.BackgroundLight);
+ }
+ return color;
+ }
+
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
var width = r - l;
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellRenderer.cs
index 079585280840..c54aa140c971 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellRenderer.cs
@@ -92,14 +92,12 @@ void IAppearanceObserver.OnAppearanceChanged(ShellAppearance appearance)
// These are the primary colors in our styles.xml file
- public static Color DefaultBackgroundColor => ResolveThemeColor(Color.FromArgb("#2c3e50"), Color.FromArgb("#1B3147"));
-
- public static readonly Color DefaultForegroundColor = Colors.White;
- public static readonly Color DefaultTitleColor = Colors.White;
+ public static Color DefaultBackgroundColor => ResolveThemeColor(RuntimeFeature.IsMaterial3Enabled ? Color.FromArgb("#FEF7FF") : Color.FromArgb("#2c3e50"), RuntimeFeature.IsMaterial3Enabled ? Color.FromArgb("#141218") : Color.FromArgb("#1B3147"));
+ public static Color DefaultForegroundColor => ResolveThemeColor(RuntimeFeature.IsMaterial3Enabled ? Color.FromArgb("#1D1B20") : Colors.Black, RuntimeFeature.IsMaterial3Enabled ? Color.FromArgb("#E6E0E9") : Colors.White);
+ public static Color DefaultTitleColor => ResolveThemeColor(RuntimeFeature.IsMaterial3Enabled ? Color.FromArgb("#1D1B20") : Colors.White, RuntimeFeature.IsMaterial3Enabled ? Color.FromArgb("#E6E0E9") : Colors.White);
public static readonly Color DefaultUnselectedColor = Color.FromRgba(255, 255, 255, 180);
- internal static Color DefaultBottomNavigationViewBackgroundColor => ResolveThemeColor(Colors.White, Color.FromArgb("#1B3147"));
-
- internal static bool IsDarkTheme => (Application.Current?.RequestedTheme == AppTheme.Dark);
+ internal static Color DefaultBottomNavigationViewBackgroundColor => ResolveThemeColor(RuntimeFeature.IsMaterial3Enabled ? Color.FromArgb("#F3EDF7") : Colors.White, RuntimeFeature.IsMaterial3Enabled ? Color.FromArgb("#1D1B20") : Color.FromArgb("#1B3147"));
+ internal static bool IsDarkTheme => Application.Current?.RequestedTheme == AppTheme.Dark;
static Color ResolveThemeColor(Color light, Color dark)
{
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellSectionRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellSectionRenderer.cs
index a3f4513ccaf2..5bb034f40632 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellSectionRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellSectionRenderer.cs
@@ -84,6 +84,7 @@ void AView.IOnClickListener.OnClick(AView v)
protected IShellContext ShellContext => _shellContext;
IShellSectionController SectionController => (IShellSectionController)ShellSection;
IMauiContext MauiContext => ShellContext.Shell.Handler.MauiContext;
+ Toolbar _shellToolbar;
public ShellSectionRenderer(IShellContext shellContext)
{
@@ -108,9 +109,9 @@ public override AView OnCreateView(LayoutInflater inflater, ViewGroup container,
int actionBarHeight = context.GetActionBarHeight();
- var shellToolbar = new Toolbar(shellSection);
- ShellToolbarTracker.ApplyToolbarChanges(_shellContext.Shell.Toolbar, shellToolbar);
- _toolbar = (AToolbar)shellToolbar.ToPlatform(_shellContext.Shell.FindMauiContext());
+ _shellToolbar = new Toolbar(shellSection);
+ ShellToolbarTracker.ApplyToolbarChanges(_shellContext.Shell.Toolbar, _shellToolbar);
+ _toolbar = (AToolbar)_shellToolbar.ToPlatform(_shellContext.Shell.FindMauiContext());
appbar.AddView(_toolbar);
_tablayout = PlatformInterop.CreateShellTabLayout(context, appbar, actionBarHeight);
@@ -135,7 +136,7 @@ public override AView OnCreateView(LayoutInflater inflater, ViewGroup container,
}
_toolbarTracker = _shellContext.CreateTrackerForToolbar(_toolbar);
- _toolbarTracker.SetToolbar(shellToolbar);
+ _toolbarTracker.SetToolbar(_shellToolbar);
_toolbarTracker.Page = currentPage;
_viewPager.CurrentItem = currentIndex;
@@ -228,6 +229,7 @@ void Destroy()
_toolbarTracker = null;
_tablayout = null;
_toolbar = null;
+ _shellToolbar = null;
_viewPager = null;
_rootView = null;
@@ -240,6 +242,17 @@ public override void OnDestroy()
Destroy();
base.OnDestroy();
}
+
+ public override void OnHiddenChanged(bool hidden)
+ {
+ base.OnHiddenChanged(hidden);
+
+ if (!hidden && _shellToolbar?.Handler != null)
+ {
+ _shellToolbar.Handler.UpdateValue(nameof(Toolbar.TitleView));
+ }
+ }
+
protected override void Dispose(bool disposing)
{
if (_disposed)
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarAppearanceTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarAppearanceTracker.cs
index 597a57ddb6ea..1c466efc50ba 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarAppearanceTracker.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarAppearanceTracker.cs
@@ -45,7 +45,6 @@ protected virtual void SetColors(AToolbar toolbar, IShellToolbarTracker toolbarT
shellToolbar.BarTextColor = title ?? ShellRenderer.DefaultTitleColor;
shellToolbar.BarBackground = new SolidColorBrush(background ?? ShellRenderer.DefaultBackgroundColor);
shellToolbar.IconColor = foreground ?? ShellRenderer.DefaultForegroundColor;
- toolbarTracker.TintColor = foreground ?? ShellRenderer.DefaultForegroundColor;
}
#region IDisposable
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarTracker.cs
index feda1cdbb481..e403a8e4c08e 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarTracker.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarTracker.cs
@@ -297,7 +297,13 @@ void OnShellNavigated(object sender, ShellNavigatedEventArgs e)
void HandleShellPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.Is(Shell.FlyoutIconProperty))
+ {
+ UpdateLeftBarButtonItem();
+ }
+ else if (e.Is(Shell.ForegroundColorProperty))
+ {
UpdateLeftBarButtonItem();
+ }
}
BackButtonBehavior _backButtonBehavior = null;
@@ -326,6 +332,10 @@ protected virtual void OnPagePropertyChanged(object sender, PropertyChangedEvent
}
else if (e.PropertyName == Shell.TitleViewProperty.PropertyName)
UpdateTitleView();
+ else if (e.PropertyName == Shell.ForegroundColorProperty.PropertyName)
+ {
+ UpdateLeftBarButtonItem();
+ }
}
void OnBackButtonBehaviorChanged(object sender, PropertyChangedEventArgs e)
@@ -423,7 +433,7 @@ protected virtual async void UpdateLeftBarButtonItem(Context context, AToolbar t
DrawerArrowDrawable icon = null;
bool defaultDrawerArrowDrawable = false;
- var tintColor = Colors.White;
+ var tintColor = Shell.GetForegroundColor(page) ?? Shell.GetForegroundColor(_shell);
if (TintColor != null)
tintColor = TintColor;
@@ -471,23 +481,23 @@ protected virtual async void UpdateLeftBarButtonItem(Context context, AToolbar t
icon = _flyoutIconDrawerDrawable;
}
- if (icon == null && (_flyoutBehavior == FlyoutBehavior.Flyout || CanNavigateBack))
+ if (icon == null && (_flyoutBehavior == FlyoutBehavior.Flyout || (CanNavigateBack && backButtonVisible)))
{
_drawerArrowDrawable ??= new DrawerArrowDrawable(context.GetThemedContext());
icon = _drawerArrowDrawable;
defaultDrawerArrowDrawable = true;
}
- icon?.Progress = (CanNavigateBack) ? 1 : 0;
+ icon?.Progress = (CanNavigateBack && backButtonVisible) ? 1 : 0;
- if (command != null || CanNavigateBack)
+ if (command != null || (CanNavigateBack && backButtonVisible))
{
_drawerToggle.DrawerIndicatorEnabled = false;
if (backButtonVisibleFromBehavior && (backButtonVisible || !defaultDrawerArrowDrawable))
toolbar.NavigationIcon = icon;
}
- else if (_flyoutBehavior == FlyoutBehavior.Flyout || !defaultDrawerArrowDrawable)
+ else if (_flyoutBehavior == FlyoutBehavior.Flyout || (!defaultDrawerArrowDrawable && backButtonVisible))
{
bool drawerEnabled = isEnabled && icon != null;
_drawerToggle.DrawerIndicatorEnabled = drawerEnabled;
@@ -503,6 +513,7 @@ protected virtual async void UpdateLeftBarButtonItem(Context context, AToolbar t
else
{
_drawerToggle.DrawerIndicatorEnabled = false;
+ toolbar.NavigationIcon = null;
}
_drawerToggle.SyncState();
@@ -557,7 +568,7 @@ protected virtual void UpdateToolbarIconAccessibilityText(AToolbar toolbar, Shel
else if (image == null ||
toolbar.SetNavigationContentDescription(image) == null)
{
- if (CanNavigateBack)
+ if (CanNavigateBack && _toolbar?.BackButtonVisible == true)
toolbar.SetNavigationContentDescription(Resource.String.nav_app_bar_navigate_up_description);
else
toolbar.SetNavigationContentDescription(Resource.String.nav_app_bar_open_drawer_description);
@@ -600,13 +611,22 @@ void UpdateNavBarHasShadow(Page page)
{
if (page == null || !_appBar.IsAlive())
return;
-
if (Shell.GetNavBarHasShadow(page))
{
- if (_appBarElevation <= 0)
- _appBarElevation = _appBar.Context.ToPixels(4);
+ if (RuntimeFeature.IsMaterial3Enabled)
+ {
+ // AppBar elevation is set 0f to match Material 3 AppBar behavior.
+ _appBar.SetElevation(0f);
+ }
+ else
+ {
+ if (_appBarElevation <= 0)
+ {
+ _appBarElevation = _appBar.Context.ToPixels(4);
+ }
- _appBar.SetElevation(_appBarElevation);
+ _appBar.SetElevation(_appBarElevation);
+ }
}
else
{
@@ -648,10 +668,10 @@ protected virtual void UpdateToolbarItems(AToolbar toolbar, Page page)
{
var menu = toolbar.Menu;
SearchHandler = Shell.GetSearchHandler(page);
- if (SearchHandler != null && SearchHandler.SearchBoxVisibility != SearchBoxVisibility.Hidden)
+ if (SearchHandler is not null && SearchHandler.SearchBoxVisibility != SearchBoxVisibility.Hidden)
{
var context = ShellContext.AndroidContext;
- if (_searchView == null)
+ if (_searchView is null)
{
_searchView = GetSearchView(context);
_searchView.SearchHandler = SearchHandler;
@@ -677,21 +697,31 @@ protected virtual void UpdateToolbarItems(AToolbar toolbar, Page page)
icon.SetColorFilter(TintColor.ToPlatform(Colors.White), FilterMode.SrcAtop);
item.SetShowAsAction(ShowAsAction.IfRoom | ShowAsAction.CollapseActionView);
- if (_searchView.View.Parent != null)
+ if (_searchView.View.Parent is not null)
+ {
_searchView.View.RemoveFromParent();
+ }
item.SetActionView(_searchView.View);
item.Dispose();
}
else if (SearchHandler.SearchBoxVisibility == SearchBoxVisibility.Expanded)
{
+ // Remove the placeholder menu item, if it exists, added for collapsible mode.
+ if (menu.FindItem(_placeholderMenuItemId) is not null)
+ {
+ menu.RemoveItem(_placeholderMenuItemId);
+ }
+
if (_searchView.View.Parent != _platformToolbar)
+ {
_platformToolbar.AddView(_searchView.View);
+ }
}
}
else
{
- if (_searchView != null)
+ if (_searchView is not null)
{
_searchView.View.RemoveFromParent();
_searchView.View.ViewAttachedToWindow -= OnSearchViewAttachedToWindow;
@@ -789,8 +819,15 @@ public override void Draw(Canvas canvas)
bool pressed = false;
if (IconBitmap != null)
{
- ADrawableCompat.SetTint(IconBitmap, TintColor.ToPlatform());
- ADrawableCompat.SetTintMode(IconBitmap, PorterDuff.Mode.SrcAtop);
+ if (TintColor is not null)
+ {
+ ADrawableCompat.SetTint(IconBitmap, TintColor.ToPlatform());
+ ADrawableCompat.SetTintMode(IconBitmap, PorterDuff.Mode.SrcAtop);
+ }
+ else
+ {
+ ADrawableCompat.SetTintList(IconBitmap, null); // Clear tint to preserve original icon colors
+ }
IconBitmap.SetBounds(Bounds.Left, Bounds.Top, Bounds.Right, Bounds.Bottom);
IconBitmap.Draw(canvas);
@@ -800,7 +837,7 @@ public override void Draw(Canvas canvas)
var paint = new Paint { AntiAlias = true };
paint.TextSize = _defaultSize;
#pragma warning disable CA1416 // https://github.com/xamarin/xamarin-android/issues/6962
- paint.Color = pressed ? _pressedBackgroundColor.ToPlatform() : TintColor.ToPlatform();
+ paint.Color = pressed ? _pressedBackgroundColor.ToPlatform() : (TintColor ?? Colors.White).ToPlatform();
#pragma warning restore CA1416
paint.SetStyle(Paint.Style.Fill);
var y = (Bounds.Height() + paint.TextSize) / 2;
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/SafeShellTabBarAppearanceTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/SafeShellTabBarAppearanceTracker.cs
index 168d119d8e2c..e0ecca140f76 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/SafeShellTabBarAppearanceTracker.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/SafeShellTabBarAppearanceTracker.cs
@@ -28,8 +28,8 @@ public virtual void SetAppearance(UITabBarController controller, ShellAppearance
{
IShellAppearanceElement appearanceElement = appearance;
var backgroundColor = appearanceElement.EffectiveTabBarBackgroundColor;
- var foregroundColor = appearanceElement.EffectiveTabBarForegroundColor; // Currently unused
- var disabledColor = appearanceElement.EffectiveTabBarDisabledColor; // Unused on iOS
+ var foregroundColor = appearanceElement.EffectiveTabBarForegroundColor;
+ var disabledColor = appearanceElement.EffectiveTabBarDisabledColor;
var unselectedColor = appearanceElement.EffectiveTabBarUnselectedColor;
var titleColor = appearanceElement.EffectiveTabBarTitleColor;
@@ -75,6 +75,7 @@ void UpdateiOS15TabBarAppearance(UITabBarController controller, ShellAppearance
var foregroundColor = appearanceElement.EffectiveTabBarForegroundColor;
var unselectedColor = appearanceElement.EffectiveTabBarUnselectedColor;
var titleColor = appearanceElement.EffectiveTabBarTitleColor;
+ var disabledColor = appearanceElement.EffectiveTabBarDisabledColor;
controller.TabBar
.UpdateiOS15TabBarAppearance(
@@ -86,6 +87,19 @@ void UpdateiOS15TabBarAppearance(UITabBarController controller, ShellAppearance
backgroundColor,
titleColor ?? foregroundColor,
unselectedColor);
+
+ // Set disabled color in the global appearance for text-only tabs
+ if (disabledColor is not null && _tabBarAppearance is not null)
+ {
+ var disabledUIColor = disabledColor.ToPlatform();
+ var disabledAttributes = new UIStringAttributes { ForegroundColor = disabledUIColor };
+
+ _tabBarAppearance.StackedLayoutAppearance.Disabled.TitleTextAttributes = disabledAttributes;
+ _tabBarAppearance.InlineLayoutAppearance.Disabled.TitleTextAttributes = disabledAttributes;
+ _tabBarAppearance.CompactInlineLayoutAppearance.Disabled.TitleTextAttributes = disabledAttributes;
+
+ controller.TabBar.StandardAppearance = controller.TabBar.ScrollEdgeAppearance = _tabBarAppearance;
+ }
}
void UpdateTabBarAppearance(UITabBarController controller, ShellAppearance appearance)
@@ -95,6 +109,7 @@ void UpdateTabBarAppearance(UITabBarController controller, ShellAppearance appea
var foregroundColor = appearanceElement.EffectiveTabBarForegroundColor;
var unselectedColor = appearanceElement.EffectiveTabBarUnselectedColor;
var titleColor = appearanceElement.EffectiveTabBarTitleColor;
+ var disabledColor = appearanceElement.EffectiveTabBarDisabledColor;
var tabBar = controller.TabBar;
@@ -113,6 +128,10 @@ void UpdateTabBarAppearance(UITabBarController controller, ShellAppearance appea
tabBar.TintColor = (foregroundColor ?? titleColor).ToPlatform();
UITabBarItem.Appearance.SetTitleTextAttributes(new UIStringAttributes { ForegroundColor = (titleColor ?? foregroundColor).ToPlatform() }, UIControlState.Selected);
}
+
+ // Set disabled color for text-only tabs
+ if (disabledColor is not null && disabledColor.IsNotDefault())
+ UITabBarItem.Appearance.SetTitleTextAttributes(new UIStringAttributes { ForegroundColor = disabledColor.ToPlatform() }, UIControlState.Disabled);
}
}
}
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/SearchHandlerAppearanceTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/SearchHandlerAppearanceTracker.cs
index 3096d9eab8f4..8547b700ddc6 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/SearchHandlerAppearanceTracker.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/SearchHandlerAppearanceTracker.cs
@@ -40,7 +40,6 @@ public SearchHandlerAppearanceTracker(UISearchBar searchBar, SearchHandler searc
_uiSearchBar.OnEditingStarted += OnEditingStarted;
_uiSearchBar.OnEditingStopped += OnEditingEnded;
_uiSearchBar.TextChanged += OnTextChanged;
- _uiSearchBar.ShowsCancelButton = false;
GetDefaultSearchBarColors(_uiSearchBar);
var uiTextField = searchBar.FindDescendantView();
UpdateSearchBarColors();
@@ -151,25 +150,25 @@ void UpdateSearchBarBackgroundColor(UITextField textField)
if (!_hasCustomBackground && backGroundColor == null)
return;
- var backgroundView = textField.Subviews[0];
-
if (backGroundColor == null)
{
- backgroundView.Layer.CornerRadius = 0;
- backgroundView.ClipsToBounds = false;
- backgroundView.BackgroundColor = _defaultBackgroundColor;
+ // Reset to defaults
+ textField.Layer.CornerRadius = 0;
+ textField.ClipsToBounds = false;
+ textField.BackgroundColor = _defaultBackgroundColor;
+ _hasCustomBackground = false;
+ }
+ else
+ {
+ _hasCustomBackground = true;
+ textField.Layer.CornerRadius = 10;
+ textField.ClipsToBounds = true;
+ if (_defaultBackgroundColor is null)
+ {
+ _defaultBackgroundColor = backGroundColor.ToPlatform();
+ }
+ textField.BackgroundColor = backGroundColor.ToPlatform();
}
-
- _hasCustomBackground = true;
-
- backgroundView.Layer.CornerRadius = 10;
- backgroundView.ClipsToBounds = true;
- if (_defaultBackgroundColor == null)
- _defaultBackgroundColor = backgroundView.BackgroundColor;
-
- UIColor backgroundColor = backGroundColor.ToPlatform();
- backgroundView.BackgroundColor = backgroundColor;
- textField.BackgroundColor = backgroundColor;
}
void UpdateCancelButtonColor(UIButton cancelButton)
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellItemRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellItemRenderer.cs
index 5d29846d0125..4c9428cf5bec 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellItemRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellItemRenderer.cs
@@ -4,6 +4,7 @@
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
+using CoreGraphics;
using Foundation;
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
using Microsoft.Maui.Graphics;
@@ -142,6 +143,60 @@ public override void ViewDidLoad()
};
}
+ public override void ViewDidAppear(bool animated)
+ {
+ base.ViewDidAppear(animated);
+ ApplyInitialDisabledState();
+ }
+
+ void ApplyInitialDisabledState()
+ {
+ if (TabBar.Items is null)
+ return;
+
+ var items = ShellItemController?.GetItems();
+ if (items is null)
+ return;
+
+ for (int i = 0; i < items.Count && i < TabBar.Items.Length; i++)
+ {
+ if (!items[i].IsEnabled)
+ UpdateTabBarItemEnabled(TabBar.Items[i], false);
+ }
+ }
+
+ void UpdateTabBarItemEnabled(UITabBarItem tabBarItem, bool isEnabled)
+ {
+ tabBarItem.Enabled = isEnabled;
+
+ var disabledColor = Shell.GetTabBarDisabledColor(_context.Shell)?.ToPlatform();
+ if (disabledColor is null)
+ return;
+
+ // Per-item text attributes needed for dynamic enable/disable changes
+ var textAttributes = isEnabled ? null : new UIStringAttributes { ForegroundColor = disabledColor };
+ tabBarItem.SetTitleTextAttributes(textAttributes, UIControlState.Disabled);
+
+ // Tint icon image since UITabBarAppearance.Disabled.IconColor doesn't work
+ if (tabBarItem.Image is not null)
+ {
+ tabBarItem.Image = isEnabled
+ ? tabBarItem.Image.ImageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
+ : CreateTintedImage(tabBarItem.Image, disabledColor).ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
+ }
+ }
+
+ UIImage CreateTintedImage(UIImage image, UIColor color)
+ {
+ var renderer = new UIGraphicsImageRenderer(image.Size, new UIGraphicsImageRendererFormat { Opaque = false, Scale = image.CurrentScale });
+ return renderer.CreateImage(ctx =>
+ {
+ image.Draw(new CGRect(CGPoint.Empty, image.Size));
+ color.SetFill();
+ ctx.FillRect(new CGRect(CGPoint.Empty, image.Size), CGBlendMode.SourceIn);
+ });
+ }
+
void IDisconnectable.Disconnect()
{
if (_sectionRenderers != null)
@@ -220,6 +275,9 @@ protected virtual void OnItemsCollectionChanged(object sender, NotifyCollectionC
RemoveRenderer(renderer);
}
}
+
+ // Recalculate IsInMoreTab for remaining renderers since tab positions shifted after removal.
+ UpdateIsInMoreTabForRenderers();
}
if (e.NewItems != null && e.NewItems.Count > 0)
@@ -280,10 +338,12 @@ protected virtual void OnShellSectionPropertyChanged(object sender, PropertyChan
var shellSection = (ShellSection)sender;
var renderer = RendererForShellContent(shellSection);
var index = ViewControllers.ToList().IndexOf(renderer.ViewController);
- TabBar.Items[index].Enabled = shellSection.IsEnabled;
+ if (TabBar.Items is not null && index >= 0 && index < TabBar.Items.Length)
+ UpdateTabBarItemEnabled(TabBar.Items[index], shellSection.IsEnabled);
}
}
+
protected virtual void UpdateShellAppearance(ShellAppearance appearance)
{
if (appearance == null)
@@ -324,6 +384,22 @@ void SetTabItemsEnabledState()
}
}
+ void UpdateIsInMoreTabForRenderers()
+ {
+ const int maxTabs = 5;
+ var currentViewControllers = ViewControllers;
+ if (currentViewControllers == null)
+ return;
+
+ bool willUseMore = currentViewControllers.Length > maxTabs;
+ for (int i = 0; i < currentViewControllers.Length; i++)
+ {
+ var renderer = RendererForViewController(currentViewControllers[i]);
+ if (renderer != null)
+ renderer.IsInMoreTab = willUseMore && i >= maxTabs - 1;
+ }
+ }
+
void CreateTabRenderers()
{
if (ShellItem.CurrentItem == null)
@@ -455,11 +531,19 @@ void UpdateLargeTitles()
if (page is null || !OperatingSystem.IsIOSVersionAtLeast(11))
return;
+ // Shell doesn't have the property PrefersLargeTitles, so we should not update the large titles
+ // if users doen't explicitly set the LargeTitleDisplay property on the Page.
+ // todo net 11: Add PrefersLargeTitles to Shell and use that here.
+ if (!page.IsSet(PlatformConfiguration.iOSSpecific.Page.LargeTitleDisplayProperty))
+ {
+ return;
+ }
+
var largeTitleDisplayMode = page.OnThisPlatform().LargeTitleDisplay();
if (SelectedViewController is UINavigationController navigationController)
{
- navigationController.NavigationBar.PrefersLargeTitles = largeTitleDisplayMode == LargeTitleDisplayMode.Always;
+ navigationController.NavigationBar.PrefersLargeTitles = largeTitleDisplayMode != LargeTitleDisplayMode.Never;
var top = navigationController.TopViewController;
if (top is not null)
{
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellItemTransition.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellItemTransition.cs
index 090939cce980..44d650904b14 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellItemTransition.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellItemTransition.cs
@@ -1,4 +1,5 @@
#nullable disable
+using System;
using System.Threading.Tasks;
using ObjCRuntime;
using UIKit;
@@ -14,7 +15,15 @@ public Task Transition(IShellItemRenderer oldRenderer, IShellItemRenderer newRen
var newView = newRenderer.ViewController.View;
oldView.Layer.RemoveAllAnimations();
- newView.Alpha = 0;
+
+ // On iOS 26+, setting newView.Alpha = 0 before the fade-in causes Liquid Glass
+ // tab bar icons to composite at zero opacity, resulting in missing tab icons.
+ // We skip the initial alpha=0 on iOS 26+, which means there is no fade-in
+ // animation on iOS 26+ — this is intentional to avoid the rendering issue.
+ if (!(OperatingSystem.IsIOSVersionAtLeast(26) || OperatingSystem.IsMacCatalystVersionAtLeast(26)))
+ {
+ newView.Alpha = 0;
+ }
oldView.Superview.InsertSubviewAbove(newView, oldView);
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellNavBarAppearanceTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellNavBarAppearanceTracker.cs
index e31120c1b798..67f8acc8fa15 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellNavBarAppearanceTracker.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellNavBarAppearanceTracker.cs
@@ -122,8 +122,14 @@ void UpdateiOS13NavigationBarAppearance(UINavigationController controller, Shell
// Set ForegroundColor
var foreground = appearance.ForegroundColor;
- if (foreground != null)
+ if (foreground is not null)
+ {
navBar.TintColor = foreground.ToPlatform();
+ }
+ else if (_defaultTint is not null) // Reset to default if user set ForegroundColor to null
+ {
+ navBar.TintColor = _defaultTint;
+ }
// Set BackgroundColor
var background = appearance.BackgroundColor;
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs
index 025ae0e77993..15f18ad20962 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs
@@ -79,6 +79,8 @@ public Page Page
SearchHandlerAppearanceTracker? _searchHandlerAppearanceTracker;
IFontManager _fontManager;
bool _isVisiblePage;
+ NSObject? _keyboardWillHideObserver;
+ bool _pendingKeyboardNavigation;
BackButtonBehavior? BackButtonBehavior { get; set; }
UINavigationItem? NavigationItem { get; set; }
@@ -93,6 +95,8 @@ public ShellPageRendererTracker(IShellContext context)
_context.Shell.PropertyChanged += HandleShellPropertyChanged;
_fontManager = context.Shell.RequireFontManager();
+
+ _keyboardWillHideObserver = UIKeyboard.Notifications.ObserveWillHide(OnKeyboardWillHide);
}
public void OnFlyoutBehaviorChanged(FlyoutBehavior behavior)
@@ -108,7 +112,10 @@ protected virtual void HandleShellPropertyChanged(object sender, PropertyChanged
if (e.Is(VisualElement.FlowDirectionProperty))
UpdateFlowDirection();
else if (e.Is(Shell.FlyoutIconProperty) || e.Is(Shell.ForegroundColorProperty))
+ {
UpdateLeftToolbarItems();
+ UpdateRightBarButtonItemTintColors();
+ }
}
#nullable disable
@@ -155,6 +162,7 @@ protected virtual void OnPagePropertyChanged(object sender, PropertyChangedEvent
else if (e.PropertyName == Shell.ForegroundColorProperty.PropertyName)
{
UpdateLeftToolbarItems();
+ UpdateRightBarButtonItemTintColors();
}
}
@@ -276,7 +284,7 @@ protected virtual void OnRendererSet()
ViewController.AutomaticallyAdjustsScrollViewInsets = false;
}
}
-
+
internal void UpdateTitleViewInternal()
{
UpdateTitleView();
@@ -456,9 +464,37 @@ protected virtual void UpdateToolbarItems()
NavigationItem.SetRightBarButtonItems(primaries is null ? Array.Empty() : primaries.ToArray(), false);
+ UpdateRightBarButtonItemTintColors();
UpdateLeftToolbarItems();
}
+ /// iOS 26+: LiquidGlass no longer inherits the foreground color from the navigation bar's TintColor.
+ /// Explicitly set TintColor on each right bar button item to ensure the Shell.ForegroundColor is applied.
+ void UpdateRightBarButtonItemTintColors()
+ {
+ if (!(OperatingSystem.IsIOSVersionAtLeast(26) || OperatingSystem.IsMacCatalystVersionAtLeast(26)))
+ {
+ return;
+ }
+
+ if (NavigationItem?.RightBarButtonItems is not { Length: > 0 } rightItems)
+ {
+ return;
+ }
+
+ var foregroundColor = _context?.Shell?.CurrentPage?.GetValue(Shell.ForegroundColorProperty) ??
+ _context?.Shell?.GetValue(Shell.ForegroundColorProperty);
+
+ var platformColor = foregroundColor is Graphics.Color shellForegroundColor
+ ? shellForegroundColor.ToPlatform()
+ : null;
+
+ foreach (var item in rightItems)
+ {
+ item.TintColor = platformColor;
+ }
+ }
+
void UpdateLeftToolbarItems()
{
var shell = _context?.Shell;
@@ -534,7 +570,8 @@ void UpdateLeftToolbarItems()
}
}
}
- else if (String.IsNullOrWhiteSpace(text) && IsRootPage && _flyoutBehavior == FlyoutBehavior.Flyout)
+ // Show hamburger icon if it's the root page, or if the back button is not visible.
+ else if (String.IsNullOrWhiteSpace(text) && (IsRootPage || !backButtonVisible) && _flyoutBehavior == FlyoutBehavior.Flyout)
{
icon = DrawHamburger();
}
@@ -542,7 +579,7 @@ void UpdateLeftToolbarItems()
if (icon != null)
{
NavigationItem.LeftBarButtonItem =
- new UIBarButtonItem(icon, UIBarButtonItemStyle.Plain, (s, e) => LeftBarButtonItemHandler(ViewController, IsRootPage)) { Enabled = enabled };
+ new UIBarButtonItem(icon, UIBarButtonItemStyle.Plain, (s, e) => LeftBarButtonItemHandler(ViewController, (IsRootPage || !backButtonVisible))) { Enabled = enabled };
// For iOS 26+, explicitly set the tint color on the bar button item
// because the navigation bar's tint color is not automatically inherited
@@ -564,7 +601,7 @@ void UpdateLeftToolbarItems()
{
if (String.IsNullOrWhiteSpace(image?.AutomationId))
{
- if (IsRootPage)
+ if (IsRootPage || !backButtonVisible)
{
NavigationItem.LeftBarButtonItem.AccessibilityIdentifier = "OK";
NavigationItem.LeftBarButtonItem.AccessibilityLabel = "Menu";
@@ -1119,6 +1156,9 @@ void OnPageLoaded(object? sender, EventArgs e)
return;
}
+ // Set flag when page is loaded during navigation - this is when the keyboard issue can occur
+ _pendingKeyboardNavigation = true;
+
UpdateToolbarItemsInternal();
//UIKIt will try to override our colors when the SearchController is inside the NavigationBar
@@ -1175,6 +1215,51 @@ void SetDisappeared()
_context.Shell.Toolbar.PropertyChanged -= OnToolbarPropertyChanged;
}
+ void OnKeyboardWillHide(object? sender, UIKeyboardEventArgs e)
+ {
+ // Keyboard dismissal during page load can cause iOS to misposition the view behind the navigation bar.
+ // Detect and correct this by repositioning the view below the navigation bar when needed.
+
+ if (_disposed || ViewController?.View is null || ViewController.NavigationController is null)
+ return;
+
+ // Only apply fix during the problematic timing window (page load with navigation)
+ if (!_pendingKeyboardNavigation)
+ return;
+
+ var navController = ViewController.NavigationController;
+ var navBar = navController.NavigationBar;
+
+ if (navBar.Hidden || navBar.Frame.Height <= 0)
+ return;
+
+ // Don't interfere with SearchHandler's keyboard management when it's active
+ if (_searchController?.Active == true)
+ return;
+
+ var currentFrame = ViewController.View.Frame;
+ var navBarBottom = navBar.Frame.Bottom;
+
+ if (currentFrame.Y == 0 && navBarBottom > 0 &&
+ navController.ViewControllers?.Length > 1 &&
+ ViewController == navController.TopViewController)
+ {
+ // Adjust height to fit available space after Y position change
+ var yOffset = navBarBottom - currentFrame.Y;
+ var correctFrame = new CGRect(
+ currentFrame.X,
+ navBarBottom,
+ currentFrame.Width,
+ currentFrame.Height - yOffset
+ );
+
+ ViewController.View.Frame = correctFrame;
+ }
+
+ // Clear flag after handling keyboard dismissal once
+ _pendingKeyboardNavigation = false;
+ }
+
#endregion SearchHandler
#region IDisposable Support
@@ -1221,6 +1306,9 @@ protected virtual void Dispose(bool disposing)
if (NavigationItem?.TitleView is TitleViewContainer tvc)
tvc.Disconnect();
+
+ _keyboardWillHideObserver?.Dispose();
+ _keyboardWillHideObserver = null;
}
_context = null;
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs
index aabf4cc14bc7..7c1976f95c50 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs
@@ -36,18 +36,35 @@ public ShellSection ShellSection
IShellSectionController ShellSectionController => ShellSection;
+ UINavigationController MoreNavigationController => (ParentViewController as UITabBarController)?.MoreNavigationController;
+
public UIViewController ViewController => this;
#endregion IShellContentRenderer
#region IAppearanceObserver
+ ShellAppearance _shellAppearance;
+
void IAppearanceObserver.OnAppearanceChanged(ShellAppearance appearance)
{
+ _shellAppearance = appearance;
if (appearance == null)
+ {
_appearanceTracker.ResetAppearance(this);
+ if (MoreNavigationController is not null)
+ {
+ _appearanceTracker.ResetAppearance(MoreNavigationController);
+ }
+ }
else
+ {
_appearanceTracker.SetAppearance(this, appearance);
+ if (MoreNavigationController is not null)
+ {
+ _appearanceTracker.SetAppearance(MoreNavigationController, appearance);
+ }
+ }
}
#endregion IAppearanceObserver
@@ -70,6 +87,13 @@ void IAppearanceObserver.OnAppearanceChanged(ShellAppearance appearance)
ShellSection _shellSection;
bool _ignorePopCall;
+ // Prevents multiple concurrent GoToAsync("..") dispatches from SendPop().
+ // On iOS 26+, delegate methods (ShouldPopItem, DidPopItem) can fire in any order
+ // and combinations that may cause SendPop() to be called multiple times.
+ // Once a back-navigation dispatch is in flight, all subsequent calls are blocked
+ // until it completes (success or cancel).
+ bool _sendPopPending;
+
// When setting base.ViewControllers iOS doesn't modify the property right away.
// if you set base.ViewControllers to a new array and then retrieve base.ViewControllers
// iOS will return the previous array until the new array has been processed
@@ -115,19 +139,34 @@ bool DidPopItem(UINavigationBar _, UINavigationItem __)
if (_shellSection.Stack.Count == NavigationBar.Items.Length)
return true;
- // Stacks out of sync = user-initiated navigation
+ // Stacks out of sync: treat as user-initiated back (e.g., swipe-back).
+ // On iOS 26+, this can also fire during ShouldPopItem and programmatic PopViewController;
+ // SendPop() contains the _sendPopPending guard to prevent any double-dispatch in those cases.
return SendPop();
}
- internal bool SendPop()
+ internal bool SendPop(UIViewController topViewController = null)
{
// this means the pop is already done, nothing we can do
if (ActiveViewControllers().Length < NavigationBar.Items.Length)
return true;
+ // On iOS 26+, delegate methods (ShouldPopItem, DidPopItem) can fire in any order
+ // and fire multiple times for a single user back action. Guard against multiple
+ // concurrent GoToAsync("..") dispatches to prevent navigating to the wrong page.
+ if (OperatingSystem.IsIOSVersionAtLeast(26) || OperatingSystem.IsMacCatalystVersionAtLeast(26))
+ {
+ if (_sendPopPending)
+ {
+ return false;
+ }
+ _sendPopPending = true;
+ }
+
+ topViewController ??= TopViewController;
foreach (var tracker in _trackers)
{
- if (tracker.Value.ViewController == TopViewController)
+ if (tracker.Value.ViewController == topViewController)
{
var behavior = Shell.GetEffectiveBackButtonBehavior(tracker.Value.Page);
var command = behavior.GetPropertyIfSet(BackButtonBehavior.CommandProperty, null);
@@ -139,7 +178,13 @@ internal bool SendPop()
{
command.Execute(commandParameter);
}
+ _sendPopPending = false; // reset before returning
+ return false;
+ }
+ // Allow the page to intercept back navigation via OnBackButtonPressed
+ if (tracker.Value.Page?.SendBackButtonPressed() == true)
+ {
return false;
}
@@ -147,7 +192,6 @@ internal bool SendPop()
}
}
-
// Do not remove, wonky behavior on some versions of iOS if you dont dispatch
// Shane: ^ not sure if this is true anymore because of how
// we now route this through "GoToAsync"
@@ -155,7 +199,14 @@ internal bool SendPop()
{
var navItemsCount = NavigationBar.Items.Length;
- await _context.Shell.GoToAsync("..", true);
+ try
+ {
+ await _context.Shell.GoToAsync("..", true);
+ }
+ finally
+ {
+ _sendPopPending = false;
+ }
// This means the navigation was cancelled
if (NavigationBar.Items.Length == navItemsCount)
@@ -191,6 +242,7 @@ public override void ViewDidDisappear(bool animated)
_popCompletionTask?.TrySetResult(false);
_popCompletionTask = null;
+ _sendPopPending = false;
base.ViewDidDisappear(animated);
}
@@ -226,6 +278,15 @@ public override void ViewDidLayoutSubviews()
}
}
+ public override void DidMoveToParentViewController(UIViewController parent)
+ {
+ if (_shellAppearance is not null && MoreNavigationController is not null)
+ {
+ _appearanceTracker?.SetAppearance(MoreNavigationController, _shellAppearance);
+ }
+ base.DidMoveToParentViewController(parent);
+ }
+
public override void ViewDidLoad()
{
if (_disposed)
@@ -611,6 +672,8 @@ public override void PushViewController(UIViewController viewController, bool an
if (IsInMoreTab && ParentViewController is UITabBarController tabBarController)
{
tabBarController.MoreNavigationController.PushViewController(viewController, animated);
+ viewController.NavigationItem.BackAction = UIAction.Create((e) => SendPop(tabBarController.MoreNavigationController.TopViewController));
+ HandleMoreNavigationCompletionTasks(viewController);
}
else
{
@@ -623,12 +686,30 @@ public override UIViewController PopViewController(bool animated)
_pendingViewControllers = null;
if (IsInMoreTab && ParentViewController is UITabBarController tabBarController)
{
- return tabBarController.MoreNavigationController.PopViewController(animated);
+ var viewController = tabBarController.MoreNavigationController.PopViewController(animated);
+ HandleMoreNavigationCompletionTasks(viewController);
+ return viewController;
}
return base.PopViewController(animated);
}
+ private void HandleMoreNavigationCompletionTasks(UIViewController viewController)
+ {
+ var tasks = _completionTasks;
+ var popTask = _popCompletionTask;
+
+ if (tasks.TryGetValue(viewController, out var source))
+ {
+ source.TrySetResult(true);
+ tasks.Remove(viewController);
+ }
+ else if (popTask != null)
+ {
+ popTask.TrySetResult(true);
+ }
+ }
+
UIViewController[] ActiveViewControllers() =>
_pendingViewControllers ?? base.ViewControllers;
diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRootRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRootRenderer.cs
index 6d880ec642a6..cc45d37bb0bc 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRootRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRootRenderer.cs
@@ -40,6 +40,10 @@ public class ShellSectionRootRenderer : UIViewController, IShellSectionRootRende
UIViewPropertyAnimator _pageAnimation;
UIEdgeInsets _additionalSafeArea = UIEdgeInsets.Zero;
+#if MACCATALYST
+ CGRect _previousFrameHeader;
+#endif
+
ShellSection ShellSection
{
get;
@@ -563,6 +567,16 @@ void LayoutHeader()
CGRect frame = new CGRect(View.Bounds.X, headerTop, View.Bounds.Width, HeaderHeight);
_blurView.Frame = frame;
_header.ViewController.View.Frame = frame;
+#if MACCATALYST
+ if (frame.Width != _previousFrameHeader.Width || frame.Height != _previousFrameHeader.Height)
+ {
+ _previousFrameHeader = frame;
+ if (_header.ViewController is ShellSectionRootHeader rootHeader)
+ {
+ rootHeader.CollectionView.CollectionViewLayout.InvalidateLayout();
+ }
+ }
+#endif
}
nfloat left;
diff --git a/src/Controls/src/Core/Compatibility/Handlers/TabbedPage/iOS/TabbedRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/TabbedPage/iOS/TabbedRenderer.cs
index 78a0b58b383a..3ab57244b64b 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/TabbedPage/iOS/TabbedRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/TabbedPage/iOS/TabbedRenderer.cs
@@ -100,6 +100,7 @@ public void SetElement(VisualElement element)
UpdateSelectedTabColors();
UpdateBarTranslucent();
UpdatePageSpecifics();
+ UpdateFlowDirection();
}
public UIViewController ViewController
@@ -190,7 +191,7 @@ void OnPagePropertyChanged(object sender, PropertyChangedEventArgs e)
UpdateTabBarItem(page);
}
}
-
+
public override void TraitCollectionDidChange(UITraitCollection previousTraitCollection)
{
if (previousTraitCollection.VerticalSizeClass == TraitCollection.VerticalSizeClass)
@@ -245,6 +246,8 @@ void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
SetNeedsStatusBarAppearanceUpdate();
SelectedViewController = controller;
}
+ else if (e.PropertyName == nameof(TabbedPage.FlowDirection))
+ UpdateFlowDirection();
else if (e.PropertyName == TabbedPage.BarBackgroundColorProperty.PropertyName)
UpdateBarBackgroundColor();
else if (e.PropertyName == TabbedPage.BarBackgroundProperty.PropertyName)
@@ -505,6 +508,14 @@ void UpdateTabBarItems()
}
}
+ void UpdateFlowDirection()
+ {
+ if (Tabbed is null)
+ return;
+
+ View.UpdateFlowDirection(Tabbed);
+ }
+
void UpdateChildrenOrderIndex(UIViewController[] viewControllers)
{
if (Tabbed is not TabbedPage tabbed)
@@ -669,4 +680,4 @@ void IElementHandler.DisconnectHandler()
}
#endregion
}
-}
+}
\ No newline at end of file
diff --git a/src/Controls/src/Core/ContentConverter.cs b/src/Controls/src/Core/ContentConverter.cs
index 3d0a3e896db2..50d76aa70245 100644
--- a/src/Controls/src/Core/ContentConverter.cs
+++ b/src/Controls/src/Core/ContentConverter.cs
@@ -44,9 +44,10 @@ static View ConfigureView(View view, ContentPresenter presenter)
return view;
}
- static Label ConvertToLabel(string textContent, ContentPresenter presenter)
+ static ContentLabel ConvertToLabel(string textContent, ContentPresenter presenter)
{
- var label = new Label
+ // Use ContentLabel instead of Label to avoid interference from global styles
+ var label = new ContentLabel
{
Text = textContent
};
@@ -129,4 +130,20 @@ static bool HasTemplateAncestor(ContentPresenter presenter, Type type)
return false;
}
}
-}
\ No newline at end of file
+
+ // Internal label type used by ContentPresenter to avoid interference from global Label styles.
+ // MAUI resolves implicit styles by looking up Type.FullName as the key in ResourceDictionary,
+ // starting from the element's own Resources and walking up the visual tree (see MergedStyle.RegisterImplicitStyles).
+ // By storing an empty Style keyed to typeof(ContentLabel).FullName in the element's own Resources,
+ // the lookup finds it locally and short-circuits — the global Label style (even with ApplyToDerivedTypes)
+ // is never reached. This keeps properties like TextColor unset, so ShouldSetBinding returns true
+ // and the binding to the templated parent (e.g. RadioButton.TextColor) can be established.
+ internal class ContentLabel : Label
+ {
+ static readonly Style s_style = new Style(typeof(ContentLabel));
+ public ContentLabel()
+ {
+ Resources = new ResourceDictionary { { typeof(ContentLabel).FullName, s_style } };
+ }
+ }
+}
diff --git a/src/Controls/src/Core/Entry/Entry.Android.cs b/src/Controls/src/Core/Entry/Entry.Android.cs
index 985683a1ee0f..0f90bf2ca3b7 100644
--- a/src/Controls/src/Core/Entry/Entry.Android.cs
+++ b/src/Controls/src/Core/Entry/Entry.Android.cs
@@ -29,5 +29,34 @@ public static void MapText(IEntryHandler handler, Entry entry)
Platform.EditTextExtensions.UpdateText(handler.PlatformView, entry);
}
+
+ // TODO: Material3: Make it public in .NET 11
+ // MaterialEntryHandler-specific overloads
+ internal static void MapImeOptions(EntryHandler2 handler, Entry entry)
+ {
+ if (handler.PlatformView?.EditText is null)
+ {
+ return;
+ }
+
+ Platform.EditTextExtensions.UpdateImeOptions(handler.PlatformView.EditText, entry);
+ }
+
+ // TODO: Material3: Make it public in .NET 11
+ internal static void MapText(EntryHandler2 handler, Entry entry)
+ {
+ if (handler.PlatformView?.EditText is null)
+ {
+ return;
+ }
+
+ if (handler.DataFlowDirection == DataFlowDirection.FromPlatform)
+ {
+ Platform.EditTextExtensions.UpdateTextFromPlatform(handler.PlatformView.EditText, entry);
+ return;
+ }
+
+ Platform.EditTextExtensions.UpdateText(handler.PlatformView.EditText, entry);
+ }
}
}
diff --git a/src/Controls/src/Core/Entry/Entry.Mapper.cs b/src/Controls/src/Core/Entry/Entry.Mapper.cs
index 9acb2b4127b6..1d3d9285c740 100644
--- a/src/Controls/src/Core/Entry/Entry.Mapper.cs
+++ b/src/Controls/src/Core/Entry/Entry.Mapper.cs
@@ -20,6 +20,19 @@ public partial class Entry
EntryHandler.Mapper.ReplaceMapping(nameof(Text), MapText);
EntryHandler.Mapper.ReplaceMapping(nameof(TextTransform), MapText);
+ // Material3 Entry Handler mappings
+#if ANDROID
+ if (RuntimeFeature.IsMaterial3Enabled)
+ {
+ EntryHandler2.Mapper.ReplaceMapping(PlatformConfiguration.AndroidSpecific.Entry.ImeOptionsProperty.PropertyName, MapImeOptions);
+ EntryHandler2.Mapper.ReplaceMapping(nameof(Text), MapText);
+ EntryHandler2.Mapper.ReplaceMapping(nameof(TextTransform), MapText);
+ EntryHandler2.Mapper.AppendToMapping(nameof(VisualElement.IsFocused), InputView.MapIsFocused);
+ EntryHandler2.Mapper.AppendToMapping(nameof(VisualElement.IsVisible), InputView.MapIsVisible);
+ EntryHandler2.CommandMapper.PrependToMapping(nameof(IEntry.Focus), InputView.MapFocus);
+ }
+#endif
+
#if IOS || ANDROID
EntryHandler.Mapper.AppendToMapping(nameof(VisualElement.IsFocused), InputView.MapIsFocused);
EntryHandler.Mapper.AppendToMapping(nameof(VisualElement.IsVisible), InputView.MapIsVisible);
diff --git a/src/Controls/src/Core/Handlers/Items/Android/Adapters/CarouselViewAdapter.cs b/src/Controls/src/Core/Handlers/Items/Android/Adapters/CarouselViewAdapter.cs
index 96b2c5eef98b..1ba7570300ef 100644
--- a/src/Controls/src/Core/Handlers/Items/Android/Adapters/CarouselViewAdapter.cs
+++ b/src/Controls/src/Core/Handlers/Items/Android/Adapters/CarouselViewAdapter.cs
@@ -18,6 +18,8 @@ internal CarouselViewAdapter(CarouselView itemsView, Func CarouselView.Loop && !(ItemsSource is EmptySource)
&& ItemsSource.Count > 0 ? CarouselViewLoopManager.LoopScale : ItemsSource.Count;
+ protected override bool IsSelectionEnabled(global::Android.Views.ViewGroup parent, int viewType) => false;
+
public override int GetItemViewType(int position)
{
int positionInList = GetPositionInList(position);
diff --git a/src/Controls/src/Core/Handlers/Items/Android/Adapters/EmptyViewAdapter.cs b/src/Controls/src/Core/Handlers/Items/Android/Adapters/EmptyViewAdapter.cs
index fd4ccadfdd17..449ffeaf72d1 100644
--- a/src/Controls/src/Core/Handlers/Items/Android/Adapters/EmptyViewAdapter.cs
+++ b/src/Controls/src/Core/Handlers/Items/Android/Adapters/EmptyViewAdapter.cs
@@ -316,6 +316,11 @@ void UpdateHeaderFooterHeight(object item, bool isHeader)
if (item is DataTemplate dataTemplate)
{
var content = dataTemplate.CreateContent() as IView;
+
+ if (content?.Handler is null)
+ {
+ TemplateHelpers.GetHandler(content as View, ItemsView.FindMauiContext());
+ }
size = content.Measure(double.PositiveInfinity, double.PositiveInfinity);
}
diff --git a/src/Controls/src/Core/Handlers/Items/Android/Adapters/SelectableItemsViewAdapter.cs b/src/Controls/src/Core/Handlers/Items/Android/Adapters/SelectableItemsViewAdapter.cs
index 41f5619c3ea7..2b36f38e29fc 100644
--- a/src/Controls/src/Core/Handlers/Items/Android/Adapters/SelectableItemsViewAdapter.cs
+++ b/src/Controls/src/Core/Handlers/Items/Android/Adapters/SelectableItemsViewAdapter.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using Android.Content;
+using Android.Views;
using AndroidX.RecyclerView.Widget;
using Object = Java.Lang.Object;
@@ -28,6 +29,14 @@ public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int positi
return;
}
+ // Header and footer view holders should not participate in selection tracking.
+ // They are not data items and calling GetItem() on their positions would cause
+ // an ArgumentOutOfRangeException due to the header index adjustment.
+ if (ItemsSource.IsHeader(position) || ItemsSource.IsFooter(position))
+ {
+ return;
+ }
+
// Watch for clicks so the user can select the item held by this ViewHolder
selectable.Clicked += SelectableClicked;
@@ -58,6 +67,16 @@ internal void ClearPlatformSelection()
}
}
+ internal void UpdateSelectionMode()
+ {
+ // Update click listeners for all currently visible ViewHolders when SelectionMode changes
+ bool selectionEnabled = ItemsView.SelectionMode is not SelectionMode.None;
+ for (int i = 0; i < _currentViewHolders.Count; i++)
+ {
+ _currentViewHolders[i].UpdateClickListener(selectionEnabled);
+ }
+ }
+
internal void MarkPlatformSelection(SelectableItemsView selectableItemsView)
{
if (_currentViewHolders.Count == 0)
@@ -146,6 +165,16 @@ int[] GetSelectedPositions()
return Array.Empty();
}
+ protected override bool IsSelectionEnabled(ViewGroup parent, int viewType)
+ {
+ if (ItemsView == null)
+ {
+ return false;
+ }
+ // Disable click listeners when SelectionMode is None to prevent TalkBack from announcing items as clickable
+ return ItemsView.SelectionMode != SelectionMode.None;
+ }
+
bool PositionIsSelected(int position)
{
var selectedPositions = GetSelectedPositions();
diff --git a/src/Controls/src/Core/Handlers/Items/Android/Adapters/StructuredItemsViewAdapter.cs b/src/Controls/src/Core/Handlers/Items/Android/Adapters/StructuredItemsViewAdapter.cs
index 86913f1ade7e..42c8055d9a73 100644
--- a/src/Controls/src/Core/Handlers/Items/Android/Adapters/StructuredItemsViewAdapter.cs
+++ b/src/Controls/src/Core/Handlers/Items/Android/Adapters/StructuredItemsViewAdapter.cs
@@ -104,15 +104,16 @@ public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int positi
protected override void BindTemplatedItemViewHolder(TemplatedItemViewHolder templatedItemViewHolder, object context)
{
- if (ItemsView.ItemSizingStrategy == ItemSizingStrategy.MeasureFirstItem)
- {
- templatedItemViewHolder.Bind(context, ItemsView, _reportMeasure, _size);
-
- if (templatedItemViewHolder.ItemView is ItemContentView itemContentView)
- {
- itemContentView.RetrieveStaticSize = _retrieveStaticSize;
+ var itemViewType = templatedItemViewHolder.ItemViewType;
+ if (ItemsView.ItemSizingStrategy == ItemSizingStrategy.MeasureFirstItem && itemViewType != ItemViewType.Header && itemViewType != ItemViewType.Footer && itemViewType != ItemViewType.GroupHeader && itemViewType != ItemViewType.GroupFooter)
+ {
+ templatedItemViewHolder.Bind(context, ItemsView, _reportMeasure, _size);
+
+ if (templatedItemViewHolder.ItemView is ItemContentView itemContentView)
+ {
+ itemContentView.RetrieveStaticSize = _retrieveStaticSize;
}
- }
+ }
else
{
base.BindTemplatedItemViewHolder(templatedItemViewHolder, context);
diff --git a/src/Controls/src/Core/Handlers/Items/Android/ItemContentView.cs b/src/Controls/src/Core/Handlers/Items/Android/ItemContentView.cs
index 71f0ff2136fc..c07bbfa766fd 100644
--- a/src/Controls/src/Core/Handlers/Items/Android/ItemContentView.cs
+++ b/src/Controls/src/Core/Handlers/Items/Android/ItemContentView.cs
@@ -66,6 +66,10 @@ internal void Recycle()
RemoveView(platformView);
}
+ // Capture the current platform view before disconnecting handlers, because
+ // DisconnectHandlers() may null out the handler's PlatformView/ContainerView.
+ View?.DisconnectHandlers();
+
Content = null;
_pixelSize = null;
_reportMeasure = null;
@@ -151,8 +155,6 @@ _pixelSize is not null &&
{
_pixelSize = null;
}
-
- _pixelSize = null;
}
}
diff --git a/src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableGroupedSource.cs b/src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableGroupedSource.cs
index 596f1224c28a..a8b8e79fe26a 100644
--- a/src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableGroupedSource.cs
+++ b/src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableGroupedSource.cs
@@ -144,6 +144,13 @@ public object GetGroup(int groupIndex)
public IItemsViewSource GetGroupItemsViewSource(int groupIndex)
{
+ // The uint cast is being used as an optimization to handle both negative numbers and out-of-bounds indices in a single comparison
+ if ((uint) groupIndex >= (uint) _groups.Count)
+ {
+ System.Diagnostics.Debug.WriteLine($"Invalid Group index: {groupIndex}, Group count: {_groups.Count}");
+ return null;
+ }
+
return _groups[groupIndex];
}
@@ -226,7 +233,10 @@ void UpdateGroupTracking()
for (int n = 0; n < _groupSource.Count; n++)
{
- if (_groupSource[n] is IEnumerable list)
+ // Use ICollection (not IEnumerable) so that flat collections whose item type
+ // implements IEnumerable (e.g. string → IEnumerable) are not mistaken
+ // for groups, which would render a header/footer per item.
+ if (_groupSource[n] is ICollection list)
{
var source = ItemsSourceFactory.Create(list, _groupableItemsView, this);
source.HasFooter = _hasGroupFooters;
@@ -287,6 +297,24 @@ void Reload()
void Add(NotifyCollectionChangedEventArgs args)
{
+ var count = 0;
+
+ foreach (var item in args.NewItems)
+ {
+ // Count only real groups (ICollection); flat items like strings must not
+ // trigger a section insertion — they would cause _groups[groupIndex] to be
+ // out-of-range after UpdateGroupTracking skips non-ICollection items.
+ if (item is ICollection)
+ {
+ count++;
+ }
+ }
+
+ if (count == 0)
+ {
+ return;
+ }
+
var groupIndex = args.NewStartingIndex > -1 ? args.NewStartingIndex : _groupSource.IndexOf(args.NewItems[0]);
var groupCount = args.NewItems.Count;
@@ -307,6 +335,24 @@ void Add(NotifyCollectionChangedEventArgs args)
void Remove(NotifyCollectionChangedEventArgs args)
{
+ var count = 0;
+
+ foreach (var item in args.OldItems)
+ {
+ // Count only real groups (ICollection); flat items like strings must not
+ // trigger a section removal — they would cause _groups[groupIndex] to be
+ // out-of-range after UpdateGroupTracking skips non-ICollection items.
+ if (item is ICollection)
+ {
+ count++;
+ }
+ }
+
+ if (count == 0)
+ {
+ return;
+ }
+
var groupIndex = args.OldStartingIndex;
if (groupIndex < 0)
diff --git a/src/Controls/src/Core/Handlers/Items/Android/MauiCarouselRecyclerView.cs b/src/Controls/src/Core/Handlers/Items/Android/MauiCarouselRecyclerView.cs
index 2917b6060ed7..58a771a64b39 100644
--- a/src/Controls/src/Core/Handlers/Items/Android/MauiCarouselRecyclerView.cs
+++ b/src/Controls/src/Core/Handlers/Items/Android/MauiCarouselRecyclerView.cs
@@ -35,7 +35,9 @@ public MauiCarouselRecyclerView(Context context, Func getItemsLayo
public override bool OnInterceptTouchEvent(MotionEvent ev)
{
if (!IsSwipeEnabled)
+ {
return false;
+ }
return base.OnInterceptTouchEvent(ev);
}
diff --git a/src/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cs b/src/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cs
index 2cacd2f34def..bcb5a628d46e 100644
--- a/src/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cs
+++ b/src/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cs
@@ -20,6 +20,8 @@ public class MauiRecyclerView : Recycler
where TAdapter : ItemsViewAdapter
where TItemsViewSource : IItemsViewSource
{
+ const int InvalidPosition = -1;
+
protected TAdapter ItemsViewAdapter;
protected TItemsView ItemsView;
@@ -197,6 +199,14 @@ public virtual void UpdateEmptyView()
_emptyCollectionObserver.Start(ItemsViewAdapter);
+ // When the EmptyView swaps while _emptyViewAdapter is active, RecyclerView can
+ // reuse a stale item ViewHolder from the shared pool at the empty position.
+ // Clear the pool before refreshing the EmptyView adapter to prevent that reuse.
+ if (GetAdapter() == _emptyViewAdapter)
+ {
+ GetRecycledViewPool().Clear();
+ }
+
_emptyViewAdapter.NotifyDataSetChanged();
}
else
@@ -390,6 +400,12 @@ public virtual void ScrollTo(ScrollToRequestEventArgs args)
var position = DetermineTargetPosition(args);
+ if (position < 0)
+ {
+ System.Diagnostics.Debug.WriteLine($"Invalid scroll request: position = {position}");
+ return;
+ }
+
if (args.IsAnimated)
{
ScrollHelper.AnimateScrollToPosition(position, args.ScrollToPosition);
@@ -432,6 +448,11 @@ protected virtual int DetermineTargetPosition(ScrollToRequestEventArgs args)
else if (ItemsViewAdapter.ItemsSource is IGroupableItemsViewSource groupItemSource)
{
item = FindBoundItemInGroup(args, groupItemSource);
+
+ if (item is null)
+ {
+ return InvalidPosition;
+ }
}
}
@@ -440,18 +461,10 @@ protected virtual int DetermineTargetPosition(ScrollToRequestEventArgs args)
private static object FindBoundItemInGroup(ScrollToRequestEventArgs args, IGroupableItemsViewSource groupItemSource)
{
- if (args.GroupIndex >= 0 &&
- args.GroupIndex < groupItemSource.Count)
- {
- var group = groupItemSource.GetGroupItemsViewSource(args.GroupIndex);
+ var group = groupItemSource.GetGroupItemsViewSource(args.GroupIndex);
- if (group is not null)
- {
- // GetItem calls AdjustIndexRequest, which subtracts 1 if we have a header (UngroupedItemsSource does not do this)
- return group.GetItem(args.Index + 1);
- }
- }
- return groupItemSource.GetItem(args.Index);
+ // GetItem calls AdjustIndexRequest, which subtracts 1 if we have a header (UngroupedItemsSource does not do this)
+ return group?.GetItem(args.Index + 1);
}
protected virtual void UpdateItemSpacing()
@@ -471,12 +484,26 @@ protected virtual void UpdateItemSpacing()
if (_itemDecoration is SpacingItemDecoration spacingDecoration)
{
- // SpacingItemDecoration applies spacing to all items & all 4 sides of the items.
- // We need to adjust the padding on the RecyclerView so this spacing isn't visible around the outer edge of our control.
- // Horizontal & vertical spacing should only exist between items.
- var horizontalPadding = -spacingDecoration.HorizontalOffset;
- var verticalPadding = -spacingDecoration.VerticalOffset;
- SetPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
+ // SpacingItemDecoration now removes spacing on outer edges (first/last row or column),
+ // so we only need negative padding on the cross-axis for grid layouts to compensate
+ // for the spacing between columns (vertical grid) or rows (horizontal grid).
+ if (ItemsLayout is GridItemsLayout gridItemsLayout)
+ {
+ if (gridItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal)
+ {
+ var verticalPadding = -spacingDecoration.VerticalOffset;
+ SetPadding(0, verticalPadding, 0, verticalPadding);
+ }
+ else
+ {
+ var horizontalPadding = -spacingDecoration.HorizontalOffset;
+ SetPadding(horizontalPadding, 0, horizontalPadding, 0);
+ }
+ }
+ else
+ {
+ SetPadding(0, 0, 0, 0);
+ }
}
}
@@ -511,6 +538,7 @@ protected virtual void LayoutPropertyChanged(object sender, PropertyChangedEvent
if (GetLayoutManager() is GridLayoutManager gridLayoutManager)
{
gridLayoutManager.SpanCount = ((GridItemsLayout)ItemsLayout).Span;
+ UpdateItemSpacing();
}
}
else if (propertyChanged.IsOneOf(Microsoft.Maui.Controls.ItemsLayout.SnapPointsTypeProperty, Microsoft.Maui.Controls.ItemsLayout.SnapPointsAlignmentProperty))
diff --git a/src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs b/src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs
index 8f285d875aae..04cb789c9347 100644
--- a/src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs
+++ b/src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs
@@ -11,8 +11,10 @@ public class RecyclerViewScrollListener : Recycler
bool _disposed;
int _horizontalOffset, _verticalOffset;
TItemsView _itemsView;
+ bool _pendingRemainingItemsThresholdReached;
readonly bool _getCenteredItemOnXAndY = false;
bool _hasCompletedFirstLayout = false;
+ bool _scrollStarted;
public RecyclerViewScrollListener(TItemsView itemsView, ItemsViewAdapter itemsViewAdapter) : this(itemsView, itemsViewAdapter, false)
{
@@ -33,6 +35,12 @@ internal void UpdateAdapter(ItemsViewAdapter items
_hasCompletedFirstLayout = false;
}
+ public override void OnScrollStateChanged(RecyclerView recyclerView, int newState)
+ {
+ base.OnScrollStateChanged(recyclerView, newState);
+ _scrollStarted = newState != RecyclerView.ScrollStateIdle;
+ }
+
public override void OnScrolled(RecyclerView recyclerView, int dx, int dy)
{
base.OnScrolled(recyclerView, dx, dy);
@@ -68,7 +76,7 @@ public override void OnScrolled(RecyclerView recyclerView, int dx, int dy)
// Don't send RemainingItemsThresholdReached event for non-linear layout managers
// This can also happen if a layout pass has not happened yet
- if (Last == -1 || ItemsViewAdapter is null || _itemsView.RemainingItemsThreshold == -1)
+ if (Last == -1 || ItemsViewAdapter is null || !_scrollStarted || _itemsView.RemainingItemsThreshold == -1)
{
return;
}
@@ -91,10 +99,34 @@ public override void OnScrolled(RecyclerView recyclerView, int dx, int dy)
if (isThresholdReached)
{
- _itemsView.SendRemainingItemsThresholdReached();
+ HandleRemainingItemsThresholdReached();
+ }
+ }
+
+ public override void OnScrollStateChanged(RecyclerView recyclerView, int newState)
+ {
+ base.OnScrollStateChanged(recyclerView, newState);
+
+ // If we have a pending threshold reached event and the RecyclerView is now idle,
+ // it's safe to trigger the event without the risk of modifying the adapter during a scroll callback
+ if (_pendingRemainingItemsThresholdReached && newState == RecyclerView.ScrollStateIdle)
+ {
+ _pendingRemainingItemsThresholdReached = false;
+ if (!_disposed && _itemsView is not null)
+ {
+ _itemsView.SendRemainingItemsThresholdReached();
+ }
}
}
+ void HandleRemainingItemsThresholdReached()
+ {
+ // Mark that we need to trigger the threshold reached event
+ // This will be handled when the RecyclerView transitions to idle state
+ // to avoid the "Cannot call this method in a scroll callback" exception
+ _pendingRemainingItemsThresholdReached = true;
+ }
+
protected virtual (int First, int Center, int Last) GetVisibleItemsIndex(RecyclerView recyclerView)
{
var firstVisibleItemIndex = -1;
@@ -176,6 +208,7 @@ protected override void Dispose(bool disposing)
{
_itemsView = null;
ItemsViewAdapter = null;
+ _pendingRemainingItemsThresholdReached = false;
}
_disposed = true;
diff --git a/src/Controls/src/Core/Handlers/Items/Android/SelectableViewHolder.cs b/src/Controls/src/Core/Handlers/Items/Android/SelectableViewHolder.cs
index 88b1a451cdee..3dce2b8de830 100644
--- a/src/Controls/src/Core/Handlers/Items/Android/SelectableViewHolder.cs
+++ b/src/Controls/src/Core/Handlers/Items/Android/SelectableViewHolder.cs
@@ -14,14 +14,30 @@ public abstract class SelectableViewHolder : RecyclerView.ViewHolder, global::An
bool _isSelected;
Drawable _selectedDrawable;
Drawable _selectableItemDrawable;
- readonly bool _isSelectionEnabled;
+ bool _isSelectionEnabled;
protected SelectableViewHolder(global::Android.Views.View itemView, bool isSelectionEnabled = true) : base(itemView)
{
- if (isSelectionEnabled)
- itemView.SetOnClickListener(this);
+ UpdateClickListener(isSelectionEnabled);
+ }
+
+ internal void UpdateClickListener(bool enableSelection)
+ {
+ _isSelectionEnabled = enableSelection;
+
+ if (enableSelection)
+ {
+ // Only attach if not already registered; HasOnClickListeners is true once
+ // SetOnClickListener has been called, so this guard prevents duplicate listeners.
+ if (!ItemView.HasOnClickListeners)
+ ItemView.SetOnClickListener(this);
+ }
+ else
+ {
+ ItemView.SetOnClickListener(null);
+ }
- _isSelectionEnabled = isSelectionEnabled;
+ ItemView.Clickable = enableSelection;
}
public bool IsSelected
diff --git a/src/Controls/src/Core/Handlers/Items/Android/SpacingItemDecoration.cs b/src/Controls/src/Core/Handlers/Items/Android/SpacingItemDecoration.cs
index c0545e9c368a..ca2525e79658 100644
--- a/src/Controls/src/Core/Handlers/Items/Android/SpacingItemDecoration.cs
+++ b/src/Controls/src/Core/Handlers/Items/Android/SpacingItemDecoration.cs
@@ -13,6 +13,10 @@ public class SpacingItemDecoration : RecyclerView.ItemDecoration
public int VerticalOffset { get; }
+ int _span = 1;
+
+ ItemsLayoutOrientation _orientation;
+
public SpacingItemDecoration(Context context, IItemsLayout itemsLayout)
{
// The original "SpacingItemDecoration" applied spacing based on an item's current span index.
@@ -35,6 +39,8 @@ public SpacingItemDecoration(Context context, IItemsLayout itemsLayout)
case GridItemsLayout gridItemsLayout:
horizontalOffset = gridItemsLayout.HorizontalItemSpacing / 2.0;
verticalOffset = gridItemsLayout.VerticalItemSpacing / 2.0;
+ _span = gridItemsLayout.Span;
+ _orientation = gridItemsLayout.Orientation;
break;
case LinearItemsLayout listItemsLayout:
if (listItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal)
@@ -47,10 +53,12 @@ public SpacingItemDecoration(Context context, IItemsLayout itemsLayout)
horizontalOffset = 0;
verticalOffset = listItemsLayout.ItemSpacing / 2.0;
}
+ _orientation = listItemsLayout.Orientation;
break;
default:
horizontalOffset = 0;
verticalOffset = 0;
+ _orientation = ItemsLayoutOrientation.Vertical;
break;
}
@@ -62,10 +70,39 @@ public override void GetItemOffsets(ARect outRect, AView view, RecyclerView pare
{
base.GetItemOffsets(outRect, view, parent, state);
+ int position = parent.GetChildAdapterPosition(view);
+ if (position == RecyclerView.NoPosition)
+ return;
+
+ int itemCount = state.ItemCount;
+ if (itemCount <= 0)
+ return;
+
outRect.Left = HorizontalOffset;
outRect.Right = HorizontalOffset;
outRect.Bottom = VerticalOffset;
outRect.Top = VerticalOffset;
+
+ // Remove spacing on the outer edges so spacing only appears between items.
+ // A linear layout is effectively span=1, so the same math works for both.
+ int rowCol = _span <= 1 ? position : position / _span;
+ int totalRowsCols = _span <= 1 ? itemCount : (itemCount + _span - 1) / _span;
+ int lastRowCol = totalRowsCols - 1;
+
+ if (_orientation == ItemsLayoutOrientation.Vertical)
+ {
+ if (rowCol == 0)
+ outRect.Top = 0;
+ if (rowCol == lastRowCol)
+ outRect.Bottom = 0;
+ }
+ else
+ {
+ if (rowCol == 0)
+ outRect.Left = 0;
+ if (rowCol == lastRowCol)
+ outRect.Right = 0;
+ }
}
}
}
\ No newline at end of file
diff --git a/src/Controls/src/Core/Handlers/Items/Android/TemplatedItemViewHolder.cs b/src/Controls/src/Core/Handlers/Items/Android/TemplatedItemViewHolder.cs
index cffac7ede766..184d53455b63 100644
--- a/src/Controls/src/Core/Handlers/Items/Android/TemplatedItemViewHolder.cs
+++ b/src/Controls/src/Core/Handlers/Items/Android/TemplatedItemViewHolder.cs
@@ -42,6 +42,14 @@ public void Recycle(ItemsView itemsView)
}
itemsView.RemoveLogicalChild(View);
+
+ // Disconnect and clear the handler via ItemContentView.Recycle(), which calls
+ // DisconnectHandlers() before releasing Content. Reset _selectedTemplate so the
+ // next Bind() call always goes through the templateChanging path and recreates
+ // the handler (since we just disconnected it).
+ _itemContentView.Recycle();
+ View = null; // clear reference to the disconnected view
+ _selectedTemplate = null; // force templateChanging=true on next Bind() to recreate the view
}
public void Bind(object itemBindingContext, ItemsView itemsView,
diff --git a/src/Controls/src/Core/Handlers/Items/CarouselViewHandler.cs b/src/Controls/src/Core/Handlers/Items/CarouselViewHandler.cs
index 019796a0c899..279359ec443a 100644
--- a/src/Controls/src/Core/Handlers/Items/CarouselViewHandler.cs
+++ b/src/Controls/src/Core/Handlers/Items/CarouselViewHandler.cs
@@ -16,6 +16,9 @@ public CarouselViewHandler(PropertyMapper mapper = null) : base(mapper ?? Mapper
{
#if TIZEN || ANDROID
[Controls.CarouselView.ItemsLayoutProperty.PropertyName] = MapItemsLayout,
+#endif
+#if IOS || MACCATALYST
+ [Controls.VisualElement.IsEnabledProperty.PropertyName] = MapIsEnabled,
#endif
[Controls.CarouselView.IsSwipeEnabledProperty.PropertyName] = MapIsSwipeEnabled,
[Controls.CarouselView.PeekAreaInsetsProperty.PropertyName] = MapPeekAreaInsets,
diff --git a/src/Controls/src/Core/Handlers/Items/CarouselViewHandler.iOS.cs b/src/Controls/src/Core/Handlers/Items/CarouselViewHandler.iOS.cs
index ab58b0c0076d..4c2a3ff206b2 100644
--- a/src/Controls/src/Core/Handlers/Items/CarouselViewHandler.iOS.cs
+++ b/src/Controls/src/Core/Handlers/Items/CarouselViewHandler.iOS.cs
@@ -35,6 +35,12 @@ protected override void ScrollToRequested(object sender, ScrollToRequestEventArg
}
}
+ // TODO: Change the modifier to public in .NET 11.
+ internal static void MapIsEnabled(CarouselViewHandler handler, CarouselView carouselView)
+ {
+ handler.Controller?.CollectionView?.UpdateIsEnabled(carouselView);
+ }
+
public static void MapIsSwipeEnabled(CarouselViewHandler handler, CarouselView carouselView)
{
handler.Controller.CollectionView.ScrollEnabled = carouselView.IsSwipeEnabled;
diff --git a/src/Controls/src/Core/Handlers/Items/ItemsViewHandler.Windows.cs b/src/Controls/src/Core/Handlers/Items/ItemsViewHandler.Windows.cs
index f1d75f9cba54..6b1dacf47be5 100644
--- a/src/Controls/src/Core/Handlers/Items/ItemsViewHandler.Windows.cs
+++ b/src/Controls/src/Core/Handlers/Items/ItemsViewHandler.Windows.cs
@@ -133,24 +133,34 @@ void OnItemsVectorChanged(global::Windows.Foundation.Collections.IObservableVect
if (sender is not ItemCollection items)
return;
- var itemsCount = items.Count;
+ ListViewBase.DispatcherQueue.TryEnqueue(() =>
+ {
+ if (VirtualView is null || ListViewBase is null)
+ {
+ return;
+ }
- if (itemsCount == 0)
- return;
+ var itemsCount = items.Count;
- if (VirtualView.ItemsUpdatingScrollMode == ItemsUpdatingScrollMode.KeepItemsInView)
- {
- var firstItem = items[0];
- // Keeps the first item in the list displayed when new items are added.
- ListViewBase.ScrollIntoView(firstItem);
- }
+ if (itemsCount == 0)
+ {
+ return;
+ }
- if (VirtualView.ItemsUpdatingScrollMode == ItemsUpdatingScrollMode.KeepLastItemInView)
- {
- var lastItem = items[itemsCount - 1];
- // Adjusts the scroll offset to keep the last item in the list displayed when new items are added.
- ListViewBase.ScrollIntoView(lastItem, ScrollIntoViewAlignment.Leading);
- }
+ if (VirtualView.ItemsUpdatingScrollMode == ItemsUpdatingScrollMode.KeepItemsInView)
+ {
+ var firstItem = items[0];
+ // Keeps the first item in the list displayed when new items are added.
+ ListViewBase.ScrollIntoView(firstItem);
+ }
+
+ if (VirtualView.ItemsUpdatingScrollMode == ItemsUpdatingScrollMode.KeepLastItemInView)
+ {
+ var lastItem = items[itemsCount - 1];
+ // Adjusts the scroll offset to keep the last item in the list displayed when new items are added.
+ ListViewBase.ScrollIntoView(lastItem, ScrollIntoViewAlignment.Leading);
+ }
+ });
}
protected abstract ListViewBase SelectListViewBase();
@@ -184,7 +194,12 @@ private void CleanUpCollectionViewSource(ListViewBase platformView)
foreach (var item in platformView.GetChildren())
{
var element = item.GetVisualElement();
- VirtualView.RemoveLogicalChild(element);
+
+ if (element is not null)
+ {
+ element.DisconnectHandlers();
+ VirtualView.RemoveLogicalChild(element);
+ }
}
}
diff --git a/src/Controls/src/Core/Handlers/Items/SelectableItemsViewHandler.Android.cs b/src/Controls/src/Core/Handlers/Items/SelectableItemsViewHandler.Android.cs
index d32b9d489d67..a4c384b72331 100644
--- a/src/Controls/src/Core/Handlers/Items/SelectableItemsViewHandler.Android.cs
+++ b/src/Controls/src/Core/Handlers/Items/SelectableItemsViewHandler.Android.cs
@@ -15,6 +15,13 @@ public static void MapSelectedItems(SelectableItemsViewHandler handl
=> handler.PlatformView.UpdateSelection(itemsView);
public static void MapSelectionMode(SelectableItemsViewHandler handler, SelectableItemsView itemsView)
- => handler.PlatformView.UpdateSelection(itemsView);
+ {
+ // CollectionView (the only SelectableItemsView today) uses ReorderableItemsViewAdapter at runtime.
+ // The cast is intentional; invariant generics prevent casting through the SelectableItemsViewAdapter base.
+ var adapter = handler.PlatformView.GetAdapter() as ReorderableItemsViewAdapter;
+ adapter?.UpdateSelectionMode();
+
+ handler.PlatformView.UpdateSelection(itemsView);
+ }
}
}
diff --git a/src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Android.cs b/src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Android.cs
index dcf87f722158..109892b41cb3 100644
--- a/src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Android.cs
+++ b/src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Android.cs
@@ -21,7 +21,14 @@ public static void MapFooterTemplate(StructuredItemsViewHandler hand
}
public static void MapItemsLayout(StructuredItemsViewHandler handler, StructuredItemsView itemsView)
- => (handler.PlatformView as IMauiRecyclerView)?.UpdateLayoutManager();
+ {
+ if (handler.PlatformView is IMauiRecyclerView recyclerView)
+ {
+ recyclerView.UpdateAdapter();
+ recyclerView.UpdateScrollingMode();
+ recyclerView.UpdateLayoutManager();
+ }
+ }
public static void MapItemSizingStrategy(StructuredItemsViewHandler handler, StructuredItemsView itemsView)
=> (handler.PlatformView as IMauiRecyclerView)?.UpdateAdapter();
diff --git a/src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Windows.cs b/src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Windows.cs
index 09b8636591c6..b1476f190ba3 100644
--- a/src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Windows.cs
+++ b/src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Windows.cs
@@ -325,7 +325,10 @@ void UpdateItemsLayoutItemSpacing()
switch (ListViewBase)
{
case FormsListView formsListView:
- formsListView.ItemContainerStyle = GetVerticalItemContainerStyle(linearItemsLayout);
+ // Set the ItemContainerStyle based on the orientation
+ formsListView.ItemContainerStyle = (linearItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal)
+ ? GetHorizontalItemContainerStyle(linearItemsLayout)
+ : GetVerticalItemContainerStyle(linearItemsLayout);
break;
case WListView listView:
listView.ItemContainerStyle = GetHorizontalItemContainerStyle(linearItemsLayout);
diff --git a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs
index 595e7a5a393f..04d41a5607a4 100644
--- a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs
+++ b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs
@@ -360,7 +360,20 @@ void InvalidateMeasureIfContentSizeChanged()
return _emptyUIView.Frame.Size.ToSize();
}
- return CollectionView.CollectionViewLayout.CollectionViewContentSize.ToSize();
+ var contentSize = CollectionView.CollectionViewLayout.CollectionViewContentSize.ToSize();
+
+ // For horizontal layouts, items use the CollectionView frame height as their height
+ // (ConstrainedDimension = frame height). When no items are loaded (Width == 0),
+ // contentSize.Height reflects the container's frame height rather than actual content.
+ // This creates a circular sizing issue in Auto-height containers: the frame grows
+ // based on the incorrect content height and locks in at an excessive value even after
+ // items load. Reset to 0 so MinimumHeight / HeightRequest can determine the correct size.
+ if (IsHorizontal && contentSize.Width == 0)
+ {
+ contentSize.Height = 0;
+ }
+
+ return contentSize;
}
void ConstrainItemsToBounds()
diff --git a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs
index 428d8c992c80..277a61adae28 100644
--- a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs
+++ b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs
@@ -1,5 +1,6 @@
#nullable disable
using System;
+using System.Collections.Generic;
using System.Linq;
using CoreGraphics;
using Foundation;
@@ -119,14 +120,15 @@ protected virtual (bool VisibleItems, NSIndexPath First, NSIndexPath Center, NSI
if (collectionView is null)
return default;
- var indexPathsForVisibleItems = collectionView.IndexPathsForVisibleItems.OrderBy(x => x.Row).ToList();
+ // Sort visible item index paths by section and then by row for consistent order in both grouped and ungrouped sources
+ var indexPathsForVisibleItems = collectionView.IndexPathsForVisibleItems.OrderBy(x => x.Section).ThenBy(x => x.Row).ToList();
var visibleItems = indexPathsForVisibleItems.Count > 0;
NSIndexPath firstVisibleItemIndex = null, centerItemIndex = null, lastVisibleItemIndex = null;
if (visibleItems)
{
- firstVisibleItemIndex = indexPathsForVisibleItems.First();
+ firstVisibleItemIndex = GetFirstVisibleIndexPathUsingLayoutAttributes(collectionView, indexPathsForVisibleItems);
centerItemIndex = GetCenteredIndexPath(collectionView);
lastVisibleItemIndex = indexPathsForVisibleItems.Last();
}
@@ -164,6 +166,48 @@ static int GetItemIndex(NSIndexPath indexPath, IItemsViewSource itemSource)
return index;
}
+ static NSIndexPath GetFirstVisibleIndexPathUsingLayoutAttributes(UICollectionView collectionView, IEnumerable indexPathsForVisibleItems)
+ {
+ if (!indexPathsForVisibleItems.Any())
+ return null;
+
+ var layout = collectionView.CollectionViewLayout;
+ if (layout is null)
+ return indexPathsForVisibleItems.First();
+
+ var visibleRect = new CGRect(collectionView.ContentOffset, collectionView.Bounds.Size);
+ var layoutAttributes = layout.LayoutAttributesForElementsInRect(visibleRect);
+ if (layoutAttributes is null || layoutAttributes.Length == 0)
+ return indexPathsForVisibleItems.First();
+
+ var flowLayout = layout as UICollectionViewFlowLayout;
+ bool isVertical = flowLayout?.ScrollDirection != UICollectionViewScrollDirection.Horizontal;
+ // Find the first visible cell (not headers/footers) based on scroll direction
+ NSIndexPath firstVisibleIndexPath = null;
+ nfloat minPosition = nfloat.MaxValue;
+
+ for (int i = 0; i < layoutAttributes.Length; i++)
+ {
+ var attr = layoutAttributes[i];
+ // Skip non-cell elements (headers, footers, decorations)
+ if (attr.RepresentedElementCategory != UICollectionElementCategory.Cell)
+ continue;
+
+ // Skip items that don't intersect with visible rect
+ if (!attr.Frame.IntersectsWith(visibleRect))
+ continue;
+
+ nfloat position = isVertical ? attr.Frame.Y : attr.Frame.X;
+ if (position < minPosition)
+ {
+ minPosition = position;
+ firstVisibleIndexPath = attr.IndexPath;
+ }
+ }
+
+ return firstVisibleIndexPath ?? indexPathsForVisibleItems.First();
+ }
+
static NSIndexPath GetCenteredIndexPath(UICollectionView collectionView)
{
NSIndexPath centerItemIndex = null;
diff --git a/src/Controls/src/Core/Handlers/Items/iOS/ObservableGroupedSource.cs b/src/Controls/src/Core/Handlers/Items/iOS/ObservableGroupedSource.cs
index 3a26a708245e..516292e6ed2a 100644
--- a/src/Controls/src/Core/Handlers/Items/iOS/ObservableGroupedSource.cs
+++ b/src/Controls/src/Core/Handlers/Items/iOS/ObservableGroupedSource.cs
@@ -240,8 +240,26 @@ void Add(NotifyCollectionChangedEventArgs args)
return;
}
+ // Only count items that are actual groups (ICollection); flat items like strings or model objects
+ // must not increment _groupCount or trigger InsertSections on UICollectionView
+ int count = 0;
+
+ foreach (var item in args.NewItems)
+ {
+ // Count only ICollection items — string and plain model objects are not groups
+ if (item is ICollection)
+ {
+ count++;
+ }
+ }
+
+ // None of the new items are groups; no section changes needed
+ if (count == 0)
+ {
+ return;
+ }
+
var startIndex = args.NewStartingIndex > -1 ? args.NewStartingIndex : _groupSource.IndexOf(args.NewItems[0]);
- var count = args.NewItems.Count;
_groupCount += count;
// Adding a group will change the section index for all subsequent groups, so the easiest thing to do
@@ -270,12 +288,30 @@ void Remove(NotifyCollectionChangedEventArgs args)
return;
}
+ // Only count items that are actual groups (ICollection); flat items must not decrement
+ // _groupCount or trigger DeleteSections for sections that were never created
+ int count = 0;
+
+ foreach (var item in args.OldItems)
+ {
+ // Count only ICollection items — string and plain model objects are not groups
+ if (item is ICollection)
+ {
+ count++;
+ }
+ }
+
+ // None of the removed items are groups; no section changes needed
+ if (count == 0)
+ {
+ return;
+ }
+
// Removing a group will change the section index for all subsequent groups, so the easiest thing to do
// is to reset all the group tracking to get it up-to-date
ResetGroupTracking();
// Since we have a start index, we can be more clever about removing the item(s) (and get the nifty animations)
- var count = args.OldItems.Count;
_groupCount -= count;
// Queue up the updates to the UICollectionView
@@ -408,12 +444,26 @@ void Update(Action update)
int GroupsCount()
{
+ int count = 0;
if (_groupSource is IList list)
- return list.Count;
+ {
+ foreach (var group in list)
+ {
+ if (group is ICollection)
+ {
+ count++;
+ }
+ }
+ return count;
+ }
- int count = 0;
foreach (var item in _groupSource)
- count++;
+ {
+ if (item is ICollection)
+ {
+ count++;
+ }
+ }
return count;
}
}
diff --git a/src/Controls/src/Core/Handlers/Items/iOS/SelectableItemsViewController.cs b/src/Controls/src/Core/Handlers/Items/iOS/SelectableItemsViewController.cs
index e7d2ecb6ced8..1f00d0f482e1 100644
--- a/src/Controls/src/Core/Handlers/Items/iOS/SelectableItemsViewController.cs
+++ b/src/Controls/src/Core/Handlers/Items/iOS/SelectableItemsViewController.cs
@@ -55,37 +55,49 @@ internal void SelectItem(object selectedItem)
if (index.Section > -1 && index.Item > -1)
{
// Ensure the selected index is updated after the collection view's items generation is completed
- CollectionView.PerformBatchUpdates(null, _ =>
+ if (!CollectionView.IsLoaded())
{
- // Ensure ItemsSource hasn't been disposed
- if (ItemsSource is EmptySource)
+ CollectionView.PerformBatchUpdates(null, _ =>
{
- return;
- }
+ ValidateAndSelectItem(selectedItem, originalSource);
+ });
+ }
+ else
+ {
+ ValidateAndSelectItem(selectedItem, originalSource);
+ }
+ }
+ }
- // Exit if the ItemsSource reference no longer matches the one captured at invocation.
- if (!ReferenceEquals(ItemsView.ItemsSource, originalSource))
- {
- return;
- }
+ void ValidateAndSelectItem(object selectedItem, object originalSource)
+ {
+ // Ensure ItemsSource hasn't been disposed
+ if (ItemsSource is EmptySource)
+ {
+ return;
+ }
- // Recalculate the index for the selectedItem now that the collection may have changed.(Adding, deleting etc..)
- var updatedIndex = GetIndexForItem(selectedItem);
- if (updatedIndex.Section < 0 || updatedIndex.Item < 0)
- {
- return;
- }
+ // Exit if the ItemsSource reference no longer matches the one captured at invocation.
+ if (!ReferenceEquals(ItemsView.ItemsSource, originalSource))
+ {
+ return;
+ }
- // Retrieve the current item at that index and verify it still equals the intended selection.
- var liveItem = GetItemAtIndex(updatedIndex);
- if (!Equals(liveItem, selectedItem))
- {
- return;
- }
+ // Recalculate the index for the selectedItem now that the collection may have changed.(Adding, deleting etc..)
+ var updatedIndex = GetIndexForItem(selectedItem);
+ if (updatedIndex.Section < 0 || updatedIndex.Item < 0)
+ {
+ return;
+ }
- CollectionView.SelectItem(index, true, UICollectionViewScrollPosition.None);
- });
+ // Retrieve the current item at that index and verify it still equals the intended selection.
+ var liveItem = GetItemAtIndex(updatedIndex);
+ if (!Equals(liveItem, selectedItem))
+ {
+ return;
}
+
+ CollectionView.SelectItem(updatedIndex, true, UICollectionViewScrollPosition.None);
}
// Called by Forms to clear the native selection
diff --git a/src/Controls/src/Core/Handlers/Items2/CarouselViewHandler2.iOS.cs b/src/Controls/src/Core/Handlers/Items2/CarouselViewHandler2.iOS.cs
index e439eeda3dc4..a43bbcb51680 100644
--- a/src/Controls/src/Core/Handlers/Items2/CarouselViewHandler2.iOS.cs
+++ b/src/Controls/src/Core/Handlers/Items2/CarouselViewHandler2.iOS.cs
@@ -22,7 +22,7 @@ public CarouselViewHandler2(PropertyMapper mapper = null) : base(mapper ?? Mappe
public static PropertyMapper Mapper = new(ItemsViewMapper)
{
-
+ [Controls.VisualElement.IsEnabledProperty.PropertyName] = MapIsEnabled,
[Controls.CarouselView.IsSwipeEnabledProperty.PropertyName] = MapIsSwipeEnabled,
[Controls.CarouselView.PeekAreaInsetsProperty.PropertyName] = MapPeekAreaInsets,
[Controls.CarouselView.IsBounceEnabledProperty.PropertyName] = MapIsBounceEnabled,
@@ -69,6 +69,12 @@ protected override void ScrollToRequested(object sender, ScrollToRequestEventArg
}
}
+ // TODO: Change the modifier to public in .NET 11.
+ internal static void MapIsEnabled(CarouselViewHandler2 handler, CarouselView carouselView)
+ {
+ handler.Controller?.CollectionView?.UpdateIsEnabled(carouselView);
+ }
+
public static void MapIsSwipeEnabled(CarouselViewHandler2 handler, CarouselView carouselView)
{
handler.Controller.CollectionView.ScrollEnabled = carouselView.IsSwipeEnabled;
diff --git a/src/Controls/src/Core/Handlers/Items2/CollectionViewHandler2.iOS.cs b/src/Controls/src/Core/Handlers/Items2/CollectionViewHandler2.iOS.cs
index f708cf9fea54..7ed8b7eea894 100644
--- a/src/Controls/src/Core/Handlers/Items2/CollectionViewHandler2.iOS.cs
+++ b/src/Controls/src/Core/Handlers/Items2/CollectionViewHandler2.iOS.cs
@@ -82,6 +82,7 @@ internal void SetCachedFirstItemSize(CoreGraphics.CGSize size)
[StructuredItemsView.FooterTemplateProperty.PropertyName] = MapFooterTemplate,
[StructuredItemsView.HeaderProperty.PropertyName] = MapHeaderTemplate,
[StructuredItemsView.FooterProperty.PropertyName] = MapFooterTemplate,
+ [StructuredItemsView.ItemsLayoutProperty.PropertyName] = MapItemsLayout,
[StructuredItemsView.ItemSizingStrategyProperty.PropertyName] = MapItemSizingStrategy,
[GroupableItemsView.GroupHeaderTemplateProperty.PropertyName] = MapHeaderTemplate,
[GroupableItemsView.GroupFooterTemplateProperty.PropertyName] = MapFooterTemplate,
@@ -186,8 +187,6 @@ protected override UICollectionViewLayout SelectLayout()
var itemSizingStrategy = ItemsView.ItemSizingStrategy;
var itemsLayout = ItemsView.ItemsLayout;
- SubscribeToItemsLayoutPropertyChanged(itemsLayout);
-
if (itemsLayout is GridItemsLayout gridItemsLayout)
{
return LayoutFactory2.CreateGrid(gridItemsLayout, groupInfo, headerFooterInfo);
@@ -219,6 +218,7 @@ public static void MapFooterTemplate(CollectionViewHandler2 handler, StructuredI
public static void MapItemsLayout(CollectionViewHandler2 handler, StructuredItemsView itemsView)
{
+ handler.UpdateItemsLayoutSubscription(itemsView.ItemsLayout);
handler.UpdateLayout();
}
@@ -227,23 +227,44 @@ public static void MapItemSizingStrategy(CollectionViewHandler2 handler, Structu
handler.UpdateLayout();
}
- void SubscribeToItemsLayoutPropertyChanged(IItemsLayout itemsLayout)
+ IItemsLayout _subscribedItemsLayout;
+
+ protected override void DisconnectHandler(UIView platformView)
+ {
+ base.DisconnectHandler(platformView);
+ UpdateItemsLayoutSubscription(null);
+ }
+
+ internal void UpdateItemsLayoutSubscription(IItemsLayout newLayout)
{
- if (itemsLayout is not null)
+ if (_subscribedItemsLayout == newLayout)
{
- itemsLayout.PropertyChanged += (sender, args) =>
- {
- if (args.PropertyName == nameof(ItemsLayout.SnapPointsAlignment) ||
- args.PropertyName == nameof(ItemsLayout.SnapPointsType) ||
- args.PropertyName == nameof(GridItemsLayout.VerticalItemSpacing) ||
- args.PropertyName == nameof(GridItemsLayout.HorizontalItemSpacing) ||
- args.PropertyName == nameof(GridItemsLayout.Span) ||
- args.PropertyName == nameof(LinearItemsLayout.ItemSpacing))
-
- {
- UpdateLayout();
- }
- };
+ return;
+ }
+
+ if (_subscribedItemsLayout is not null)
+ {
+ _subscribedItemsLayout.PropertyChanged -= OnItemsLayoutPropertyChanged;
+ }
+
+ _subscribedItemsLayout = newLayout;
+
+ if (_subscribedItemsLayout is not null)
+ {
+ _subscribedItemsLayout.PropertyChanged += OnItemsLayoutPropertyChanged;
+ }
+ }
+
+ void OnItemsLayoutPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs args)
+ {
+ if (args.PropertyName == nameof(ItemsLayout.SnapPointsAlignment) ||
+ args.PropertyName == nameof(ItemsLayout.SnapPointsType) ||
+ args.PropertyName == nameof(GridItemsLayout.VerticalItemSpacing) ||
+ args.PropertyName == nameof(GridItemsLayout.HorizontalItemSpacing) ||
+ args.PropertyName == nameof(GridItemsLayout.Span) ||
+ args.PropertyName == nameof(LinearItemsLayout.ItemSpacing))
+ {
+ UpdateLayout();
}
}
}
diff --git a/src/Controls/src/Core/Handlers/Items2/ItemsViewHandler2.iOS.cs b/src/Controls/src/Core/Handlers/Items2/ItemsViewHandler2.iOS.cs
index b56dcd9f4ef9..8f55ca5eb2d0 100644
--- a/src/Controls/src/Core/Handlers/Items2/ItemsViewHandler2.iOS.cs
+++ b/src/Controls/src/Core/Handlers/Items2/ItemsViewHandler2.iOS.cs
@@ -197,11 +197,65 @@ Size EnsureContentSizeForScrollDirection(double widthConstraint, double heightCo
var scrollDirection = Controller.GetScrollDirection();
// If contentSize is zero in the relevant dimension (height for vertical, width for horizontal),
- // it means none of the content has been realized yet; we need to return the expansive size
- // the collection view wants by default to get it to start measuring its content
+ // it means none of the content has been realized yet.
if ((scrollDirection == UICollectionViewScrollDirection.Vertical && contentSize.Height == 0) ||
(scrollDirection == UICollectionViewScrollDirection.Horizontal && contentSize.Width == 0))
{
+ var collectionView = Controller.CollectionView;
+
+ // When the CollectionView has not yet been added to a window (pre-mount measurement),
+ // UICollectionViewCompositionalLayout hasn't run a layout pass and therefore
+ // CollectionViewContentSize is still zero. Force a layout pass with the given constraints
+ // so the layout can compute actual content size from its items.
+ if (collectionView.Window == null)
+ {
+ // Local helper to clamp layout constraints to finite, non-negative nfloat values.
+ nfloat ClampConstraint(double constraint, nfloat fallback)
+ {
+ // Treat NaN, infinity, and negative values as invalid and fall back.
+ if (double.IsNaN(constraint) || double.IsInfinity(constraint) || constraint < 0)
+ return fallback;
+
+ var value = (nfloat)constraint;
+
+ // Guard against overflow to infinity/NaN or negative after casting.
+ var valueAsDouble = (double)value;
+ if (double.IsNaN(valueAsDouble) || double.IsInfinity(valueAsDouble) || value < 0)
+ return fallback;
+
+ return value;
+ }
+
+ var previousFrame = collectionView.Frame;
+ try
+ {
+ // Give the CollectionView a finite available size so the layout calculates correctly
+ var frameWidth = ClampConstraint(widthConstraint, UIView.UILayoutFittingExpandedSize.Width);
+ var frameHeight = ClampConstraint(heightConstraint, UIView.UILayoutFittingExpandedSize.Height);
+
+ collectionView.Frame = new CoreGraphics.CGRect(0, 0, frameWidth, frameHeight);
+ collectionView.SetNeedsLayout();
+ collectionView.LayoutIfNeeded();
+
+ // Re-read the content size now that the layout has run
+ contentSize = Controller.GetSize();
+ }
+ finally
+ {
+ // Always restore the original frame
+ collectionView.Frame = previousFrame;
+ }
+
+ // If the forced layout produced a valid size, return it directly
+ if ((scrollDirection == UICollectionViewScrollDirection.Vertical && contentSize.Height > 0) ||
+ (scrollDirection == UICollectionViewScrollDirection.Horizontal && contentSize.Width > 0))
+ {
+ return contentSize;
+ }
+ }
+
+ // Fallback: return the expansive size the collection view wants by default
+ // to get it to start measuring its content
var desiredSize = base.GetDesiredSize(widthConstraint, heightConstraint);
if (scrollDirection == UICollectionViewScrollDirection.Vertical)
{
@@ -210,6 +264,14 @@ Size EnsureContentSizeForScrollDirection(double widthConstraint, double heightCo
else
{
contentSize.Width = desiredSize.Width;
+
+ // For horizontal layouts, items use FractionalHeight(1f), meaning their height equals
+ // the CollectionView's current frame height. When no items are loaded (Width == 0),
+ // contentSize.Height reflects the container's frame height rather than actual content.
+ // This creates a circular sizing issue in Auto-height containers: the frame grows based
+ // on the incorrect content height and stays locked in even after items load.
+ // Reset to 0 so that MinimumHeight / HeightRequest can determine the correct size.
+ contentSize.Height = 0;
}
}
diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewDelegator2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewDelegator2.cs
index 5fd94e697991..f51ae203b5af 100644
--- a/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewDelegator2.cs
+++ b/src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewDelegator2.cs
@@ -1,5 +1,6 @@
#nullable disable
using System;
+using System.Collections.Generic;
using System.Linq;
using CoreGraphics;
using Foundation;
@@ -120,14 +121,15 @@ protected virtual (bool VisibleItems, NSIndexPath First, NSIndexPath Center, NSI
if (collectionView is null)
return default;
- var indexPathsForVisibleItems = collectionView.IndexPathsForVisibleItems.OrderBy(x => x.Row).ToList();
+ // Sort visible item index paths by section and then by row for consistent order in both grouped and ungrouped sources
+ var indexPathsForVisibleItems = collectionView.IndexPathsForVisibleItems.OrderBy(x => x.Section).ThenBy(x => x.Row).ToList();
var visibleItems = indexPathsForVisibleItems.Count > 0;
NSIndexPath firstVisibleItemIndex = null, centerItemIndex = null, lastVisibleItemIndex = null;
if (visibleItems)
{
- firstVisibleItemIndex = indexPathsForVisibleItems.First();
+ firstVisibleItemIndex = GetFirstVisibleIndexPathUsingLayoutAttributes(collectionView, indexPathsForVisibleItems);
centerItemIndex = GetCenteredIndexPath(collectionView);
lastVisibleItemIndex = indexPathsForVisibleItems.Last();
}
@@ -165,6 +167,48 @@ static int GetItemIndex(NSIndexPath indexPath, IItemsViewSource itemSource)
return index;
}
+ static NSIndexPath GetFirstVisibleIndexPathUsingLayoutAttributes(UICollectionView collectionView, IEnumerable indexPathsForVisibleItems)
+ {
+ if (!indexPathsForVisibleItems.Any())
+ return null;
+
+ var layout = collectionView.CollectionViewLayout;
+ if (layout is null)
+ return indexPathsForVisibleItems.First();
+
+ var visibleRect = new CGRect(collectionView.ContentOffset, collectionView.Bounds.Size);
+ var layoutAttributes = layout.LayoutAttributesForElementsInRect(visibleRect);
+ if (layoutAttributes is null || layoutAttributes.Length == 0)
+ return indexPathsForVisibleItems.First();
+
+ var flowLayout = layout as UICollectionViewFlowLayout;
+ bool isVertical = flowLayout?.ScrollDirection != UICollectionViewScrollDirection.Horizontal;
+ // Find the first visible cell (not headers/footers) based on scroll direction
+ NSIndexPath firstVisibleIndexPath = null;
+ nfloat minPosition = nfloat.MaxValue;
+
+ for (int i = 0; i < layoutAttributes.Length; i++)
+ {
+ var attr = layoutAttributes[i];
+ // Skip non-cell elements (headers, footers, decorations)
+ if (attr.RepresentedElementCategory != UICollectionElementCategory.Cell)
+ continue;
+
+ // Skip items that don't intersect with visible rect
+ if (!attr.Frame.IntersectsWith(visibleRect))
+ continue;
+
+ nfloat position = isVertical ? attr.Frame.Y : attr.Frame.X;
+ if (position < minPosition)
+ {
+ minPosition = position;
+ firstVisibleIndexPath = attr.IndexPath;
+ }
+ }
+
+ return firstVisibleIndexPath ?? indexPathsForVisibleItems.First();
+ }
+
static NSIndexPath GetCenteredIndexPath(UICollectionView collectionView)
{
NSIndexPath centerItemIndex = null;
diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cs
index a61b16aa851f..235472aeab54 100644
--- a/src/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cs
+++ b/src/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cs
@@ -93,7 +93,7 @@ static UICollectionViewLayout CreateListLayout(UICollectionViewScrollDirection s
//create global header and footer
layoutConfiguration.BoundarySupplementaryItems = CreateSupplementaryItems(null, layoutHeaderFooterInfo, scrollDirection, groupWidth, groupHeight);
- var layout = new CustomUICollectionViewCompositionalLayout(snapInfo, (sectionIndex, environment) =>
+ var layout = new CustomUICollectionViewCompositionalLayout(snapInfo, groupingInfo, layoutHeaderFooterInfo, (sectionIndex, environment) =>
{
// Each item has a size
var itemSize = NSCollectionLayoutSize.Create(itemWidth, itemHeight);
@@ -151,7 +151,7 @@ static UICollectionViewLayout CreateGridLayout(UICollectionViewScrollDirection s
var layoutConfiguration = new UICollectionViewCompositionalLayoutConfiguration();
layoutConfiguration.ScrollDirection = scrollDirection;
- var layout = new CustomUICollectionViewCompositionalLayout(snapInfo, (sectionIndex, environment) =>
+ var layout = new CustomUICollectionViewCompositionalLayout(snapInfo, groupingInfo, headerFooterInfo, (sectionIndex, environment) =>
{
// Each item has a size
var itemSize = NSCollectionLayoutSize.Create(itemWidth, itemHeight);
@@ -450,11 +450,15 @@ class CustomUICollectionViewCompositionalLayout : UICollectionViewCompositionalL
{
LayoutSnapInfo _snapInfo;
ItemsUpdatingScrollMode _itemsUpdatingScrollMode;
+ LayoutGroupingInfo? _groupingInfo;
+ LayoutHeaderFooterInfo? _headerFooterInfo;
- public CustomUICollectionViewCompositionalLayout(LayoutSnapInfo snapInfo, UICollectionViewCompositionalLayoutSectionProvider sectionProvider, UICollectionViewCompositionalLayoutConfiguration configuration, ItemsUpdatingScrollMode itemsUpdatingScrollMode) : base(sectionProvider, configuration)
+ public CustomUICollectionViewCompositionalLayout(LayoutSnapInfo snapInfo, LayoutGroupingInfo? groupingInfo, LayoutHeaderFooterInfo? headerFooterInfo, UICollectionViewCompositionalLayoutSectionProvider sectionProvider, UICollectionViewCompositionalLayoutConfiguration configuration, ItemsUpdatingScrollMode itemsUpdatingScrollMode) : base(sectionProvider, configuration)
{
_snapInfo = snapInfo;
_itemsUpdatingScrollMode = itemsUpdatingScrollMode;
+ _groupingInfo = groupingInfo;
+ _headerFooterInfo = headerFooterInfo;
}
public override void FinalizeCollectionViewUpdates()
@@ -558,6 +562,33 @@ public override CGPoint TargetContentOffset(CGPoint proposedContentOffset, CGPoi
Configuration.ScrollDirection);
}
+ public override CGSize CollectionViewContentSize
+ {
+ get
+ {
+ if (CollectionView != null)
+ {
+ bool hasGlobalHeaders = _headerFooterInfo?.HasHeader == true || _headerFooterInfo?.HasFooter == true;
+ bool hasGroupHeaders = _groupingInfo?.HasHeader == true || _groupingInfo?.HasFooter == true;
+
+ if (hasGlobalHeaders || hasGroupHeaders)
+ {
+ return base.CollectionViewContentSize;
+ }
+
+ if (CollectionView.NumberOfSections() > 0 &&
+ CollectionView.NumberOfItemsInSection(0) > 0)
+ {
+ return base.CollectionViewContentSize;
+ }
+
+ return CGSize.Empty;
+ }
+
+ return base.CollectionViewContentSize;
+ }
+ }
+
CGPoint ScrollSingle(SnapPointsAlignment alignment, CGPoint proposedContentOffset, CGPoint scrollingVelocity)
{
// Get the viewport of the UICollectionView at the current content offset
diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/SelectableItemsViewController2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/SelectableItemsViewController2.cs
index 74f14b077884..cc02ddde501e 100644
--- a/src/Controls/src/Core/Handlers/Items2/iOS/SelectableItemsViewController2.cs
+++ b/src/Controls/src/Core/Handlers/Items2/iOS/SelectableItemsViewController2.cs
@@ -56,37 +56,49 @@ internal void SelectItem(object selectedItem)
if (index.Section > -1 && index.Item > -1)
{
// Ensure the selected index is updated after the collection view's items generation is completed
- CollectionView.PerformBatchUpdates(null, _ =>
+ if (!CollectionView.IsLoaded())
{
- // Ensure ItemsSource hasn't been disposed
- if (ItemsSource is Items.EmptySource)
+ CollectionView.PerformBatchUpdates(null, _ =>
{
- return;
- }
+ ValidateAndSelectItem(selectedItem, originalSource);
+ });
+ }
+ else
+ {
+ ValidateAndSelectItem(selectedItem, originalSource);
+ }
+ }
+ }
- // Exit if the ItemsSource reference no longer matches the one captured at invocation.
- if (!ReferenceEquals(ItemsView.ItemsSource, originalSource))
- {
- return;
- }
+ private void ValidateAndSelectItem(object selectedItem, object originalSource)
+ {
+ // Ensure ItemsSource hasn't been disposed
+ if (ItemsSource is Items.EmptySource)
+ {
+ return;
+ }
- // Recalculate the index for the selectedItem now that the collection may have changed.(Adding, deleting etc..)
- var updatedIndex = GetIndexForItem(selectedItem);
- if (updatedIndex.Section < 0 || updatedIndex.Item < 0)
- {
- return;
- }
+ // Exit if the ItemsSource reference no longer matches the one captured at invocation.
+ if (!ReferenceEquals(ItemsView.ItemsSource, originalSource))
+ {
+ return;
+ }
- // Retrieve the current item at that index and verify it still equals the intended selection.
- var liveItem = GetItemAtIndex(updatedIndex);
- if (!Equals(liveItem, selectedItem))
- {
- return;
- }
+ // Recalculate the index for the selectedItem now that the collection may have changed.(Adding, deleting etc..)
+ var updatedIndex = GetIndexForItem(selectedItem);
+ if (updatedIndex.Section < 0 || updatedIndex.Item < 0)
+ {
+ return;
+ }
- CollectionView.SelectItem(index, true, UICollectionViewScrollPosition.None);
- });
+ // Retrieve the current item at that index and verify it still equals the intended selection.
+ var liveItem = GetItemAtIndex(updatedIndex);
+ if (!Equals(liveItem, selectedItem))
+ {
+ return;
}
+
+ CollectionView.SelectItem(updatedIndex, true, UICollectionViewScrollPosition.None);
}
// Called by Forms to clear the native selection
diff --git a/src/Controls/src/Core/Handlers/Shell/ShellHandler.Windows.cs b/src/Controls/src/Core/Handlers/Shell/ShellHandler.Windows.cs
index 22b3572a6a28..a1308c79b9eb 100644
--- a/src/Controls/src/Core/Handlers/Shell/ShellHandler.Windows.cs
+++ b/src/Controls/src/Core/Handlers/Shell/ShellHandler.Windows.cs
@@ -43,6 +43,7 @@ private void OnLoaded(object sender, UI.Xaml.RoutedEventArgs e)
UpdateValue(nameof(Shell.FlyoutIcon));
UpdateValue(nameof(Shell.FlyoutBackground));
UpdateValue(nameof(Shell.FlyoutBackgroundImage));
+ UpdateValue(nameof(Shell.FlyoutVerticalScrollMode));
}
protected override void DisconnectHandler(ShellView platformView)
diff --git a/src/Controls/src/Core/Handlers/Shell/ShellHandler.cs b/src/Controls/src/Core/Handlers/Shell/ShellHandler.cs
index 47b43fed67e1..d93a1c779f73 100644
--- a/src/Controls/src/Core/Handlers/Shell/ShellHandler.cs
+++ b/src/Controls/src/Core/Handlers/Shell/ShellHandler.cs
@@ -37,6 +37,7 @@ public partial class ShellHandler
[nameof(Shell.FlowDirection)] = MapFlowDirection,
[nameof(Shell.FlyoutBackgroundImage)] = MapFlyoutBackgroundImage,
[nameof(Shell.FlyoutBackgroundImageAspect)] = MapFlyoutBackgroundImage,
+ [nameof(Shell.FlyoutVerticalScrollMode)] = MapFlyoutVerticalScrollMode,
#endif
};
diff --git a/src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs b/src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs
index cd006d288762..545ce838cabc 100644
--- a/src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs
+++ b/src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs
@@ -78,6 +78,14 @@ internal static IMauiHandlersCollection AddControlsHandlers(this IMauiHandlersCo
handlersCollection.AddHandler();
handlersCollection.AddHandler();
handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
}
else
{
@@ -86,6 +94,14 @@ internal static IMauiHandlersCollection AddControlsHandlers(this IMauiHandlersCo
handlersCollection.AddHandler();
handlersCollection.AddHandler();
handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
}
#else
handlersCollection.AddHandler();
@@ -93,22 +109,22 @@ internal static IMauiHandlersCollection AddControlsHandlers(this IMauiHandlersCo
handlersCollection.AddHandler();
handlersCollection.AddHandler();
handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
+ handlersCollection.AddHandler();
#endif
handlersCollection.AddHandler();
- handlersCollection.AddHandler();
handlersCollection.AddHandler();
handlersCollection.AddHandler();
- handlersCollection.AddHandler();
- handlersCollection.AddHandler();
handlersCollection.AddHandler();
- handlersCollection.AddHandler();
handlersCollection.AddHandler();
- handlersCollection.AddHandler();
handlersCollection.AddHandler();
- handlersCollection.AddHandler();
- handlersCollection.AddHandler();
handlersCollection.AddHandler();
- handlersCollection.AddHandler();
handlersCollection.AddHandler();
handlersCollection.AddHandler();
if (RuntimeFeature.IsHybridWebViewSupported)
@@ -116,6 +132,7 @@ internal static IMauiHandlersCollection AddControlsHandlers(this IMauiHandlersCo
// NOTE: not registered under NativeAOT or TrimMode=Full scenarios
handlersCollection.AddHandler();
}
+
handlersCollection.AddHandler();
handlersCollection.AddHandler();
handlersCollection.AddHandler();
diff --git a/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs b/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs
index cc14cea74c5f..9e1ee609a23e 100644
--- a/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs
+++ b/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs
@@ -1,4 +1,8 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
using System.ComponentModel;
+using System.Linq;
using Microsoft.Maui.Controls.Shapes;
using Microsoft.Maui.Graphics;
@@ -161,10 +165,44 @@ void BindIndicatorItems()
}
});
- BindableLayout.SetItemsSource(this, _indicatorView.ItemsSource);
+ // Get the filtered items source based on MaximumVisible
+ var itemsSource = GetFilteredItemsSource();
+ BindableLayout.SetItemsSource(this, itemsSource);
+
BindableLayout.SetItemTemplate(this, indicatorTemplate);
}
+ IEnumerable? GetFilteredItemsSource()
+ {
+ if (_indicatorView.ItemsSource is null || _indicatorView.MaximumVisible <= 0)
+ {
+ return null;
+ }
+
+ var itemsSource = _indicatorView.ItemsSource;
+ int totalCount = itemsSource is ICollection col
+ ? col.Count
+ : itemsSource.Cast().Count();
+
+ if (totalCount <= _indicatorView.MaximumVisible)
+ {
+ return itemsSource;
+ }
+
+ var filteredItems = new List(_indicatorView.MaximumVisible);
+ foreach (var item in itemsSource)
+ {
+ if (filteredItems.Count >= _indicatorView.MaximumVisible)
+ {
+ break;
+ }
+
+ filteredItems.Add(item);
+ }
+
+ return filteredItems;
+ }
+
public void Remove()
{
_indicatorView.PropertyChanged -= IndicatorViewPropertyChanged;
diff --git a/src/Controls/src/Core/Internals/SwipeGestureExtensions.cs b/src/Controls/src/Core/Internals/SwipeGestureExtensions.cs
new file mode 100644
index 000000000000..4d51af2d1c47
--- /dev/null
+++ b/src/Controls/src/Core/Internals/SwipeGestureExtensions.cs
@@ -0,0 +1,72 @@
+using System;
+
+namespace Microsoft.Maui.Controls.Internals
+{
+ ///
+ /// Extension methods for swipe gesture-related operations
+ ///
+ internal static class SwipeGestureExtensions
+ {
+ ///
+ /// Normalizes a rotation angle to be within the range [0, 360)
+ ///
+ /// The rotation angle in degrees
+ /// The normalized rotation angle in the range [0, 360)
+ internal static double NormalizeRotation(this double rotation)
+ {
+ return ((rotation % 360) + 360) % 360;
+ }
+
+ internal static SwipeDirection TransformSwipeDirectionForRotation(SwipeDirection direction, double rotation)
+ {
+ if (double.IsNaN(rotation) || double.IsInfinity(rotation))
+ {
+ return direction;
+ }
+
+ // Normalize rotation to [0, 360) range to handle negative angles and values > 360
+ var normalizedRotation = rotation.NormalizeRotation();
+
+ // Round to nearest 90-degree increment (0, 90, 180, 270) to work with cardinal rotations
+ var rotationRounded = Math.Round(normalizedRotation / 90) * 90;
+
+ // Only transform direction if rotation is close to a cardinal angle (within 45 degrees) to prevent transformation for arbitrary rotations
+ if (Math.Abs(normalizedRotation - rotationRounded) > 45)
+ {
+ return direction;
+ }
+
+ // Calculate rotation steps as multiples of 90 degrees (0=0°, 1=90°, 2=180°, 3=270°) for directional transformation
+ var rotationSteps = (int)(rotationRounded / 90) % 4;
+ return rotationSteps switch
+ {
+ 0 => direction, // No rotation
+ 1 => direction switch // 90° clockwise
+ {
+ SwipeDirection.Up => SwipeDirection.Right,
+ SwipeDirection.Right => SwipeDirection.Down,
+ SwipeDirection.Down => SwipeDirection.Left,
+ SwipeDirection.Left => SwipeDirection.Up,
+ _ => direction
+ },
+ 2 => direction switch // 180°
+ {
+ SwipeDirection.Up => SwipeDirection.Down,
+ SwipeDirection.Right => SwipeDirection.Left,
+ SwipeDirection.Down => SwipeDirection.Up,
+ SwipeDirection.Left => SwipeDirection.Right,
+ _ => direction
+ },
+ 3 => direction switch // 270° clockwise (90° counter-clockwise)
+ {
+ SwipeDirection.Up => SwipeDirection.Left,
+ SwipeDirection.Right => SwipeDirection.Up,
+ SwipeDirection.Down => SwipeDirection.Right,
+ SwipeDirection.Left => SwipeDirection.Down,
+ _ => direction
+ },
+ _ => direction
+ };
+ }
+ }
+}
diff --git a/src/Controls/src/Core/Items/ItemsView.cs b/src/Controls/src/Core/Items/ItemsView.cs
index 5d2aee9be013..31c7a99c26f2 100644
--- a/src/Controls/src/Core/Items/ItemsView.cs
+++ b/src/Controls/src/Core/Items/ItemsView.cs
@@ -223,6 +223,9 @@ public ItemsUpdatingScrollMode ItemsUpdatingScrollMode
public void ScrollTo(int index, int groupIndex = -1,
ScrollToPosition position = ScrollToPosition.MakeVisible, bool animate = true)
{
+ if (DismissScroll())
+ return;
+
OnScrollToRequested(new ScrollToRequestEventArgs(index, groupIndex, position, animate));
}
@@ -236,6 +239,9 @@ public void ScrollTo(int index, int groupIndex = -1,
public void ScrollTo(object item, object group = null,
ScrollToPosition position = ScrollToPosition.MakeVisible, bool animate = true)
{
+ if (DismissScroll())
+ return;
+
OnScrollToRequested(new ScrollToRequestEventArgs(item, group, position, animate));
}
@@ -312,5 +318,10 @@ private protected override string GetDebuggerDisplay()
var itemsSourceText = DebuggerDisplayHelpers.GetDebugText(nameof(ItemsSource), ItemsSource?.GetType());
return $"{base.GetDebuggerDisplay()}, {itemsSourceText}";
}
+
+ private bool DismissScroll()
+ {
+ return Handler is null || ItemsSource is null;
+ }
}
}
diff --git a/src/Controls/src/Core/Layout/FlexLayout.cs b/src/Controls/src/Core/Layout/FlexLayout.cs
index 5c66c4aeeb9f..3554518e20e0 100644
--- a/src/Controls/src/Core/Layout/FlexLayout.cs
+++ b/src/Controls/src/Core/Layout/FlexLayout.cs
@@ -543,10 +543,15 @@ void AddFlexItem(int index, IView child)
else
{
// Arrange pass, do not ever run a measure here!
+ // When WidthRequest/HeightRequest is explicitly set, the Flex.Item already
+ // has the correct value from EnsureFlexItemPropertiesUpdated(). Return NaN
+ // so layout_item preserves that value instead of overwriting it with a
+ // potentially stale DesiredSize from a previous measure pass. This fixes
+ // dynamic WidthRequest not updating on Android (see #31109).
request = child.DesiredSize;
}
- w = (float)request.Width;
- h = (float)request.Height;
+ w = (!inMeasureMode && !float.IsNaN(it.Width)) ? float.NaN : (float)request.Width;
+ h = (!inMeasureMode && !float.IsNaN(it.Height)) ? float.NaN : (float)request.Height;
};
}
diff --git a/src/Controls/src/Core/NavigationPage/NavigationPageToolbar.cs b/src/Controls/src/Core/NavigationPage/NavigationPageToolbar.cs
index 5933f6f3445d..6c2af2e75508 100644
--- a/src/Controls/src/Core/NavigationPage/NavigationPageToolbar.cs
+++ b/src/Controls/src/Core/NavigationPage/NavigationPageToolbar.cs
@@ -181,9 +181,12 @@ void UpdateBackButton()
// Set this before BackButtonVisible triggers an update to the handler
// This way all useful information is present
+ // Show drawer toggle (hamburger icon) when FlyoutPage should display toolbar button
+ // and either we're on the root page or the back button is explicitly hidden.
+ // This ensures flyout access remains available even when back button is disabled (#21646)
if (Parent is FlyoutPage flyout && flyout.ShouldShowToolbarButton()
#if !WINDOWS // TODO NET 10 : Move this logic to ShouldShowToolbarButton
- && !anyPagesPushed.Value
+ && (!anyPagesPushed.Value || !BackButtonVisible)
#endif
)
_drawerToggleVisible = true;
diff --git a/src/Controls/src/Core/Platform/Android/Extensions/EditTextExtensions.cs b/src/Controls/src/Core/Platform/Android/Extensions/EditTextExtensions.cs
index a686a2837943..b420272e64d9 100644
--- a/src/Controls/src/Core/Platform/Android/Extensions/EditTextExtensions.cs
+++ b/src/Controls/src/Core/Platform/Android/Extensions/EditTextExtensions.cs
@@ -1,4 +1,5 @@
#nullable disable
+using System;
using Android.Text;
using Android.Widget;
using AndroidX.Core.Widget;
@@ -56,9 +57,35 @@ internal static void UpdateTextFromPlatform(this EditText editText, InputView in
if (oldText != newText)
{
- // This update is happening while inputting text into the EditText, so we want to avoid
- // resettting the cursor position and selection
- editText.SetTextKeepState(newText);
+ // Use Editable.Replace() instead of SetTextKeepState() to avoid re-entering setText()
+ // inside afterTextChanged.
+ // SetTextKeepState() causes EmojiTextWatcher to crash with stale span positions
+ // when a StringFormat binding updates the text programmatically.
+ // https://github.com/dotnet/maui/issues/25728
+ var editable = editText.EditableText;
+
+ if (editable is not null)
+ {
+ // Preserve cursor position across the replace (matches SetTextKeepState behavior)
+ int selStart = editText.SelectionStart;
+ int selEnd = editText.SelectionEnd;
+
+ editable.Replace(0, editable.Length(), newText);
+
+ // Clamp selection to new text length before restoring
+ int len = editText.Text?.Length ?? 0;
+ selStart = Math.Min(selStart, len);
+ selEnd = Math.Min(selEnd, len);
+
+ if (selStart >= 0 && selEnd >= selStart)
+ {
+ editText.SetSelection(selStart, selEnd);
+ }
+ }
+ else
+ {
+ editText.SetTextKeepState(newText);
+ }
}
}
}
diff --git a/src/Controls/src/Core/Platform/Android/Extensions/FormattedStringExtensions.cs b/src/Controls/src/Core/Platform/Android/Extensions/FormattedStringExtensions.cs
index 62cf61d1aca0..f60a47dc86e3 100644
--- a/src/Controls/src/Core/Platform/Android/Extensions/FormattedStringExtensions.cs
+++ b/src/Controls/src/Core/Platform/Android/Extensions/FormattedStringExtensions.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Text;
using Android.Content;
using Android.Graphics;
@@ -83,12 +84,12 @@ public static SpannableString ToSpannableString(
if (span.LineHeight >= 0)
spannable.SetSpan(new PlatformLineHeightSpan(context, (float)span.LineHeight, (float)defaultFontSize), start, end, SpanTypes.InclusiveExclusive);
- // CharacterSpacing
- var characterSpacing = span.CharacterSpacing >= 0
+ // CharacterSpacing with validation
+ var characterSpacing = span.IsSet(Span.CharacterSpacingProperty)
? span.CharacterSpacing
: defaultCharacterSpacing;
- if (characterSpacing >= 0)
- spannable.SetSpan(new PlatformFontSpan(characterSpacing.ToEm()), start, end, SpanTypes.InclusiveInclusive);
+ characterSpacing = Math.Max(0, characterSpacing);
+ spannable.SetSpan(new PlatformFontSpan(characterSpacing.ToEm()), start, end, SpanTypes.InclusiveInclusive);
var font = span.GetEffectiveFont(defaultFontSize, defaultFont);
if (!font.IsDefault)
diff --git a/src/Controls/src/Core/Platform/Android/Extensions/ToolbarExtensions.cs b/src/Controls/src/Core/Platform/Android/Extensions/ToolbarExtensions.cs
index 1fae68522c6f..dccc50ef476d 100644
--- a/src/Controls/src/Core/Platform/Android/Extensions/ToolbarExtensions.cs
+++ b/src/Controls/src/Core/Platform/Android/Extensions/ToolbarExtensions.cs
@@ -11,6 +11,8 @@
using Android.Views;
using AndroidX.AppCompat.Graphics.Drawable;
using AndroidX.AppCompat.Widget;
+using AndroidX.Core.View;
+using AndroidX.Core.View.Accessibility;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Primitives;
using AGraphics = Android.Graphics;
@@ -104,13 +106,13 @@ public static void UpdateTitleIcon(this AToolbar nativeToolbar, Toolbar toolbar)
public static void UpdateBackButton(this AToolbar nativeToolbar, Toolbar toolbar)
{
- if (toolbar.BackButtonVisible)
- {
- var context =
+ var context =
nativeToolbar.Context?.GetThemedContext() ??
nativeToolbar.Context ??
toolbar.Handler?.MauiContext?.Context;
+ if (toolbar.BackButtonVisible)
+ {
nativeToolbar.NavigationIcon ??= new DrawerArrowDrawable(context!);
if (nativeToolbar.NavigationIcon is DrawerArrowDrawable iconDrawable)
iconDrawable.Progress = 1;
@@ -136,6 +138,9 @@ public static void UpdateBackButton(this AToolbar nativeToolbar, Toolbar toolbar
}
else
{
+ // Reinitialize navigation icon to display flyout (hamburger) menu
+ // This ensures the icon is shown when back button is not visible
+ nativeToolbar.NavigationIcon = new DrawerArrowDrawable(context!);
if (nativeToolbar.NavigationIcon is DrawerArrowDrawable iconDrawable)
iconDrawable.Progress = 0;
@@ -399,6 +404,56 @@ static void UpdateMenuItem(AToolbar toolbar,
textView.SetTextColor(tintColor.MultiplyAlpha(0.302f).ToPlatform());
}
}
+
+ SetSemanticProperties(item, toolbar.FindViewById(menuitem.ItemId));
+ }
+
+ static void SetSemanticProperties(ToolbarItem menuItem, AView? view)
+ {
+ if (view == null)
+ return;
+
+ var semantics = SemanticProperties.UpdateSemantics(menuItem, null);
+ var desc = semantics?.Description;
+ var hint = semantics?.Hint;
+
+ // Only apply delegate if we have meaningful accessibility information
+ if (!string.IsNullOrWhiteSpace(desc) || !string.IsNullOrWhiteSpace(hint))
+ {
+ view.ImportantForAccessibility = ImportantForAccessibility.Yes;
+ ViewCompat.SetAccessibilityDelegate(view, new AccessibilityDelegateCompatImpl(desc, hint));
+ }
+ else
+ {
+ // Remove any previously set delegate if no accessibility info is present
+ ViewCompat.SetAccessibilityDelegate(view, null);
+ }
+ }
+
+ class AccessibilityDelegateCompatImpl : AccessibilityDelegateCompat
+ {
+ private readonly string? _desc;
+ private readonly string? _hint;
+
+ public AccessibilityDelegateCompatImpl(string? desc, string? hint)
+ {
+ _desc = desc;
+ _hint = hint;
+ }
+
+ public override void OnInitializeAccessibilityNodeInfo(AView? host, AccessibilityNodeInfoCompat? info)
+ {
+ base.OnInitializeAccessibilityNodeInfo(host, info);
+
+ if (host == null || info == null)
+ return;
+
+ if (!string.IsNullOrWhiteSpace(_desc))
+ info.ContentDescription = _desc;
+
+ if (!string.IsNullOrWhiteSpace(_hint))
+ info.HintText = _hint;
+ }
}
internal static void UpdateMenuItemIcon(this IMauiContext mauiContext, IMenuItem menuItem, ToolbarItem toolBarItem, Color? tintColor)
diff --git a/src/Controls/src/Core/Platform/Android/TabbedPageManager.cs b/src/Controls/src/Core/Platform/Android/TabbedPageManager.cs
index d1f243c972c5..aba7ab713e4d 100644
--- a/src/Controls/src/Core/Platform/Android/TabbedPageManager.cs
+++ b/src/Controls/src/Core/Platform/Android/TabbedPageManager.cs
@@ -124,17 +124,21 @@ public virtual void SetElement(TabbedPage tabbedPage)
((IPageController)Element).InternalChildren.CollectionChanged -= OnChildrenCollectionChanged;
Element.Appearing -= OnTabbedPageAppearing;
Element.Disappearing -= OnTabbedPageDisappearing;
+
RemoveTabs();
+
_viewPager.LayoutChange -= OnLayoutChanged;
_viewPager.Adapter = null;
}
Element = tabbedPage;
+
if (Element is not null)
{
_viewPager.LayoutChange += OnLayoutChanged;
Element.Appearing += OnTabbedPageAppearing;
Element.Disappearing += OnTabbedPageDisappearing;
+
_viewPager.Adapter = new MultiPageFragmentStateAdapter(tabbedPage, FragmentManager, _context) { CountOverride = tabbedPage.Children.Count };
if (IsBottomTabPlacement)
@@ -218,7 +222,10 @@ void RemoveTabs()
protected virtual void OnTabbedPageDisappearing(object sender, EventArgs e)
{
- RemoveTabs();
+ // Intentionally left empty. Previously this called RemoveTabs() which
+ // destroyed tab fragments, causing them to not restore properly after
+ // modal navigation. Tabs should remain visible and intact.
+ // TODO: This method can be removed in .NET 11.
}
protected virtual void OnTabbedPageAppearing(object sender, EventArgs e)
@@ -594,6 +601,14 @@ public virtual void UpdateBarBackground()
{
newGradientBrush.Parent = Element;
newGradientBrush.InvalidateGradientBrushRequested += OnBarBackgroundChanged;
+ if (_bottomNavigationView is not null && _bottomNavigationView.Elevation > 0)
+ {
+ _bottomNavigationView.Elevation = 0;
+ }
+ }
+ else if (_currentBarBackground is SolidColorBrush && _bottomNavigationView is not null && _bottomNavigationView.Elevation == 0)
+ {
+ _bottomNavigationView.Elevation = _bottomNavigationView.Context.Resources.GetDimension(Resource.Dimension.design_bottom_navigation_elevation);
}
RefreshBarBackground();
diff --git a/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Windows.cs b/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Windows.cs
index c4306ddc8f79..958acfc53dce 100644
--- a/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Windows.cs
+++ b/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Windows.cs
@@ -452,7 +452,8 @@ void HandleSwipe(ManipulationDeltaRoutedEventArgs e, View view)
foreach (SwipeGestureRecognizer recognizer in view.GestureRecognizers.GetGesturesFor())
{
- ((ISwipeGestureController)recognizer).SendSwipe(view, e.Delta.Translation.X + e.Cumulative.Translation.X, e.Delta.Translation.Y + e.Cumulative.Translation.Y);
+ ((ISwipeGestureController)recognizer).SendSwipe(view, e.Cumulative.Translation.X, e.Cumulative.Translation.Y);
+ e.Handled = true;
}
}
@@ -471,7 +472,8 @@ void HandlePan(ManipulationDeltaRoutedEventArgs e, View view)
{
recognizer.SendPanStarted(view, PanGestureRecognizer.CurrentId.Value);
}
- recognizer.SendPan(view, e.Delta.Translation.X + e.Cumulative.Translation.X, e.Delta.Translation.Y + e.Cumulative.Translation.Y, PanGestureRecognizer.CurrentId.Value);
+ recognizer.SendPan(view, e.Cumulative.Translation.X, e.Cumulative.Translation.Y, PanGestureRecognizer.CurrentId.Value);
+ e.Handled = true;
}
_wasPanGestureStartedSent = true;
}
@@ -497,6 +499,7 @@ void HandlePinch(ManipulationDeltaRoutedEventArgs e, View view)
}
recognizer.SendPinch(view, e.Delta.Scale, scaleOriginPoint);
+ e.Handled = true;
}
_wasPinchGestureStartedSent = true;
@@ -554,7 +557,7 @@ void OnPointerExited(object sender, PointerRoutedEventArgs e)
{
SwipeComplete(true);
- if (!_isPanning)
+ if (!_isPanning && !_isPinching && !_isSwiping)
{
_fingers.Remove(e.Pointer.PointerId);
}
diff --git a/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.iOS.cs b/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.iOS.cs
index 9968111c3657..4c45a6b1581a 100644
--- a/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.iOS.cs
+++ b/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.iOS.cs
@@ -265,7 +265,10 @@ static void ProcessRecognizerHandlerTap(
var view = eventTracker?._handler.VirtualView as View;
if (swipeGestureRecognizer != null && view != null)
- swipeGestureRecognizer.SendSwiped(view, direction);
+ {
+ var transformedDirection = SwipeGestureExtensions.TransformSwipeDirectionForRotation(direction, view.Rotation);
+ swipeGestureRecognizer.SendSwiped(view, transformedDirection);
+ }
});
var uiRecognizer = CreateSwipeRecognizer(swipeRecognizer.Direction, returnAction, 1);
return new List { uiRecognizer };
@@ -403,7 +406,15 @@ UISwipeGestureRecognizer CreateSwipeRecognizer(SwipeDirection direction, Action<
var result = new UISwipeGestureRecognizer();
result.NumberOfTouchesRequired = (uint)numFingers;
result.Direction = (UISwipeGestureRecognizerDirection)direction;
- result.ShouldRecognizeSimultaneously = (g, o) => true;
+ result.ShouldRecognizeSimultaneously = (g, o) =>
+ {
+ if (o.View is UIScrollView)
+ {
+ return false;
+ }
+
+ return true;
+ };
result.AddTarget(() => action(direction));
return result;
}
diff --git a/src/Controls/src/Core/Platform/Windows/CollectionView/GroupTemplateContext.cs b/src/Controls/src/Core/Platform/Windows/CollectionView/GroupTemplateContext.cs
index 1c08270c0bf2..d698213bde74 100644
--- a/src/Controls/src/Core/Platform/Windows/CollectionView/GroupTemplateContext.cs
+++ b/src/Controls/src/Core/Platform/Windows/CollectionView/GroupTemplateContext.cs
@@ -21,26 +21,7 @@ public GroupTemplateContext(ItemTemplateContext headerItemTemplateContext,
Items = items;
}
else
- {
- // If the group items are not an IList, then we'll have to append the footer the hard way
-
- var groupFooterTemplateItemType = footerItemTemplateContext.Item?.GetType();
-
- if (groupFooterTemplateItemType is not IList)
- {
- var listPlusFooter = new List();
-
- foreach (var item in (items as IEnumerable))
- {
- listPlusFooter.Add(item);
- }
-
- listPlusFooter.Add(footerItemTemplateContext);
-
- Items = listPlusFooter;
- return;
- }
-
+ {
// UWP ListViewBase does not support group footers. So we're going to fake the footer by adding an
// extra item to the ItemsSource so the footer shows up at the end of the group.
@@ -51,6 +32,19 @@ public GroupTemplateContext(ItemTemplateContext headerItemTemplateContext,
Items = itemsList;
return;
}
+
+ // If the group items are not an IList, then we'll have to append the footer the hard way
+
+ var listPlusFooter = new List();
+
+ foreach (var item in (items as IEnumerable))
+ {
+ listPlusFooter.Add(item);
+ }
+
+ listPlusFooter.Add(footerItemTemplateContext);
+
+ Items = listPlusFooter;
}
}
}
diff --git a/src/Controls/src/Core/Platform/Windows/CollectionView/ObservableItemTemplateCollection.cs b/src/Controls/src/Core/Platform/Windows/CollectionView/ObservableItemTemplateCollection.cs
index 6011db3a84d1..98c8a9c53028 100644
--- a/src/Controls/src/Core/Platform/Windows/CollectionView/ObservableItemTemplateCollection.cs
+++ b/src/Controls/src/Core/Platform/Windows/CollectionView/ObservableItemTemplateCollection.cs
@@ -114,7 +114,11 @@ void AddToSource(NotifyCollectionChangedEventArgs args)
for (int n = 0; n < count; n++)
{
var newItem = (ItemTemplateContext)args.NewItems[n];
- _itemsSource.Insert(startIndex, newItem.Item);
+ if (newItem is not GroupFooterItemTemplateContext)
+ {
+ _itemsSource.Insert(startIndex, newItem.Item);
+ startIndex++;
+ }
}
}
@@ -132,7 +136,10 @@ void RemoveFromSource(NotifyCollectionChangedEventArgs args)
for (int n = startIndex + count - 1; n >= startIndex; n--)
{
- _itemsSource.RemoveAt(n);
+ if ((ItemTemplateContext)args.OldItems[n - startIndex] is not GroupFooterItemTemplateContext)
+ {
+ _itemsSource.RemoveAt(n);
+ }
}
}
diff --git a/src/Controls/src/Core/Platform/Windows/Extensions/FormattedStringExtensions.cs b/src/Controls/src/Core/Platform/Windows/Extensions/FormattedStringExtensions.cs
index fa3af73b1ad4..2e235cb926fd 100644
--- a/src/Controls/src/Core/Platform/Windows/Extensions/FormattedStringExtensions.cs
+++ b/src/Controls/src/Core/Platform/Windows/Extensions/FormattedStringExtensions.cs
@@ -29,7 +29,8 @@ public static void UpdateInlines(this TextBlock textBlock, Label label)
label.HorizontalTextAlignment,
label.ToFont(),
label.TextColor,
- label.TextTransform);
+ label.TextTransform,
+ label.CharacterSpacing);
public static void UpdateInlines(
this TextBlock textBlock,
@@ -40,6 +41,20 @@ public static void UpdateInlines(
Font? defaultFont = null,
Color? defaultColor = null,
TextTransform defaultTextTransform = TextTransform.Default)
+ => UpdateInlines(textBlock, fontManager, formattedString, defaultLineHeight, defaultHorizontalAlignment, defaultFont, defaultColor, defaultTextTransform, defaultCharacterSpacing: 0d);
+
+ // Private overload that supports CharacterSpacing inheritance
+ // TODO: Make this method public in .NET 11
+ static void UpdateInlines(
+ this TextBlock textBlock,
+ IFontManager fontManager,
+ FormattedString formattedString,
+ double defaultLineHeight,
+ TextAlignment defaultHorizontalAlignment,
+ Font? defaultFont,
+ Color? defaultColor,
+ TextTransform defaultTextTransform,
+ double defaultCharacterSpacing)
{
var textBlockInlines = textBlock.Inlines;
textBlockInlines.Clear();
@@ -53,7 +68,8 @@ public static void UpdateInlines(
defaultHorizontalAlignment,
defaultFont,
defaultColor,
- defaultTextTransform).ToArray();
+ defaultTextTransform,
+ defaultCharacterSpacing).ToArray();
var lineHeights = new List(runs.Length);
foreach (var (run, _, _) in runs)
@@ -96,6 +112,19 @@ public static IEnumerable> ToRunAndColorsTuples(
Font? defaultFont = null,
Color? defaultColor = null,
TextTransform defaultTextTransform = TextTransform.Default)
+ => ToRunAndColorsTuples(formattedString, fontManager, defaultLineHeight, defaultHorizontalAlignment, defaultFont, defaultColor, defaultTextTransform, defaultCharacterSpacing: 0d);
+
+ // Private overload that supports CharacterSpacing inheritance
+ // TODO: Make this method public in .NET 11
+ static IEnumerable> ToRunAndColorsTuples(
+ this FormattedString formattedString,
+ IFontManager fontManager,
+ double defaultLineHeight,
+ TextAlignment defaultHorizontalAlignment,
+ Font? defaultFont,
+ Color? defaultColor,
+ TextTransform defaultTextTransform,
+ double defaultCharacterSpacing)
{
var runs = new List>();
@@ -104,7 +133,7 @@ public static IEnumerable> ToRunAndColorsTuples(
for (var i = 0; i < formattedString.Spans.Count; i++)
{
var span = formattedString.Spans[i];
- var run = span.ToRunAndColorsTuple(fontManager, defaultFont, defaultColor, defaultTextTransform);
+ var run = span.ToRunAndColorsTuple(fontManager, defaultFont, defaultColor, defaultTextTransform, defaultCharacterSpacing);
runs.Add(run);
}
}
@@ -118,6 +147,17 @@ public static Tuple ToRunAndColorsTuple(
Font? defaultFont = null,
Color? defaultColor = null,
TextTransform defaultTextTransform = TextTransform.Default)
+ => ToRunAndColorsTuple(span, fontManager, defaultFont, defaultColor, defaultTextTransform, defaultCharacterSpacing: 0d);
+
+ // Private overload that supports CharacterSpacing inheritance
+ // TODO: Make this method public in .NET 11
+ static Tuple ToRunAndColorsTuple(
+ this Span span,
+ IFontManager fontManager,
+ Font? defaultFont,
+ Color? defaultColor,
+ TextTransform defaultTextTransform,
+ double defaultCharacterSpacing)
{
var defaultFontSize = defaultFont?.Size ?? fontManager.DefaultFontSize;
@@ -137,7 +177,12 @@ public static Tuple ToRunAndColorsTuple(
if (span.IsSet(Span.TextDecorationsProperty))
run.TextDecorations = (global::Windows.UI.Text.TextDecorations)span.TextDecorations;
- run.CharacterSpacing = span.CharacterSpacing.ToEm();
+ // CharacterSpacing with inheritance and validation
+ var characterSpacing = span.IsSet(Span.CharacterSpacingProperty)
+ ? span.CharacterSpacing
+ : defaultCharacterSpacing;
+ characterSpacing = Math.Max(0, characterSpacing);
+ run.CharacterSpacing = characterSpacing.ToEm();
return Tuple.Create(run, span.TextColor, span.BackgroundColor);
}
diff --git a/src/Controls/src/Core/Platform/Windows/Extensions/ToolbarExtensions.cs b/src/Controls/src/Core/Platform/Windows/Extensions/ToolbarExtensions.cs
index 7348dbd80992..14bcba5ed81c 100644
--- a/src/Controls/src/Core/Platform/Windows/Extensions/ToolbarExtensions.cs
+++ b/src/Controls/src/Core/Platform/Windows/Extensions/ToolbarExtensions.cs
@@ -50,6 +50,12 @@ public static void UpdateTitleView(this MauiToolbar platformToolbar, Toolbar too
{
_ = toolbar.Handler?.MauiContext ?? throw new ArgumentNullException(nameof(toolbar.Handler.MauiContext));
+ if (toolbar.TitleView?.Handler != null)
+ {
+ // Disconnect the handler to ensure the TitleView is properly detached when reusing the same page instance.
+ toolbar.TitleView.Handler.DisconnectHandler();
+ }
+
platformToolbar.TitleView = toolbar.TitleView?.ToPlatform(toolbar.Handler.MauiContext);
if (toolbar.TitleView is IView view)
diff --git a/src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs b/src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs
index 31c75c13dc61..9ec1a6589a39 100644
--- a/src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs
+++ b/src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs
@@ -28,7 +28,9 @@ public static class FormattedStringExtensions
label.HorizontalTextAlignment,
label.ToFont(),
label.TextColor,
- label.TextTransform);
+ label.TextTransform,
+ label.LineBreakMode,
+ label.CharacterSpacing);
public static NSAttributedString ToNSAttributedString(
this FormattedString formattedString,
@@ -38,19 +40,35 @@ public static NSAttributedString ToNSAttributedString(
Font? defaultFont = null,
Color? defaultColor = null,
TextTransform defaultTextTransform = TextTransform.Default)
+ => formattedString.ToNSAttributedString(fontManager, defaultLineHeight, defaultHorizontalAlignment, defaultFont, defaultColor, defaultTextTransform, LineBreakMode.WordWrap, defaultCharacterSpacing: 0d);
+
+ internal static NSAttributedString ToNSAttributedString(
+ this FormattedString formattedString,
+ IFontManager fontManager,
+ double defaultLineHeight,
+ TextAlignment defaultHorizontalAlignment,
+ Font? defaultFont,
+ Color? defaultColor,
+ TextTransform defaultTextTransform,
+ LineBreakMode lineBreakMode,
+ double defaultCharacterSpacing = 0d)
{
if (formattedString == null)
+ {
return new NSAttributedString(string.Empty);
+ }
var attributed = new NSMutableAttributedString();
for (int i = 0; i < formattedString.Spans.Count; i++)
{
Span span = formattedString.Spans[i];
if (span.Text == null)
+ {
continue;
+ }
attributed.Append(span.ToNSAttributedString(fontManager, defaultLineHeight, defaultHorizontalAlignment,
- defaultFont, defaultColor, defaultTextTransform));
+ defaultFont, defaultColor, defaultTextTransform, lineBreakMode, defaultCharacterSpacing));
}
return attributed;
@@ -64,6 +82,18 @@ public static NSAttributedString ToNSAttributedString(
Font? defaultFont = null,
Color? defaultColor = null,
TextTransform defaultTextTransform = TextTransform.Default)
+ => span.ToNSAttributedString(fontManager, defaultLineHeight, defaultHorizontalAlignment, defaultFont, defaultColor, defaultTextTransform, LineBreakMode.WordWrap, defaultCharacterSpacing: 0d);
+
+ internal static NSAttributedString ToNSAttributedString(
+ this Span span,
+ IFontManager fontManager,
+ double defaultLineHeight,
+ TextAlignment defaultHorizontalAlignment,
+ Font? defaultFont,
+ Color? defaultColor,
+ TextTransform defaultTextTransform,
+ LineBreakMode lineBreakMode,
+ double defaultCharacterSpacing = 0d)
{
var defaultFontSize = defaultFont?.Size ?? fontManager.DefaultFontSize;
@@ -71,7 +101,9 @@ public static NSAttributedString ToNSAttributedString(
var text = TextTransformUtilities.GetTransformedText(span.Text, transform);
if (text is null)
+ {
return new NSAttributedString(string.Empty);
+ }
var style = new NSMutableParagraphStyle();
var lineHeight = span.LineHeight >= 0
@@ -91,6 +123,17 @@ public static NSAttributedString ToNSAttributedString(
_ => UITextAlignment.Left
};
+ style.LineBreakMode = lineBreakMode switch
+ {
+ LineBreakMode.NoWrap => UILineBreakMode.Clip,
+ LineBreakMode.WordWrap => UILineBreakMode.WordWrap,
+ LineBreakMode.CharacterWrap => UILineBreakMode.CharacterWrap,
+ LineBreakMode.HeadTruncation => UILineBreakMode.HeadTruncation,
+ LineBreakMode.TailTruncation => UILineBreakMode.TailTruncation,
+ LineBreakMode.MiddleTruncation => UILineBreakMode.MiddleTruncation,
+ _ => UILineBreakMode.WordWrap
+ };
+
var font = span.GetEffectiveFont(defaultFontSize, defaultFont);
var hasUnderline = false;
var hasStrikethrough = false;
@@ -103,6 +146,12 @@ public static NSAttributedString ToNSAttributedString(
var platformFont = font.IsDefault ? null : font.ToUIFont(fontManager);
+ // CharacterSpacing with validation
+ var characterSpacing = span.IsSet(Span.CharacterSpacingProperty)
+ ? span.CharacterSpacing
+ : defaultCharacterSpacing;
+ characterSpacing = Math.Max(0, characterSpacing);
+
#if !MACOS
var attrString = new NSAttributedString(
text,
@@ -112,7 +161,7 @@ public static NSAttributedString ToNSAttributedString(
underlineStyle: hasUnderline ? NSUnderlineStyle.Single : NSUnderlineStyle.None,
strikethroughStyle: hasStrikethrough ? NSUnderlineStyle.Single : NSUnderlineStyle.None,
paragraphStyle: style,
- kerning: (float)span.CharacterSpacing);
+ kerning: (float)characterSpacing);
#else
var attrString = new NSAttributedString(
text,
@@ -122,7 +171,7 @@ public static NSAttributedString ToNSAttributedString(
underlineStyle: hasUnderline ? NSUnderlineStyle.Single : NSUnderlineStyle.None,
strikethroughStyle: hasStrikethrough ? NSUnderlineStyle.Single : NSUnderlineStyle.None,
paragraphStyle: style,
- kerningAdjustment: (float)span.CharacterSpacing);
+ kerningAdjustment: (float)characterSpacing);
#endif
return attrString;
diff --git a/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt
index cc66326af4d7..002deafaf285 100644
--- a/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt
+++ b/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt
@@ -1,4 +1,12 @@
-#nullable enable
+#nullable enable
+*REMOVED*~static readonly Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer.DefaultForegroundColor -> Microsoft.Maui.Graphics.Color
+*REMOVED*~static readonly Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer.DefaultTitleColor -> Microsoft.Maui.Graphics.Color
+~static Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer.DefaultForegroundColor.get -> Microsoft.Maui.Graphics.Color
+~static Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer.DefaultTitleColor.get -> Microsoft.Maui.Graphics.Color
override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? propertyName = null) -> void
~override Microsoft.Maui.Controls.Handlers.Items.MauiRecyclerView.OnInterceptTouchEvent(Android.Views.MotionEvent e) -> bool
~override Microsoft.Maui.Controls.Handlers.Items.MauiRecyclerView.OnTouchEvent(Android.Views.MotionEvent e) -> bool
+override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRenderer.OnHiddenChanged(bool hidden) -> void
+~override Microsoft.Maui.Controls.Handlers.Items.RecyclerViewScrollListener.OnScrollStateChanged(AndroidX.RecyclerView.Widget.RecyclerView recyclerView, int newState) -> void
+~override Microsoft.Maui.Controls.Handlers.Items.SelectableItemsViewAdapter.IsSelectionEnabled(Android.Views.ViewGroup parent, int viewType) -> bool
+~override Microsoft.Maui.Controls.Handlers.Items.RecyclerViewScrollListener.OnScrollStateChanged(AndroidX.RecyclerView.Widget.RecyclerView recyclerView, int newState) -> void
diff --git a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt
index f340c9351931..21d15383214b 100644
--- a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt
+++ b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt
@@ -3,3 +3,6 @@ override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? property
~override Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer.ViewWillTransitionToSize(CoreGraphics.CGSize toSize, UIKit.IUIViewControllerTransitionCoordinator coordinator) -> void
override Microsoft.Maui.Controls.Platform.Compatibility.ShellTableViewController.LoadView() -> void
*REMOVED*~override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRootRenderer.TraitCollectionDidChange(UIKit.UITraitCollection previousTraitCollection) -> void
+~override Microsoft.Maui.Controls.Handlers.Items2.CollectionViewHandler2.DisconnectHandler(UIKit.UIView platformView) -> void
+override Microsoft.Maui.Controls.Platform.Compatibility.ShellItemRenderer.ViewDidAppear(bool animated) -> void
+~override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRenderer.DidMoveToParentViewController(UIKit.UIViewController parent) -> void
diff --git a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
index f340c9351931..21d15383214b 100644
--- a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
+++ b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
@@ -3,3 +3,6 @@ override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? property
~override Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer.ViewWillTransitionToSize(CoreGraphics.CGSize toSize, UIKit.IUIViewControllerTransitionCoordinator coordinator) -> void
override Microsoft.Maui.Controls.Platform.Compatibility.ShellTableViewController.LoadView() -> void
*REMOVED*~override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRootRenderer.TraitCollectionDidChange(UIKit.UITraitCollection previousTraitCollection) -> void
+~override Microsoft.Maui.Controls.Handlers.Items2.CollectionViewHandler2.DisconnectHandler(UIKit.UIView platformView) -> void
+override Microsoft.Maui.Controls.Platform.Compatibility.ShellItemRenderer.ViewDidAppear(bool animated) -> void
+~override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRenderer.DidMoveToParentViewController(UIKit.UIViewController parent) -> void
diff --git a/src/Controls/src/Core/RadioButton/RadioButton.Android.cs b/src/Controls/src/Core/RadioButton/RadioButton.Android.cs
index e39c5e5bc415..a0953b03d008 100644
--- a/src/Controls/src/Core/RadioButton/RadioButton.Android.cs
+++ b/src/Controls/src/Core/RadioButton/RadioButton.Android.cs
@@ -1,4 +1,7 @@
using AView = Android.Views.View;
+using Microsoft.Maui.Handlers;
+using Microsoft.Maui.Controls.Internals;
+using AndroidX.AppCompat.Widget;
namespace Microsoft.Maui.Controls
{
@@ -24,6 +27,15 @@ public static void MapContent(IRadioButtonHandler handler, RadioButton radioButt
}
RadioButtonHandler.MapContent(handler, radioButton);
+ if ((radioButton.TextTransform is TextTransform.Lowercase or TextTransform.Uppercase) && !string.IsNullOrEmpty(radioButton.Content?.ToString()))
+ {
+ var platformRadioButton = RadioButtonHandler.GetPlatformRadioButton(handler);
+ if (platformRadioButton is not null)
+ {
+ var transformedText = TextTransformUtilities.GetTransformedText(radioButton.Content.ToString(), radioButton.TextTransform);
+ platformRadioButton.Text = transformedText;
+ }
+ }
}
static AView? CreatePlatformView(ViewHandler radioButton)
diff --git a/src/Controls/src/Core/RadioButton/RadioButton.Mapper.cs b/src/Controls/src/Core/RadioButton/RadioButton.Mapper.cs
index 05fbf99dc26a..29faa533aba7 100644
--- a/src/Controls/src/Core/RadioButton/RadioButton.Mapper.cs
+++ b/src/Controls/src/Core/RadioButton/RadioButton.Mapper.cs
@@ -14,7 +14,10 @@ public partial class RadioButton
internal new static void RemapForControls()
{
RadioButtonHandler.Mapper.ReplaceMapping(nameof(IRadioButton.Content), MapContent);
-
+#if ANDROID || WINDOWS
+ //On iOS, since a custom approach is used for RadioButton, TextTransform is applied through the Label control.
+ RadioButtonHandler.Mapper.ReplaceMapping(nameof(TextTransform), MapContent);
+#endif
#if ANDROID
RadioButtonHandler.PlatformViewFactory = CreatePlatformView;
#endif
diff --git a/src/Controls/src/Core/RadioButton/RadioButton.Windows.cs b/src/Controls/src/Core/RadioButton/RadioButton.Windows.cs
index 58c481f2d0c9..469955f1d8bc 100644
--- a/src/Controls/src/Core/RadioButton/RadioButton.Windows.cs
+++ b/src/Controls/src/Core/RadioButton/RadioButton.Windows.cs
@@ -1,4 +1,5 @@
-using Microsoft.UI.Xaml;
+using Microsoft.Maui.Controls.Internals;
+using Microsoft.UI.Xaml;
using Windows.Foundation;
namespace Microsoft.Maui.Controls
@@ -21,6 +22,15 @@ public static void MapContent(IRadioButtonHandler handler, RadioButton radioButt
}
RadioButtonHandler.MapContent(handler, radioButton);
+
+ if ((radioButton.TextTransform is TextTransform.Lowercase or TextTransform.Uppercase) && !string.IsNullOrEmpty(radioButton.Content?.ToString()))
+ {
+ if (handler.PlatformView is Microsoft.UI.Xaml.Controls.RadioButton platformRadioButton)
+ {
+ var transformedText = TextTransformUtilities.GetTransformedText(radioButton.Content.ToString(), radioButton.TextTransform);
+ platformRadioButton.Content = transformedText;
+ }
+ }
}
}
}
diff --git a/src/Controls/src/Core/RadioButton/RadioButton.cs b/src/Controls/src/Core/RadioButton/RadioButton.cs
index 88441378372d..1b0705728014 100644
--- a/src/Controls/src/Core/RadioButton/RadioButton.cs
+++ b/src/Controls/src/Core/RadioButton/RadioButton.cs
@@ -26,7 +26,7 @@ public partial class RadioButton : TemplatedView, IElementConfiguration
/// The string "Checked".
public const string CheckedVisualState = "Checked";
-
+
///
/// The visual state name for when the radio button is unchecked.
///
@@ -38,13 +38,13 @@ public partial class RadioButton : TemplatedView, IElementConfiguration
/// The string "Root".
public const string TemplateRootName = "Root";
-
+
///
/// The name of the checked indicator element in the control template.
///
/// The string "CheckedIndicator".
public const string CheckedIndicator = "CheckedIndicator";
-
+
///
/// The name of the unchecked button element in the control template.
///
@@ -625,12 +625,21 @@ static View BuildDefaultTemplate()
nameScope.RegisterName(UncheckedButton, normalEllipse);
nameScope.RegisterName(CheckedIndicator, checkMark);
nameScope.RegisterName("ContentPresenter", contentPresenter);
+ nameScope.RegisterName("Grid", grid);
VisualStateGroupList visualStateGroups = new VisualStateGroupList();
var common = new VisualStateGroup() { Name = "Common" };
common.States.Add(new VisualState() { Name = VisualStateManager.CommonStates.Normal });
- common.States.Add(new VisualState() { Name = VisualStateManager.CommonStates.Disabled });
+ VisualState disabledVisualState = new VisualState() { Name = VisualStateManager.CommonStates.Disabled };
+ disabledVisualState.Setters.Add(
+ new Setter()
+ {
+ Property = Grid.OpacityProperty,
+ TargetName = "Grid",
+ Value = 0.4f
+ });
+ common.States.Add(disabledVisualState);
visualStateGroups.Add(common);
@@ -686,12 +695,15 @@ static View BuildDefaultTemplate()
///
/// The string representation of the content, or the result of ToString() if content is not a string.
///
- /// If is a , a warning is logged and the ToString() representation is used instead.
+ /// If is a and no is set, a warning is logged
+ /// and the ToString() representation is used instead. When a ControlTemplate is applied, View content is supported.
///
public string ContentAsString()
{
var content = Content;
- if (content is View)
+ // Only log warning if Content is a View AND no ControlTemplate is set
+ // When ControlTemplate is set, View content IS supported (per documentation)
+ if (content is View && ResolveControlTemplate() == null)
{
Application.Current?.FindMauiContext()?.CreateLogger()?.LogWarning("Warning - {RuntimePlatform} does not support View as the {PropertyName} property of RadioButton; the return value of the ToString() method will be displayed instead.", DeviceInfo.Platform, ContentProperty.PropertyName);
}
diff --git a/src/Controls/src/Core/SearchBar/SearchBar.Android.cs b/src/Controls/src/Core/SearchBar/SearchBar.Android.cs
index 2abec0b32b2d..51c974c79b6b 100644
--- a/src/Controls/src/Core/SearchBar/SearchBar.Android.cs
+++ b/src/Controls/src/Core/SearchBar/SearchBar.Android.cs
@@ -12,5 +12,17 @@ public static void MapText(ISearchBarHandler handler, SearchBar searchBar)
{
Platform.SearchViewExtensions.UpdateText(handler.PlatformView, searchBar);
}
+
+ // Material3 specific overload for SearchBarHandler2
+ // TODO: Material3: Make it public in .NET 11
+ internal static void MapText(SearchBarHandler2 handler, SearchBar searchBar)
+ {
+ if (handler.PlatformView?.EditText is null)
+ {
+ return;
+ }
+
+ Platform.EditTextExtensions.UpdateText(handler.PlatformView.EditText, searchBar);
+ }
}
}
diff --git a/src/Controls/src/Core/SearchBar/SearchBar.Mapper.cs b/src/Controls/src/Core/SearchBar/SearchBar.Mapper.cs
index 3278ba05f637..d05efd265ec8 100644
--- a/src/Controls/src/Core/SearchBar/SearchBar.Mapper.cs
+++ b/src/Controls/src/Core/SearchBar/SearchBar.Mapper.cs
@@ -18,9 +18,26 @@ static SearchBar()
// Adjust the mappings to preserve Controls.SearchBar legacy behaviors
#if IOS
SearchBarHandler.Mapper.ReplaceMapping(PlatformConfiguration.iOSSpecific.SearchBar.SearchBarStyleProperty.PropertyName, MapSearchBarStyle);
+ SearchBarHandler.Mapper.ReplaceMapping(nameof(IsEnabled), MapUserInteraction);
+ SearchBarHandler.Mapper.ReplaceMapping(nameof(ISearchBar.IsReadOnly), MapUserInteraction);
+ SearchBarHandler.Mapper.ReplaceMapping(nameof(InputTransparent), MapUserInteraction);
#endif
+#if ANDROID
+ if (RuntimeFeature.IsMaterial3Enabled)
+ {
+ // Material3 SearchBar handler mappings
+ SearchBarHandler2.Mapper.ReplaceMapping(nameof(Text), MapText);
+ SearchBarHandler2.Mapper.ReplaceMapping(nameof(TextTransform), MapText);
+ }
+ else
+ {
+ SearchBarHandler.Mapper.ReplaceMapping(nameof(Text), MapText);
+ SearchBarHandler.Mapper.ReplaceMapping(nameof(TextTransform), MapText);
+ }
+#else
SearchBarHandler.Mapper.ReplaceMapping(nameof(Text), MapText);
SearchBarHandler.Mapper.ReplaceMapping(nameof(TextTransform), MapText);
+#endif
#if IOS || ANDROID
SearchBarHandler.Mapper.AppendToMapping(nameof(VisualElement.IsFocused), InputView.MapIsFocused);
diff --git a/src/Controls/src/Core/SearchBar/SearchBar.iOS.cs b/src/Controls/src/Core/SearchBar/SearchBar.iOS.cs
index 445e64cf116a..d140cae18130 100644
--- a/src/Controls/src/Core/SearchBar/SearchBar.iOS.cs
+++ b/src/Controls/src/Core/SearchBar/SearchBar.iOS.cs
@@ -19,5 +19,10 @@ public static void MapText(ISearchBarHandler handler, SearchBar searchBar)
Platform.SearchBarExtensions.UpdateText(handler.PlatformView, searchBar);
SearchBarHandler.MapFormatting(handler, searchBar);
}
+
+ internal static void MapUserInteraction(ISearchBarHandler handler, SearchBar searchBar)
+ {
+ handler.PlatformView.UserInteractionEnabled = searchBar.IsEnabled && !searchBar.IsReadOnly && !searchBar.InputTransparent;
+ }
}
}
diff --git a/src/Controls/src/Core/Shell/BaseShellItem.cs b/src/Controls/src/Core/Shell/BaseShellItem.cs
index 19711d358c9f..adcbf9d5df81 100644
--- a/src/Controls/src/Core/Shell/BaseShellItem.cs
+++ b/src/Controls/src/Core/Shell/BaseShellItem.cs
@@ -272,6 +272,18 @@ internal static void PropagateFromParent(BindableProperty property, Element me)
if (me == null || me.Parent == null)
return;
+ // For Shell.NavBarIsVisibleProperty, find the shell instance for more accurate propagation
+ if (property == Shell.NavBarIsVisibleProperty)
+ {
+ var shell = me.FindParentOfType();
+ if (shell is not null && shell.IsSet(property) && !me.IsSet(property))
+ {
+ // Get the value from the Shell directly
+ me.SetValue(property, shell.GetValue(property));
+ return;
+ }
+ }
+
Propagate(property, me.Parent, me, false);
}
diff --git a/src/Controls/src/Core/Shell/SearchHandler.cs b/src/Controls/src/Core/Shell/SearchHandler.cs
index 958be4bd455f..1bf1f5059a78 100644
--- a/src/Controls/src/Core/Shell/SearchHandler.cs
+++ b/src/Controls/src/Core/Shell/SearchHandler.cs
@@ -6,6 +6,7 @@
using System.Windows.Input;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Graphics;
+using Microsoft.Maui.Devices;
using static Microsoft.Maui.Controls.VisualElement;
namespace Microsoft.Maui.Controls
@@ -314,6 +315,12 @@ void ISearchHandlerController.ItemSelected(object obj)
{
OnItemSelected(obj);
SetValue(SelectedItemPropertyKey, obj, specificity: SetterSpecificity.FromHandler);
+ if (!(DeviceInfo.Platform == DevicePlatform.WinUI))
+ {
+ // For WinUI the query is confirmed in the QuerySubmitted event,
+ // so we don't want to call OnQueryConfirmed here as it would cause the command to execute twice
+ OnQueryConfirmed();
+ }
}
void ISearchHandlerController.QueryConfirmed()
diff --git a/src/Controls/src/Core/Shell/Shell.cs b/src/Controls/src/Core/Shell/Shell.cs
index 36d5e2610d9e..c84bac32f857 100644
--- a/src/Controls/src/Core/Shell/Shell.cs
+++ b/src/Controls/src/Core/Shell/Shell.cs
@@ -84,7 +84,17 @@ private static void OnNavBarIsVisibleChanged(BindableObject bindable, object old
?? (bindable as BaseShellItem)?.FindParentOfType()
?? (bindable as Page)?.FindParentOfType();
- shell?.OnPropertyChanged(NavBarIsVisibleProperty.PropertyName);
+ if (shell != null)
+ {
+ // Notify about the property change
+ shell.OnPropertyChanged(NavBarIsVisibleProperty.PropertyName);
+
+ // Explicitly propagate the property change to all children
+ if (shell is IPropertyPropagationController controller)
+ {
+ controller.PropagatePropertyChanged(NavBarIsVisibleProperty.PropertyName);
+ }
+ }
}
///
@@ -994,6 +1004,40 @@ void IShellController.UpdateCurrentState(ShellNavigationSource source)
if (result?.Location != oldState?.Location)
{
SetValueFromRenderer(CurrentStatePropertyKey, result);
+
+ // Fire NavigatingFrom after state is updated so CurrentPage is the destination.
+ // _previousPage was captured in SendNavigating before navigation started.
+ if (_previousPage is not null)
+ {
+ NavigationType navigationType = NavigationType.Replace;
+
+ switch (source)
+ {
+ case ShellNavigationSource.Pop:
+ navigationType = NavigationType.Pop;
+ break;
+ case ShellNavigationSource.Push:
+ navigationType = NavigationType.Push;
+ break;
+ case ShellNavigationSource.PopToRoot:
+ navigationType = NavigationType.PopToRoot;
+ break;
+ case ShellNavigationSource.Insert:
+ navigationType = NavigationType.Insert;
+ break;
+ case ShellNavigationSource.Remove:
+ navigationType = NavigationType.Remove;
+ break;
+ case ShellNavigationSource.ShellItemChanged:
+ case ShellNavigationSource.ShellSectionChanged:
+ case ShellNavigationSource.ShellContentChanged:
+ navigationType = NavigationType.Replace;
+ break;
+ }
+
+ _previousPage.SendNavigatingFrom(new NavigatingFromEventArgs(CurrentPage, navigationType));
+ }
+
_navigationManager.HandleNavigated(new ShellNavigatedEventArgs(oldState, CurrentState, source));
}
}
@@ -1220,6 +1264,9 @@ public Task GoToAsync(ShellNavigationState state, bool animate, ShellNavigationQ
ShellNavigationManager _navigationManager;
ShellFlyoutItemsManager _flyoutManager;
Page _previousPage;
+ NavigationType _navigationType;
+ Page _pendingPreviousPage;
+ NavigationType _pendingNavigationType;
/// Initializes a new instance of the class.
public Shell()
@@ -1635,35 +1682,35 @@ void SendNavigated(ShellNavigatedEventArgs args)
if (_previousPage != null)
_previousPage.PropertyChanged -= OnCurrentPagePropertyChanged;
- NavigationType navigationType = NavigationType.Replace;
+ _navigationType = NavigationType.Replace;
switch (args.Source)
{
case ShellNavigationSource.Pop:
- navigationType = NavigationType.Pop;
+ _navigationType = NavigationType.Pop;
break;
case ShellNavigationSource.ShellItemChanged:
- navigationType = NavigationType.Replace;
+ _navigationType = NavigationType.Replace;
break;
case ShellNavigationSource.ShellSectionChanged:
- navigationType = NavigationType.Replace;
+ _navigationType = NavigationType.Replace;
break;
case ShellNavigationSource.ShellContentChanged:
- navigationType = NavigationType.Replace;
+ _navigationType = NavigationType.Replace;
break;
case ShellNavigationSource.Push:
- navigationType = NavigationType.Push;
+ _navigationType = NavigationType.Push;
break;
case ShellNavigationSource.PopToRoot:
- navigationType = NavigationType.PopToRoot;
+ _navigationType = NavigationType.PopToRoot;
break;
case ShellNavigationSource.Insert:
- navigationType = NavigationType.Insert;
+ _navigationType = NavigationType.Insert;
break;
}
- _previousPage?.SendNavigatedFrom(new NavigatedFromEventArgs(CurrentPage, navigationType));
- CurrentPage?.SendNavigatedTo(new NavigatedToEventArgs(_previousPage, navigationType));
+ _previousPage?.SendNavigatedFrom(new NavigatedFromEventArgs(CurrentPage, _navigationType));
+ PropagateSendNavigatedTo();
_previousPage = null;
if (CurrentPage != null)
@@ -1672,6 +1719,44 @@ void SendNavigated(ShellNavigatedEventArgs args)
CurrentItem?.Handler?.UpdateValue(Shell.TabBarIsVisibleProperty.PropertyName);
}
+ void OnCurrentPageLoaded(object sender, EventArgs e)
+ {
+ if (sender is Page page)
+ {
+ page.Loaded -= OnCurrentPageLoaded;
+ page.SendNavigatedTo(new NavigatedToEventArgs(_pendingPreviousPage, _pendingNavigationType));
+ _pendingPreviousPage = null;
+ _pendingNavigationType = default;
+#if ANDROID
+ // Restore flyout behavior observers after deferred NavigatedTo timing
+ // Android requires this call to maintain flyout functionality
+ CurrentContent?.EvaluateDisconnect();
+#endif
+ }
+ }
+
+ void PropagateSendNavigatedTo()
+ {
+ if (CurrentPage is null)
+ {
+ return;
+ }
+
+ // On Windows, the Loaded event has already fired (IsLoaded=true), so SendNavigatedTo runs immediately without altering the flow.
+ if (CurrentPage.IsLoaded)
+ {
+ CurrentPage.SendNavigatedTo(new NavigatedToEventArgs(_previousPage, _navigationType));
+ }
+ else
+ {
+ // Capture before _previousPage is nulled in SendNavigated() so OnCurrentPageLoaded
+ // receives the correct PreviousPage in NavigatedToEventArgs when it fires asynchronously.
+ _pendingPreviousPage = _previousPage;
+ _pendingNavigationType = _navigationType;
+ CurrentPage.Loaded += OnCurrentPageLoaded;
+ }
+ }
+
internal PropertyChangedEventHandler CurrentPagePropertyChanged;
void OnCurrentPagePropertyChanged(object sender, PropertyChangedEventArgs e)
@@ -1689,35 +1774,16 @@ void SendNavigating(ShellNavigatingEventArgs args)
if (!args.Cancelled)
{
- NavigationType navigationType = NavigationType.Replace;
-
- switch (args.Source)
- {
- case ShellNavigationSource.Pop:
- navigationType = NavigationType.Pop;
- break;
- case ShellNavigationSource.ShellItemChanged:
- navigationType = NavigationType.Replace;
- break;
- case ShellNavigationSource.ShellSectionChanged:
- navigationType = NavigationType.Replace;
- break;
- case ShellNavigationSource.ShellContentChanged:
- navigationType = NavigationType.Replace;
- break;
- case ShellNavigationSource.Push:
- navigationType = NavigationType.Push;
- break;
- case ShellNavigationSource.PopToRoot:
- navigationType = NavigationType.PopToRoot;
- break;
- case ShellNavigationSource.Insert:
- navigationType = NavigationType.Insert;
- break;
- }
-
+ // Capture the current page now; SendNavigatingFrom will be called in
+ // UpdateCurrentState after the shell state is updated, ensuring CurrentPage
+ // correctly reflects the destination page at that point.
_previousPage = CurrentPage;
- CurrentPage?.SendNavigatingFrom(new NavigatingFromEventArgs(CurrentPage, navigationType));
+ }
+
+ // Unsubscribe Loaded handler if navigating away before page loads to prevent memory leaks.
+ if (CurrentPage != null && !CurrentPage.IsLoaded)
+ {
+ CurrentPage.Loaded -= OnCurrentPageLoaded;
}
}
diff --git a/src/Controls/src/Core/Shell/ShellContent.cs b/src/Controls/src/Core/Shell/ShellContent.cs
index 68c3f10a8ceb..1faa996f76db 100644
--- a/src/Controls/src/Core/Shell/ShellContent.cs
+++ b/src/Controls/src/Core/Shell/ShellContent.cs
@@ -97,6 +97,12 @@ Page IShellContentController.GetOrCreateContent()
}
result = ContentCache ?? (Page)template.CreateContent(content, this);
+
+ if (GetValue(QueryAttributesProperty) is ShellRouteParameters delayedQueryParams)
+ {
+ result?.SetValue(QueryAttributesProperty, delayedQueryParams);
+ }
+
ContentCache = result;
}
@@ -112,9 +118,6 @@ Page IShellContentController.GetOrCreateContent()
if (result is NavigationPage)
throw new NotSupportedException("Shell is currently not compatible with NavigationPage. Shell has Navigation built in and doesn't require a NavigationPage.");
- if (GetValue(QueryAttributesProperty) is ShellRouteParameters delayedQueryParams)
- result.SetValue(QueryAttributesProperty, delayedQueryParams);
-
return result;
}
@@ -416,8 +419,18 @@ static void ApplyQueryAttributes(object content, ShellRouteParameters query, She
}
else
{
- var castValue = Convert.ChangeType(value, prop.PropertyType);
- prop.SetValue(content, castValue);
+ // Handle nullable types
+ Type targetType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
+
+ if (value == null)
+ {
+ prop.SetValue(content, null);
+ }
+ else
+ {
+ var castValue = Convert.ChangeType(value, targetType);
+ prop.SetValue(content, castValue);
+ }
}
}
}
diff --git a/src/Controls/src/Core/Shell/ShellElementCollection.cs b/src/Controls/src/Core/Shell/ShellElementCollection.cs
index ac88242d85aa..1e94532aab4d 100644
--- a/src/Controls/src/Core/Shell/ShellElementCollection.cs
+++ b/src/Controls/src/Core/Shell/ShellElementCollection.cs
@@ -23,6 +23,7 @@ internal abstract class ShellElementCollection :
public bool IsReadOnly => Inner.IsReadOnly;
IList _inner;
IList _visibleItems;
+ bool _isItemsCleared;
protected ShellElementCollection()
{
@@ -119,6 +120,7 @@ public void Clear()
}
Inner.Clear();
+ _isItemsCleared = true;
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, list));
}
@@ -153,6 +155,13 @@ void InnerCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
CheckVisibility(element);
}
+ // When items are cleared, the flyout behavior is set to disabled state. When adding items again, we need to update the flyout behavior. The flyout state should be updated only when the second item is added to the view.
+ if (_isItemsCleared && Count == 2 && this is ShellItemCollection shellItemCollection)
+ {
+ var shell = shellItemCollection[0].Parent as Shell;
+ shell?.NotifyFlyoutBehaviorObservers();
+ _isItemsCleared = false;
+ }
}
if (e.OldItems != null)
diff --git a/src/Controls/src/Core/Shell/ShellNavigationManager.cs b/src/Controls/src/Core/Shell/ShellNavigationManager.cs
index dbe205c51e56..efac0f54f2e6 100644
--- a/src/Controls/src/Core/Shell/ShellNavigationManager.cs
+++ b/src/Controls/src/Core/Shell/ShellNavigationManager.cs
@@ -313,14 +313,27 @@ public static void ApplyQueryAttributes(Element element, ShellRouteParameters qu
var mergedData = MergeData(element, filteredQuery, isPopping);
//if we are pop or navigating back, we need to apply the query attributes to the ShellContent
- if (isPopping)
+ if (isPopping && mergedData.Count > 0 )
{
element.SetValue(ShellContent.QueryAttributesProperty, mergedData);
}
- baseShellItem.ApplyQueryAttributes(mergedData);
+
+ // Skip applying query attributes if the merged data is empty and we're popping back
+ // This respects when user calls query.Clear() - they don't want attributes applied on back navigation
+ if (mergedData.Count > 0 || !isPopping)
+ {
+ baseShellItem.ApplyQueryAttributes(mergedData);
+ }
}
else if (isLastItem)
- element.SetValue(ShellContent.QueryAttributesProperty, MergeData(element, query, isPopping));
+ {
+ var mergedData = MergeData(element, query, isPopping);
+ // Skip setting query attributes if the merged data is empty and we're popping back
+ if (mergedData.Count > 0 || !isPopping)
+ {
+ element.SetValue(ShellContent.QueryAttributesProperty, mergedData);
+ }
+ }
ShellRouteParameters MergeData(Element shellElement, ShellRouteParameters data, bool isPopping)
{
diff --git a/src/Controls/src/Core/Shell/ShellSection.cs b/src/Controls/src/Core/Shell/ShellSection.cs
index e17f4c12d855..119eac000ce7 100644
--- a/src/Controls/src/Core/Shell/ShellSection.cs
+++ b/src/Controls/src/Core/Shell/ShellSection.cs
@@ -524,9 +524,9 @@ internal async Task GoToAsync(ShellNavigationRequest request, ShellRouteParamete
if (globalRoutes == null || globalRoutes.Count == 0)
{
if (_navStack.Count == 2)
- await OnPopAsync(animate ?? false);
+ await OnPopAsync(animate ?? true);
else
- await OnPopToRootAsync(animate ?? false);
+ await OnPopToRootAsync(animate ?? true);
return;
}
@@ -844,10 +844,21 @@ protected virtual async Task OnPopToRootAsync(bool animated)
RequestType = NavigationRequestType.PopToRoot
};
- InvokeNavigationRequest(args);
+ PresentedPageDisappearing();
var oldStack = _navStack;
_navStack = new List { null };
+ // NOTE:
+ // We intentionally raise PresentedPageAppearing (and thus SendPageAppearing/OnAppearing)
+ // before issuing the platform navigation request and awaiting its completion. This matches
+ // the behavior used for single-level Pop navigation and keeps Shell lifecycle events
+ // consistent across navigation patterns. At this point the root page may not yet be
+ // visually presented by the native stack (the pop-to-root animation can still be in
+ // progress), but Shell-level state (e.g., tab bar visibility) must already be updated
+ // so the platform reads the correct value when it commits the native navigation.
+ PresentedPageAppearing();
+ InvokeNavigationRequest(args);
+
if (args.Task != null)
await args.Task;
@@ -856,11 +867,14 @@ protected virtual async Task OnPopToRootAsync(bool animated)
for (int i = 1; i < oldStack.Count; i++)
{
- oldStack[i].SendDisappearing();
+ // RemovePage is called for all pages. SendDisappearing is only called for
+ // intermediate pages; the top page's disappearing event was already fired
+ // by PresentedPageDisappearing() above to avoid duplicate lifecycle events.
+ if (i < oldStack.Count - 1)
+ oldStack[i].SendDisappearing();
RemovePage(oldStack[i]);
}
- PresentedPageAppearing();
}
protected virtual Task OnPushAsync(Page page, bool animated)
diff --git a/src/Controls/src/Core/SwipeView/SwipeView.cs b/src/Controls/src/Core/SwipeView/SwipeView.cs
index d0bb86c0534d..18a27764e189 100644
--- a/src/Controls/src/Core/SwipeView/SwipeView.cs
+++ b/src/Controls/src/Core/SwipeView/SwipeView.cs
@@ -258,6 +258,7 @@ public IPlatformElementConfiguration On() where T : IConfigPlat
double _previousScrollX;
double _previousScrollY;
View? _scrollParent;
+ Element? _templateParent;
SwipeDirection? _swipeDirection;
ISwipeItems ISwipeView.LeftItems => new HandlerSwipeItems(LeftItems);
@@ -313,60 +314,83 @@ void OnPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEven
private protected override void OnParentChangedCore()
{
- if (_scrollParent != null)
+ base.OnParentChangedCore();
+
+ if (_templateParent != null)
{
- if (_scrollParent is ScrollView scrollView)
- {
- scrollView.Scrolled -= OnParentScrolled;
- }
+ _templateParent.ParentChanged -= OnTemplateParentChanged;
+ _templateParent = null;
+ }
+ UnsubscribeFromParentScrolledEvents();
+ // Try direct discovery first (works when the visual tree is already connected)
+ if (SubscribeToNearestScrollParent(this))
+ return;
+
+ // Deferred discovery for DataTemplate scenarios where the tree isn't fully connected yet
+ _templateParent = this.FindParentWith(x => x.Parent == null, false);
+ if (_templateParent != null)
+ _templateParent.ParentChanged += OnTemplateParentChanged;
+ }
+
+ void UnsubscribeFromParentScrolledEvents()
+ {
+ if (_scrollParent is ScrollView scrollView)
+ {
+ scrollView.Scrolled -= OnParentScrolled;
+ }
#pragma warning disable CS0618 // Type or member is obsolete
- if (_scrollParent is ListView listView)
- {
- listView.Scrolled -= OnParentScrolled;
- return;
- }
+ else if (_scrollParent is ListView listView)
+ {
+ listView.Scrolled -= OnParentScrolled;
+ }
#pragma warning restore CS0618 // Type or member is obsolete
+ else if (_scrollParent is CollectionView collectionView)
+ {
+ collectionView.Scrolled -= OnParentScrolled;
+ }
+ _scrollParent = null;
+ }
- if (_scrollParent is Microsoft.Maui.Controls.CollectionView collectionView)
- {
- collectionView.Scrolled -= OnParentScrolled;
- }
+ void OnTemplateParentChanged(object? sender, EventArgs e)
+ {
+ UnsubscribeFromParentScrolledEvents();
- _scrollParent = null;
+ if (_templateParent?.Parent != null)
+ {
+ SubscribeToNearestScrollParent(_templateParent);
}
+ }
- base.OnParentChangedCore();
+ bool SubscribeToNearestScrollParent(Element startElement)
+ {
+ _scrollParent = startElement.FindParentOfType();
- if (_scrollParent == null)
+ if (_scrollParent is ScrollView scrollView)
{
- _scrollParent = this.FindParentOfType();
-
- if (_scrollParent is ScrollView scrollView)
- {
- scrollView.Scrolled += OnParentScrolled;
- return;
- }
+ scrollView.Scrolled += OnParentScrolled;
+ return true;
+ }
#pragma warning disable CS0618 // Type or member is obsolete
- _scrollParent = this.FindParentOfType();
-#pragma warning restore CS0618 // Type or member is obsolete
+ _scrollParent = startElement.FindParentOfType();
-#pragma warning disable CS0618 // Type or member is obsolete
- if (_scrollParent is ListView listView)
- {
- listView.Scrolled += OnParentScrolled;
- return;
- }
+ if (_scrollParent is ListView listView)
+ {
+ listView.Scrolled += OnParentScrolled;
+ return true;
+ }
#pragma warning restore CS0618 // Type or member is obsolete
- _scrollParent = this.FindParentOfType();
+ _scrollParent = startElement.FindParentOfType();
- if (_scrollParent is Microsoft.Maui.Controls.CollectionView collectionView)
- {
- collectionView.Scrolled += OnParentScrolled;
- }
+ if (_scrollParent is Microsoft.Maui.Controls.CollectionView collectionView)
+ {
+ collectionView.Scrolled += OnParentScrolled;
+ return true;
}
+
+ return false;
}
void UpdateMargin()
@@ -380,8 +404,8 @@ void UpdateMargin()
void OnParentScrolled(object? sender, ScrolledEventArgs e)
{
- var horizontalDelta = e.ScrollX - _previousScrollX;
- var verticalDelta = e.ScrollY - _previousScrollY;
+ var horizontalDelta = Math.Abs(e.ScrollX - _previousScrollX);
+ var verticalDelta = Math.Abs(e.ScrollY - _previousScrollY);
if (horizontalDelta > SwipeMinimumDelta || verticalDelta > SwipeMinimumDelta)
((ISwipeView)this).RequestClose(new SwipeViewCloseRequest(true));
@@ -392,7 +416,7 @@ void OnParentScrolled(object? sender, ScrolledEventArgs e)
void OnParentScrolled(object? sender, ItemsViewScrolledEventArgs e)
{
- if (e.HorizontalDelta > SwipeMinimumDelta || e.VerticalDelta > SwipeMinimumDelta)
+ if (Math.Abs(e.HorizontalDelta) > SwipeMinimumDelta || Math.Abs(e.VerticalDelta) > SwipeMinimumDelta)
((ISwipeView)this).RequestClose(new SwipeViewCloseRequest(true));
}
diff --git a/src/Controls/src/Core/VisualElement/VisualElement.cs b/src/Controls/src/Core/VisualElement/VisualElement.cs
index 4db4e3ad0042..863154f7a8b8 100644
--- a/src/Controls/src/Core/VisualElement/VisualElement.cs
+++ b/src/Controls/src/Core/VisualElement/VisualElement.cs
@@ -1672,13 +1672,13 @@ protected internal virtual void ChangeVisualState()
{
VisualStateManager.GoToState(this, VisualStateManager.CommonStates.Disabled);
}
- else if (IsPointerOver)
- {
- VisualStateManager.GoToState(this, VisualStateManager.CommonStates.PointerOver);
- }
else
{
- VisualStateManager.GoToState(this, VisualStateManager.CommonStates.Normal);
+ bool isSelected = this.IsElementInSelectedState();
+ string targetState = isSelected ? VisualStateManager.CommonStates.Selected
+ : (IsPointerOver ? VisualStateManager.CommonStates.PointerOver : VisualStateManager.CommonStates.Normal);
+
+ VisualStateManager.GoToState(this, targetState);
}
if (IsEnabled)
diff --git a/src/Controls/src/Core/VisualStateManager.cs b/src/Controls/src/Core/VisualStateManager.cs
index 3910d823a91a..4c315d0fe681 100644
--- a/src/Controls/src/Core/VisualStateManager.cs
+++ b/src/Controls/src/Core/VisualStateManager.cs
@@ -779,6 +779,20 @@ internal static bool HasVisualState(this VisualElement element, string name)
return false;
}
+
+ internal static bool IsElementInSelectedState(this VisualElement element)
+ {
+ var groups = VisualStateManager.GetVisualStateGroups(element);
+ foreach (var group in groups)
+ {
+ if (group.CurrentState?.Name == VisualStateManager.CommonStates.Selected)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
internal class WatchAddList : IList
diff --git a/src/Controls/src/Core/Window/Window.cs b/src/Controls/src/Core/Window/Window.cs
index 429eccb62f12..21025b297fc9 100644
--- a/src/Controls/src/Core/Window/Window.cs
+++ b/src/Controls/src/Core/Window/Window.cs
@@ -44,11 +44,13 @@ public partial class Window : NavigableElement, IWindow, IToolbarElement, IMenuB
/// Bindable property for .
public static readonly BindableProperty WidthProperty = BindableProperty.Create(
- nameof(Width), typeof(double), typeof(Window), Primitives.Dimension.Unset);
+ nameof(Width), typeof(double), typeof(Window), Primitives.Dimension.Unset,
+ propertyChanged: OnSizePropertyChanged);
/// Bindable property for .
public static readonly BindableProperty HeightProperty = BindableProperty.Create(
- nameof(Height), typeof(double), typeof(Window), Primitives.Dimension.Unset);
+ nameof(Height), typeof(double), typeof(Window), Primitives.Dimension.Unset,
+ propertyChanged: OnSizePropertyChanged);
/// Bindable property for .
public static readonly BindableProperty MaximumWidthProperty = BindableProperty.Create(
@@ -228,6 +230,16 @@ double GetSizeCoordinate(BindableProperty property)
int _batchFrameUpdate = 0;
+ static void OnSizePropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var window = (Window)bindable;
+ // Only trigger SizeChanged if not being updated from platform (FrameChanged)
+ if (window._batchFrameUpdate == 0)
+ {
+ window.SizeChanged?.Invoke(window, EventArgs.Empty);
+ }
+ }
+
void IWindow.FrameChanged(Rect frame)
{
if (new Rect(X, Y, Width, Height) == frame)
diff --git a/src/Controls/src/SourceGen/KnownMarkups.cs b/src/Controls/src/SourceGen/KnownMarkups.cs
index c9ade31c9a2a..72094fedfdcc 100644
--- a/src/Controls/src/SourceGen/KnownMarkups.cs
+++ b/src/Controls/src/SourceGen/KnownMarkups.cs
@@ -548,10 +548,9 @@ static bool TryGetXDataType(ElementNode node, SourceGenContext context, out ITyp
return false;
}
- if (!dataType.TryResolveTypeSymbol(null, context.Compilation, context.XmlnsCache, context.TypeCache, out INamedTypeSymbol? symbol) && symbol is not null)
+ if (!dataType.TryResolveTypeSymbol(null, context.Compilation, context.XmlnsCache, context.TypeCache, out INamedTypeSymbol? symbol) || symbol is null)
{
- // TODO report the right diagnostic
- context.ReportDiagnostic(Diagnostic.Create(Descriptors.XamlParserError, location, "Cannot resolve x:DataType type"));
+ context.ReportDiagnostic(Diagnostic.Create(Descriptors.TypeResolution, location, dataTypeName));
return false;
}
diff --git a/src/Controls/src/SourceGen/XmlTypeExtensions.cs b/src/Controls/src/SourceGen/XmlTypeExtensions.cs
index 0726a8a4d5ea..3c08ef92133d 100644
--- a/src/Controls/src/SourceGen/XmlTypeExtensions.cs
+++ b/src/Controls/src/SourceGen/XmlTypeExtensions.cs
@@ -67,6 +67,12 @@ public static bool TryResolveTypeSymbol(this XmlType xmlType, Action
if (types.Length == 1)
{
symbol = types[0];
+ // If the match is via the "Extension" suffix but the type is static, it cannot be
+ // an IMarkupExtension (static types cannot implement interfaces). In that case, skip
+ // this match and continue to try the exact name. This prevents a static helper class
+ // like MyEnumExtension from being returned instead of the MyEnum enum type. (#34021)
+ if (suffix == "Extension" && symbol.IsStatic)
+ continue;
if (symbol.IsGenericType && xmlType.TypeArguments is not null)
{
var typeArgs = xmlType.TypeArguments.Select(typeArg => typeArg.GetTypeSymbol(reportDiagnostic, compilation, xmlnsCache, typeCache)!).ToArray();
diff --git a/src/Controls/src/Xaml/ApplyPropertiesVisitor.cs b/src/Controls/src/Xaml/ApplyPropertiesVisitor.cs
index e513cb13e37c..e3034080322e 100644
--- a/src/Controls/src/Xaml/ApplyPropertiesVisitor.cs
+++ b/src/Controls/src/Xaml/ApplyPropertiesVisitor.cs
@@ -281,6 +281,15 @@ void ProvideValue(ref object value, ElementNode node, object source, XmlName pro
}
catch (Exception e)
{
+ // StaticResourceExtension must always rethrow even when a handler is present —
+ // unlike other markup extensions, swallowing this exception would silently apply
+ // an invalid value to the property. Notify the handler first to log the error.
+ if (markupExtension is StaticResourceExtension)
+ {
+ Context.ExceptionHandler?.Invoke(e);
+ throw;
+ }
+
if (Context.ExceptionHandler != null)
Context.ExceptionHandler(e);
else
diff --git a/src/Controls/src/Xaml/MarkupExtensions/BindingExtension.cs b/src/Controls/src/Xaml/MarkupExtensions/BindingExtension.cs
index 02228ad40da6..5d86ae0daa12 100644
--- a/src/Controls/src/Xaml/MarkupExtensions/BindingExtension.cs
+++ b/src/Controls/src/Xaml/MarkupExtensions/BindingExtension.cs
@@ -95,7 +95,17 @@ BindingBase CreateBinding()
UpdateSourceEventName = UpdateSourceEventName,
FallbackValue = FallbackValue,
TargetNullValue = TargetNullValue,
- DataType = bindingXDataType,
+ // When Source is set to a concrete element reference (e.g. x:Reference), the
+ // DataType from IXamlDataTypeProvider reflects the DataTemplate item type, not
+ // the explicit source type. Assigning that mismatched DataType causes
+ // BindingExpression.Apply to null-out the binding source when
+ // IsXamlCBindingWithSourceCompilationEnabled is true (.NET 10 default for
+ // AOT/trimmed builds). See https://github.com/dotnet/maui/issues/33291.
+ //
+ // RelativeBindingSource is excluded: the developer likely set x:DataType on
+ // the binding to describe the expected type of the resolved ancestor, and
+ // that validation should be preserved.
+ DataType = (Source is null || Source is RelativeBindingSource) ? bindingXDataType : null,
};
}
}
diff --git a/src/Controls/src/Xaml/MarkupExtensions/StaticResourceExtension.cs b/src/Controls/src/Xaml/MarkupExtensions/StaticResourceExtension.cs
index ef8757d883e2..26fe06836982 100644
--- a/src/Controls/src/Xaml/MarkupExtensions/StaticResourceExtension.cs
+++ b/src/Controls/src/Xaml/MarkupExtensions/StaticResourceExtension.cs
@@ -30,16 +30,15 @@ public object ProvideValue(IServiceProvider serviceProvider)
&& !TryGetApplicationLevelResource(Key, out resource, out resourceDictionary))
{
var xmlLineInfo = serviceProvider.GetService(typeof(IXmlLineInfoProvider)) is IXmlLineInfoProvider xmlLineInfoProvider ? xmlLineInfoProvider.XmlLineInfo : null;
+ var ex = new XamlParseException($"StaticResource not found for key {Key}", xmlLineInfo);
if (Controls.Internals.ResourceLoader.ExceptionHandler2 is var ehandler && ehandler != null)
{
- var ex = new XamlParseException($"StaticResource not found for key {Key}", xmlLineInfo);
var rootObjectProvider = (IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider));
var root = rootObjectProvider.RootObject;
ehandler.Invoke((ex, XamlFilePathAttribute.GetFilePathForObject(root)));
- return null;
}
- else
- throw new XamlParseException($"StaticResource not found for key {Key}", xmlLineInfo);
+ // Throw an exception when the key is not found
+ throw ex;
}
Diagnostics.ResourceDictionaryDiagnostics.OnStaticResourceResolved(resourceDictionary, Key, valueProvider.TargetObject, valueProvider.TargetProperty);
diff --git a/src/Controls/src/Xaml/XmlTypeXamlExtensions.cs b/src/Controls/src/Xaml/XmlTypeXamlExtensions.cs
index dfef113220cd..6eab676b6346 100644
--- a/src/Controls/src/Xaml/XmlTypeXamlExtensions.cs
+++ b/src/Controls/src/Xaml/XmlTypeXamlExtensions.cs
@@ -108,6 +108,15 @@ public static IEnumerable GetTypeReferences(
if ((returnTypeName == null || returnTypeName == typeInfo.typeName) //only return multiple types if they share the same name. avoid returning both BindingExtension and Binding
&& (type = refFromTypeInfo(typeInfo)) != null)
{
+ // If this is an Extension-suffix match but the type is static (cannot implement
+ // IMarkupExtension), skip it and continue to try the exact name. This prevents
+ // a static helper class like MyEnumExtension from shadowing MyEnum. (#34021)
+ if (returnTypeName == null
+ && typeInfo.typeName.EndsWith("Extension", StringComparison.Ordinal)
+ && typeInfo.typeName != elementName
+ && type is System.Type runtimeType
+ && runtimeType.IsAbstract && runtimeType.IsSealed) // static in reflection: abstract+sealed
+ continue;
returnTypeName = typeInfo.typeName;
yield return type;
}
diff --git a/src/Controls/tests/Core.UnitTests/FlexOrderTests.cs b/src/Controls/tests/Core.UnitTests/FlexOrderTests.cs
index 3a087779144d..257698b9e31b 100644
--- a/src/Controls/tests/Core.UnitTests/FlexOrderTests.cs
+++ b/src/Controls/tests/Core.UnitTests/FlexOrderTests.cs
@@ -45,5 +45,124 @@ public void TestOrderingElements()
Assert.Equal(label1.Bounds, new Rect(0, 40, 912, 20));
Assert.Equal(label0.Bounds, new Rect(0, 60, 912, 20));
}
+
+ [Fact]
+ public void TestReverseOrderingElements()
+ {
+ // Children inserted in order 0..3, but Order values are reversed (3, 2, 1, 0)
+ // so layout should place label0 last and label3 first
+ var label0 = MockPlatformSizeService.Sub();
+ var label1 = MockPlatformSizeService.Sub();
+ var label2 = MockPlatformSizeService.Sub();
+ var label3 = MockPlatformSizeService.Sub();
+
+ FlexLayout.SetOrder(label0, 3);
+ FlexLayout.SetOrder(label1, 2);
+ FlexLayout.SetOrder(label2, 1);
+ FlexLayout.SetOrder(label3, 0);
+
+ var layout = new FlexLayout
+ {
+ IsPlatformEnabled = true,
+ Direction = FlexDirection.Column,
+ Children = { label0, label1, label2, label3 }
+ };
+
+ layout.Layout(new Rect(0, 0, 912, 912));
+
+ // label3 (Order=0), label2 (Order=1), label1 (Order=2), label0 (Order=3)
+ Assert.Equal(new Rect(0, 0, 912, 20), label3.Bounds);
+ Assert.Equal(new Rect(0, 20, 912, 20), label2.Bounds);
+ Assert.Equal(new Rect(0, 40, 912, 20), label1.Bounds);
+ Assert.Equal(new Rect(0, 60, 912, 20), label0.Bounds);
+ }
+
+ [Fact]
+ public void TestStableSortPreservesInsertionOrder()
+ {
+ // When multiple children share the same Order value,
+ // they must keep their original insertion order (stable sort)
+ var label0 = MockPlatformSizeService.Sub();
+ var label1 = MockPlatformSizeService.Sub();
+ var label2 = MockPlatformSizeService.Sub();
+ var label3 = MockPlatformSizeService.Sub();
+
+ // label0 has high Order, label1-3 share Order=0 → stability matters
+ FlexLayout.SetOrder(label0, 1);
+ FlexLayout.SetOrder(label1, 0);
+ FlexLayout.SetOrder(label2, 0);
+ FlexLayout.SetOrder(label3, 0);
+
+ var layout = new FlexLayout
+ {
+ IsPlatformEnabled = true,
+ Direction = FlexDirection.Column,
+ Children = { label0, label1, label2, label3 }
+ };
+
+ layout.Layout(new Rect(0, 0, 912, 912));
+
+ // label1, label2, label3 all have Order=0 → preserve insertion order
+ // label0 has Order=1 → comes last
+ Assert.Equal(new Rect(0, 0, 912, 20), label1.Bounds);
+ Assert.Equal(new Rect(0, 20, 912, 20), label2.Bounds);
+ Assert.Equal(new Rect(0, 40, 912, 20), label3.Bounds);
+ Assert.Equal(new Rect(0, 60, 912, 20), label0.Bounds);
+ }
+
+ [Fact]
+ public void TestNegativeOrderValues()
+ {
+ // Negative Order values should sort before zero
+ var label0 = MockPlatformSizeService.Sub();
+ var label1 = MockPlatformSizeService.Sub();
+ var label2 = MockPlatformSizeService.Sub();
+
+ FlexLayout.SetOrder(label0, 1);
+ FlexLayout.SetOrder(label1, 0);
+ FlexLayout.SetOrder(label2, -1);
+
+ var layout = new FlexLayout
+ {
+ IsPlatformEnabled = true,
+ Direction = FlexDirection.Column,
+ Children = { label0, label1, label2 }
+ };
+
+ layout.Layout(new Rect(0, 0, 912, 912));
+
+ // label2 (Order=-1), label1 (Order=0), label0 (Order=1)
+ Assert.Equal(new Rect(0, 0, 912, 20), label2.Bounds);
+ Assert.Equal(new Rect(0, 20, 912, 20), label1.Bounds);
+ Assert.Equal(new Rect(0, 40, 912, 20), label0.Bounds);
+ }
+
+ [Fact]
+ public void TestOrderWithRowDirection()
+ {
+ // Verify ordering works in Row direction too (horizontal layout)
+ var label0 = MockPlatformSizeService.Sub();
+ var label1 = MockPlatformSizeService.Sub();
+ var label2 = MockPlatformSizeService.Sub();
+
+ FlexLayout.SetOrder(label0, 2);
+ FlexLayout.SetOrder(label1, 0);
+ FlexLayout.SetOrder(label2, 1);
+
+ var layout = new FlexLayout
+ {
+ IsPlatformEnabled = true,
+ Direction = FlexDirection.Row,
+ Children = { label0, label1, label2 }
+ };
+
+ layout.Layout(new Rect(0, 0, 912, 912));
+
+ // label1 (Order=0), label2 (Order=1), label0 (Order=2)
+ // In row direction, x position changes
+ Assert.Equal(0d, label1.Bounds.X);
+ Assert.True(label2.Bounds.X > label1.Bounds.X);
+ Assert.True(label0.Bounds.X > label2.Bounds.X);
+ }
}
}
diff --git a/src/Controls/tests/Core.UnitTests/Gestures/SwipeGestureRecognizerTests.cs b/src/Controls/tests/Core.UnitTests/Gestures/SwipeGestureRecognizerTests.cs
index d1a22a4e9308..e7121eee45a9 100644
--- a/src/Controls/tests/Core.UnitTests/Gestures/SwipeGestureRecognizerTests.cs
+++ b/src/Controls/tests/Core.UnitTests/Gestures/SwipeGestureRecognizerTests.cs
@@ -117,5 +117,56 @@ public void SwipedEventDirectionMatchesTotalYTestWithFlags()
((ISwipeGestureController)swipe).DetectSwipe(view, SwipeDirection.Up | SwipeDirection.Down);
Assert.Equal(SwipeDirection.Up, direction);
}
+
+ [Theory]
+ [InlineData(SwipeDirection.Up, 90.0, SwipeDirection.Right)]
+ [InlineData(SwipeDirection.Right, 90.0, SwipeDirection.Down)]
+ [InlineData(SwipeDirection.Down, 90.0, SwipeDirection.Left)]
+ [InlineData(SwipeDirection.Left, 90.0, SwipeDirection.Up)]
+ public void TransformSwipeDirectionForRotation_90DegreeClockwise_ReturnsCorrectDirection(
+ SwipeDirection direction, double rotation, SwipeDirection expected)
+ {
+ var result = Internals.SwipeGestureExtensions.TransformSwipeDirectionForRotation(direction, rotation);
+
+ Assert.Equal(expected, result);
+ }
+
+ [Theory]
+ [InlineData(SwipeDirection.Up, 180.0, SwipeDirection.Down)]
+ [InlineData(SwipeDirection.Right, 180.0, SwipeDirection.Left)]
+ [InlineData(SwipeDirection.Down, 180.0, SwipeDirection.Up)]
+ [InlineData(SwipeDirection.Left, 180.0, SwipeDirection.Right)]
+ public void TransformSwipeDirectionForRotation_180Degree_ReturnsCorrectDirection(
+ SwipeDirection direction, double rotation, SwipeDirection expected)
+ {
+ var result = Internals.SwipeGestureExtensions.TransformSwipeDirectionForRotation(direction, rotation);
+
+ Assert.Equal(expected, result);
+ }
+
+ [Theory]
+ [InlineData(SwipeDirection.Up, 270.0, SwipeDirection.Left)]
+ [InlineData(SwipeDirection.Right, 270.0, SwipeDirection.Up)]
+ [InlineData(SwipeDirection.Down, 270.0, SwipeDirection.Right)]
+ [InlineData(SwipeDirection.Left, 270.0, SwipeDirection.Down)]
+ public void TransformSwipeDirectionForRotation_270DegreeClockwise_ReturnsCorrectDirection(
+ SwipeDirection direction, double rotation, SwipeDirection expected)
+ {
+ var result = Internals.SwipeGestureExtensions.TransformSwipeDirectionForRotation(direction, rotation);
+
+ Assert.Equal(expected, result);
+ }
+
+ [Theory]
+ [InlineData(SwipeDirection.Up, double.NaN, SwipeDirection.Up)]
+ [InlineData(SwipeDirection.Right, double.PositiveInfinity, SwipeDirection.Right)]
+ [InlineData(SwipeDirection.Down, double.NegativeInfinity, SwipeDirection.Down)]
+ public void TransformSwipeDirectionForRotation_InvalidRotation_ReturnsOriginalDirection(
+ SwipeDirection direction, double rotation, SwipeDirection expected)
+ {
+ var result = Internals.SwipeGestureExtensions.TransformSwipeDirectionForRotation(direction, rotation);
+
+ Assert.Equal(expected, result);
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Controls/tests/Core.UnitTests/Layouts/FlexLayoutTests.cs b/src/Controls/tests/Core.UnitTests/Layouts/FlexLayoutTests.cs
index 6867dcb29098..72a65af89924 100644
--- a/src/Controls/tests/Core.UnitTests/Layouts/FlexLayoutTests.cs
+++ b/src/Controls/tests/Core.UnitTests/Layouts/FlexLayoutTests.cs
@@ -33,6 +33,23 @@ protected override Size MeasureOverride(double widthConstraint, double heightCon
}
}
+ class FixedSizeLabel : Label
+ {
+ readonly double _width;
+ readonly double _height;
+
+ public FixedSizeLabel(double width, double height)
+ {
+ _width = width;
+ _height = height;
+ }
+
+ protected override Size MeasureOverride(double widthConstraint, double heightConstraint)
+ {
+ return new Size(_width, _height);
+ }
+ }
+
[Fact]
public void FlexLayoutMeasuresImagesUnconstrained()
{
@@ -218,5 +235,169 @@ public void UnconstrainedMeasureHonorsFlexDirection(double widthConstraint, doub
Assert.Equal(0, flexFrame.X);
Assert.Equal(0, flexFrame.Y);
}
+
+ // Test view that respects WidthRequest/HeightRequest in MeasureOverride,
+ // simulating real controls like Grid, StackLayout, etc.
+ class FlexTestView : View
+ {
+ protected override Size MeasureOverride(double widthConstraint, double heightConstraint)
+ {
+ var w = WidthRequest >= 0 ? Math.Min(WidthRequest, widthConstraint) : Math.Min(100, widthConstraint);
+ var h = HeightRequest >= 0 ? Math.Min(HeightRequest, heightConstraint) : Math.Min(50, heightConstraint);
+ return new Size(w, h);
+ }
+ }
+
+ // Regression test for https://github.com/dotnet/maui/issues/31109
+ // Verifies that dynamically changing WidthRequest on a FlexLayout child
+ // is correctly reflected during an arrange-only pass (no preceding measure).
+ [Fact]
+ public void ArrangeOnlyPassUsesUpdatedWidthRequest()
+ {
+ var root = new Grid();
+ var flexLayout = new FlexLayout() { Direction = FlexDirection.Row };
+ var view = new FlexTestView { WidthRequest = 200 };
+
+ root.Add(flexLayout);
+ (flexLayout as IFlexLayout).Add(view as IView);
+
+ // Initial measure + arrange
+ var measure = flexLayout.CrossPlatformMeasure(1000, 1000);
+ flexLayout.CrossPlatformArrange(new Rect(Point.Zero, measure));
+
+ var initialFrame = (flexLayout as IFlexLayout).GetFlexFrame(view as IView);
+ Assert.Equal(200, initialFrame.Width);
+
+ // Change WidthRequest without re-measuring
+ view.WidthRequest = 300;
+
+ // Arrange-only pass (simulates Android arrange without preceding measure)
+ flexLayout.CrossPlatformArrange(new Rect(0, 0, 1000, 1000));
+
+ var updatedFrame = (flexLayout as IFlexLayout).GetFlexFrame(view as IView);
+ Assert.Equal(300, updatedFrame.Width);
+ }
+
+ // Regression test for https://github.com/dotnet/maui/issues/31109
+ // Verifies that changing HeightRequest during arrange-only pass works correctly.
+ [Fact]
+ public void ArrangeOnlyPassUsesUpdatedHeightRequest()
+ {
+ var root = new Grid();
+ var flexLayout = new FlexLayout() { Direction = FlexDirection.Column };
+ var view = new FlexTestView { HeightRequest = 80 };
+
+ root.Add(flexLayout);
+ (flexLayout as IFlexLayout).Add(view as IView);
+
+ // Initial measure + arrange
+ var measure = flexLayout.CrossPlatformMeasure(1000, 1000);
+ flexLayout.CrossPlatformArrange(new Rect(Point.Zero, measure));
+
+ var initialFrame = (flexLayout as IFlexLayout).GetFlexFrame(view as IView);
+ Assert.Equal(80, initialFrame.Height);
+
+ // Change HeightRequest without re-measuring
+ view.HeightRequest = 120;
+
+ // Arrange-only pass
+ flexLayout.CrossPlatformArrange(new Rect(0, 0, 1000, 1000));
+
+ var updatedFrame = (flexLayout as IFlexLayout).GetFlexFrame(view as IView);
+ Assert.Equal(120, updatedFrame.Height);
+ }
+
+ // Regression test for https://github.com/dotnet/maui/issues/31109
+ // Verifies that children without explicit WidthRequest still use DesiredSize during arrange.
+ [Fact]
+ public void ArrangeOnlyPassUsesDesiredSizeWhenNoWidthRequest()
+ {
+ var root = new Grid();
+ var flexLayout = new FlexLayout() { Direction = FlexDirection.Row };
+ var view = new TestLabel(); // No WidthRequest set, MeasureOverride returns (150, 100)
+
+ root.Add(flexLayout);
+ (flexLayout as IFlexLayout).Add(view as IView);
+
+ // Initial measure + arrange
+ var measure = flexLayout.CrossPlatformMeasure(1000, 1000);
+ flexLayout.CrossPlatformArrange(new Rect(Point.Zero, measure));
+
+ var initialFrame = (flexLayout as IFlexLayout).GetFlexFrame(view as IView);
+ Assert.Equal(150, initialFrame.Width);
+
+ // Arrange-only pass (should still use DesiredSize since no WidthRequest)
+ flexLayout.CrossPlatformArrange(new Rect(0, 0, 1000, 1000));
+
+ var afterFrame = (flexLayout as IFlexLayout).GetFlexFrame(view as IView);
+ Assert.Equal(150, afterFrame.Width);
+ }
+
+ // Regression test for https://github.com/dotnet/maui/issues/31109
+ // Verifies that clearing WidthRequest (setting to -1) falls back to DesiredSize during arrange.
+ [Fact]
+ public void ArrangeOnlyPassFallsBackToDesiredSizeWhenWidthRequestCleared()
+ {
+ var root = new Grid();
+ var flexLayout = new FlexLayout() { Direction = FlexDirection.Row };
+ var view = new FlexTestView { WidthRequest = 200 };
+
+ root.Add(flexLayout);
+ (flexLayout as IFlexLayout).Add(view as IView);
+
+ // Initial measure + arrange with explicit WidthRequest
+ var measure = flexLayout.CrossPlatformMeasure(1000, 1000);
+ flexLayout.CrossPlatformArrange(new Rect(Point.Zero, measure));
+
+ var initialFrame = (flexLayout as IFlexLayout).GetFlexFrame(view as IView);
+ Assert.Equal(200, initialFrame.Width);
+
+ // Clear WidthRequest and re-measure so DesiredSize reflects auto-sizing
+ view.WidthRequest = -1;
+ flexLayout.CrossPlatformMeasure(1000, 1000);
+ flexLayout.CrossPlatformArrange(new Rect(0, 0, 1000, 1000));
+
+ // Should fall back to the auto-sized DesiredSize (100 from FlexTestView default)
+ var clearedFrame = (flexLayout as IFlexLayout).GetFlexFrame(view as IView);
+ Assert.Equal(100, clearedFrame.Width);
+ }
+
+ [Fact]
+ public void GrowItemsPreserveNaturalSizeAndDistributeFreeSpaceEqually_Issue34464()
+ {
+ // Items with different natural widths but equal Grow values should each receive
+ // an equal share of the available free space added on top of their natural width.
+ // Before the fix, the natural size was zeroed and the inflated flex_dim was
+ // distributed proportionally, causing items with larger natural sizes to receive
+ // less growth than smaller items (violating the flex-grow spec).
+ var root = new Grid();
+ var controlsFlexLayout = new FlexLayout();
+ var flexLayout = controlsFlexLayout as IFlexLayout;
+
+ // item1 is narrower (50px), item2 is wider (100px); both have equal Grow
+ var item1 = new FixedSizeLabel(50, 50);
+ var item2 = new FixedSizeLabel(100, 50);
+
+ FlexLayout.SetGrow(item1, 1);
+ FlexLayout.SetShrink(item1, 0);
+ FlexLayout.SetGrow(item2, 1);
+ FlexLayout.SetShrink(item2, 0);
+
+ root.Add(controlsFlexLayout);
+ flexLayout.Add(item1 as IView);
+ flexLayout.Add(item2 as IView);
+
+ // Container = 300px; total natural width = 150px; free space = 150px.
+ // With Grow=1 on both items each should receive 75px of extra space:
+ // item1 expected: 50 + 75 = 125
+ // item2 expected: 100 + 75 = 175
+ _ = flexLayout.CrossPlatformMeasure(300, 200);
+
+ var frame1 = flexLayout.GetFlexFrame(item1 as IView);
+ var frame2 = flexLayout.GetFlexFrame(item2 as IView);
+
+ Assert.Equal(125, frame1.Width);
+ Assert.Equal(175, frame2.Width);
+ }
}
}
diff --git a/src/Controls/tests/Core.UnitTests/RadioButtonContentAsStringTests.cs b/src/Controls/tests/Core.UnitTests/RadioButtonContentAsStringTests.cs
new file mode 100644
index 000000000000..8434f42e98fa
--- /dev/null
+++ b/src/Controls/tests/Core.UnitTests/RadioButtonContentAsStringTests.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Linq;
+using Xunit;
+
+namespace Microsoft.Maui.Controls.Core.UnitTests
+{
+ ///
+ /// Tests for RadioButton ContentAsString() warning behavior.
+ /// Issue #33829: Warning should not be logged when ControlTemplate is set because
+ /// View content IS supported in that scenario.
+ ///
+ [Category("RadioButton")]
+ public class RadioButtonContentAsStringTests : BaseTestFixture
+ {
+ public RadioButtonContentAsStringTests()
+ {
+ ApplicationExtensions.CreateAndSetMockApplication();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ Application.ClearCurrent();
+ }
+ base.Dispose(disposing);
+ }
+
+ [Fact]
+ public void ContentAsStringDoesNotLogWarningWhenControlTemplateIsSet()
+ {
+ // Arrange: RadioButton with ControlTemplate AND View Content
+ // This scenario IS supported per documentation, so no warning should be logged
+ var radioButton = new RadioButton
+ {
+ ControlTemplate = RadioButton.DefaultTemplate,
+ Content = new Label { Text = "Test Label" }
+ };
+
+ // Act
+ var result = radioButton.ContentAsString();
+
+ // Assert: No warning should be logged when ControlTemplate is set
+ Assert.True(MockApplication.MockLogger.Messages.Count == 0,
+ "No warning should be logged when ControlTemplate is set. " +
+ $"Found: {MockApplication.MockLogger.Messages.FirstOrDefault()}");
+ Assert.NotNull(result);
+ }
+
+ [Fact]
+ public void ContentAsStringLogsWarningWhenNoControlTemplate()
+ {
+ // Arrange: RadioButton without ControlTemplate but with View Content
+ // This scenario is NOT supported, so warning should be logged
+ var radioButton = new RadioButton
+ {
+ Content = new Label { Text = "Test Label" }
+ };
+
+ // Act
+ var result = radioButton.ContentAsString();
+
+ // Assert: Warning SHOULD be logged when ControlTemplate is null
+ Assert.Single(MockApplication.MockLogger.Messages);
+ Assert.Contains("does not support View as the Content property", MockApplication.MockLogger.Messages.First(), StringComparison.Ordinal);
+ }
+ }
+}
diff --git a/src/Controls/tests/Core.UnitTests/ShellElementCollectionTests.cs b/src/Controls/tests/Core.UnitTests/ShellElementCollectionTests.cs
index 2d58e6777516..ed0d31a2eced 100644
--- a/src/Controls/tests/Core.UnitTests/ShellElementCollectionTests.cs
+++ b/src/Controls/tests/Core.UnitTests/ShellElementCollectionTests.cs
@@ -31,5 +31,69 @@ public void ClearFiresOnlyOneRemovedEvent()
shellSection.Items.Clear();
Assert.Equal(1, firedCount);
}
+
+ // Regression tests for https://github.com/dotnet/maui/issues/28078
+ // Shell crashes with IllegalArgumentException when tapping flyout after dynamically replacing items.
+ // Fix: ShellElementCollection tracks _isItemsCleared and calls NotifyFlyoutBehaviorObservers()
+ // when the item count reaches 2 after a clear, ensuring platform handlers update their flyout state.
+
+ [Fact]
+ public void FlyoutBehaviorObserverNotifiedWhenItemsReplacedWithMultiple()
+ {
+ // Arrange: Shell starts with a single implicit item (flyout disabled)
+ var shell = new Shell();
+ shell.Items.Add(CreateShellItem(asImplicit: true));
+
+ var notifiedBehaviors = new List();
+ var observer = new TestFlyoutBehaviorObserver(b => notifiedBehaviors.Add(b));
+ (shell as IShellController).AddFlyoutBehaviorObserver(observer);
+ notifiedBehaviors.Clear(); // Ignore initial notification from AddFlyoutBehaviorObserver
+
+ // Act: Simulate login flow — clear single item and replace with multiple items
+ shell.Items.Clear();
+ shell.Items.Add(CreateShellItem(asImplicit: true));
+ shell.Items.Add(CreateShellItem(asImplicit: true));
+
+ // Assert: Observer was notified with Flyout behavior when second item was added
+ Assert.Contains(FlyoutBehavior.Flyout, notifiedBehaviors);
+ }
+
+ [Fact]
+ public void FlyoutBehaviorObserverNotifiedExactlyOnceWhenMoreThanTwoItemsAddedAfterClear()
+ {
+ // Arrange: Shell starts with a single implicit item (flyout disabled)
+ var shell = new Shell();
+ shell.Items.Add(CreateShellItem(asImplicit: true));
+
+ int flyoutNotificationCount = 0;
+ var observer = new TestFlyoutBehaviorObserver(b =>
+ {
+ if (b == FlyoutBehavior.Flyout)
+ flyoutNotificationCount++;
+ });
+ (shell as IShellController).AddFlyoutBehaviorObserver(observer);
+ flyoutNotificationCount = 0; // Ignore initial notification from AddFlyoutBehaviorObserver
+
+ // Act: Clear and add 3 items
+ shell.Items.Clear();
+ shell.Items.Add(CreateShellItem(asImplicit: true));
+ shell.Items.Add(CreateShellItem(asImplicit: true)); // Notification fires here (count==2)
+ shell.Items.Add(CreateShellItem(asImplicit: true)); // Should NOT fire again (_isItemsCleared already reset)
+
+ // Assert: Observer notified with Flyout exactly once — at count==2, not again at count==3
+ Assert.Equal(1, flyoutNotificationCount);
+ }
+
+ class TestFlyoutBehaviorObserver : IFlyoutBehaviorObserver
+ {
+ readonly Action _onChanged;
+
+ public TestFlyoutBehaviorObserver(Action onChanged)
+ {
+ _onChanged = onChanged;
+ }
+
+ public void OnFlyoutBehaviorChanged(FlyoutBehavior behavior) => _onChanged(behavior);
+ }
}
}
diff --git a/src/Controls/tests/Core.UnitTests/ShellTests.cs b/src/Controls/tests/Core.UnitTests/ShellTests.cs
index a54c61bd9c89..a0897d7d4265 100644
--- a/src/Controls/tests/Core.UnitTests/ShellTests.cs
+++ b/src/Controls/tests/Core.UnitTests/ShellTests.cs
@@ -445,6 +445,21 @@ public async Task DotDotAdheresToAnimationParameter()
Assert.True(shell.LastPopWasAnimated);
}
+ [Fact]
+ public async Task BackNavigationDefaultsToAnimatedWhenNotSpecified()
+ {
+ Routing.RegisterRoute(nameof(BackNavigationDefaultsToAnimatedWhenNotSpecified), typeof(ContentPage));
+ var shellContent = new ShellContent();
+ var shell = new TestShell(new TestFlyoutItem(new TestShellSection(shellContent)));
+ await shell.GoToAsync(nameof(BackNavigationDefaultsToAnimatedWhenNotSpecified));
+
+ // Navigate back without specifying animate parameter (null)
+ await shell.GoToAsync("..");
+
+ // Should default to animated (true) to match other navigation methods
+ Assert.True(shell.LastPopWasAnimated);
+ }
+
[Fact]
public async Task DefaultRoutesMaintainedIfThatsAllThereIs()
{
diff --git a/src/Controls/tests/Core.UnitTests/SwipeViewTests.cs b/src/Controls/tests/Core.UnitTests/SwipeViewTests.cs
index 67c1cf21896b..19cf3979fcdf 100644
--- a/src/Controls/tests/Core.UnitTests/SwipeViewTests.cs
+++ b/src/Controls/tests/Core.UnitTests/SwipeViewTests.cs
@@ -486,5 +486,85 @@ class TestViewModel
{
public Command TestCommand { get; set; }
}
+
+ static object GetPrivateField(object obj, string fieldName)
+ {
+ var field = obj.GetType().GetField(fieldName,
+ System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ return field?.GetValue(obj);
+ }
+
+ [Fact]
+ public void SwipeViewFindsScrollParentDirectlyWhenTreeIsConnected()
+ {
+ // Non-DataTemplate scenario: SwipeView added to an already-connected ScrollView
+ var scrollView = new ScrollView();
+ var contentView = new ContentView();
+ scrollView.Content = contentView;
+
+ var swipeView = new SwipeView();
+ contentView.Content = swipeView;
+
+ // SwipeView should find the ScrollView via direct parent walk
+ Assert.Equal(scrollView, GetPrivateField(swipeView, "_scrollParent"));
+ }
+
+ [Fact]
+ public void SwipeViewFindsScrollParentAfterTemplateParentConnected()
+ {
+ // DataTemplate scenario: SwipeView inside ContentView (template root with no parent)
+ var swipeView = new SwipeView();
+ var contentView = new ContentView { Content = swipeView };
+
+ // At this point contentView.Parent is null, simulating an unattached template root.
+ Assert.Null(GetPrivateField(swipeView, "_scrollParent"));
+
+ // Connect the template root to a ScrollView (simulating CollectionView adding the item)
+ var scrollView = new ScrollView { Content = contentView };
+
+ // SwipeView should now have discovered the ScrollView via deferred discovery
+ Assert.Equal(scrollView, GetPrivateField(swipeView, "_scrollParent"));
+ }
+
+ [Fact]
+ public void SwipeViewResubscribesToScrollParentAfterRemovalAndReaddition()
+ {
+ var swipeView = new SwipeView();
+ var contentView1 = new ContentView { Content = swipeView };
+ var scrollView1 = new ScrollView { Content = contentView1 };
+
+ Assert.Equal(scrollView1, GetPrivateField(swipeView, "_scrollParent"));
+
+ // Remove SwipeView from the tree entirely
+ contentView1.Content = null;
+
+ Assert.Null(GetPrivateField(swipeView, "_scrollParent"));
+
+ // Re-add SwipeView to a different tree with a different ScrollView
+ var contentView2 = new ContentView { Content = swipeView };
+ var scrollView2 = new ScrollView { Content = contentView2 };
+
+ // SwipeView should discover the new ScrollView, not remain stuck on the old one
+ Assert.Equal(scrollView2, GetPrivateField(swipeView, "_scrollParent"));
+ }
+
+ [Fact]
+ public void SwipeViewRediscoversScrollParentWhenTemplateRootIsReparented()
+ {
+ // Simulate virtualization: template root is detached then reattached
+ var swipeView = new SwipeView();
+ var contentView = new ContentView { Content = swipeView };
+
+ var scrollView1 = new ScrollView { Content = contentView };
+ Assert.Equal(scrollView1, GetPrivateField(swipeView, "_scrollParent"));
+
+ // Detach the template root (virtualization removal)
+ scrollView1.Content = null;
+ Assert.Null(GetPrivateField(swipeView, "_scrollParent"));
+
+ // Reattach to a different ScrollView (virtualization re-use)
+ var scrollView2 = new ScrollView { Content = contentView };
+ Assert.Equal(scrollView2, GetPrivateField(swipeView, "_scrollParent"));
+ }
}
}
diff --git a/src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj b/src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj
index 286bc101440d..a71d2584a3cc 100644
--- a/src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj
+++ b/src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj
@@ -49,8 +49,10 @@
+
+
diff --git a/src/Controls/tests/DeviceTests/ControlsDeviceTestExtensions.cs b/src/Controls/tests/DeviceTests/ControlsDeviceTestExtensions.cs
index 5f7dec1aac41..fcaffadc58b3 100644
--- a/src/Controls/tests/DeviceTests/ControlsDeviceTestExtensions.cs
+++ b/src/Controls/tests/DeviceTests/ControlsDeviceTestExtensions.cs
@@ -48,6 +48,10 @@ public static MauiAppBuilder ConfigureTestBuilder(this MauiAppBuilder mauiAppBui
handlers.AddHandler(typeof(VerticalStackLayout), typeof(LayoutHandler));
handlers.AddHandler(typeof(Controls.Window), typeof(WindowHandlerStub));
handlers.AddHandler(typeof(Controls.ContentPage), typeof(PageHandler));
+#if ANDROID || IOS || MACCATALYST
+ // Add Maps handlers for device tests
+ handlers.AddMauiMaps();
+#endif
#if WINDOWS
handlers.AddHandler(typeof(MauiAppNewWindowStub), typeof(ApplicationHandler));
#endif
diff --git a/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.Android.cs b/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.Android.cs
index e2551da6db6f..1673735ccca3 100644
--- a/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.Android.cs
+++ b/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.Android.cs
@@ -144,6 +144,105 @@ await InvokeOnMainThreadAsync(() =>
});
}
+ [Fact(DisplayName = "CollectionView with SelectionMode None should not have click listeners")]
+ public async Task SelectionModeNoneDoesNotSetClickListeners()
+ {
+ SetupBuilder();
+
+ var collectionView = new CollectionView
+ {
+ ItemsSource = new[] { "Item 1", "Item 2", "Item 3" },
+ SelectionMode = SelectionMode.None
+ };
+
+ await InvokeOnMainThreadAsync(() =>
+ {
+ var handler = CreateHandler(collectionView);
+ var viewHolder = LayoutAndGetViewHolder(handler.PlatformView);
+
+ Assert.False(viewHolder.ItemView.HasOnClickListeners,
+ "Items should not have click listeners when SelectionMode is None");
+ });
+ }
+
+ [Fact(DisplayName = "CollectionView SelectionMode Single → None removes click listeners")]
+ public async Task SelectionModeSingleToNoneRemovesClickListeners()
+ {
+ SetupBuilder();
+
+ var collectionView = new CollectionView
+ {
+ ItemsSource = new[] { "Item 1", "Item 2", "Item 3" },
+ SelectionMode = SelectionMode.Single
+ };
+
+ await InvokeOnMainThreadAsync(() =>
+ {
+ var handler = CreateHandler(collectionView);
+ var viewHolder = LayoutAndGetViewHolder(handler.PlatformView);
+
+ Assert.True(viewHolder.ItemView.HasOnClickListeners,
+ "Items should have click listeners when SelectionMode is Single");
+
+ collectionView.SelectionMode = SelectionMode.None;
+
+ Assert.False(viewHolder.ItemView.HasOnClickListeners,
+ "Items should not have click listeners after changing SelectionMode to None");
+ });
+ }
+
+ [Fact(DisplayName = "CollectionView SelectionMode None → Single attaches click listeners")]
+ public async Task SelectionModeNoneToSingleAttachesClickListeners()
+ {
+ SetupBuilder();
+
+ var collectionView = new CollectionView
+ {
+ ItemsSource = new[] { "Item 1", "Item 2", "Item 3" },
+ SelectionMode = SelectionMode.None
+ };
+
+ await InvokeOnMainThreadAsync(() =>
+ {
+ var handler = CreateHandler(collectionView);
+ var viewHolder = LayoutAndGetViewHolder(handler.PlatformView);
+
+ Assert.False(viewHolder.ItemView.HasOnClickListeners,
+ "Items should not have click listeners when SelectionMode is None");
+
+ collectionView.SelectionMode = SelectionMode.Single;
+
+ Assert.True(viewHolder.ItemView.HasOnClickListeners,
+ "Items should have click listeners after changing SelectionMode from None to Single");
+ });
+ }
+
+ [Fact(DisplayName = "CollectionView SelectionMode Single → Multiple keeps click listeners")]
+ public async Task SelectionModeSingleToMultipleKeepsClickListeners()
+ {
+ SetupBuilder();
+
+ var collectionView = new CollectionView
+ {
+ ItemsSource = new[] { "Item 1", "Item 2", "Item 3" },
+ SelectionMode = SelectionMode.Single
+ };
+
+ await InvokeOnMainThreadAsync(() =>
+ {
+ var handler = CreateHandler(collectionView);
+ var viewHolder = LayoutAndGetViewHolder(handler.PlatformView);
+
+ Assert.True(viewHolder.ItemView.HasOnClickListeners,
+ "Items should have click listeners when SelectionMode is Single");
+
+ collectionView.SelectionMode = SelectionMode.Multiple;
+
+ Assert.True(viewHolder.ItemView.HasOnClickListeners,
+ "Items should still have click listeners after changing SelectionMode from Single to Multiple");
+ });
+ }
+
class MockCollectionChangedNotifier : ICollectionChangedNotifier
{
public int InsertCount;
@@ -184,6 +283,22 @@ public void NotifyItemRemoved(IItemsViewSource source, int startIndex)
}
}
+ // Forces the RecyclerView to measure and lay itself out at 500×500 dp, then
+ // returns the ViewHolder at position 0. Centralises boilerplate shared by all
+ // click-listener tests so each test stays focused on its assertion.
+ static global::AndroidX.RecyclerView.Widget.RecyclerView.ViewHolder LayoutAndGetViewHolder(
+ global::AndroidX.RecyclerView.Widget.RecyclerView recyclerView)
+ {
+ recyclerView.Measure(
+ global::Android.Views.View.MeasureSpec.MakeMeasureSpec(500, global::Android.Views.MeasureSpecMode.AtMost),
+ global::Android.Views.View.MeasureSpec.MakeMeasureSpec(500, global::Android.Views.MeasureSpecMode.AtMost));
+ recyclerView.Layout(0, 0, 500, 500);
+
+ var viewHolder = recyclerView.FindViewHolderForAdapterPosition(0);
+ Assert.NotNull(viewHolder);
+ return viewHolder!;
+ }
+
Rect GetCollectionViewCellBounds(IView cellContent)
{
if (!cellContent.ToPlatform().IsLoaded())
diff --git a/src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.Android.cs b/src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.Android.cs
index 252382176454..6e4b57060ec7 100644
--- a/src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.Android.cs
+++ b/src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.Android.cs
@@ -3,6 +3,7 @@
using AndroidX.AppCompat.Widget;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers;
+using Microsoft.Maui.Platform;
using Xunit;
namespace Microsoft.Maui.DeviceTests
@@ -34,11 +35,7 @@ static int GetPlatformCursorPosition(EditorHandler editorHandler)
static int GetPlatformSelectionLength(EditorHandler editorHandler)
{
var textView = GetPlatformControl(editorHandler);
-
- if (textView != null)
- return textView.SelectionEnd - textView.SelectionStart;
-
- return -1;
+ return textView?.GetSelectedTextLength() ?? -1;
}
Task GetPlatformOpacity(EditorHandler editorHandler)
@@ -139,6 +136,33 @@ public async Task RotationYConsistent()
Assert.Equal(expected, platformRotationY);
}
+ [Fact]
+ [Description("The SelectionLength property should handle right-to-left text selection correctly and not return negative values")]
+ public async Task SelectionLengthRightToLeft()
+ {
+ var editor = new Editor()
+ {
+ Text = "Hello World"
+ };
+
+ var handler = await CreateHandlerAsync(editor);
+ var platformControl = GetPlatformControl(handler);
+
+ await InvokeOnMainThreadAsync(() =>
+ {
+ platformControl.SetSelection(5, 0); // SelectionStart=5, SelectionEnd=0
+ int platformSelectionLength = GetPlatformSelectionLength(handler);
+ Assert.True(platformSelectionLength >= 0,
+ $"Platform selection length should never be negative, but got: {platformSelectionLength}");
+ Assert.Equal(5, platformSelectionLength);
+
+ // The virtual view should also show positive selection length
+ Assert.True(editor.SelectionLength >= 0,
+ $"Virtual view selection length should never be negative, but got: {editor.SelectionLength}");
+ Assert.Equal(5, editor.SelectionLength);
+ });
+ }
+
[Fact]
[Description("The Rotation property of a Editor should match with native Rotation")]
public async Task RotationConsistent()
diff --git a/src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.Android.cs b/src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.Android.cs
index e5fb5a30c6bc..e124e6b4a66c 100644
--- a/src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.Android.cs
+++ b/src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.Android.cs
@@ -3,6 +3,7 @@
using AndroidX.AppCompat.Widget;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers;
+using Microsoft.Maui.Platform;
using Xunit;
namespace Microsoft.Maui.DeviceTests
@@ -33,13 +34,8 @@ static int GetPlatformCursorPosition(EntryHandler entryHandler)
static int GetPlatformSelectionLength(EntryHandler entryHandler)
{
var editText = GetPlatformControl(entryHandler);
-
- if (editText != null)
- return editText.SelectionEnd - editText.SelectionStart;
-
- return -1;
+ return editText?.GetSelectedTextLength() ?? -1;
}
-
Task GetPlatformOpacity(EntryHandler entryHandler)
{
return InvokeOnMainThreadAsync(() =>
@@ -165,6 +161,33 @@ public async Task RotationConsistent()
Assert.Equal(expected, platformRotation);
}
+ [Fact]
+ [Description("The SelectionLength property should handle right-to-left text selection correctly and not return negative values")]
+ public async Task SelectionLengthRightToLeft()
+ {
+ var entry = new Entry()
+ {
+ Text = "Hello World"
+ };
+
+ var handler = await CreateHandlerAsync(entry);
+ var platformControl = GetPlatformControl(handler);
+
+ await InvokeOnMainThreadAsync(() =>
+ {
+ platformControl.SetSelection(5, 0);
+ int platformSelectionLength = GetPlatformSelectionLength(handler);
+ Assert.True(platformSelectionLength >= 0,
+ $"Platform selection length should never be negative, but got: {platformSelectionLength}");
+ Assert.Equal(5, platformSelectionLength);
+
+ // The virtual view should also show positive selection length
+ Assert.True(entry.SelectionLength >= 0,
+ $"Virtual view selection length should never be negative, but got: {entry.SelectionLength}");
+ Assert.Equal(5, entry.SelectionLength);
+ });
+ }
+
//src/Compatibility/Core/tests/Android/TranslationTests.cs
[Fact]
[Description("The Translation property of a Entry should match with native Translation")]
diff --git a/src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.iOS.cs b/src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.iOS.cs
index 281c855ad94c..b0efa8c24351 100644
--- a/src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.iOS.cs
+++ b/src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.iOS.cs
@@ -374,5 +374,48 @@ public async Task EntryAlignmentMatchesFlowDirection(bool isExplicit, FlowDirect
Assert.Equal(expectedAlignment, nativeAlignment);
}
+
+ [Fact]
+ public async Task IsPasswordTogglePreservesText()
+ {
+ // https://github.com/dotnet/maui/issues/30085
+ var entry = new Entry
+ {
+ Text = "secret123",
+ IsPassword = false,
+ };
+
+ SetupBuilder();
+
+ await InvokeOnMainThreadAsync(async () =>
+ {
+ var handler = CreateHandler(entry);
+ var platformControl = GetPlatformControl(handler);
+
+ // Simulate the field being focused (first responder)
+ platformControl.BecomeFirstResponder();
+
+ // Toggle IsPassword on — iOS internally clears SecureTextEntry text
+ entry.IsPassword = true;
+ handler.UpdateValue(nameof(IEntry.IsPassword));
+
+ var platformText = platformControl.Text;
+ Assert.Equal("secret123", platformText);
+ Assert.Equal("secret123", entry.Text);
+
+ // Toggle IsPassword off and back on again
+ entry.IsPassword = false;
+ handler.UpdateValue(nameof(IEntry.IsPassword));
+
+ entry.IsPassword = true;
+ handler.UpdateValue(nameof(IEntry.IsPassword));
+
+ platformText = platformControl.Text;
+ Assert.Equal("secret123", platformText);
+ Assert.Equal("secret123", entry.Text);
+
+ platformControl.ResignFirstResponder();
+ });
+ }
}
}
\ No newline at end of file
diff --git a/src/Controls/tests/DeviceTests/Elements/Map/MapTests.cs b/src/Controls/tests/DeviceTests/Elements/Map/MapTests.cs
new file mode 100644
index 000000000000..337a6efe29f9
--- /dev/null
+++ b/src/Controls/tests/DeviceTests/Elements/Map/MapTests.cs
@@ -0,0 +1,120 @@
+using System.Threading.Tasks;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Controls.Maps;
+using Microsoft.Maui.Devices.Sensors;
+using Microsoft.Maui.Maps;
+using Xunit;
+
+namespace Microsoft.Maui.DeviceTests
+{
+ // Android requires a Google Maps API key to instantiate MapView,
+ // which is not available in the test environment.
+#if IOS || MACCATALYST
+ [Category(TestCategory.Map)]
+ public partial class MapTests : ControlsHandlerTestBase
+ {
+ static Polygon CreatePolygon() => new()
+ {
+ Geopath =
+ {
+ new Location(47.6458, -122.1419),
+ new Location(47.6458, -122.1119),
+ new Location(47.6558, -122.1119),
+ new Location(47.6558, -122.1419)
+ }
+ };
+
+ // Regression test for https://github.com/dotnet/maui/issues/30097
+ [Fact]
+ public async Task ClearMapElementsResetsMapElementId()
+ {
+ var map = new Map();
+ await CreateHandlerAsync(map);
+
+ var polygon1 = CreatePolygon();
+ var polygon2 = CreatePolygon();
+
+ // Multiple add/clear cycles reproduce the original issue
+ for (int cycle = 0; cycle < 3; cycle++)
+ {
+ await InvokeOnMainThreadAsync(() =>
+ {
+ map.MapElements.Add(polygon1);
+ map.MapElements.Add(polygon2);
+ });
+
+ Assert.Equal(2, map.MapElements.Count);
+
+ await InvokeOnMainThreadAsync(() => map.MapElements.Clear());
+
+ Assert.Empty(map.MapElements);
+ Assert.Null(polygon1.MapElementId);
+ Assert.Null(polygon2.MapElementId);
+ }
+ }
+
+ [Fact]
+ public async Task ClearResetsMapElementIdForAllElementTypes()
+ {
+ var map = new Map();
+ await CreateHandlerAsync(map);
+
+ var polygon = CreatePolygon();
+
+ var polyline = new Polyline
+ {
+ Geopath =
+ {
+ new Location(47.6358, -122.1319),
+ new Location(47.6378, -122.1299),
+ new Location(47.6398, -122.1279)
+ }
+ };
+
+ var circle = new Circle
+ {
+ Center = new Location(47.6400, -122.1300),
+ Radius = Distance.FromMeters(500)
+ };
+
+ await InvokeOnMainThreadAsync(() =>
+ {
+ map.MapElements.Add(polygon);
+ map.MapElements.Add(polyline);
+ map.MapElements.Add(circle);
+ });
+
+ Assert.Equal(3, map.MapElements.Count);
+
+ await InvokeOnMainThreadAsync(() => map.MapElements.Clear());
+
+ Assert.Empty(map.MapElements);
+ Assert.Null(polygon.MapElementId);
+ Assert.Null(polyline.MapElementId);
+ Assert.Null(circle.MapElementId);
+ }
+
+ [Fact]
+ public async Task RemoveSingleElementPreservesOtherMapElementIds()
+ {
+ var map = new Map();
+ await CreateHandlerAsync(map);
+
+ var polygon1 = CreatePolygon();
+ var polygon2 = CreatePolygon();
+
+ await InvokeOnMainThreadAsync(() =>
+ {
+ map.MapElements.Add(polygon1);
+ map.MapElements.Add(polygon2);
+ });
+
+ await InvokeOnMainThreadAsync(() => map.MapElements.Remove(polygon1));
+
+ Assert.Single(map.MapElements);
+ Assert.Contains(polygon2, map.MapElements);
+ Assert.NotNull(polygon2.MapElementId);
+ }
+ }
+#endif
+}
\ No newline at end of file
diff --git a/src/Controls/tests/DeviceTests/Elements/Modal/ModalTests.Windows.cs b/src/Controls/tests/DeviceTests/Elements/Modal/ModalTests.Windows.cs
index bc80b3b7cad9..5c29234788a8 100644
--- a/src/Controls/tests/DeviceTests/Elements/Modal/ModalTests.Windows.cs
+++ b/src/Controls/tests/DeviceTests/Elements/Modal/ModalTests.Windows.cs
@@ -151,5 +151,170 @@ await CreateHandlerAndAddToWindow(window,
Assert.True(windowRootView.NavigationViewControl.ButtonHolderGrid.Visibility == UI.Xaml.Visibility.Visible);
}));
}
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public async Task ModalPageDisablesHitTestOnUnderlyingPage(bool useColor)
+ {
+ SetupBuilder();
+
+ var navPage = new NavigationPage(new ContentPage() { Content = new Label() { Text = "Root Page" } });
+
+ await CreateHandlerAndAddToWindow(new Window(navPage),
+ async (handler) =>
+ {
+ ContentPage modalPage = new ContentPage() { Content = new Label() { Text = "Modal Page" } };
+
+ if (useColor)
+ modalPage.BackgroundColor = Colors.Purple.WithAlpha(0.5f);
+ else
+ modalPage.Background = new SolidColorBrush(Colors.Purple.WithAlpha(0.5f));
+
+ var rootPageRootView = navPage.FindMauiContext().GetNavigationRootManager().RootView;
+
+ await navPage.CurrentPage.Navigation.PushModalAsync(modalPage);
+ await OnLoadedAsync(modalPage);
+
+ var modalRootView = modalPage.FindMauiContext().GetNavigationRootManager().RootView;
+
+ // The underlying page should have IsHitTestVisible disabled
+ Assert.False(rootPageRootView.IsHitTestVisible,
+ "Underlying page should have IsHitTestVisible=false when a modal is displayed");
+
+ // The modal page should have IsHitTestVisible enabled
+ Assert.True(modalRootView.IsHitTestVisible,
+ "Modal page should have IsHitTestVisible=true");
+
+ await navPage.CurrentPage.Navigation.PopModalAsync();
+ await OnUnloadedAsync(modalPage);
+
+ // After popping the modal, the underlying page should be interactive again
+ Assert.True(rootPageRootView.IsHitTestVisible,
+ "Underlying page should have IsHitTestVisible=true after modal is dismissed");
+ });
+ }
+
+ [Fact]
+ public async Task ModalPageFocusTrapsAndRestoresCorrectly()
+ {
+ SetupBuilder();
+
+ var button = new Button() { Text = "Test Button" };
+ var rootPage = new ContentPage() { Content = button };
+ var navPage = new NavigationPage(rootPage);
+
+ await CreateHandlerAndAddToWindow(new Window(navPage),
+ async (handler) =>
+ {
+ var modalButton = new Button() { Text = "Modal Button" };
+ var modalPage = new ContentPage()
+ {
+ Content = modalButton,
+ BackgroundColor = Colors.Purple.WithAlpha(0.5f)
+ };
+
+ var container = (WindowRootViewContainer)handler.PlatformView.Content;
+
+ // Push modal
+ await navPage.CurrentPage.Navigation.PushModalAsync(modalPage);
+ await OnLoadedAsync(modalPage);
+
+ var rootPageRootView = navPage.FindMauiContext().GetNavigationRootManager().RootView;
+ var modalRootView = modalPage.FindMauiContext().GetNavigationRootManager().RootView;
+
+ // Underlying page should be non-interactive
+ Assert.False(rootPageRootView.IsHitTestVisible);
+
+ // Pop modal
+ await navPage.CurrentPage.Navigation.PopModalAsync();
+ await OnUnloadedAsync(modalPage);
+
+ // After pop, the root page should be fully interactive
+ Assert.True(rootPageRootView.IsHitTestVisible,
+ "Root page should be hit-test visible after modal pop");
+
+ // The root page should still be in the visual tree
+ Assert.Contains(rootPageRootView, container.CachedChildren);
+
+ // The modal should be removed
+ Assert.DoesNotContain(modalRootView, container.CachedChildren);
+ });
+ }
+
+ [Fact]
+ public async Task NestedModalPagesMaintainHitTestVisibilityAndFocusTrap()
+ {
+ SetupBuilder();
+
+ var button = new Button() { Text = "Root Button" };
+ var rootPage = new ContentPage() { Content = button };
+ var navPage = new NavigationPage(rootPage);
+
+ await CreateHandlerAndAddToWindow(new Window(navPage),
+ async (handler) =>
+ {
+ var modalButtonA = new Button() { Text = "Modal A Button" };
+ var modalPageA = new ContentPage()
+ {
+ Content = modalButtonA,
+ BackgroundColor = Colors.Green.WithAlpha(0.5f)
+ };
+
+ var modalButtonB = new Button() { Text = "Modal B Button" };
+ var modalPageB = new ContentPage()
+ {
+ Content = modalButtonB,
+ BackgroundColor = Colors.Red.WithAlpha(0.5f)
+ };
+
+ var container = (WindowRootViewContainer)handler.PlatformView.Content;
+
+ // Push first modal (A)
+ await navPage.CurrentPage.Navigation.PushModalAsync(modalPageA);
+ await OnLoadedAsync(modalPageA);
+
+ var rootPageRootView = navPage.FindMauiContext().GetNavigationRootManager().RootView;
+ var modalARootView = modalPageA.FindMauiContext().GetNavigationRootManager().RootView;
+
+ // Underlying root page should be non-interactive while modal A is showing
+ Assert.False(rootPageRootView.IsHitTestVisible);
+ Assert.Contains(modalARootView, container.CachedChildren);
+
+ // Push second modal (B) on top of A
+ await navPage.CurrentPage.Navigation.PushModalAsync(modalPageB);
+ await OnLoadedAsync(modalPageB);
+
+ var modalBRootView = modalPageB.FindMauiContext().GetNavigationRootManager().RootView;
+
+ // Root should still be non-interactive with topmost modal B showing
+ Assert.False(rootPageRootView.IsHitTestVisible);
+ Assert.Contains(modalBRootView, container.CachedChildren);
+
+ // Pop topmost modal (B)
+ await navPage.CurrentPage.Navigation.PopModalAsync();
+ await OnUnloadedAsync(modalPageB);
+
+ // After popping B, modal A is still visible, so the root page
+ // should remain non-interactive (focus trap still active)
+ Assert.False(rootPageRootView.IsHitTestVisible);
+ Assert.Contains(modalARootView, container.CachedChildren);
+ Assert.DoesNotContain(modalBRootView, container.CachedChildren);
+
+ // Now pop modal A
+ await navPage.CurrentPage.Navigation.PopModalAsync();
+ await OnUnloadedAsync(modalPageA);
+
+ // After popping the last modal, the root page should be interactive again
+ Assert.True(rootPageRootView.IsHitTestVisible,
+ "Root page should be hit-test visible after all modals are popped");
+
+ // The root page should still be in the visual tree
+ Assert.Contains(rootPageRootView, container.CachedChildren);
+
+ // Modal A should now be removed from the visual tree
+ Assert.DoesNotContain(modalARootView, container.CachedChildren);
+ });
+ }
}
}
diff --git a/src/Controls/tests/DeviceTests/Elements/SearchBar/SearchBarTests.cs b/src/Controls/tests/DeviceTests/Elements/SearchBar/SearchBarTests.cs
index 97915f810b4b..4a2fcddbde35 100644
--- a/src/Controls/tests/DeviceTests/Elements/SearchBar/SearchBarTests.cs
+++ b/src/Controls/tests/DeviceTests/Elements/SearchBar/SearchBarTests.cs
@@ -132,5 +132,62 @@ protected override Task GetPlatformText(SearchBarHandler handler) =>
SearchBarTests.GetPlatformText(handler);
}
#endif
+
+#if MACCATALYST || IOS
+ [Theory(DisplayName = "SearchBar renders with specified WidthRequest on iOS/Mac")]
+ [InlineData(200)]
+ [InlineData(250)]
+ [InlineData(300)]
+ public async Task ValidateSearchBarWidthRequestRendering(double requestedWidth)
+ {
+ var searchBar = new SearchBar
+ {
+ WidthRequest = requestedWidth,
+ };
+
+ await InvokeOnMainThreadAsync(() =>
+ {
+ var handler = CreateHandler(searchBar);
+ var platformControl = GetPlatformControl(handler);
+
+ double actualWidth = 0;
+
+ if (platformControl is UIKit.UISearchBar uiSearchBar)
+ {
+ actualWidth = uiSearchBar.Frame.Width;
+ }
+
+ Assert.Equal(requestedWidth, actualWidth);
+ });
+ }
+
+
+ [Theory(DisplayName = "SearchBar renders with specified HeightRequest on iOS/Mac")]
+ [InlineData(100)]
+ [InlineData(150)]
+ [InlineData(80)]
+ public async Task ValidateSearchBarHeightRequestRendering(double requestedHeight)
+ {
+ var searchBar = new SearchBar
+ {
+ HeightRequest = requestedHeight,
+ };
+
+ await InvokeOnMainThreadAsync(() =>
+ {
+ var handler = CreateHandler(searchBar);
+ var platformControl = GetPlatformControl(handler);
+
+ double actualHeight = 0;
+
+ if (platformControl is UIKit.UISearchBar uiSearchBar)
+ {
+ actualHeight = uiSearchBar.Frame.Height;
+ }
+
+ Assert.Equal(requestedHeight, actualHeight);
+ });
+ }
+#endif
}
}
diff --git a/src/Controls/tests/DeviceTests/Elements/TabbedPage/TabbedPageTests.Android.cs b/src/Controls/tests/DeviceTests/Elements/TabbedPage/TabbedPageTests.Android.cs
index 5fcb7a5bf9b3..05937889f271 100644
--- a/src/Controls/tests/DeviceTests/Elements/TabbedPage/TabbedPageTests.Android.cs
+++ b/src/Controls/tests/DeviceTests/Elements/TabbedPage/TabbedPageTests.Android.cs
@@ -209,6 +209,49 @@ public async Task RotationConsistent()
var platformRotation = await InvokeOnMainThreadAsync(() => handler.PlatformView.Rotation);
Assert.Equal(expected, platformRotation);
}
+
+ [Fact]
+ [Description("BottomNavigationView should extend to screen bottom in Edge-to-Edge mode (Issue 33344)")]
+ public async Task BottomNavigationViewExtendsToScreenBottom()
+ {
+ SetupBuilder();
+
+ var tabbedPage = new TabbedPage
+ {
+ Children =
+ {
+ new ContentPage() { Title = "Page1" },
+ new ContentPage() { Title = "Page2" }
+ },
+ BarBackgroundColor = Colors.Orange
+ };
+
+ Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific.TabbedPage
+ .SetToolbarPlacement(tabbedPage, Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific.ToolbarPlacement.Bottom);
+
+ await CreateHandlerAndAddToWindow(tabbedPage, async handler =>
+ {
+ var bottomNavView = GetBottomNavigationView(handler);
+ Assert.NotNull(bottomNavView);
+
+ // Wait for layout to complete
+ await AssertEventually(() => bottomNavView.Height > 0);
+
+ var location = new int[2];
+ bottomNavView.GetLocationOnScreen(location);
+ var bottomNavBottom = location[1] + bottomNavView.Height;
+
+ var decorView = MauiContext.Context.GetActivity()?.Window?.DecorView;
+ Assert.NotNull(decorView);
+
+ decorView.GetLocationOnScreen(location);
+ var screenHeight = location[1] + decorView.Height;
+
+ Assert.True(Math.Abs(screenHeight - bottomNavBottom) < 2,
+ $"BottomNavigationView should extend to screen bottom. Expected bottom at {screenHeight}px, but was at {bottomNavBottom}px (gap of {screenHeight - bottomNavBottom}px)");
+ });
+ }
+
BottomNavigationView GetBottomNavigationView(IPlatformViewHandler tabViewHandler)
{
var layout = tabViewHandler.PlatformView.FindParent((view) => view is CoordinatorLayout)
diff --git a/src/Controls/tests/DeviceTests/TestCategory.cs b/src/Controls/tests/DeviceTests/TestCategory.cs
index 85300e927b9d..f98ca4a4007f 100644
--- a/src/Controls/tests/DeviceTests/TestCategory.cs
+++ b/src/Controls/tests/DeviceTests/TestCategory.cs
@@ -28,6 +28,7 @@ public static class TestCategory
public const string Layout = "Layout";
public const string Lifecycle = nameof(Lifecycle);
public const string ListView = "ListView";
+ public const string Map = "Map";
public const string MenuFlyout = nameof(MenuFlyout);
public const string Mapper = nameof(Mapper);
public const string Memory = nameof(Memory);
diff --git a/src/Controls/tests/DeviceTests/Xaml/StaticResourceTests.cs b/src/Controls/tests/DeviceTests/Xaml/StaticResourceTests.cs
new file mode 100644
index 000000000000..f8ae44a982b6
--- /dev/null
+++ b/src/Controls/tests/DeviceTests/Xaml/StaticResourceTests.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Controls.Xaml;
+using Xunit;
+
+namespace Microsoft.Maui.DeviceTests;
+
+[Category(TestCategory.Xaml)]
+public class StaticResourceTests : IDisposable
+{
+ [Fact("Issue #23903: Missing ControlTemplate with exception handler should throw")]
+ [RequiresUnreferencedCode("XAML parsing may require unreferenced code")]
+ public void MissingControlTemplate_WithExceptionHandler_ShouldThrow()
+ {
+ // Issue #23903: StaticResourceExtension should always throw when resource is not found,
+ // even when an exception handler is present (for debug/hot reload scenarios).
+ // This prevents the app from crashing when relaunching.
+
+ Controls.Internals.ResourceLoader.ExceptionHandler2 = (ex) => { };
+
+ var xaml = """
+
+
+
+ """;
+
+ var page = new ContentPage();
+
+ // Should throw an exception even with handler present
+ bool exceptionThrown = false;
+ try
+ {
+ page.LoadFromXaml(xaml);
+ }
+ catch (Exception)
+ {
+ exceptionThrown = true;
+ }
+
+ Assert.True(exceptionThrown, "Expected an exception to be thrown for missing ControlTemplate");
+ }
+
+ public void Dispose()
+ {
+ Controls.Internals.ResourceLoader.ExceptionHandler2 = null;
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_DateSelectedEvent_FiresOnDateChange.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_DateSelectedEvent_FiresOnDateChange.png
new file mode 100644
index 000000000000..f688f31fb2bc
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_DateSelectedEvent_FiresOnDateChange.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_Format_D_LongDatePattern.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_Format_D_LongDatePattern.png
new file mode 100644
index 000000000000..585c5d4d9c04
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_Format_D_LongDatePattern.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_Format_F_FullDateLongTime.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_Format_F_FullDateLongTime.png
new file mode 100644
index 000000000000..21a00b3378d8
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_Format_F_FullDateLongTime.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_Format_f_FullDateShortTime.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_Format_f_FullDateShortTime.png
new file mode 100644
index 000000000000..535a5fdc0ffe
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_Format_f_FullDateShortTime.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_InitialState_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_InitialState_VerifyVisualState.png
new file mode 100644
index 000000000000..854415d2e734
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_InitialState_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_ModifyOldDateAndNewDate_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_ModifyOldDateAndNewDate_VerifyVisualState.png
new file mode 100644
index 000000000000..a6b883c2441f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_ModifyOldDateAndNewDate_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_OldDateAndNewDate_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_OldDateAndNewDate_VerifyVisualState.png
new file mode 100644
index 000000000000..764cf26cef9d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_OldDateAndNewDate_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetCharacterSpacingAndDate_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetCharacterSpacingAndDate_VerifyVisualState.png
new file mode 100644
index 000000000000..f3bd34f1ab07
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetCharacterSpacingAndDate_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetDateAndFlowDirection_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetDateAndFlowDirection_VerifyVisualState.png
new file mode 100644
index 000000000000..de40a3a12ac4
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetDateAndFlowDirection_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetDateAndIsEnabled_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetDateAndIsEnabled_VerifyVisualState.png
new file mode 100644
index 000000000000..bd18ccb2c318
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetDateAndIsEnabled_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetDateAndIsVisible_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetDateAndIsVisible_VerifyVisualState.png
new file mode 100644
index 000000000000..81820c91be52
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetDateAndIsVisible_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetDateAndShadowOpacity_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetDateAndShadowOpacity_VerifyVisualState.png
new file mode 100644
index 000000000000..b87c9a46b1e0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetDateAndShadowOpacity_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetDateAndTextColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetDateAndTextColor_VerifyVisualState.png
new file mode 100644
index 000000000000..e1bbfe418ec8
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetDateAndTextColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontAttributesAndFontFamily_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontAttributesAndFontFamily_VerifyVisualState.png
new file mode 100644
index 000000000000..8854232add4c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontAttributesAndFontFamily_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontAttributesAndFontSize_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontAttributesAndFontSize_VerifyVisualState.png
new file mode 100644
index 000000000000..cd02f3bf962a
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontAttributesAndFontSize_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontAttributesAndFormat_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontAttributesAndFormat_VerifyVisualState.png
new file mode 100644
index 000000000000..063dc132b0e7
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontAttributesAndFormat_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontAttributesAndFormat_f_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontAttributesAndFormat_f_VerifyVisualState.png
new file mode 100644
index 000000000000..a29b17ba2683
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontAttributesAndFormat_f_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontFamilyAndFontSize_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontFamilyAndFontSize_VerifyVisualState.png
new file mode 100644
index 000000000000..e4ae4d3b2a5b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontFamilyAndFontSize_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontFamilyAndFormat_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontFamilyAndFormat_VerifyVisualState.png
new file mode 100644
index 000000000000..85a8e2df69b6
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontFamilyAndFormat_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontFamilyAndFormat_f_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontFamilyAndFormat_f_VerifyVisualState.png
new file mode 100644
index 000000000000..fe9cab724ba0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontFamilyAndFormat_f_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontSizeAndFormat_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontSizeAndFormat_VerifyVisualState.png
new file mode 100644
index 000000000000..8803039d55d7
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontSizeAndFormat_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontSizeAndFormat_f_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontSizeAndFormat_f_VerifyVisualState.png
new file mode 100644
index 000000000000..45c6083aebeb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetFontSizeAndFormat_f_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetMaximumDateAndDate_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetMaximumDateAndDate_VerifyVisualState.png
new file mode 100644
index 000000000000..19197bb8c1e7
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetMaximumDateAndDate_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetMinimumDateAndDate_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetMinimumDateAndDate_VerifyVisualState.png
new file mode 100644
index 000000000000..f5a0b1196d92
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3DatePicker_SetMinimumDateAndDate_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_CharacterSpacingWithFontFamily_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_CharacterSpacingWithFontFamily_VerifyVisualState.png
new file mode 100644
index 000000000000..7e8d6f9148bd
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_CharacterSpacingWithFontFamily_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_CharacterSpacingWithMaxLength_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_CharacterSpacingWithMaxLength_VerifyVisualState.png
new file mode 100644
index 000000000000..ac3ba9b3ecea
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_CharacterSpacingWithMaxLength_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_CharacterSpacing_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_CharacterSpacing_VerifyVisualState.png
new file mode 100644
index 000000000000..c86736425578
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_CharacterSpacing_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_FlowDirection_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_FlowDirection_VerifyVisualState.png
new file mode 100644
index 000000000000..d07c56bd555b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_FlowDirection_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_FontAttributes_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_FontAttributes_VerifyVisualState.png
new file mode 100644
index 000000000000..dd8873f99164
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_FontAttributes_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_FontFamily_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_FontFamily_VerifyVisualState.png
new file mode 100644
index 000000000000..77d639b8adf0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_FontFamily_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_FontSize_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_FontSize_VerifyVisualState.png
new file mode 100644
index 000000000000..c50a8f169f3f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_FontSize_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_HorizontalAlignmentWithCharacterSpacing_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_HorizontalAlignmentWithCharacterSpacing_VerifyVisualState.png
new file mode 100644
index 000000000000..9087a5736ed4
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_HorizontalAlignmentWithCharacterSpacing_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_HorizontalAndVerticalAlignment_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_HorizontalAndVerticalAlignment_VerifyVisualState.png
new file mode 100644
index 000000000000..60e96e1f24a3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_HorizontalAndVerticalAlignment_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_InitialState_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_InitialState_VerifyVisualState.png
new file mode 100644
index 000000000000..02b1d203384b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_InitialState_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsEnabled_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsEnabled_VerifyVisualState.png
new file mode 100644
index 000000000000..a134dd2daa86
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsEnabled_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPasswordWithCharacterSpacing_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPasswordWithCharacterSpacing_VerifyVisualState.png
new file mode 100644
index 000000000000..ebebe1ae8cad
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPasswordWithCharacterSpacing_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPasswordWithFontSize_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPasswordWithFontSize_VerifyVisualState.png
new file mode 100644
index 000000000000..cab7939fe2f5
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPasswordWithFontSize_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPasswordWithHorizontalAlignment_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPasswordWithHorizontalAlignment_VerifyVisualState.png
new file mode 100644
index 000000000000..52117c3aadb0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPasswordWithHorizontalAlignment_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPasswordWithMaxLength_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPasswordWithMaxLength_VerifyVisualState.png
new file mode 100644
index 000000000000..dda4e95f40fc
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPasswordWithMaxLength_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPasswordWithVerticalAlignment_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPasswordWithVerticalAlignment_VerifyVisualState.png
new file mode 100644
index 000000000000..29736ec3f5e6
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPasswordWithVerticalAlignment_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPassword_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPassword_VerifyVisualState.png
new file mode 100644
index 000000000000..0f8c589dd91e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_IsPassword_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderColorAndTextColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderColorAndTextColor_VerifyVisualState.png
new file mode 100644
index 000000000000..c95098555be2
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderColorAndTextColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderColor_VerifyVisualState.png
new file mode 100644
index 000000000000..61d3739dc89e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderText_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderText_VerifyVisualState.png
new file mode 100644
index 000000000000..d8e99f7b8c42
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderText_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithFontAttributes_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithFontAttributes_VerifyVisualState.png
new file mode 100644
index 000000000000..3a640290381d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithFontAttributes_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithFontFamily_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithFontFamily_VerifyVisualState.png
new file mode 100644
index 000000000000..f0a26c623920
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithFontFamily_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithFontSize_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithFontSize_VerifyVisualState.png
new file mode 100644
index 000000000000..c211f52d29af
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithFontSize_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithHorizontalAlignment_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithHorizontalAlignment_VerifyVisualState.png
new file mode 100644
index 000000000000..0c4023ed700f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithHorizontalAlignment_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithPasswordTrue_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithPasswordTrue_VerifyVisualState.png
new file mode 100644
index 000000000000..56d0098fdf11
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithPasswordTrue_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithShadow_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithShadow_VerifyVisualState.png
new file mode 100644
index 000000000000..48ba282dafbc
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithShadow_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithVerticalAlignment_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithVerticalAlignment_VerifyVisualState.png
new file mode 100644
index 000000000000..416d2a5ba6aa
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_PlaceholderWithVerticalAlignment_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_Shadow_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_Shadow_VerifyVisualState.png
new file mode 100644
index 000000000000..d182ddb8e5aa
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_Shadow_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_TextColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_TextColor_VerifyVisualState.png
new file mode 100644
index 000000000000..7d456491ef27
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_TextColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_TextWithHorizontalAlignment_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_TextWithHorizontalAlignment_VerifyVisualState.png
new file mode 100644
index 000000000000..7de397fb7f7b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_TextWithHorizontalAlignment_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_TextWithVerticalAlignment_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_TextWithVerticalAlignment_VerifyVisualState.png
new file mode 100644
index 000000000000..23567e93c8d9
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_TextWithVerticalAlignment_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_VerticalAlignmentWithCharacterSpacing_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_VerticalAlignmentWithCharacterSpacing_VerifyVisualState.png
new file mode 100644
index 000000000000..0132f5bfce4a
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Entry_VerticalAlignmentWithCharacterSpacing_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ChangeFlowDirection_RTL_VerifyLabel.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ChangeFlowDirection_RTL_VerifyLabel.png
new file mode 100644
index 000000000000..00389027f079
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ChangeFlowDirection_RTL_VerifyLabel.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ProgressToMethod_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ProgressToMethod_VerifyVisualState.png
new file mode 100644
index 000000000000..9583e30eeb13
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ProgressToMethod_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressColorAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressColorAndBackgroundColor_VerifyVisualState.png
new file mode 100644
index 000000000000..9eb5b1a6be7d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressColorAndBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressNegativeValue.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressNegativeValue.png
new file mode 100644
index 000000000000..caa56f9260ac
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressNegativeValue.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressOutOfRange.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressOutOfRange.png
new file mode 100644
index 000000000000..0dfced6f2c75
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_SetProgressOutOfRange.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ToggleShadow_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ToggleShadow_VerifyVisualState.png
new file mode 100644
index 000000000000..7ad732cac3ec
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3ProgressBar_ToggleShadow_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeBackgroundColor_VerifyVisualState.png
new file mode 100644
index 000000000000..a55cd27aec4c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeFlowDirection_RTL_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeFlowDirection_RTL_VerifyVisualState.png
new file mode 100644
index 000000000000..144639cfb7a8
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeFlowDirection_RTL_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeMaxTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeMaxTrackColor_VerifyVisualState.png
new file mode 100644
index 000000000000..acf7bf9db122
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeMaxTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeMinTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeMinTrackColor_VerifyVisualState.png
new file mode 100644
index 000000000000..f3f5700af0bf
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeMinTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeThumbColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeThumbColor_VerifyVisualState.png
new file mode 100644
index 000000000000..bf6532766a6c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeThumbColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeThumbImageSource_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeThumbImageSource_VerifyVisualState.png
new file mode 100644
index 000000000000..8713a37434fb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_ChangeThumbImageSource_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetEnabledStateToFalse_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetEnabledStateToFalse_VerifyVisualState.png
new file mode 100644
index 000000000000..192557326b5d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetEnabledStateToFalse_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsEnableAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsEnableAndBackgroundColor_VerifyVisualState.png
new file mode 100644
index 000000000000..d174f78eef22
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsEnableAndBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsEnableAndMaxTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsEnableAndMaxTrackColor_VerifyVisualState.png
new file mode 100644
index 000000000000..97d4e5f50adb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsEnableAndMaxTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsEnableAndMinTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsEnableAndMinTrackColor_VerifyVisualState.png
new file mode 100644
index 000000000000..42cb5779d562
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsEnableAndMinTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsEnableAndThumbColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsEnableAndThumbColor_VerifyVisualState.png
new file mode 100644
index 000000000000..8ffce2974a7f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsEnableAndThumbColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsVisibleAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsVisibleAndBackgroundColor_VerifyVisualState.png
new file mode 100644
index 000000000000..78860dcd17ae
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsVisibleAndBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsVisibleAndMaxTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsVisibleAndMaxTrackColor_VerifyVisualState.png
new file mode 100644
index 000000000000..7a868396c159
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsVisibleAndMaxTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsVisibleAndMinTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsVisibleAndMinTrackColor_VerifyVisualState.png
new file mode 100644
index 000000000000..a0f0413c4f26
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsVisibleAndMinTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsVisibleAndThumbColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsVisibleAndThumbColor_VerifyVisualState.png
new file mode 100644
index 000000000000..b36d7b6a643e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetIsVisibleAndThumbColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMaxTrackAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMaxTrackAndBackgroundColor_VerifyVisualState.png
new file mode 100644
index 000000000000..93083e6e434f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMaxTrackAndBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMaxTrackColorAndFlowDirection_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMaxTrackColorAndFlowDirection_VerifyVisualState.png
new file mode 100644
index 000000000000..d08ade6688a3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMaxTrackColorAndFlowDirection_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMaximumAndChangeFlowDirection_RTL.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMaximumAndChangeFlowDirection_RTL.png
new file mode 100644
index 000000000000..d9589475ab1a
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMaximumAndChangeFlowDirection_RTL.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMinTrackAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMinTrackAndBackgroundColor_VerifyVisualState.png
new file mode 100644
index 000000000000..5a18cea000df
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMinTrackAndBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMinTrackAndMaxTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMinTrackAndMaxTrackColor_VerifyVisualState.png
new file mode 100644
index 000000000000..1324cea7be4a
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMinTrackAndMaxTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMinTrackColorAndFlowDirection_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMinTrackColorAndFlowDirection_VerifyVisualState.png
new file mode 100644
index 000000000000..ba774b725d30
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetMinTrackColorAndFlowDirection_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetThumbAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetThumbAndBackgroundColor_VerifyVisualState.png
new file mode 100644
index 000000000000..ecd633d970d2
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetThumbAndBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetThumbAndMaxTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetThumbAndMaxTrackColor_VerifyVisualState.png
new file mode 100644
index 000000000000..c83e6361fb57
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetThumbAndMaxTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetThumbAndMinTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetThumbAndMinTrackColor_VerifyVisualState.png
new file mode 100644
index 000000000000..d1523245707f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetThumbAndMinTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png
new file mode 100644
index 000000000000..b5f1955fd9cd
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetValueAndFlowDirection_RTL_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetValueAndFlowDirection_RTL_VerifyVisualState.png
new file mode 100644
index 000000000000..5af933c10269
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetValueAndFlowDirection_RTL_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetValueAndMaxTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetValueAndMaxTrackColor_VerifyVisualState.png
new file mode 100644
index 000000000000..ec7ce09bc6cc
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetValueAndMaxTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetValueAndMinTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetValueAndMinTrackColor_VerifyVisualState.png
new file mode 100644
index 000000000000..97a891a11be3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetValueAndMinTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetValueAndThumbImageSource_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetValueAndThumbImageSource_VerifyVisualState.png
new file mode 100644
index 000000000000..3eda05b0554a
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetValueAndThumbImageSource_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetVisibilityToFalse_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetVisibilityToFalse_VerifyVisualState.png
new file mode 100644
index 000000000000..3666465db424
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Slider_SetVisibilityToFalse_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_Click_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_Click_VerifyVisualState.png
new file mode 100644
index 000000000000..7a675a3ecf98
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_Click_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_InitialState_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_InitialState_VerifyVisualState.png
new file mode 100644
index 000000000000..b58e09990af4
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_InitialState_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_SetFlowDirectionAndToggled_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_SetFlowDirectionAndToggled_VerifyVisualState.png
new file mode 100644
index 000000000000..7d4255c4ed39
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_SetFlowDirectionAndToggled_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_SetOnColorAndThumbColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_SetOnColorAndThumbColor_VerifyVisualState.png
new file mode 100644
index 000000000000..c2cd8c202d39
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_SetOnColorAndThumbColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_SetThumbColorAndOnColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_SetThumbColorAndOnColor_VerifyVisualState.png
new file mode 100644
index 000000000000..539cfba827d3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_SetThumbColorAndOnColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_SetToggledAndOnColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_SetToggledAndOnColor_VerifyVisualState.png
new file mode 100644
index 000000000000..74c343d4abac
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3Switch_SetToggledAndOnColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_InitialState_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_InitialState_VerifyVisualState.png
new file mode 100644
index 000000000000..18d0bd56fe9e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_InitialState_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetCancelButtonAndTextColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetCancelButtonAndTextColor_VerifyVisualState.png
new file mode 100644
index 000000000000..7785736fc532
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetCancelButtonAndTextColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFlowDirection_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFlowDirection_VerifyVisualState.png
new file mode 100644
index 000000000000..34074107994b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFlowDirection_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAttributesAndFontFamily_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAttributesAndFontFamily_VerifyVisualState.png
new file mode 100644
index 000000000000..c526ed487beb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAttributesAndFontFamily_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAttributesAndFontSize_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAttributesAndFontSize_VerifyVisualState.png
new file mode 100644
index 000000000000..4c13fbccb818
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAttributesAndFontSize_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAttributesAndPlaceholderText_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAttributesAndPlaceholderText_VerifyVisualState.png
new file mode 100644
index 000000000000..1e7a3d0f79d1
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAttributesAndPlaceholderText_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAttributesAndTextTransform_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAttributesAndTextTransform_VerifyVisualState.png
new file mode 100644
index 000000000000..ed702af4afd8
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAttributesAndTextTransform_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAttributesAndText_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAttributesAndText_VerifyVisualState.png
new file mode 100644
index 000000000000..d0d73aeb0395
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAttributesAndText_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAutoScalingFalse_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAutoScalingFalse_VerifyVisualState.png
new file mode 100644
index 000000000000..3102351d3372
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontAutoScalingFalse_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontFamilyAndFontSize_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontFamilyAndFontSize_VerifyVisualState.png
new file mode 100644
index 000000000000..13003a47bdd0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontFamilyAndFontSize_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontFamilyAndPlaceholder_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontFamilyAndPlaceholder_VerifyVisualState.png
new file mode 100644
index 000000000000..c76f1c62fd58
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontFamilyAndPlaceholder_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontFamilyAndText_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontFamilyAndText_VerifyVisualState.png
new file mode 100644
index 000000000000..a52567969f5b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontFamilyAndText_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontSizeAndPlaceholder_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontSizeAndPlaceholder_VerifyVisualState.png
new file mode 100644
index 000000000000..4b4d260c2135
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontSizeAndPlaceholder_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontSizeAndText_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontSizeAndText_VerifyVisualState.png
new file mode 100644
index 000000000000..8e3d92a07868
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetFontSizeAndText_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetHorizontalAndVerticalTextAlignment_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetHorizontalAndVerticalTextAlignment_VerifyVisualState.png
new file mode 100644
index 000000000000..5470e80deabb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetHorizontalAndVerticalTextAlignment_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetHorizontalTextAlignmentAndPlaceholder_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetHorizontalTextAlignmentAndPlaceholder_VerifyVisualState.png
new file mode 100644
index 000000000000..21b1202bf7eb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetHorizontalTextAlignmentAndPlaceholder_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetHorizontalTextAlignmentAndText_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetHorizontalTextAlignmentAndText_VerifyVisualState.png
new file mode 100644
index 000000000000..cfaa119ebc50
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetHorizontalTextAlignmentAndText_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetIsTextPredictionEnabledAndText_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetIsTextPredictionEnabledAndText_VerifyVisualState.png
new file mode 100644
index 000000000000..4d462fb76761
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetIsTextPredictionEnabledAndText_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetPlaceholderAndCharacterSpacing_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetPlaceholderAndCharacterSpacing_VerifyVisualState.png
new file mode 100644
index 000000000000..f680b8575b6d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetPlaceholderAndCharacterSpacing_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetPlaceholderAndPlaceholderColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetPlaceholderAndPlaceholderColor_VerifyVisualState.png
new file mode 100644
index 000000000000..525401f1f68e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetPlaceholderAndPlaceholderColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetPlaceholderAndVerticalTextAlignment_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetPlaceholderAndVerticalTextAlignment_VerifyVisualState.png
new file mode 100644
index 000000000000..236b2c76447b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetPlaceholderAndVerticalTextAlignment_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetPlaceholderColorAndTextColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetPlaceholderColorAndTextColor_VerifyVisualState.png
new file mode 100644
index 000000000000..fff8e3c98264
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetPlaceholderColorAndTextColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetShadow_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetShadow_VerifyVisualState.png
new file mode 100644
index 000000000000..f2174829f208
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetShadow_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetTextAndCharacterSpacing_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetTextAndCharacterSpacing_VerifyVisualState.png
new file mode 100644
index 000000000000..7ead1c12e96b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetTextAndCharacterSpacing_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetTextAndVerticalTextAlignment_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetTextAndVerticalTextAlignment_VerifyVisualState.png
new file mode 100644
index 000000000000..39cb3bbf1058
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/SearchBar_Material3_SetTextAndVerticalTextAlignment_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyGraphicsViewWithoutGrayLine.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyGraphicsViewWithoutGrayLine.png
deleted file mode 100644
index 88102b12c0c5..000000000000
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyGraphicsViewWithoutGrayLine.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3FontImageWithFontColorGreen.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3FontImageWithFontColorGreen.png
new file mode 100644
index 000000000000..da24642bdfc3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3FontImageWithFontColorGreen.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3FontImageWithFontSize.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3FontImageWithFontSize.png
new file mode 100644
index 000000000000..201d93ac51d2
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3FontImageWithFontSize.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFillWithFileSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFillWithFileSource.png
new file mode 100644
index 000000000000..ba26e5fe5861
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFillWithFileSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFillWithFontImageSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFillWithFontImageSource.png
new file mode 100644
index 000000000000..7910b04191c1
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFillWithFontImageSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFillWithStreamSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFillWithStreamSource.png
new file mode 100644
index 000000000000..6288410f5c4c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFillWithStreamSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFillWithUriSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFillWithUriSource.png
new file mode 100644
index 000000000000..a7e374890fa2
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFillWithUriSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFitWithFileSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFitWithFileSource.png
new file mode 100644
index 000000000000..9daee150f1ed
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFitWithFileSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFitWithFontImageSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFitWithFontImageSource.png
new file mode 100644
index 000000000000..bee9ef704f18
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFitWithFontImageSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFitWithStreamSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFitWithStreamSource.png
new file mode 100644
index 000000000000..c855d20c7265
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFitWithStreamSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFitWithUriSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFitWithUriSource.png
new file mode 100644
index 000000000000..235d60955c01
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_AspectFitWithUriSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_CenterWithFileSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_CenterWithFileSource.png
new file mode 100644
index 000000000000..a90a629a5580
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_CenterWithFileSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_CenterWithFontImageSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_CenterWithFontImageSource.png
new file mode 100644
index 000000000000..68171cb80841
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_CenterWithFontImageSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_CenterWithStreamSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_CenterWithStreamSource.png
new file mode 100644
index 000000000000..045114bd0918
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_CenterWithStreamSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_CenterWithUriSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_CenterWithUriSource.png
new file mode 100644
index 000000000000..24effaa88f6e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_CenterWithUriSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_FillWithFileSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_FillWithFileSource.png
new file mode 100644
index 000000000000..4c6edc94d89d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_FillWithFileSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_FillWithFontImageSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_FillWithFontImageSource.png
new file mode 100644
index 000000000000..d76ceb80c1a5
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_FillWithFontImageSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_FillWithStreamSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_FillWithStreamSource.png
new file mode 100644
index 000000000000..55f16403145d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_FillWithStreamSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_FillWithUriSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_FillWithUriSource.png
new file mode 100644
index 000000000000..148152222b2e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageAspect_FillWithUriSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_AspectFillWithImageSourceFromFontImage.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_AspectFillWithImageSourceFromFontImage.png
new file mode 100644
index 000000000000..60d37d1d25d9
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_AspectFillWithImageSourceFromFontImage.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_AspectFitWithImageSourceFromFile.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_AspectFitWithImageSourceFromFile.png
new file mode 100644
index 000000000000..7c706540f9bb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_AspectFitWithImageSourceFromFile.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_AspectFitWithImageSourceFromFontImage.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_AspectFitWithImageSourceFromFontImage.png
new file mode 100644
index 000000000000..2dc147c7f45c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_AspectFitWithImageSourceFromFontImage.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_AspectFitWithImageSourceFromUri.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_AspectFitWithImageSourceFromUri.png
new file mode 100644
index 000000000000..622dd830aeba
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_AspectFitWithImageSourceFromUri.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_CenterWithImageSourceFromFile.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_CenterWithImageSourceFromFile.png
new file mode 100644
index 000000000000..fb61359d0824
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_CenterWithImageSourceFromFile.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_CenterWithImageSourceFromFontImage.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_CenterWithImageSourceFromFontImage.png
new file mode 100644
index 000000000000..4c604ab076b4
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_CenterWithImageSourceFromFontImage.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_FillWithImageSourceFromFile.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_FillWithImageSourceFromFile.png
new file mode 100644
index 000000000000..684ec7f04125
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_FillWithImageSourceFromFile.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_FillWithImageSourceFromFontImage.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_FillWithImageSourceFromFontImage.png
new file mode 100644
index 000000000000..9d805a171deb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_FillWithImageSourceFromFontImage.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_FillWithImageSourceFromUri.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_FillWithImageSourceFromUri.png
new file mode 100644
index 000000000000..714f188b2467
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonAspect_FillWithImageSourceFromUri.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonFlowDirectionRTL.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonFlowDirectionRTL.png
new file mode 100644
index 000000000000..45afca00d1a3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonFlowDirectionRTL.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithBorderColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithBorderColor.png
new file mode 100644
index 000000000000..86845551593b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithBorderColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithBorderColorAndWidth.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithBorderColorAndWidth.png
new file mode 100644
index 000000000000..b1acfd89a35f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithBorderColorAndWidth.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithBorderWidth.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithBorderWidth.png
new file mode 100644
index 000000000000..9c52284f7e38
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithBorderWidth.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithBorderWidthAndCornerRadius.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithBorderWidthAndCornerRadius.png
new file mode 100644
index 000000000000..9c55b4371269
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithBorderWidthAndCornerRadius.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithCornerRadius.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithCornerRadius.png
new file mode 100644
index 000000000000..0ac59f1f9c62
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithCornerRadius.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithPadding.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithPadding.png
new file mode 100644
index 000000000000..15f1f49916bc
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageButtonWithPadding.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageFlowDirectionRTL.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageFlowDirectionRTL.png
new file mode 100644
index 000000000000..3e8d213e4a05
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageFlowDirectionRTL.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageWithShadow.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageWithShadow.png
new file mode 100644
index 000000000000..d8dcd9488b5c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/VerifyMaterial3ImageWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ActionModeMenuShouldNotBeVisibleAfterSwitchingTab.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ActionModeMenuShouldNotBeVisibleAfterSwitchingTab.png
index a2239fd83d4a..e46d3a5a7db7 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ActionModeMenuShouldNotBeVisibleAfterSwitchingTab.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ActionModeMenuShouldNotBeVisibleAfterSwitchingTab.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/AdaptiveTrigger_Landscape.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/AdaptiveTrigger_Landscape.png
new file mode 100644
index 000000000000..6b0c0316449f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/AdaptiveTrigger_Landscape.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/AdaptiveTrigger_Portrait.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/AdaptiveTrigger_Portrait.png
new file mode 100644
index 000000000000..d087f6f5882c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/AdaptiveTrigger_Portrait.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ArabicStringShouldBeLeftToRight.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ArabicStringShouldBeLeftToRight.png
new file mode 100644
index 000000000000..b0f346d6ec29
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ArabicStringShouldBeLeftToRight.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BackButtonBehavior_IconOverride_CustomIconShownOnBackButton.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BackButtonBehavior_IconOverride_CustomIconShownOnBackButton.png
new file mode 100644
index 000000000000..c9bcbfac5211
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BackButtonBehavior_IconOverride_CustomIconShownOnBackButton.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks.png
new file mode 100644
index 000000000000..06e6dcb97e8b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_BackgroundColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_BackgroundColor.png
new file mode 100644
index 000000000000..cc2805a08e79
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_BackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipNull_NoCrash.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipNull_NoCrash.png
new file mode 100644
index 000000000000..4657a302f690
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipNull_NoCrash.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithNestedContent.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithNestedContent.png
new file mode 100644
index 000000000000..7a39d95392b9
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithNestedContent.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithRotation.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithRotation.png
new file mode 100644
index 000000000000..f1bbd0ac154f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithRotationAndScale.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithRotationAndScale.png
new file mode 100644
index 000000000000..a6a93d0c9278
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithRotationAndScale.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithShadow.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithShadow.png
new file mode 100644
index 000000000000..ac8467495961
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithStrokeColorBlue.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithStrokeColorBlue.png
new file mode 100644
index 000000000000..f8fc58540653
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithStrokeColorBlue.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithStrokeColorGreen.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithStrokeColorGreen.png
new file mode 100644
index 000000000000..faae13b57760
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithStrokeColorGreen.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithStrokeShapeRoundRectangle.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithStrokeShapeRoundRectangle.png
new file mode 100644
index 000000000000..11e4d0541ec4
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithStrokeShapeRoundRectangle.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithStrokeThickness.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithStrokeThickness.png
new file mode 100644
index 000000000000..7f9bf35ecc87
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ClipWithStrokeThickness.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_DefaultValues.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_DefaultValues.png
new file mode 100644
index 000000000000..e67f7f66e9a0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_DefaultValues.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_PaddingWithContent_Image.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_PaddingWithContent_Image.png
new file mode 100644
index 000000000000..aaedfd7c1745
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_PaddingWithContent_Image.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_PaddingWithContent_Label.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_PaddingWithContent_Label.png
index 6dac57a2f386..36462c28089a 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_PaddingWithContent_Label.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_PaddingWithContent_Label.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_PolygonShapeWithStrokeLineCap_Round.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_PolygonShapeWithStrokeLineCap_Round.png
index 5d393c8125d7..dc3b14005731 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_PolygonShapeWithStrokeLineCap_Round.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_PolygonShapeWithStrokeLineCap_Round.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_PolygonShapeWithStrokeLineJoin_Bevel.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_PolygonShapeWithStrokeLineJoin_Bevel.png
index bb1d25ff8662..350d63ff3b3f 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_PolygonShapeWithStrokeLineJoin_Bevel.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_PolygonShapeWithStrokeLineJoin_Bevel.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_Shadow.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_Shadow.png
index 4d1b2e2f7df3..4c554749e050 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_Shadow.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_Shadow.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ShadowWithColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ShadowWithColor.png
new file mode 100644
index 000000000000..ed9f89ff7ec6
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ShadowWithColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithContent_Button.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithContent_Button.png
new file mode 100644
index 000000000000..b0e1526301c8
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithContent_Button.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithDashArrayAndOffset.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithDashArrayAndOffset.png
index 8ae9f0ea6ff2..9322ab56fb36 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithDashArrayAndOffset.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithDashArrayAndOffset.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithPaddingAndContent_Image.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithPaddingAndContent_Image.png
deleted file mode 100644
index 014323ffb779..000000000000
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithPaddingAndContent_Image.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithRed.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithRed.png
new file mode 100644
index 000000000000..fdbf1d683b3b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithRed.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithStrokeLineJoin_Round.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithStrokeLineJoin_Round.png
index e9de980868a3..a4b6a8e962f7 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithStrokeLineJoin_Round.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithStrokeLineJoin_Round.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithStrokeShape_RoundRectangle.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithStrokeShape_RoundRectangle.png
index 73d7aa0d9e6c..6f9e23ba6998 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithStrokeShape_RoundRectangle.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithStrokeShape_RoundRectangle.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithStrokeThickness.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithStrokeThickness.png
index ede90ca817c3..69734c76263c 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithStrokeThickness.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeColorWithStrokeThickness.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithDashOffset.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithDashOffset.png
index 3ca922f8e4fa..02922bf8d498 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithDashOffset.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithDashOffset.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithDashOffsetAndStrokeLineCapRound.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithDashOffsetAndStrokeLineCapRound.png
index 7b43ba09de62..f67402e4ea8c 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithDashOffsetAndStrokeLineCapRound.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithDashOffsetAndStrokeLineCapRound.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithEllipseShapeAndStrokeLineCap_Square.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithEllipseShapeAndStrokeLineCap_Square.png
index 247c31588ebb..c06f9d8e91ab 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithEllipseShapeAndStrokeLineCap_Square.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithEllipseShapeAndStrokeLineCap_Square.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithStrokeColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithStrokeColor.png
index 256b446b8bfd..482e8d0bf50d 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithStrokeColor.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithStrokeColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithStrokeLineCap_Flat.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithStrokeLineCap_Flat.png
new file mode 100644
index 000000000000..84353329878e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithStrokeLineCap_Flat.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithStrokeLineCap_Round.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithStrokeLineCap_Round.png
index 4b07d0429ee2..75c647dcd2b7 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithStrokeLineCap_Round.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithStrokeLineCap_Round.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithStrokeLineCap_Square.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithStrokeLineCap_Square.png
index 904b53394c3a..d0f07d3218e4 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithStrokeLineCap_Square.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArrayWithStrokeLineCap_Square.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArray_Reset.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArray_Reset.png
new file mode 100644
index 000000000000..8cd21072636b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeDashArray_Reset.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeGradientBrush.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeGradientBrush.png
new file mode 100644
index 000000000000..d754e38db83e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeGradientBrush.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeMiterLimitWithStrokeLineJoin_Miter.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeMiterLimitWithStrokeLineJoin_Miter.png
new file mode 100644
index 000000000000..c1fadc3e6c35
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeMiterLimitWithStrokeLineJoin_Miter.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeRectangle_AfterChange.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeRectangle_AfterChange.png
new file mode 100644
index 000000000000..e5ed150503e1
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeRectangle_AfterChange.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeWithDashArray_Path.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeWithDashArray_Path.png
index bb2209cca1e7..1205fa698019 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeWithDashArray_Path.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeWithDashArray_Path.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeWithPolygon.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeWithPolygon.png
new file mode 100644
index 000000000000..0eb961cbea5a
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeWithPolygon.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeWithStrokeLineJoin_Bevel.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeWithStrokeLineJoin_Bevel.png
index c68664c1fad9..494e1e66b173 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeWithStrokeLineJoin_Bevel.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeWithStrokeLineJoin_Bevel.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeWithStrokeThickness_Ellipse.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeWithStrokeThickness_Ellipse.png
index 7c0d2276b3ed..ef076673279a 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeWithStrokeThickness_Ellipse.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShapeWithStrokeThickness_Ellipse.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShape_Path.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShape_Path.png
new file mode 100644
index 000000000000..18e03aad1b61
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeShape_Path.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeThicknessWithDashArray.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeThicknessWithDashArray.png
index 8bab98deeb4b..35cec47b90b9 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeThicknessWithDashArray.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeThicknessWithDashArray.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeThicknessWithStrokeLineJoin_Bevel.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeThicknessWithStrokeLineJoin_Bevel.png
index b4c4845eeae0..356cc4bda4b8 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeThicknessWithStrokeLineJoin_Bevel.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_StrokeThicknessWithStrokeLineJoin_Bevel.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ZeroPadding.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ZeroPadding.png
new file mode 100644
index 000000000000..cab7a74be600
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Border_ZeroPadding.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BottomTabColorTest.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BottomTabColorTest.png
index f450b2893f3e..a78861b4e702 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BottomTabColorTest.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BottomTabColorTest.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_BlueColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_BlueColor.png
new file mode 100644
index 000000000000..06ffc1c0dcc0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_BlueColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ClipWithColorGreen.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ClipWithColorGreen.png
new file mode 100644
index 000000000000..287a47c82102
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ClipWithColorGreen.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ClipWithCornerRadius.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ClipWithCornerRadius.png
new file mode 100644
index 000000000000..6d2e8fb38e1b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ClipWithCornerRadius.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ClipWithRotation.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ClipWithRotation.png
new file mode 100644
index 000000000000..37c2d26328d3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ClipWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ClipWithShadow.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ClipWithShadow.png
new file mode 100644
index 000000000000..09276d570b48
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ClipWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_Color.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_Color.png
new file mode 100644
index 000000000000..99d958bc2c7f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_Color.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ColorWithOpacity.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ColorWithOpacity.png
index afd69b8a07e9..6d31078677fc 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ColorWithOpacity.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ColorWithOpacity.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ColorWithOpacityAndShadow.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ColorWithOpacityAndShadow.png
index e7db92a0da75..ebf9a75d9c63 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ColorWithOpacityAndShadow.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_ColorWithOpacityAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_CornerRadiusWithColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_CornerRadiusWithColor.png
index 4918974d1f34..3d301defa2a1 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_CornerRadiusWithColor.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_CornerRadiusWithColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_CornerRadiusWithColorAndShadow.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_CornerRadiusWithColorAndShadow.png
index 279c5c0595c1..df723f311886 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_CornerRadiusWithColorAndShadow.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_CornerRadiusWithColorAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_CornerRadiusWithFlowDirection.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_CornerRadiusWithFlowDirection.png
index 48652089aa14..f9f555daacef 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_CornerRadiusWithFlowDirection.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_CornerRadiusWithFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_CornerRadiusWithOpacityAndShadow.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_CornerRadiusWithOpacityAndShadow.png
index c71245ed83ab..29e834d85faf 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_CornerRadiusWithOpacityAndShadow.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_CornerRadiusWithOpacityAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_GreenColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_GreenColor.png
new file mode 100644
index 000000000000..09b9012fa289
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_GreenColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_IsVisible.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_IsVisible.png
index fa4378209b54..f01b3a8716f0 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_IsVisible.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_IsVisible.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_OpacityZero.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_OpacityZero.png
new file mode 100644
index 000000000000..80108d1d6f5e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_OpacityZero.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_Reset.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_Reset.png
new file mode 100644
index 000000000000..3c8156ea8adc
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_Reset.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_UniformCornerRadius.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_UniformCornerRadius.png
new file mode 100644
index 000000000000..910b631ac32e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_UniformCornerRadius.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_WidthAndHeight.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_WidthAndHeight.png
new file mode 100644
index 000000000000..aecc6bb1a8af
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/BoxView_WidthAndHeight.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ButtonRTLTextAndImageShouldNotOverlap.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ButtonRTLTextAndImageShouldNotOverlap.png
new file mode 100644
index 000000000000..e46a94f392c2
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ButtonRTLTextAndImageShouldNotOverlap.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Button_ClipNull_NoCrash.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Button_ClipNull_NoCrash.png
new file mode 100644
index 000000000000..c6d03f8dbe28
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Button_ClipNull_NoCrash.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Button_ClipWithImageSource.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Button_ClipWithImageSource.png
new file mode 100644
index 000000000000..7c822f2c9062
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Button_ClipWithImageSource.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Button_ClipWithScale.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Button_ClipWithScale.png
new file mode 100644
index 000000000000..49e3ead377fd
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Button_ClipWithScale.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Button_ClipWithShadow.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Button_ClipWithShadow.png
new file mode 100644
index 000000000000..f90f37f51736
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Button_ClipWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Button_ClipWithText.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Button_ClipWithText.png
new file mode 100644
index 000000000000..1f8c1a391b40
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Button_ClipWithText.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ChangeShellContentTitle.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ChangeShellContentTitle.png
index a37475272fc1..c5659c8689da 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ChangeShellContentTitle.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ChangeShellContentTitle.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CheckBox_ChangeColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CheckBox_ChangeColor_VerifyVisualState.png
index 1bc54f32b9bf..002589f8d783 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CheckBox_ChangeColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CheckBox_ChangeColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png
index a59ab05ed3ac..cdc876006f40 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CheckBox_VerifyColorAfterReset.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CheckBox_VerifyColorAfterReset.png
new file mode 100644
index 000000000000..e7415e72de20
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CheckBox_VerifyColorAfterReset.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CheckBox_VerifyWithShadow.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CheckBox_VerifyWithShadow.png
new file mode 100644
index 000000000000..18a63b06e412
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CheckBox_VerifyWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewHeightIsCorrectAfterDelayedLoad.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewHeightIsCorrectAfterDelayedLoad.png
new file mode 100644
index 000000000000..5b68357792e4
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewHeightIsCorrectAfterDelayedLoad.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewSelectionShouldClear.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewSelectionShouldClear.png
new file mode 100644
index 000000000000..b4deadd61ac2
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewSelectionShouldClear.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewShouldChangeItemsLayout.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewShouldChangeItemsLayout.png
new file mode 100644
index 000000000000..74922ffd3a7c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewShouldChangeItemsLayout.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CompareStateTrigger_Checked.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CompareStateTrigger_Checked.png
new file mode 100644
index 000000000000..ffbfe708ec75
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CompareStateTrigger_Checked.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CompareStateTrigger_Unchecked.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CompareStateTrigger_Unchecked.png
new file mode 100644
index 000000000000..c80b68b12c1f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CompareStateTrigger_Unchecked.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipNull_NoCrash.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipNull_NoCrash.png
new file mode 100644
index 000000000000..fd1a43363a61
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipNull_NoCrash.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipWithEllipseGeometry.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipWithEllipseGeometry.png
new file mode 100644
index 000000000000..2669b578e125
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipWithEllipseGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipWithNestedClippedContent.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipWithNestedClippedContent.png
new file mode 100644
index 000000000000..230ae52972ba
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipWithNestedClippedContent.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipWithRectangleGeometry.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipWithRectangleGeometry.png
new file mode 100644
index 000000000000..7c45c3281911
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipWithRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipWithRoundRectangleGeometry.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipWithRoundRectangleGeometry.png
new file mode 100644
index 000000000000..41e8720174ad
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipWithRoundRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipWithShadow.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipWithShadow.png
new file mode 100644
index 000000000000..9a715861e41c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ContentView_ClipWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DataTrigger_ButtonDisabled.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DataTrigger_ButtonDisabled.png
new file mode 100644
index 000000000000..74b30ce3541b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DataTrigger_ButtonDisabled.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DataTrigger_ButtonEnabled.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DataTrigger_ButtonEnabled.png
new file mode 100644
index 000000000000..6055eb80cd5e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DataTrigger_ButtonEnabled.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DeviceStateTriggerShowsPlatformSpecificBackground.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DeviceStateTriggerShowsPlatformSpecificBackground.png
new file mode 100644
index 000000000000..2fd21631104f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DeviceStateTriggerShowsPlatformSpecificBackground.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DisabledTabWithGreenColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DisabledTabWithGreenColor.png
new file mode 100644
index 000000000000..bae5df4629f7
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DisabledTabWithGreenColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DrawStringShouldDrawText.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DrawStringShouldDrawText.png
new file mode 100644
index 000000000000..9e0a6601bda0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DrawStringShouldDrawText.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DrawTextWithinBounds.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DrawTextWithinBounds.png
new file mode 100644
index 000000000000..18c28051e939
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DrawTextWithinBounds.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DynamicTabSectionVisibility.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DynamicTabSectionVisibility.png
index b606e64cfbee..034691b33a48 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DynamicTabSectionVisibility.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/DynamicTabSectionVisibility.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EditorNoOverlapAfterRotateToLandscape.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EditorNoOverlapAfterRotateToLandscape.png
new file mode 100644
index 000000000000..5ce93d4867e4
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EditorNoOverlapAfterRotateToLandscape.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EditorNoOverlapAfterRotateToPortrait.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EditorNoOverlapAfterRotateToPortrait.png
new file mode 100644
index 000000000000..141ed2a15880
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EditorNoOverlapAfterRotateToPortrait.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EnabledTabWithNormalColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EnabledTabWithNormalColor.png
new file mode 100644
index 000000000000..c0c40c576483
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EnabledTabWithNormalColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EnsureCustomFlyoutIconColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EnsureCustomFlyoutIconColor.png
new file mode 100644
index 000000000000..8a56fe9656a1
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EnsureCustomFlyoutIconColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EnsureFlyoutIconAsDefaultIconColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EnsureFlyoutIconAsDefaultIconColor.png
new file mode 100644
index 000000000000..1da18ad52651
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EnsureFlyoutIconAsDefaultIconColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EnsureFlyoutIconWithForegroundColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EnsureFlyoutIconWithForegroundColor.png
new file mode 100644
index 000000000000..1fc526bfeaae
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EnsureFlyoutIconWithForegroundColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EnsureSearchBarExplicitSize.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EnsureSearchBarExplicitSize.png
new file mode 100644
index 000000000000..2e27517b8a20
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EnsureSearchBarExplicitSize.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EntryFocusedShouldNotCauseGapAfterRotation.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EntryFocusedShouldNotCauseGapAfterRotation.png
new file mode 100644
index 000000000000..344b31a7e883
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EntryFocusedShouldNotCauseGapAfterRotation.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EventTrigger_InvalidText.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EventTrigger_InvalidText.png
new file mode 100644
index 000000000000..e8851d66e448
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EventTrigger_InvalidText.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EventTrigger_ValidNumeric.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EventTrigger_ValidNumeric.png
new file mode 100644
index 000000000000..075a8fcde67f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EventTrigger_ValidNumeric.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/FlexLayoutWithBindableLayoutDisplaysLabels.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/FlexLayoutWithBindableLayoutDisplaysLabels.png
new file mode 100644
index 000000000000..aa042e655a97
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/FlexLayoutWithBindableLayoutDisplaysLabels.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/FlyoutIconRemainsVisible.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/FlyoutIconRemainsVisible.png
new file mode 100644
index 000000000000..66a6250a8d52
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/FlyoutIconRemainsVisible.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/FlyoutIconUpdatedAfterInsertPageBefore.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/FlyoutIconUpdatedAfterInsertPageBefore.png
new file mode 100644
index 000000000000..1b22cb792677
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/FlyoutIconUpdatedAfterInsertPageBefore.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/FontImageSourceShouldHonorColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/FontImageSourceShouldHonorColor.png
new file mode 100644
index 000000000000..6158848648ba
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/FontImageSourceShouldHonorColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ForegroundColorShouldbeSetandCustomIconAlignedProperly.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ForegroundColorShouldbeSetandCustomIconAlignedProperly.png
new file mode 100644
index 000000000000..7b21fdcdfeaf
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ForegroundColorShouldbeSetandCustomIconAlignedProperly.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/GroupedCollectionViewItems.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/GroupedCollectionViewItems.png
new file mode 100644
index 000000000000..8e3133551dd3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/GroupedCollectionViewItems.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/HEICImageShouldNotRenderUpsideDown.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/HEICImageShouldNotRenderUpsideDown.png
new file mode 100644
index 000000000000..9de5ee79e8fb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/HEICImageShouldNotRenderUpsideDown.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageButton_ClipWithEllipseGeometry.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageButton_ClipWithEllipseGeometry.png
new file mode 100644
index 000000000000..60402389e054
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageButton_ClipWithEllipseGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageButton_ClipWithRectangleGeometry.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageButton_ClipWithRectangleGeometry.png
new file mode 100644
index 000000000000..c0a84e0e1dad
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageButton_ClipWithRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageButton_ClipWithRoundRectangleGeometry.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageButton_ClipWithRoundRectangleGeometry.png
new file mode 100644
index 000000000000..700388de0b31
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageButton_ClipWithRoundRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageButton_ClipWithScale.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageButton_ClipWithScale.png
new file mode 100644
index 000000000000..504c26a6a860
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageButton_ClipWithScale.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageButton_ClipWithShadow.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageButton_ClipWithShadow.png
new file mode 100644
index 000000000000..cabc5e3d7036
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageButton_ClipWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageShouldLoadFromSubfolder.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageShouldLoadFromSubfolder.png
new file mode 100644
index 000000000000..1fe0d71bf8c0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ImageShouldLoadFromSubfolder.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipNull_NoCrash.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipNull_NoCrash.png
new file mode 100644
index 000000000000..f82464a58d97
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipNull_NoCrash.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithArcSegmentPath.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithArcSegmentPath.png
new file mode 100644
index 000000000000..1881a925059c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithArcSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithBezierSegmentPath.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithBezierSegmentPath.png
new file mode 100644
index 000000000000..7d5641edb58e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithBezierSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithComplexPolyBezierAndRotation.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithComplexPolyBezierAndRotation.png
new file mode 100644
index 000000000000..af0dc92a5d78
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithComplexPolyBezierAndRotation.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithComplexPolyLineGeometry.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithComplexPolyLineGeometry.png
new file mode 100644
index 000000000000..7feb4d57b5a3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithComplexPolyLineGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithEllipseGeometry.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithEllipseGeometry.png
new file mode 100644
index 000000000000..01448b4a1e38
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithEllipseGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithGeometryGroup.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithGeometryGroup.png
new file mode 100644
index 000000000000..c7f3f106b0e0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithGeometryGroup.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithLineSegmentPath.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithLineSegmentPath.png
new file mode 100644
index 000000000000..ea5f38258d59
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithLineSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithPolyBezierSegmentPath.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithPolyBezierSegmentPath.png
new file mode 100644
index 000000000000..0c6f6be6087f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithPolyBezierSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithPolyLineSegmentPath.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithPolyLineSegmentPath.png
new file mode 100644
index 000000000000..73e936f87aaa
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithPolyLineSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithPolyQuadraticBezierSegmentPath.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithPolyQuadraticBezierSegmentPath.png
new file mode 100644
index 000000000000..af88b8f5cf72
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithPolyQuadraticBezierSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithQuadraticBezierSegmentPath.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithQuadraticBezierSegmentPath.png
new file mode 100644
index 000000000000..f22e92fe41e0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithQuadraticBezierSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithRectangleGeometry.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithRectangleGeometry.png
new file mode 100644
index 000000000000..6e334db05733
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithRotation.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithRotation.png
new file mode 100644
index 000000000000..cba8914bc4ac
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithRoundRectangleGeometry.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithRoundRectangleGeometry.png
new file mode 100644
index 000000000000..a752a9a59ca9
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithRoundRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithScale.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithScale.png
new file mode 100644
index 000000000000..0f5f4f893d44
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Image_ClipWithScale.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue12324TabbedPageVisualTest.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue12324TabbedPageVisualTest.png
new file mode 100644
index 000000000000..ef7561721151
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue12324TabbedPageVisualTest.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue23377ItemSpacing.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue23377ItemSpacing.png
new file mode 100644
index 000000000000..5c207fb6e2ff
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue23377ItemSpacing.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue33400_Always.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue33400_Always.png
new file mode 100644
index 000000000000..3ba084e399b4
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue33400_Always.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue33400_Default.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue33400_Default.png
new file mode 100644
index 000000000000..7818c9ef4425
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue33400_Default.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue33400_Never.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue33400_Never.png
new file mode 100644
index 000000000000..d2855ba9bcda
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue33400_Never.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue33909ForegroundColorReset.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue33909ForegroundColorReset.png
new file mode 100644
index 000000000000..2297e4d3e68d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Issue33909ForegroundColorReset.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/LabelDisplayWithoutCroppingInsideVerticalLayout.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/LabelDisplayWithoutCroppingInsideVerticalLayout.png
new file mode 100644
index 000000000000..594e8030d6b4
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/LabelDisplayWithoutCroppingInsideVerticalLayout.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/LabelShadowRemainsAfterOpacityChange.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/LabelShadowRemainsAfterOpacityChange.png
new file mode 100644
index 000000000000..7c687368a5fb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/LabelShadowRemainsAfterOpacityChange.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Label_ClipWithDifferentFontSize.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Label_ClipWithDifferentFontSize.png
new file mode 100644
index 000000000000..72f607cfd82c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Label_ClipWithDifferentFontSize.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Label_ClipWithFormattedText.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Label_ClipWithFormattedText.png
new file mode 100644
index 000000000000..e781a4885c9f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Label_ClipWithFormattedText.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Label_ClipWithLongText.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Label_ClipWithLongText.png
new file mode 100644
index 000000000000..0f4a8ac73744
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Label_ClipWithLongText.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Label_ClipWithRotation.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Label_ClipWithRotation.png
new file mode 100644
index 000000000000..402323206bb9
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Label_ClipWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/MultiTrigger_Both_Filled.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/MultiTrigger_Both_Filled.png
new file mode 100644
index 000000000000..80db9bf96a34
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/MultiTrigger_Both_Filled.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/MultiTrigger_EmailOnly_Filled.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/MultiTrigger_EmailOnly_Filled.png
new file mode 100644
index 000000000000..d19d780138f6
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/MultiTrigger_EmailOnly_Filled.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/MultiTrigger_PhoneOnly_Filled.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/MultiTrigger_PhoneOnly_Filled.png
new file mode 100644
index 000000000000..5442848a930c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/MultiTrigger_PhoneOnly_Filled.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/NavBarUpdatesWhenSwitchingShellContent.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/NavBarUpdatesWhenSwitchingShellContent.png
index d0fe52499155..7d31334bcbc7 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/NavBarUpdatesWhenSwitchingShellContent.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/NavBarUpdatesWhenSwitchingShellContent.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/NavigatingBetweenPagesWithSetTitleViewShouldNotCrash.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/NavigatingBetweenPagesWithSetTitleViewShouldNotCrash.png
new file mode 100644
index 000000000000..f4e9c9348dde
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/NavigatingBetweenPagesWithSetTitleViewShouldNotCrash.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/NavigationPageTitleViewShouldRespectMargins.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/NavigationPageTitleViewShouldRespectMargins.png
new file mode 100644
index 000000000000..25153196ecbc
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/NavigationPageTitleViewShouldRespectMargins.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/OnlyManuallySwipedItemShouldBeOpened.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/OnlyManuallySwipedItemShouldBeOpened.png
new file mode 100644
index 000000000000..3326ff0855fb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/OnlyManuallySwipedItemShouldBeOpened.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/PageShouldNotMoveOutsideViewportWhenEntryFocusedOnPageLoad.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/PageShouldNotMoveOutsideViewportWhenEntryFocusedOnPageLoad.png
new file mode 100644
index 000000000000..e0367e7a3fa4
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/PageShouldNotMoveOutsideViewportWhenEntryFocusedOnPageLoad.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/PropertyTrigger_Focused.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/PropertyTrigger_Focused.png
new file mode 100644
index 000000000000..d3ce3b87e2d4
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/PropertyTrigger_Focused.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/PropertyTrigger_UnFocused.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/PropertyTrigger_UnFocused.png
new file mode 100644
index 000000000000..4eb94cab3e77
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/PropertyTrigger_UnFocused.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RTLModePaddingShouldWork.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RTLModePaddingShouldWork.png
new file mode 100644
index 000000000000..a011cfe0a028
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RTLModePaddingShouldWork.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RadioButtonTextColorShouldNotBeOverriddenByGlobalLabelStyle.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RadioButtonTextColorShouldNotBeOverriddenByGlobalLabelStyle.png
new file mode 100644
index 000000000000..047af67ab1ca
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RadioButtonTextColorShouldNotBeOverriddenByGlobalLabelStyle.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RefreshView_SetShadowWithCollectionView_VerifyShadowApplied.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RefreshView_SetShadowWithCollectionView_VerifyShadowApplied.png
index ce1c8b713a4c..fde8fdfe8025 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RefreshView_SetShadowWithCollectionView_VerifyShadowApplied.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RefreshView_SetShadowWithCollectionView_VerifyShadowApplied.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RefreshView_SetShadow_VerifyShadowApplied.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RefreshView_SetShadow_VerifyShadowApplied.png
index 9728cff999d6..72380b19a913 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RefreshView_SetShadow_VerifyShadowApplied.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RefreshView_SetShadow_VerifyShadowApplied.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RightToLeftFlowDirectionShouldWork.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RightToLeftFlowDirectionShouldWork.png
index 0708ab4c9c15..9dbdd245c969 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RightToLeftFlowDirectionShouldWork.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/RightToLeftFlowDirectionShouldWork.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SearchHandlerVisibilityChangesToCollapsible.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SearchHandlerVisibilityChangesToCollapsible.png
new file mode 100644
index 000000000000..0e37b5729e6b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SearchHandlerVisibilityChangesToCollapsible.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SearchHandlerVisibilityChangesToExpanded.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SearchHandlerVisibilityChangesToExpanded.png
new file mode 100644
index 000000000000..7b9288107863
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SearchHandlerVisibilityChangesToExpanded.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SelectionLengthShouldUpdateWhenEntryIsFocused.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SelectionLengthShouldUpdateWhenEntryIsFocused.png
new file mode 100644
index 000000000000..ef228d60d11d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SelectionLengthShouldUpdateWhenEntryIsFocused.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShadowShouldUpdateOnCornerRadiusChange.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShadowShouldUpdateOnCornerRadiusChange.png
new file mode 100644
index 000000000000..0ee32b56e500
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShadowShouldUpdateOnCornerRadiusChange.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShadowShouldWork.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShadowShouldWork.png
new file mode 100644
index 000000000000..b850acda31bf
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShadowShouldWork.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellColorsResetOnNavigation.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellColorsResetOnNavigation.png
index aae187df6d3a..afbea999f5c5 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellColorsResetOnNavigation.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellColorsResetOnNavigation.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_BackgroundColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_BackgroundColor.png
new file mode 100644
index 000000000000..2c37785439a2
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_BackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_BackgroundColorAndForegroundColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_BackgroundColorAndForegroundColor.png
new file mode 100644
index 000000000000..2cd9330ae71c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_BackgroundColorAndForegroundColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_BackgroundColorAndTitleColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_BackgroundColorAndTitleColor.png
new file mode 100644
index 000000000000..005f8ed7fb7d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_BackgroundColorAndTitleColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_BackgroundColorAndUnselectedColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_BackgroundColorAndUnselectedColor.png
new file mode 100644
index 000000000000..5d032676ae7b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_BackgroundColorAndUnselectedColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ForegroundColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ForegroundColor.png
new file mode 100644
index 000000000000..8204d919abe2
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ForegroundColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ForegroundColorAndTitleColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ForegroundColorAndTitleColor.png
new file mode 100644
index 000000000000..362b54707dda
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ForegroundColorAndTitleColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ForegroundColorAndUnselectedColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ForegroundColorAndUnselectedColor.png
new file mode 100644
index 000000000000..767d9203c16d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ForegroundColorAndUnselectedColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_IsVisibleFalse.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_IsVisibleFalse.png
new file mode 100644
index 000000000000..7c5974150ea5
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_IsVisibleFalse.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_IsVisibleTrue.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_IsVisibleTrue.png
new file mode 100644
index 000000000000..5cb3d447f5f0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_IsVisibleTrue.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_NavBarVisibilityHide.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_NavBarVisibilityHide.png
new file mode 100644
index 000000000000..f735c1ccfe61
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_NavBarVisibilityHide.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_NavBarVisibilityShow.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_NavBarVisibilityShow.png
new file mode 100644
index 000000000000..2af63e34e944
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_NavBarVisibilityShow.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_PresentationModeAnimated.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_PresentationModeAnimated.png
new file mode 100644
index 000000000000..068bc0a1fbd3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_PresentationModeAnimated.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_PresentationModeModal.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_PresentationModeModal.png
new file mode 100644
index 000000000000..62f961cd4ada
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_PresentationModeModal.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_PresentationModeModalAnimated.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_PresentationModeModalAnimated.png
new file mode 100644
index 000000000000..9dbb3e471655
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_PresentationModeModalAnimated.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_PresentationModeModalNotAnimated.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_PresentationModeModalNotAnimated.png
new file mode 100644
index 000000000000..f94a4170ec09
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_PresentationModeModalNotAnimated.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_PresentationModeNotAnimated.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_PresentationModeNotAnimated.png
new file mode 100644
index 000000000000..4f376fa262b3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_PresentationModeNotAnimated.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ShowTitleView.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ShowTitleView.png
new file mode 100644
index 000000000000..633c49ca9fc3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ShowTitleView.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ShowTitleViewHidden.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ShowTitleViewHidden.png
new file mode 100644
index 000000000000..bbe3c39b1d79
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ShowTitleViewHidden.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ShowTitleViewWithBackgroundColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ShowTitleViewWithBackgroundColor.png
new file mode 100644
index 000000000000..898e3a789afc
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_ShowTitleViewWithBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_TitleColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_TitleColor.png
new file mode 100644
index 000000000000..36fa0254f586
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_TitleColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_UnselectedColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_UnselectedColor.png
new file mode 100644
index 000000000000..84cb716601f8
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_UnselectedColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_VerifyForegroundColorResetForBackButton.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_VerifyForegroundColorResetForBackButton.png
new file mode 100644
index 000000000000..8e59379beed6
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellPages_VerifyForegroundColorResetForBackButton.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellTabBarBackgroundColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellTabBarBackgroundColor.png
new file mode 100644
index 000000000000..fa76e26e9169
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShellTabBarBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShouldAppearFlyoutIconAndContentPageTitle.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShouldAppearFlyoutIconAndContentPageTitle.png
new file mode 100644
index 000000000000..87613b9370f6
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShouldAppearFlyoutIconAndContentPageTitle.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShouldMeasureFirstItemInHorizontalLayouts.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShouldMeasureFirstItemInHorizontalLayouts.png
new file mode 100644
index 000000000000..b333188d00c5
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShouldMeasureFirstItemInHorizontalLayouts.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShouldUpdateSearchHandlerOnPageNavigation.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShouldUpdateSearchHandlerOnPageNavigation.png
index d00789fdca29..4f57e2f82eea 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShouldUpdateSearchHandlerOnPageNavigation.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShouldUpdateSearchHandlerOnPageNavigation.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SliderShouldChangeThumbImageAndResetIt.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SliderShouldChangeThumbImageAndResetIt.png
index 9c7bdee2fb6d..d0fc26d2309c 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SliderShouldChangeThumbImageAndResetIt.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SliderShouldChangeThumbImageAndResetIt.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SliderThumbImageShouldBeScaled.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SliderThumbImageShouldBeScaled.png
new file mode 100644
index 000000000000..00ddb986bfde
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SliderThumbImageShouldBeScaled.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeBackgroundColor_VerifyVisualState.png
index 668c539df002..33ae63c2170d 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeBackgroundColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeFlowDirection_RTL_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeFlowDirection_RTL_VerifyVisualState.png
index 1bd65197c8b4..c28612152f71 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeFlowDirection_RTL_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeFlowDirection_RTL_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeMaxTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeMaxTrackColor_VerifyVisualState.png
index 5c0ce0ecc9f3..807ec64c258c 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeMaxTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeMaxTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeMinTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeMinTrackColor_VerifyVisualState.png
index 59fde738e547..52236dced81b 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeMinTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeMinTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeThumbColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeThumbColor_VerifyVisualState.png
index 4da5c16cc9d1..7388c195f7fa 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeThumbColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeThumbColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeThumbImageSource_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeThumbImageSource_VerifyVisualState.png
index 6d7a0236a20c..0d4836d29d78 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeThumbImageSource_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_ChangeThumbImageSource_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_FlowDirection_RTL_SetMaximumValue_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_FlowDirection_RTL_SetMaximumValue_VerifyVisualState.png
index 52c6c83a21f9..059dcbbd1bcc 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_FlowDirection_RTL_SetMaximumValue_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_FlowDirection_RTL_SetMaximumValue_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_FlowDirection_RTL_SetMinimumValue_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_FlowDirection_RTL_SetMinimumValue_VerifyVisualState.png
index b2168772fa84..5d2f05d394db 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_FlowDirection_RTL_SetMinimumValue_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_FlowDirection_RTL_SetMinimumValue_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_FlowDirection_RTL_SetValue_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_FlowDirection_RTL_SetValue_VerifyVisualState.png
index bd621b390283..42a4d03197e8 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_FlowDirection_RTL_SetValue_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_FlowDirection_RTL_SetValue_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_Reenabled_Normal_State.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_Reenabled_Normal_State.png
new file mode 100644
index 000000000000..d73c5a11e836
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_Reenabled_Normal_State.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_Reset_Disabled_State.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_Reset_Disabled_State.png
new file mode 100644
index 000000000000..abd09194328f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_Reset_Disabled_State.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetBackgroundColorAndIsEnable_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetBackgroundColorAndIsEnable_VerifyVisualState.png
index f0c3e0741151..8cc3a175e8e6 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetBackgroundColorAndIsEnable_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetBackgroundColorAndIsEnable_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetBackgroundColorAndMaxTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetBackgroundColorAndMaxTrackColor_VerifyVisualState.png
index 1ae32f4830aa..8ad9a1751b9c 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetBackgroundColorAndMaxTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetBackgroundColorAndMaxTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetBackgroundColorAndMinTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetBackgroundColorAndMinTrackColor_VerifyVisualState.png
index 4403e472c612..3716d900da4a 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetBackgroundColorAndMinTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetBackgroundColorAndMinTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetBackgroundColorAndThumbColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetBackgroundColorAndThumbColor_VerifyVisualState.png
index 28fc2ce5468f..6e50dba90afd 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetBackgroundColorAndThumbColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetBackgroundColorAndThumbColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetEnabledStateToFalse_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetEnabledStateToFalse_VerifyVisualState.png
index 9754e154a0ed..603aa2107b20 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetEnabledStateToFalse_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetEnabledStateToFalse_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetFlowDirectionAndMaxTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetFlowDirectionAndMaxTrackColor_VerifyVisualState.png
index 7d0d1c0cfc45..3993af3a3b2d 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetFlowDirectionAndMaxTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetFlowDirectionAndMaxTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetFlowDirectionAndMinTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetFlowDirectionAndMinTrackColor_VerifyVisualState.png
index 59fc482dc223..305f05d10393 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetFlowDirectionAndMinTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetFlowDirectionAndMinTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsEnableAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsEnableAndBackgroundColor_VerifyVisualState.png
index 118c0b9ed487..7e5f5a3e53ae 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsEnableAndBackgroundColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsEnableAndBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsEnableAndMaxTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsEnableAndMaxTrackColor_VerifyVisualState.png
index 0cc145f6baf4..9d84fbdd047f 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsEnableAndMaxTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsEnableAndMaxTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsEnableAndMinTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsEnableAndMinTrackColor_VerifyVisualState.png
index ce8911e2d19e..d5c91e511fac 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsEnableAndMinTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsEnableAndMinTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsEnableAndThumbColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsEnableAndThumbColor_VerifyVisualState.png
index b579bf61ce7a..b636ca60fc2e 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsEnableAndThumbColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsEnableAndThumbColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsVisibleAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsVisibleAndBackgroundColor_VerifyVisualState.png
index c48f29b2ed86..dd9e9cd618e5 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsVisibleAndBackgroundColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsVisibleAndBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsVisibleAndMaxTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsVisibleAndMaxTrackColor_VerifyVisualState.png
index 605908a128c0..dc93b053a77d 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsVisibleAndMaxTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsVisibleAndMaxTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsVisibleAndMinTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsVisibleAndMinTrackColor_VerifyVisualState.png
index 649fe7d0a4aa..ac9f8b6e642b 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsVisibleAndMinTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsVisibleAndMinTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsVisibleAndThumbColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsVisibleAndThumbColor_VerifyVisualState.png
index 2bc51df4a370..f4d556b92783 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsVisibleAndThumbColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetIsVisibleAndThumbColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackAndBackgroundColor_VerifyVisualState.png
index 62999ac06a82..44332bcbc0d5 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackAndBackgroundColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackAndBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackAndMinTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackAndMinTrackColor_VerifyVisualState.png
index cd805274e866..3864284e6c4b 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackAndMinTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackAndMinTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackAndThumbColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackAndThumbColor_VerifyVisualState.png
index 530db95d189f..d4d75627e0c3 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackAndThumbColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackAndThumbColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackColorAndValue_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackColorAndValue_VerifyVisualState.png
index 23fa86490139..bb0ef6a8405b 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackColorAndValue_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackColorAndValue_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackColorTestFlowDirection_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackColorTestFlowDirection_VerifyVisualState.png
index d3db3394380f..75bd43475089 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackColorTestFlowDirection_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaxTrackColorTestFlowDirection_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaximumAndChangeFlowDirection_RTL.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaximumAndChangeFlowDirection_RTL.png
index 42c16a659f7e..ca72951f72d9 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaximumAndChangeFlowDirection_RTL.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMaximumAndChangeFlowDirection_RTL.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackAndBackgroundColor_VerifyVisualState.png
index 84ecbfe3f7b0..fd36dc9664cd 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackAndBackgroundColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackAndBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackAndMaxTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackAndMaxTrackColor_VerifyVisualState.png
index f3db18b9cce8..e617fe091138 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackAndMaxTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackAndMaxTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackAndThumbColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackAndThumbColor_VerifyVisualState.png
index 6ce4eb08dd30..58db1a0f49ec 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackAndThumbColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackAndThumbColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackColorAndValue_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackColorAndValue_VerifyVisualState.png
index e6a24540d2d8..f8767ea4cca1 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackColorAndValue_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackColorAndValue_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackColorTestFlowDirection_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackColorTestFlowDirection_VerifyVisualState.png
index d70632c52fda..abf7ca37afe7 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackColorTestFlowDirection_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinTrackColorTestFlowDirection_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinimumAndChangeFlowDirection_RTL.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinimumAndChangeFlowDirection_RTL.png
index aee1bd143e25..1b7a32555436 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinimumAndChangeFlowDirection_RTL.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetMinimumAndChangeFlowDirection_RTL.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbAndBackgroundColor_VerifyVisualState.png
index 9dc9abd0a9c9..83045762aca6 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbAndBackgroundColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbAndBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbAndMaxTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbAndMaxTrackColor_VerifyVisualState.png
index d74d725050a4..e0c88268140e 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbAndMaxTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbAndMaxTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbAndMinTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbAndMinTrackColor_VerifyVisualState.png
index 45c7fcf75053..36cf3eca85d3 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbAndMinTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbAndMinTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png
index e0253ff42b2b..5f43208371f2 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbColorAndThumbImageSource_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbImageSourceAndReset_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbImageSourceAndReset_VerifyVisualState.png
new file mode 100644
index 000000000000..4ee4bbc4da88
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbImageSourceAndReset_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbImageSourceAndThumbColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbImageSourceAndThumbColor_VerifyVisualState.png
index e3757bca9113..6645630ce6c7 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbImageSourceAndThumbColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetThumbImageSourceAndThumbColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndFlowDirection_RTL_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndFlowDirection_RTL_VerifyVisualState.png
index 174ef946a809..0e36c0fd823d 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndFlowDirection_RTL_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndFlowDirection_RTL_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndMaxTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndMaxTrackColor_VerifyVisualState.png
index 544a7ffc9586..c20aaf3eb695 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndMaxTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndMaxTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndMinTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndMinTrackColor_VerifyVisualState.png
index f1d5415a8de9..c99031d73052 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndMinTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndMinTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndThumbImageSource_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndThumbImageSource_VerifyVisualState.png
index 1916a6a0fd87..86179ab4b65b 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndThumbImageSource_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetValueAndThumbImageSource_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetVisibilityToFalse_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetVisibilityToFalse_VerifyVisualState.png
index a5a2257aa841..0c1fdec09ed3 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetVisibilityToFalse_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Slider_SetVisibilityToFalse_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/StateTrigger_SwitchOff.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/StateTrigger_SwitchOff.png
new file mode 100644
index 000000000000..cbd23788c094
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/StateTrigger_SwitchOff.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/StateTrigger_SwitchOn.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/StateTrigger_SwitchOn.png
new file mode 100644
index 000000000000..8be70fd7bcd3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/StateTrigger_SwitchOn.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Stepper_ChangeFlowDirection_RTL_VerifyVisualState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Stepper_ChangeFlowDirection_RTL_VerifyVisualState.png
index 651ebcf9aaf7..24af9f9bf211 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Stepper_ChangeFlowDirection_RTL_VerifyVisualState.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/Stepper_ChangeFlowDirection_RTL_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SwipeItemFontImageSourceSizeIsRespected.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SwipeItemFontImageSourceSizeIsRespected.png
new file mode 100644
index 000000000000..5b5e3f9cfb76
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SwipeItemFontImageSourceSizeIsRespected.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SwitchThumbShouldBeVisibleWithShadow.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SwitchThumbShouldBeVisibleWithShadow.png
new file mode 100644
index 000000000000..1ea0c5a9579d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SwitchThumbShouldBeVisibleWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabBarShouldBeVisibleAfterNavigatingFromModalViaGoToAsync.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabBarShouldBeVisibleAfterNavigatingFromModalViaGoToAsync.png
new file mode 100644
index 000000000000..df1d3f454eab
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabBarShouldBeVisibleAfterNavigatingFromModalViaGoToAsync.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabBarShouldDisplayCorrectTabsAfterFirstTabBecomesInvisible.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabBarShouldDisplayCorrectTabsAfterFirstTabBecomesInvisible.png
new file mode 100644
index 000000000000..77d658bf6f47
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabBarShouldDisplayCorrectTabsAfterFirstTabBecomesInvisible.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabBarVisibilityAfterMultiLevelPopToRoot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabBarVisibilityAfterMultiLevelPopToRoot.png
new file mode 100644
index 000000000000..fd0ee9db07a1
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabBarVisibilityAfterMultiLevelPopToRoot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabbedPageFlowDirection_AfterChangingBackToLeftToRight.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabbedPageFlowDirection_AfterChangingBackToLeftToRight.png
new file mode 100644
index 000000000000..3c51307eb43c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabbedPageFlowDirection_AfterChangingBackToLeftToRight.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabbedPageFlowDirection_AfterChangingToLeftToRight.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabbedPageFlowDirection_AfterChangingToLeftToRight.png
new file mode 100644
index 000000000000..e887687ea041
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabbedPageFlowDirection_AfterChangingToLeftToRight.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabbedPageFlowDirection_DefaultRightToLeftLayout.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabbedPageFlowDirection_DefaultRightToLeftLayout.png
new file mode 100644
index 000000000000..cde3b6965cb4
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TabbedPageFlowDirection_DefaultRightToLeftLayout.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TestIssue18668.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TestIssue18668.png
new file mode 100644
index 000000000000..4ae7e75f51de
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TestIssue18668.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ToolBarSecondayItemsShouldNotUseBarTextColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ToolBarSecondayItemsShouldNotUseBarTextColor.png
index 04496b92c8ea..c34233b22d94 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ToolBarSecondayItemsShouldNotUseBarTextColor.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ToolBarSecondayItemsShouldNotUseBarTextColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ToolbarExtendsAllTheWayLeftAndRight_Shell.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ToolbarExtendsAllTheWayLeftAndRight_Shell.png
index 9950d9d55f34..010b6b918aaf 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ToolbarExtendsAllTheWayLeftAndRight_Shell.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ToolbarExtendsAllTheWayLeftAndRight_Shell.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TransparentShapeShouldNotDisplayShadow.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TransparentShapeShouldNotDisplayShadow.png
new file mode 100644
index 000000000000..0a728256da78
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/TransparentShapeShouldNotDisplayShadow.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/UpdateSearchHandlerMenuItemForTabNavigation.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/UpdateSearchHandlerMenuItemForTabNavigation.png
index fc61f02e538f..3f78bd2b23dd 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/UpdateSearchHandlerMenuItemForTabNavigation.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/UpdateSearchHandlerMenuItemForTabNavigation.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyAllIndicatorDotsShowShadowsWhenIndicatorSize.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyAllIndicatorDotsShowShadowsWhenIndicatorSize.png
new file mode 100644
index 000000000000..6ad2cea6116d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyAllIndicatorDotsShowShadowsWhenIndicatorSize.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyBackgroundColorCleared.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyBackgroundColorCleared.png
new file mode 100644
index 000000000000..b201b7f978b7
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyBackgroundColorCleared.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyBackgroundColorSet.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyBackgroundColorSet.png
new file mode 100644
index 000000000000..a557e21b09fb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyBackgroundColorSet.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyClearedTimeDoesNotShowMidnight.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyClearedTimeDoesNotShowMidnight.png
new file mode 100644
index 000000000000..582c9bc42af9
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyClearedTimeDoesNotShowMidnight.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png
new file mode 100644
index 000000000000..4d6eddbe818d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png
new file mode 100644
index 000000000000..0b724080642f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png
new file mode 100644
index 000000000000..c8e9d7674bcc
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png
new file mode 100644
index 000000000000..24e268a56e04
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png
new file mode 100644
index 000000000000..dd139cba3624
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png
new file mode 100644
index 000000000000..6a30a914f831
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyDefaultScrollToRequested.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyDefaultScrollToRequested.png
new file mode 100644
index 000000000000..707712729705
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyDefaultScrollToRequested.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png
new file mode 100644
index 000000000000..c9f13e26b255
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png
new file mode 100644
index 000000000000..d56efbf55d84
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndKeepLastItemInViewWithObservableListWhenHorizontalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndKeepLastItemInViewWithObservableListWhenHorizontalGrid.png
new file mode 100644
index 000000000000..9d0e6f0daff6
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndKeepLastItemInViewWithObservableListWhenHorizontalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndKeepLastItemInViewWithObservableListWhenHorizontalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndKeepLastItemInViewWithObservableListWhenHorizontalList.png
new file mode 100644
index 000000000000..a024bd2c2eef
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndKeepLastItemInViewWithObservableListWhenHorizontalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndKeepLastItemInViewWithObservableListWhenVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndKeepLastItemInViewWithObservableListWhenVerticalGrid.png
new file mode 100644
index 000000000000..2a5bc9379a8f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndKeepLastItemInViewWithObservableListWhenVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedList.png
new file mode 100644
index 000000000000..82db0cfa18b2
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedListWhenHorizontalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedListWhenHorizontalGrid.png
new file mode 100644
index 000000000000..a1ada738b1fb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedListWhenHorizontalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedListWhenHorizontalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedListWhenHorizontalList.png
new file mode 100644
index 000000000000..67b5ec4057cc
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedListWhenHorizontalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedListWhenVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedListWhenVerticalGrid.png
new file mode 100644
index 000000000000..338c08687a8b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedListWhenVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollection.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollection.png
new file mode 100644
index 000000000000..e6e8986b2733
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollection.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollectionWhenHorizontalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollectionWhenHorizontalGrid.png
new file mode 100644
index 000000000000..059710c605af
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollectionWhenHorizontalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollectionWhenHorizontalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollectionWhenHorizontalList.png
new file mode 100644
index 000000000000..67bc5554b064
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollectionWhenHorizontalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollectionWhenVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollectionWhenVerticalGrid.png
new file mode 100644
index 000000000000..f248be31d14c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollectionWhenVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureFirstItemsWithGroupedList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureFirstItemsWithGroupedList.png
new file mode 100644
index 000000000000..826083552c8b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureFirstItemsWithGroupedList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureFirstItemsWithObservableCollection.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureFirstItemsWithObservableCollection.png
new file mode 100644
index 000000000000..475488a05247
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureFirstItemsWithObservableCollection.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureFirstItemsWithObservableCollectionWhenVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureFirstItemsWithObservableCollectionWhenVerticalGrid.png
new file mode 100644
index 000000000000..0ff474b6029f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRAndMeasureFirstItemsWithObservableCollectionWhenVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHeaderTemplateAndFooterTemplateAndHorizontalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHeaderTemplateAndFooterTemplateAndHorizontalGrid.png
new file mode 100644
index 000000000000..d9b9e0413fc2
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHeaderTemplateAndFooterTemplateAndHorizontalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHeaderTemplateAndFooterTemplateAndHorizontalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHeaderTemplateAndFooterTemplateAndHorizontalList.png
new file mode 100644
index 000000000000..23208b7f3bac
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHeaderTemplateAndFooterTemplateAndHorizontalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHeaderTemplateAndFooterTemplateAndVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHeaderTemplateAndFooterTemplateAndVerticalGrid.png
new file mode 100644
index 000000000000..db64543bb3ef
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHeaderTemplateAndFooterTemplateAndVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHeaderTemplateAndFooterTemplateAndVerticalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHeaderTemplateAndFooterTemplateAndVerticalList.png
new file mode 100644
index 000000000000..8ecd5c899b0a
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHeaderTemplateAndFooterTemplateAndVerticalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHorizontalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHorizontalGrid.png
new file mode 100644
index 000000000000..32e74f31fc61
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHorizontalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHorizontalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHorizontalList.png
new file mode 100644
index 000000000000..2096469c1f88
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithHorizontalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithVerticalGrid.png
new file mode 100644
index 000000000000..c9b0d971234e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithVerticalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithVerticalList.png
new file mode 100644
index 000000000000..cad7c957a92d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionLTRWithVerticalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndKeepLastItemInViewWithObservableListWhenHorizontalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndKeepLastItemInViewWithObservableListWhenHorizontalGrid.png
new file mode 100644
index 000000000000..bab2838fcccd
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndKeepLastItemInViewWithObservableListWhenHorizontalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndKeepLastItemInViewWithObservableListWhenHorizontalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndKeepLastItemInViewWithObservableListWhenHorizontalList.png
new file mode 100644
index 000000000000..b6da8a21b917
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndKeepLastItemInViewWithObservableListWhenHorizontalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndKeepLastItemInViewWithObservableListWhenVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndKeepLastItemInViewWithObservableListWhenVerticalGrid.png
new file mode 100644
index 000000000000..08b496730f03
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndKeepLastItemInViewWithObservableListWhenVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedList.png
new file mode 100644
index 000000000000..dca79fcc64a0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedListWhenHorizontalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedListWhenHorizontalGrid.png
new file mode 100644
index 000000000000..ec71278b9796
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedListWhenHorizontalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedListWhenHorizontalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedListWhenHorizontalList.png
new file mode 100644
index 000000000000..67508e3cfcf2
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedListWhenHorizontalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedListWhenVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedListWhenVerticalGrid.png
new file mode 100644
index 000000000000..40d7265d1df6
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedListWhenVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollection.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollection.png
new file mode 100644
index 000000000000..f21710ea01de
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollection.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollectionWhenHorizontalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollectionWhenHorizontalGrid.png
new file mode 100644
index 000000000000..55fdb97e0380
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollectionWhenHorizontalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollectionWhenHorizontalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollectionWhenHorizontalList.png
new file mode 100644
index 000000000000..b3a265687099
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollectionWhenHorizontalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollectionWhenVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollectionWhenVerticalGrid.png
new file mode 100644
index 000000000000..68c5130dbda2
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollectionWhenVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureFirstItemsWithGroupedList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureFirstItemsWithGroupedList.png
new file mode 100644
index 000000000000..99afa6d5abfe
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureFirstItemsWithGroupedList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureFirstItemsWithObservableCollection.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureFirstItemsWithObservableCollection.png
new file mode 100644
index 000000000000..3ef08e0f711b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureFirstItemsWithObservableCollection.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureFirstItemsWithObservableCollectionWhenVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureFirstItemsWithObservableCollectionWhenVerticalGrid.png
new file mode 100644
index 000000000000..45d0c19b3700
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLAndMeasureFirstItemsWithObservableCollectionWhenVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLGroupHeaderAndFooterTemplate_WithVerticalGridAndGroupedList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLGroupHeaderAndFooterTemplate_WithVerticalGridAndGroupedList.png
new file mode 100644
index 000000000000..1966addfdf62
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLGroupHeaderAndFooterTemplate_WithVerticalGridAndGroupedList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLIsGrouped_WithHorizontalGridAndGroupedList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLIsGrouped_WithHorizontalGridAndGroupedList.png
new file mode 100644
index 000000000000..9737ca15b1d2
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLIsGrouped_WithHorizontalGridAndGroupedList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLIsGrouped_WithVerticalGridAndGroupedList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLIsGrouped_WithVerticalGridAndGroupedList.png
new file mode 100644
index 000000000000..bb6aa74dff97
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLIsGrouped_WithVerticalGridAndGroupedList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderStringAndFooterStringAndHorizontalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderStringAndFooterStringAndHorizontalGrid.png
new file mode 100644
index 000000000000..f8017acabeb5
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderStringAndFooterStringAndHorizontalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderStringAndFooterStringAndHorizontalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderStringAndFooterStringAndHorizontalList.png
new file mode 100644
index 000000000000..095c82bb27b3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderStringAndFooterStringAndHorizontalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderStringAndFooterStringAndVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderStringAndFooterStringAndVerticalGrid.png
new file mode 100644
index 000000000000..02615b246203
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderStringAndFooterStringAndVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderStringAndFooterStringAndVerticalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderStringAndFooterStringAndVerticalList.png
new file mode 100644
index 000000000000..b00885ca7119
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderStringAndFooterStringAndVerticalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderTemplateAndFooterTemplateAndHorizontalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderTemplateAndFooterTemplateAndHorizontalGrid.png
new file mode 100644
index 000000000000..d27c5478b533
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderTemplateAndFooterTemplateAndHorizontalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderTemplateAndFooterTemplateAndHorizontalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderTemplateAndFooterTemplateAndHorizontalList.png
new file mode 100644
index 000000000000..524959155be2
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderTemplateAndFooterTemplateAndHorizontalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderTemplateAndFooterTemplateAndVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderTemplateAndFooterTemplateAndVerticalGrid.png
new file mode 100644
index 000000000000..533de38d3a8f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderTemplateAndFooterTemplateAndVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderTemplateAndFooterTemplateAndVerticalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderTemplateAndFooterTemplateAndVerticalList.png
new file mode 100644
index 000000000000..42c32451ea56
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderTemplateAndFooterTemplateAndVerticalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderViewAndFooterViewAndHorizontalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderViewAndFooterViewAndHorizontalGrid.png
new file mode 100644
index 000000000000..c278aeb139a0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderViewAndFooterViewAndHorizontalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderViewAndFooterViewAndHorizontalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderViewAndFooterViewAndHorizontalList.png
new file mode 100644
index 000000000000..3fd255af7058
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderViewAndFooterViewAndHorizontalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderViewAndFooterViewAndVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderViewAndFooterViewAndVerticalGrid.png
new file mode 100644
index 000000000000..6c4c17675d3d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderViewAndFooterViewAndVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderViewAndFooterViewAndVerticalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderViewAndFooterViewAndVerticalList.png
new file mode 100644
index 000000000000..f34f7c99c31f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHeaderViewAndFooterViewAndVerticalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHorizontalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHorizontalGrid.png
new file mode 100644
index 000000000000..471b20192245
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHorizontalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHorizontalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHorizontalList.png
new file mode 100644
index 000000000000..8245ca0f999f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithHorizontalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithVerticalGrid.png
new file mode 100644
index 000000000000..5a90de083467
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithVerticalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithVerticalList.png
new file mode 100644
index 000000000000..922b19eae6ac
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlowDirectionRTLWithVerticalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPageToolbarItemsRender.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPageToolbarItemsRender.png
new file mode 100644
index 000000000000..7f60df16b54c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPageToolbarItemsRender.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPage_BackgroundColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPage_BackgroundColor.png
index 4fe5e98a06a8..dc452d673384 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPage_BackgroundColor.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPage_BackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPage_FlyoutLayoutBehaviorPopover.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPage_FlyoutLayoutBehaviorPopover.png
index 40835694985f..a739db95b98f 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPage_FlyoutLayoutBehaviorPopover.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPage_FlyoutLayoutBehaviorPopover.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPage_IsFlowDirectionRTL.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPage_IsFlowDirectionRTL.png
index c1818a10c2c1..796aa1dc1cc5 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPage_IsFlowDirectionRTL.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPage_IsFlowDirectionRTL.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPage_Title.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPage_Title.png
index d369d02e7939..8559597b5678 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPage_Title.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFlyoutPage_Title.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFontImageAreCenterAlign.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFontImageAreCenterAlign.png
new file mode 100644
index 000000000000..272e4bb3b372
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyFontImageAreCenterAlign.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGraphicsViewWithoutGrayLine.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGraphicsViewWithoutGrayLine.png
deleted file mode 100644
index b437b6199827..000000000000
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGraphicsViewWithoutGrayLine.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithCenterPositionAndHorizontalGrid_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithCenterPositionAndHorizontalGrid_Kiwi.png
new file mode 100644
index 000000000000..a538760423fb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithCenterPositionAndHorizontalGrid_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithCenterPositionAndHorizontalList_Tomato.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithCenterPositionAndHorizontalList_Tomato.png
new file mode 100644
index 000000000000..049a29bd7a96
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithCenterPositionAndHorizontalList_Tomato.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithCenterPositionAndVerticalGrid_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithCenterPositionAndVerticalGrid_Kiwi.png
new file mode 100644
index 000000000000..53d5a79e4446
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithCenterPositionAndVerticalGrid_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithCenterPositionAndVerticalList_Potato.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithCenterPositionAndVerticalList_Potato.png
new file mode 100644
index 000000000000..567f09a56910
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithCenterPositionAndVerticalList_Potato.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithEndPositionAndHorizontalGrid_Potato.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithEndPositionAndHorizontalGrid_Potato.png
new file mode 100644
index 000000000000..eb62a0ae0613
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithEndPositionAndHorizontalGrid_Potato.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithEndPositionAndHorizontalList_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithEndPositionAndHorizontalList_Kiwi.png
new file mode 100644
index 000000000000..4fc7aeec2537
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithEndPositionAndHorizontalList_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithEndPositionAndVerticalGrid_Apricot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithEndPositionAndVerticalGrid_Apricot.png
new file mode 100644
index 000000000000..c0de896aee06
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithEndPositionAndVerticalGrid_Apricot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithEndPositionAndVerticalList_Papaya.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithEndPositionAndVerticalList_Papaya.png
new file mode 100644
index 000000000000..956808e2727e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithEndPositionAndVerticalList_Papaya.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithMakeVisiblePositionAndHorizontalGrid_Pear.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithMakeVisiblePositionAndHorizontalGrid_Pear.png
new file mode 100644
index 000000000000..e2e77c4410a7
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithMakeVisiblePositionAndHorizontalGrid_Pear.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithMakeVisiblePositionAndHorizontalList_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithMakeVisiblePositionAndHorizontalList_Kiwi.png
new file mode 100644
index 000000000000..6c16b7954b78
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithMakeVisiblePositionAndHorizontalList_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithMakeVisiblePositionAndVerticalGrid_Apricot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithMakeVisiblePositionAndVerticalGrid_Apricot.png
new file mode 100644
index 000000000000..465ad9a1f8e4
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithMakeVisiblePositionAndVerticalGrid_Apricot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithMakeVisiblePositionAndVerticalList_Apricot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithMakeVisiblePositionAndVerticalList_Apricot.png
new file mode 100644
index 000000000000..805169485365
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithMakeVisiblePositionAndVerticalList_Apricot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithStartPositionAndHorizontalGrid_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithStartPositionAndHorizontalGrid_Kiwi.png
new file mode 100644
index 000000000000..68568c07ad35
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithStartPositionAndHorizontalGrid_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithStartPositionAndHorizontalList_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithStartPositionAndHorizontalList_Kiwi.png
new file mode 100644
index 000000000000..89005c96a173
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithStartPositionAndHorizontalList_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithStartPositionAndVerticalGrid_Carrot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithStartPositionAndVerticalGrid_Carrot.png
new file mode 100644
index 000000000000..c3d99fd3b331
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithStartPositionAndVerticalGrid_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithStartPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithStartPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..a04f2da55f7c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupIndexScrollToByIndexWithStartPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithCenterPositionAndHorizontalGrid_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithCenterPositionAndHorizontalGrid_Kiwi.png
new file mode 100644
index 000000000000..14b095be6ba8
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithCenterPositionAndHorizontalGrid_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithCenterPositionAndHorizontalList_Tomato.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithCenterPositionAndHorizontalList_Tomato.png
new file mode 100644
index 000000000000..e96aa4995181
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithCenterPositionAndHorizontalList_Tomato.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithCenterPositionAndVerticalGrid_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithCenterPositionAndVerticalGrid_Kiwi.png
new file mode 100644
index 000000000000..f187912bfec3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithCenterPositionAndVerticalGrid_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithEndPositionAndHorizontalGrid_Potato.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithEndPositionAndHorizontalGrid_Potato.png
new file mode 100644
index 000000000000..48ddd216c54a
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithEndPositionAndHorizontalGrid_Potato.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithEndPositionAndHorizontalList_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithEndPositionAndHorizontalList_Kiwi.png
new file mode 100644
index 000000000000..3b6661da7fbd
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithEndPositionAndHorizontalList_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithEndPositionAndVerticalGrid_Apricot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithEndPositionAndVerticalGrid_Apricot.png
new file mode 100644
index 000000000000..8ef51e9c9626
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithEndPositionAndVerticalGrid_Apricot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithMakeVisiblePositionAndHorizontalGrid_Pear.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithMakeVisiblePositionAndHorizontalGrid_Pear.png
new file mode 100644
index 000000000000..1e24e1193e79
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithMakeVisiblePositionAndHorizontalGrid_Pear.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithMakeVisiblePositionAndHorizontalList_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithMakeVisiblePositionAndHorizontalList_Kiwi.png
new file mode 100644
index 000000000000..c0399a55e186
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithMakeVisiblePositionAndHorizontalList_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithMakeVisiblePositionAndVerticalGrid_Apricot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithMakeVisiblePositionAndVerticalGrid_Apricot.png
new file mode 100644
index 000000000000..187872f8255b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithMakeVisiblePositionAndVerticalGrid_Apricot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithStartPositionAndHorizontalGrid_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithStartPositionAndHorizontalGrid_Kiwi.png
new file mode 100644
index 000000000000..dd5ff9ac73f1
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithStartPositionAndHorizontalGrid_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithStartPositionAndHorizontalList_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithStartPositionAndHorizontalList_Kiwi.png
new file mode 100644
index 000000000000..a1252e82465a
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithStartPositionAndHorizontalList_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithStartPositionAndVerticalGrid_Carrot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithStartPositionAndVerticalGrid_Carrot.png
new file mode 100644
index 000000000000..4919ba5f11a3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByIndexWithStartPositionAndVerticalGrid_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByItemWithCenterPositionAndVerticalList_Potato.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByItemWithCenterPositionAndVerticalList_Potato.png
new file mode 100644
index 000000000000..f99cc5fc7041
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByItemWithCenterPositionAndVerticalList_Potato.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByItemWithEndPositionAndVerticalList_Papaya.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByItemWithEndPositionAndVerticalList_Papaya.png
new file mode 100644
index 000000000000..9398b916dc64
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByItemWithEndPositionAndVerticalList_Papaya.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByItemWithMakeVisiblePositionAndVerticalList_Apricot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByItemWithMakeVisiblePositionAndVerticalList_Apricot.png
new file mode 100644
index 000000000000..b8436ef4bda7
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByItemWithMakeVisiblePositionAndVerticalList_Apricot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByItemWithStartPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByItemWithStartPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..17b5dde57645
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyGroupItemScrollToByItemWithStartPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyHamburgerIcon.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyHamburgerIcon.png
index 351b437320e4..9027c269c322 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyHamburgerIcon.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyHamburgerIcon.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyIndicatorViewMaximumVisibleWithTemplate.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyIndicatorViewMaximumVisibleWithTemplate.png
new file mode 100644
index 000000000000..86a5bd89488e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyIndicatorViewMaximumVisibleWithTemplate.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyLabelBackgroundIsClippedWithRectangleGeometry.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyLabelBackgroundIsClippedWithRectangleGeometry.png
new file mode 100644
index 000000000000..5389cb92444b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyLabelBackgroundIsClippedWithRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithGroupedList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithGroupedList.png
index 838aa7ee2e30..0ee3addc3cf6 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithGroupedList.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithGroupedList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithGroupedListWhenHorizontalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithGroupedListWhenHorizontalGrid.png
index d4772b23d130..4fde4c5f64fa 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithGroupedListWhenHorizontalGrid.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithGroupedListWhenHorizontalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithGroupedListWhenHorizontalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithGroupedListWhenHorizontalList.png
index e340a8ceef92..1f928c5e54dd 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithGroupedListWhenHorizontalList.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithGroupedListWhenHorizontalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithGroupedListWhenVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithGroupedListWhenVerticalGrid.png
index 5963eca5e227..01c71fa341f1 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithGroupedListWhenVerticalGrid.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithGroupedListWhenVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithObservableCollection.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithObservableCollection.png
index 24f5130518f7..462be354a8b1 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithObservableCollection.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithObservableCollection.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithObservableCollectionWhenHorizontalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithObservableCollectionWhenHorizontalGrid.png
index 0a4100ac82f3..0554dec813c6 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithObservableCollectionWhenHorizontalGrid.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithObservableCollectionWhenHorizontalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithObservableCollectionWhenHorizontalList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithObservableCollectionWhenHorizontalList.png
index ce49e9f31db0..7c70db5a322b 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithObservableCollectionWhenHorizontalList.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithObservableCollectionWhenHorizontalList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithObservableCollectionWhenVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithObservableCollectionWhenVerticalGrid.png
index 61f46d9c8bd6..d31c06c0cb1e 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithObservableCollectionWhenVerticalGrid.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureAllItemsWithObservableCollectionWhenVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureFirstItemsWithGroupedList.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureFirstItemsWithGroupedList.png
index d3ae9badce98..f4ce4e845a77 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureFirstItemsWithGroupedList.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureFirstItemsWithGroupedList.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureFirstItemsWithObservableCollection.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureFirstItemsWithObservableCollection.png
index 2f62d68a31cb..c55858b263f1 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureFirstItemsWithObservableCollection.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureFirstItemsWithObservableCollection.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureFirstItemsWithObservableCollectionWhenVerticalGrid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureFirstItemsWithObservableCollectionWhenVerticalGrid.png
index a0d1bc24e01f..60c038de49e3 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureFirstItemsWithObservableCollectionWhenVerticalGrid.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyMeasureFirstItemsWithObservableCollectionWhenVerticalGrid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyNavBarStatusAtInitialLoading.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyNavBarStatusAtInitialLoading.png
new file mode 100644
index 000000000000..5084ea7be48d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyNavBarStatusAtInitialLoading.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyNavBarStatusAtRuntime.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyNavBarStatusAtRuntime.png
new file mode 100644
index 000000000000..9d585751d9a8
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyNavBarStatusAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyRadioButtonTextWithLowerTransform.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyRadioButtonTextWithLowerTransform.png
new file mode 100644
index 000000000000..4685d06434a8
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyRadioButtonTextWithLowerTransform.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyRadioButtonTextWithUpperTransform.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyRadioButtonTextWithUpperTransform.png
new file mode 100644
index 000000000000..550cd8040f61
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyRadioButtonTextWithUpperTransform.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithCenterPositionAndHorizontalGrid_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithCenterPositionAndHorizontalGrid_Kiwi.png
new file mode 100644
index 000000000000..c066e5dfbb10
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithCenterPositionAndHorizontalGrid_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithCenterPositionAndHorizontalList_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithCenterPositionAndHorizontalList_Kiwi.png
new file mode 100644
index 000000000000..259105eb2e3a
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithCenterPositionAndHorizontalList_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithCenterPositionAndVerticalGrid_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithCenterPositionAndVerticalGrid_Kiwi.png
new file mode 100644
index 000000000000..0180910029a0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithCenterPositionAndVerticalGrid_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithCenterPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithCenterPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..4c05886fc945
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithCenterPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithEndPositionAndHorizontalGrid_Pear.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithEndPositionAndHorizontalGrid_Pear.png
new file mode 100644
index 000000000000..941ed85b48d7
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithEndPositionAndHorizontalGrid_Pear.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithEndPositionAndHorizontalList_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithEndPositionAndHorizontalList_Kiwi.png
new file mode 100644
index 000000000000..e9a960e1af65
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithEndPositionAndHorizontalList_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithEndPositionAndVerticalGrid_Radish.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithEndPositionAndVerticalGrid_Radish.png
new file mode 100644
index 000000000000..db2cd2797af0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithEndPositionAndVerticalGrid_Radish.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithEndPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithEndPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..6a73a95ef65b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithEndPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithMakeVisiblePositionAndHorizontalGrid_Pear.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithMakeVisiblePositionAndHorizontalGrid_Pear.png
new file mode 100644
index 000000000000..031e6e803789
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithMakeVisiblePositionAndHorizontalGrid_Pear.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithMakeVisiblePositionAndHorizontalList_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithMakeVisiblePositionAndHorizontalList_Kiwi.png
new file mode 100644
index 000000000000..533749f3c057
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithMakeVisiblePositionAndHorizontalList_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalGrid_Radish.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalGrid_Radish.png
new file mode 100644
index 000000000000..9d28c22553f1
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalGrid_Radish.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..d3d468ba4fb6
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithStartPositionAndHorizontalGrid_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithStartPositionAndHorizontalGrid_Kiwi.png
new file mode 100644
index 000000000000..567016c97efb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithStartPositionAndHorizontalGrid_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithStartPositionAndHorizontalList_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithStartPositionAndHorizontalList_Kiwi.png
new file mode 100644
index 000000000000..86f6ed3e98e1
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithStartPositionAndHorizontalList_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithStartPositionAndVerticalGrid_Mango.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithStartPositionAndVerticalGrid_Mango.png
new file mode 100644
index 000000000000..957c554a5f9d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithStartPositionAndVerticalGrid_Mango.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithStartPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithStartPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..0f661b4f123b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByIndexWithStartPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithCenterPositionAndHorizontalGrid_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithCenterPositionAndHorizontalGrid_Kiwi.png
new file mode 100644
index 000000000000..5a336d9d3d67
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithCenterPositionAndHorizontalGrid_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithCenterPositionAndHorizontalList_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithCenterPositionAndHorizontalList_Kiwi.png
new file mode 100644
index 000000000000..2b462d29f000
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithCenterPositionAndHorizontalList_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithCenterPositionAndVerticalGrid_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithCenterPositionAndVerticalGrid_Kiwi.png
new file mode 100644
index 000000000000..08c94f7cc1f5
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithCenterPositionAndVerticalGrid_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithCenterPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithCenterPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..57668b4bb73f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithCenterPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithEndPositionAndHorizontalGrid_Pear.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithEndPositionAndHorizontalGrid_Pear.png
new file mode 100644
index 000000000000..d72f00ce7d8f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithEndPositionAndHorizontalGrid_Pear.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithEndPositionAndHorizontalList_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithEndPositionAndHorizontalList_Kiwi.png
new file mode 100644
index 000000000000..bdebed75354d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithEndPositionAndHorizontalList_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithEndPositionAndVerticalGrid_Radish.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithEndPositionAndVerticalGrid_Radish.png
new file mode 100644
index 000000000000..d599c2fa3d6e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithEndPositionAndVerticalGrid_Radish.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithEndPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithEndPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..3e98ea332e13
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithEndPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithMakeVisiblePositionAndHorizontalGrid_Pear.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithMakeVisiblePositionAndHorizontalGrid_Pear.png
new file mode 100644
index 000000000000..2840605dc218
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithMakeVisiblePositionAndHorizontalGrid_Pear.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithMakeVisiblePositionAndHorizontalList_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithMakeVisiblePositionAndHorizontalList_Kiwi.png
new file mode 100644
index 000000000000..8c9ab3e0e582
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithMakeVisiblePositionAndHorizontalList_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithMakeVisiblePositionAndVerticalGrid_Radish.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithMakeVisiblePositionAndVerticalGrid_Radish.png
new file mode 100644
index 000000000000..1ddacd2fe150
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithMakeVisiblePositionAndVerticalGrid_Radish.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithMakeVisiblePositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithMakeVisiblePositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..ff2d85daf8bd
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithMakeVisiblePositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithStartPositionAndHorizontalGrid_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithStartPositionAndHorizontalGrid_Kiwi.png
new file mode 100644
index 000000000000..6cecb86a716c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithStartPositionAndHorizontalGrid_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithStartPositionAndHorizontalList_Kiwi.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithStartPositionAndHorizontalList_Kiwi.png
new file mode 100644
index 000000000000..cb3e1d21b650
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithStartPositionAndHorizontalList_Kiwi.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithStartPositionAndVerticalGrid_Mango.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithStartPositionAndVerticalGrid_Mango.png
new file mode 100644
index 000000000000..1e41bf2d1774
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithStartPositionAndVerticalGrid_Mango.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithStartPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithStartPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..47a3ea391824
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyScrollToByItemWithStartPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchBarBackground.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchBarBackground.png
new file mode 100644
index 000000000000..f67c72e9ad72
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchBarBackground.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchHandlerClearPlaceholderIconColor.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchHandlerClearPlaceholderIconColor.png
index bf7c7c482cca..d5c54b8b5705 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchHandlerClearPlaceholderIconColor.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchHandlerClearPlaceholderIconColor.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchHandlerItemsAreVisible.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchHandlerItemsAreVisible.png
index 6d201f48d598..8730b9a46a52 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchHandlerItemsAreVisible.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchHandlerItemsAreVisible.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchHandlerPlaceholderText.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchHandlerPlaceholderText.png
index 3065fce58707..f55a3ade7f10 100644
Binary files a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchHandlerPlaceholderText.png and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchHandlerPlaceholderText.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySliderColors.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySliderColors.png
new file mode 100644
index 000000000000..af84ad1c4029
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySliderColors.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySpanInheritsLabelCharacterSpacing.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySpanInheritsLabelCharacterSpacing.png
new file mode 100644
index 000000000000..4ae001e2341c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySpanInheritsLabelCharacterSpacing.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySwitchControlSize.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySwitchControlSize.png
new file mode 100644
index 000000000000..a2590e3fe88d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySwitchControlSize.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyTimePickerFormat.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyTimePickerFormat.png
new file mode 100644
index 000000000000..6aaa6a346a2a
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyTimePickerFormat.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyTimePickerIsNullOnInitialLoad.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyTimePickerIsNullOnInitialLoad.png
new file mode 100644
index 000000000000..3087e43c1f97
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyTimePickerIsNullOnInitialLoad.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Button_Disable.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Button_Disable.png
new file mode 100644
index 000000000000..92dd096a02f6
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Button_Disable.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Button_InitialState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Button_InitialState.png
new file mode 100644
index 000000000000..d516a22d9014
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Button_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Button_Reset.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Button_Reset.png
new file mode 100644
index 000000000000..0e899b770afa
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Button_Reset.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CheckBox_Checked.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CheckBox_Checked.png
new file mode 100644
index 000000000000..4ed9856f81ae
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CheckBox_Checked.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CheckBox_Disable.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CheckBox_Disable.png
new file mode 100644
index 000000000000..96f541a77840
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CheckBox_Disable.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CheckBox_DisableWhileChecked.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CheckBox_DisableWhileChecked.png
new file mode 100644
index 000000000000..fb1441a15e8b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CheckBox_DisableWhileChecked.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CheckBox_InitialState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CheckBox_InitialState.png
new file mode 100644
index 000000000000..f457f8d83ae7
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CheckBox_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CheckBox_Reset.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CheckBox_Reset.png
new file mode 100644
index 000000000000..030a1ba95771
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CheckBox_Reset.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_Disabled.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_Disabled.png
new file mode 100644
index 000000000000..0ccfe63fad38
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_Disabled.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_InitialState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_InitialState.png
new file mode 100644
index 000000000000..8b69f1b65c40
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_Normal.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_Normal.png
new file mode 100644
index 000000000000..4525a44ab153
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_Normal.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_Reset.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_Reset.png
new file mode 100644
index 000000000000..dfc9dea0f6d1
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_Reset.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_Selected.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_Selected.png
new file mode 100644
index 000000000000..fb8085021e7a
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_Selected.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_Selected_Multiple.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_Selected_Multiple.png
new file mode 100644
index 000000000000..84a5a4ee9523
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_CollectionView_Selected_Multiple.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Disable.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Disable.png
new file mode 100644
index 000000000000..d67ccc6381d8
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Disable.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_DisableAndEnableWhileFocused.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_DisableAndEnableWhileFocused.png
new file mode 100644
index 000000000000..b9b8a918cb37
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_DisableAndEnableWhileFocused.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_DisableAndEnableWhileUnFocused.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_DisableAndEnableWhileUnFocused.png
new file mode 100644
index 000000000000..a2f25b34e0d3
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_DisableAndEnableWhileUnFocused.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_DisableWhileFocused.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_DisableWhileFocused.png
new file mode 100644
index 000000000000..aa569b44eab7
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_DisableWhileFocused.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_DisableWhileReset.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_DisableWhileReset.png
new file mode 100644
index 000000000000..5a8089345abe
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_DisableWhileReset.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_DisableWhileUnFocused.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_DisableWhileUnFocused.png
new file mode 100644
index 000000000000..5b02627aa99c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_DisableWhileUnFocused.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Focus.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Focus.png
new file mode 100644
index 000000000000..3fc4b9d77274
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Focus.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_InitialState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_InitialState.png
new file mode 100644
index 000000000000..00c818b357ad
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Invalid.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Invalid.png
new file mode 100644
index 000000000000..ba725d6d7bf9
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Invalid.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Reset.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Reset.png
new file mode 100644
index 000000000000..11244ad0a284
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Reset.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Unfocus.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Unfocus.png
new file mode 100644
index 000000000000..0dd63c827922
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Unfocus.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Validate.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Validate.png
new file mode 100644
index 000000000000..2541c41a2c4e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Entry_Validate.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_DisableWhileNormal.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_DisableWhileNormal.png
new file mode 100644
index 000000000000..575a23c5ae62
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_DisableWhileNormal.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_DisableWhileSelected.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_DisableWhileSelected.png
new file mode 100644
index 000000000000..c9ba5dac036c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_DisableWhileSelected.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_Disabled.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_Disabled.png
new file mode 100644
index 000000000000..1990cdf4079b
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_Disabled.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_InitialState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_InitialState.png
new file mode 100644
index 000000000000..e5488865bd57
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_Reset.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_Reset.png
new file mode 100644
index 000000000000..3cfd850b5634
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_Reset.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_Selected.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_Selected.png
new file mode 100644
index 000000000000..04063b9454b4
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Label_Selected.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Slider_DisabledState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Slider_DisabledState.png
new file mode 100644
index 000000000000..f0a0e14010e1
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Slider_DisabledState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Slider_FocusedState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Slider_FocusedState.png
new file mode 100644
index 000000000000..0a08e3110519
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Slider_FocusedState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Slider_NormalOrUnfocusedState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Slider_NormalOrUnfocusedState.png
new file mode 100644
index 000000000000..97925f20a529
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Slider_NormalOrUnfocusedState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Slider_NormalState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Slider_NormalState.png
new file mode 100644
index 000000000000..49790fd7d8eb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Slider_NormalState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Slider_ResetState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Slider_ResetState.png
new file mode 100644
index 000000000000..88c43bc55ea7
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Slider_ResetState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_DisableAndResetWhileOff.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_DisableAndResetWhileOff.png
new file mode 100644
index 000000000000..8b4cb39d039e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_DisableAndResetWhileOff.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_DisableAndResetWhileOn.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_DisableAndResetWhileOn.png
new file mode 100644
index 000000000000..134f54c85a93
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_DisableAndResetWhileOn.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_DisableWhileOff.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_DisableWhileOff.png
new file mode 100644
index 000000000000..bb39543c0d2a
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_DisableWhileOff.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_DisableWhileOn.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_DisableWhileOn.png
new file mode 100644
index 000000000000..1b4c582b81fb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_DisableWhileOn.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_InitialState.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_InitialState.png
new file mode 100644
index 000000000000..72437091c149
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_Off.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_Off.png
new file mode 100644
index 000000000000..4bd33a723a84
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_Off.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_OffWhileDisableAndEnable.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_OffWhileDisableAndEnable.png
new file mode 100644
index 000000000000..0f0d5d6bf3b6
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_OffWhileDisableAndEnable.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_On.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_On.png
new file mode 100644
index 000000000000..c87727b2e838
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_On.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_OnWhileDisableAndEnable.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_OnWhileDisableAndEnable.png
new file mode 100644
index 000000000000..ac2994581e79
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_OnWhileDisableAndEnable.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_Reset.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_Reset.png
new file mode 100644
index 000000000000..3e621e3074b0
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_Reset.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_ResetWhileDisabled.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_ResetWhileDisabled.png
new file mode 100644
index 000000000000..5544f11e661d
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyVSM_Switch_ResetWhileDisabled.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_AnchorXWithAnchorY.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_AnchorXWithAnchorY.png
new file mode 100644
index 000000000000..e519fcae893c
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_AnchorXWithAnchorY.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_AnchorX_ScaleYWithRotation.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_AnchorX_ScaleYWithRotation.png
new file mode 100644
index 000000000000..f4e5ff84430e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_AnchorX_ScaleYWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_AnchorY_ScaleWithRotationY.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_AnchorY_ScaleWithRotationY.png
new file mode 100644
index 000000000000..0826a9d128df
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_AnchorY_ScaleWithRotationY.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_AnchorY_ScaleXWithRotationX.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_AnchorY_ScaleXWithRotationX.png
new file mode 100644
index 000000000000..6b7158f4dc9a
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_AnchorY_ScaleXWithRotationX.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_IsVisible.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_IsVisible.png
new file mode 100644
index 000000000000..57b7678bb108
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_IsVisible.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_Rotation.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_Rotation.png
new file mode 100644
index 000000000000..6d32086162cd
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_Rotation.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationWithRotationX.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationWithRotationX.png
new file mode 100644
index 000000000000..a775c7d53700
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationWithRotationX.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationWithScale.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationWithScale.png
new file mode 100644
index 000000000000..14fc64d1f03e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationWithScale.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationX.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationX.png
new file mode 100644
index 000000000000..b0df27b9cd3f
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationX.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationXWithRotationY.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationXWithRotationY.png
new file mode 100644
index 000000000000..847dc5bc5b29
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationXWithRotationY.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationXWithScaleX.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationXWithScaleX.png
new file mode 100644
index 000000000000..c5aec058d13e
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationXWithScaleX.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationY.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationY.png
new file mode 100644
index 000000000000..0d69216763fb
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationY.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationYWithScaleY.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationYWithScaleY.png
new file mode 100644
index 000000000000..9363e5670342
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_RotationYWithScaleY.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_Scale.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_Scale.png
new file mode 100644
index 000000000000..26fe2ba11998
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_Scale.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_ScaleWithAnchorXAndRotationY.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_ScaleWithAnchorXAndRotationY.png
new file mode 100644
index 000000000000..bae42fcf15c9
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_ScaleWithAnchorXAndRotationY.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_ScaleX.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_ScaleX.png
new file mode 100644
index 000000000000..ec3d06f5ca12
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_ScaleX.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_ScaleXWithAnchorYAndRotationY.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_ScaleXWithAnchorYAndRotationY.png
new file mode 100644
index 000000000000..8e49240f49a9
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_ScaleXWithAnchorYAndRotationY.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_ScaleY.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_ScaleY.png
new file mode 100644
index 000000000000..6a89d6fcd71a
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_ScaleY.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_ScaleYWithAnchorXAndRotationY.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_ScaleYWithAnchorXAndRotationY.png
new file mode 100644
index 000000000000..ad07a2087ab1
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VisualTransform_ScaleYWithAnchorXAndRotationY.png differ
diff --git a/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj b/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj
index 10150dce2aac..e8c5fa01e4b3 100644
--- a/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj
+++ b/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj
@@ -71,7 +71,9 @@
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/CoreViews/CorePageView.cs b/src/Controls/tests/TestCases.HostApp/CoreViews/CorePageView.cs
index 1d967735cf43..3a459b5e8de0 100644
--- a/src/Controls/tests/TestCases.HostApp/CoreViews/CorePageView.cs
+++ b/src/Controls/tests/TestCases.HostApp/CoreViews/CorePageView.cs
@@ -80,6 +80,7 @@ public override string ToString()
new GalleryPageFactory(() => new ScrollViewCoreGalleryPage(), "ScrollView Gallery"),
new GalleryPageFactory(() => new ShadowFeaturePage(), "Shadow Feature Matrix"),
new GalleryPageFactory(() => new SearchBarControlPage(), "Search Bar Feature Matrix"),
+ new GalleryPageFactory(() => new Material3_SearchBarControlPage(), "Search Bar Material3 Feature Matrix"),
new GalleryPageFactory(() => new SearchBarCoreGalleryPage(), "Search Bar Gallery"),
new GalleryPageFactory(() => new SliderCoreGalleryPage(), "Slider Gallery"),
new GalleryPageFactory(() => new StepperControlPage(), "Stepper Feature Matrix"),
@@ -94,10 +95,12 @@ public override string ToString()
new GalleryPageFactory(() => new SliderControlPage(), "Slider Feature Matrix"),
new GalleryPageFactory(() => new NavigationPageControlPage(), "NavigationPage Feature Matrix"),
new GalleryPageFactory(() => new CheckBoxControlPage(), "CheckBox Feature Matrix"),
+ new GalleryPageFactory(() => new ClipControlPage(), "Clip Feature Matrix"),
new GalleryPageFactory(() => new CollectionViewFeaturePage(), "CollectionView Feature Matrix"),
new GalleryPageFactory(() => new LabelControlPage(), "Label Feature Matrix"),
new GalleryPageFactory(() => new CarouselViewFeaturePage(), "CarouselView Feature Matrix"),
new GalleryPageFactory(() => new EntryControlPage(), "Entry Feature Matrix"),
+ new GalleryPageFactory(() => new Material3_EntryControlPage(), "Entry Material3 Feature Matrix"),
new GalleryPageFactory(() => new ImageControlPage(), "Image Feature Matrix"),
new GalleryPageFactory(() => new ImageButtonControlPage(), "ImageButton Feature Matrix"),
new GalleryPageFactory(() => new BoxViewControlPage(), "BoxView Feature Matrix"),
@@ -120,9 +123,13 @@ public override string ToString()
new GalleryPageFactory(() => new IndicatorViewControlPage(), "IndicatorView Feature Matrix"),
new GalleryPageFactory(() => new GridControlPage(), "Grid Feature Matrix"),
new GalleryPageFactory(() => new LayoutFeaturePage(), "ScrollView With LayoutOptions Feature Matrix"),
+ new GalleryPageFactory(() => new TriggersControlPage(), "Triggers Feature Matrix"),
+ new GalleryPageFactory(() => new MapControlPage(), "Map Feature Matrix"),
+ new GalleryPageFactory(() => new VisualStateManagerFeaturePage(), "VisualStateManager Feature Matrix"),
new GalleryPageFactory(() => new ShellFeaturePage(), "Shell Feature Matrix"),
new GalleryPageFactory(() => new BrushesControlPage(), "Brushes Feature Matrix"),
- new GalleryPageFactory(() => new BindableLayoutControlPage(), "BindableLayout Feature Matrix")
+ new GalleryPageFactory(() => new BindableLayoutControlPage(), "BindableLayout Feature Matrix"),
+ new GalleryPageFactory(() => new VisualTransformControlPage(), "VisualTransform Feature Matrix"),
};
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderControlsPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderControlsPage.xaml
index b8e8dbe67087..77047047db6a 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderControlsPage.xaml
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderControlsPage.xaml
@@ -4,7 +4,7 @@
xmlns:local="clr-namespace:Maui.Controls.Sample"
x:DataType="local:BorderViewModel"
x:Class="Maui.Controls.Sample.BorderControlMainPage"
- Title="ControlsPage"
+ Title="BorderControlsPage"
SafeAreaEdges="Container">
-
\ No newline at end of file
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderControlsPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderControlsPage.xaml.cs
index 787c7b56d686..2c3f4b34f056 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderControlsPage.xaml.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderControlsPage.xaml.cs
@@ -2,12 +2,10 @@ namespace Maui.Controls.Sample;
public class BorderControlPage : NavigationPage
{
- private BorderViewModel _viewModel;
-
public BorderControlPage()
{
- _viewModel = new BorderViewModel();
- PushAsync(new BorderControlMainPage(_viewModel));
+ var viewModel = new BorderViewModel();
+ PushAsync(new BorderControlMainPage(viewModel));
}
}
@@ -24,7 +22,7 @@ public BorderControlMainPage(BorderViewModel viewModel)
private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
{
- BindingContext = _viewModel = new BorderViewModel();
+ _viewModel.Reset();
await Navigation.PushAsync(new OptionsPage(_viewModel));
}
}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderOptionsPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderOptionsPage.xaml
index 57257fda674f..cea2167e9e6f 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderOptionsPage.xaml
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderOptionsPage.xaml
@@ -2,7 +2,7 @@
+ Title="BorderOptionsPage">
+
+
+
+
+
-
+
+
+
+
-
-
-
+
+
+
+
-
-
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -245,17 +289,22 @@
-
-
+
+
+
+
@@ -265,23 +314,46 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderOptionsPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderOptionsPage.xaml.cs
index 0fe3c6621ea0..78b939e56343 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderOptionsPage.xaml.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderOptionsPage.xaml.cs
@@ -21,20 +21,19 @@ private void ApplyButton_Clicked(object sender, EventArgs e)
}
private void OnPaddingChanged(object sender, TextChangedEventArgs e)
{
-
string[] parts = PaddingEntry.Text.Split(',');
if (parts.Length == 4 &&
- double.TryParse(parts[0], out double left) &&
- double.TryParse(parts[1], out double top) &&
- double.TryParse(parts[2], out double right) &&
- double.TryParse(parts[3], out double bottom))
+ double.TryParse(parts[0], NumberStyles.Any, CultureInfo.InvariantCulture, out double left) &&
+ double.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out double top) &&
+ double.TryParse(parts[2], NumberStyles.Any, CultureInfo.InvariantCulture, out double right) &&
+ double.TryParse(parts[3], NumberStyles.Any, CultureInfo.InvariantCulture, out double bottom))
{
_viewModel.Padding = new Thickness(left, top, right, bottom);
}
}
private void OnStrokeThicknessChanged(object sender, TextChangedEventArgs e)
{
- if (double.TryParse(StrokeThicknessEntry.Text, out double strokeThickness))
+ if (double.TryParse(StrokeThicknessEntry.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out double strokeThickness))
{
_viewModel.StrokeThickness = strokeThickness;
}
@@ -115,7 +114,7 @@ private void OnMiterLimitChanged(object sender, TextChangedEventArgs e)
if (_viewModel == null)
return;
- if (double.TryParse(MiterLimitEntry.Text, out var miterLimit))
+ if (double.TryParse(MiterLimitEntry.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out var miterLimit))
{
_viewModel.StrokeMiterLimit = miterLimit;
}
@@ -139,18 +138,20 @@ private void OnLineJoinChanged(object sender, CheckedChangedEventArgs e)
}
}
}
+ private Color _shadowColor = Colors.Black;
+
private void OnShadowEntryChanged(object sender, TextChangedEventArgs e)
{
if (BindingContext is BorderViewModel viewModel)
{
try
{
- double offsetX = double.Parse(OffsetXEntry.Text);
- double offsetY = double.Parse(OffsetYEntry.Text);
- double radius = double.Parse(RadiusEntry.Text);
- float opacity = float.Parse(OpacityEntry.Text, System.Globalization.CultureInfo.InvariantCulture);
+ double offsetX = double.Parse(OffsetXEntry.Text, CultureInfo.InvariantCulture);
+ double offsetY = double.Parse(OffsetYEntry.Text, CultureInfo.InvariantCulture);
+ double radius = double.Parse(RadiusEntry.Text, CultureInfo.InvariantCulture);
+ float opacity = float.Parse(OpacityEntry.Text, CultureInfo.InvariantCulture);
- viewModel.UpdateShadow(offsetX, offsetY, radius, opacity);
+ viewModel.UpdateShadow(offsetX, offsetY, radius, opacity, _shadowColor);
}
catch
{
@@ -158,6 +159,28 @@ private void OnShadowEntryChanged(object sender, TextChangedEventArgs e)
}
}
}
+
+ private void OnShadowColorClicked(object sender, EventArgs e)
+ {
+ if (sender is Button button && button.BackgroundColor != Colors.Transparent)
+ {
+ _shadowColor = button.BackgroundColor;
+ OnShadowEntryChanged(sender, new TextChangedEventArgs(string.Empty, string.Empty));
+ }
+ }
+
+ private void OnBackgroundColorClicked(object sender, EventArgs e)
+ {
+ if (sender is Button button && button.BackgroundColor != Colors.Transparent)
+ {
+ _viewModel.Background = new SolidColorBrush(button.BackgroundColor);
+ }
+ }
+
+ private void OnGradientStrokeClicked(object sender, EventArgs e)
+ {
+ _viewModel.SetGradientStroke();
+ }
private void OnContentOptionChanged(object sender, CheckedChangedEventArgs e)
{
if (sender is RadioButton rb && rb.IsChecked)
@@ -176,4 +199,4 @@ private void OnContentOptionChanged(object sender, CheckedChangedEventArgs e)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderViewModel.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderViewModel.cs
index 31d4facf2df1..3b383d062781 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderViewModel.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Border/BorderViewModel.cs
@@ -1,5 +1,4 @@
using System.ComponentModel;
-using System.Globalization;
using System.Runtime.CompilerServices;
using Microsoft.Maui.Controls.Shapes;
@@ -26,6 +25,22 @@ public BorderViewModel()
SetLabelContent();
}
+ public void Reset()
+ {
+ Padding = 5;
+ StrokeThickness = 1;
+ StrokeDashArray = null;
+ StrokeDashOffset = 0;
+ StrokeLineJoin = PenLineJoin.Miter;
+ StrokeMiterLimit = 10.0;
+ StrokeShape = new Microsoft.Maui.Controls.Shapes.Rectangle();
+ StrokeLineCap = PenLineCap.Flat;
+ Stroke = Brush.Red;
+ Background = new SolidColorBrush(Colors.LightGray);
+ Shadow = null;
+ SetLabelContent();
+ }
+
public void SetLabelContent()
{
ContentView = new Label
@@ -50,7 +65,7 @@ public void SetImageContent()
{
ContentView = new Image
{
- Source = "dotnet_bot.png", // Make sure this image is added to your resources
+ Source = "dotnet_bot.png",
WidthRequest = 100,
HeightRequest = 100,
HorizontalOptions = LayoutOptions.Center,
@@ -160,13 +175,13 @@ public IShape CreatePathShape()
public IShape CreatePolygonShape() => new Polygon
{
Points = new PointCollection
- {
- new Point(150, 0),
- new Point(250, 70),
- new Point(210, 180),
- new Point(85, 180),
- new Point(40, 70)
- }
+ {
+ new Point(150, 0),
+ new Point(250, 70),
+ new Point(210, 180),
+ new Point(85, 180),
+ new Point(40, 70)
+ }
};
private PenLineCap _strokeLineCap = PenLineCap.Flat;
@@ -190,6 +205,27 @@ public Brush Stroke
set { _stroke = value; OnPropertyChanged(nameof(Stroke)); }
}
+ public void SetGradientStroke()
+ {
+ Stroke = new LinearGradientBrush
+ {
+ StartPoint = new Point(0, 0),
+ EndPoint = new Point(1, 1),
+ GradientStops = new GradientStopCollection
+ {
+ new GradientStop(Colors.Red, 0.0f),
+ new GradientStop(Colors.Blue, 1.0f)
+ }
+ };
+ }
+
+ private Brush _background = new SolidColorBrush(Colors.LightGray);
+ public Brush Background
+ {
+ get => _background;
+ set { _background = value; OnPropertyChanged(nameof(Background)); }
+ }
+
private Shadow _shadow = null;
public Shadow Shadow
{
@@ -205,10 +241,15 @@ public Shadow Shadow
}
public void UpdateShadow(double offsetX, double offsetY, double radius, float opacity)
+ {
+ UpdateShadow(offsetX, offsetY, radius, opacity, Colors.Black);
+ }
+
+ public void UpdateShadow(double offsetX, double offsetY, double radius, float opacity, Color brushColor)
{
Shadow = new Shadow
{
- Brush = new SolidColorBrush(Colors.Black),
+ Brush = new SolidColorBrush(brushColor),
Offset = new Point(offsetX, offsetY),
Radius = (float)radius,
Opacity = opacity
@@ -220,4 +261,4 @@ protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
-}
\ No newline at end of file
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/BoxView/BoxViewControlPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/BoxView/BoxViewControlPage.xaml
index 7bd084b2ae43..db8540b13e1e 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/BoxView/BoxViewControlPage.xaml
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/BoxView/BoxViewControlPage.xaml
@@ -14,6 +14,7 @@
@@ -59,6 +60,24 @@
IsCursorVisible="False"
Text="{Binding OpacityEntryText, Mode=TwoWay}"
TextChanged="OnOpacityChanged"/>
+
+
+
+
+
+
@@ -111,14 +136,13 @@
+ IsChecked="{Binding IsRTL, Mode=TwoWay}"/>
+ VerticalOptions="Center"/>
-
\ No newline at end of file
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/BoxView/BoxViewControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/BoxView/BoxViewControlPage.xaml.cs
index 6c7bba314e47..dabd0bfe39e6 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/BoxView/BoxViewControlPage.xaml.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/BoxView/BoxViewControlPage.xaml.cs
@@ -1,4 +1,5 @@
using System;
+using System.Globalization;
using Microsoft.Maui.Controls;
namespace Maui.Controls.Sample
@@ -25,68 +26,69 @@ public BoxViewControlMainPage(BoxViewViewModel viewModel)
BindingContext = _viewModel;
}
- private void OnFlowDirectionCheckBoxChanged(object sender, CheckedChangedEventArgs e)
+ private void OnCornerRadiusEntryChanged(object sender, TextChangedEventArgs e)
{
- if (e.Value)
+ if (string.IsNullOrWhiteSpace(e.NewTextValue))
+ return;
+
+ var parts = e.NewTextValue.Split(',');
+
+ if (parts.Length == 1)
{
- _viewModel.FlowDirection = FlowDirection.RightToLeft;
+ if (double.TryParse(parts[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double uniform))
+ _viewModel.CornerRadius = new CornerRadius(uniform);
}
- else
+ else if (parts.Length == 4)
{
- _viewModel.FlowDirection = FlowDirection.LeftToRight;
+ if (double.TryParse(parts[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double topLeft) &&
+ double.TryParse(parts[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double topRight) &&
+ double.TryParse(parts[2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double bottomLeft) &&
+ double.TryParse(parts[3].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double bottomRight))
+ {
+ _viewModel.CornerRadius = new CornerRadius(topLeft, topRight, bottomLeft, bottomRight);
+ }
}
}
- private void OnColorRadioButtonChanged(object sender, EventArgs e)
+ private void OnResetChangesClicked(object sender, EventArgs e)
{
- if (sender is RadioButton radioButton && radioButton.IsChecked)
- {
- switch (radioButton.Value.ToString())
- {
- case "Red":
- _viewModel.Color = Colors.Red;
- break;
- case "Blue":
- _viewModel.Color = Colors.Blue;
- break;
- default:
- _viewModel.Color = Colors.Transparent;
- break;
- }
- }
+ _viewModel.Reset();
}
- private void OnCornerRadiusEntryChanged(object sender, TextChangedEventArgs e)
+ private void OnOpacityChanged(object sender, TextChangedEventArgs e)
{
if (string.IsNullOrWhiteSpace(e.NewTextValue))
+ {
+ _viewModel.Opacity = 1.0;
return;
+ }
- var parts = e.NewTextValue.Split(',');
-
- if (parts.Length != 4)
- return;
+ if (double.TryParse(e.NewTextValue, NumberStyles.Float, CultureInfo.InvariantCulture, out double value) && value >= 0 && value <= 1)
+ _viewModel.Opacity = value;
+ }
- if (float.TryParse(parts[0].Trim(), out float topLeft) &&
- float.TryParse(parts[1].Trim(), out float topRight) &&
- float.TryParse(parts[2].Trim(), out float bottomLeft) &&
- float.TryParse(parts[3].Trim(), out float bottomRight))
+ private void OnWidthChanged(object sender, TextChangedEventArgs e)
+ {
+ if (string.IsNullOrWhiteSpace(e.NewTextValue))
{
- _viewModel.CornerRadius = new CornerRadius(topLeft, topRight, bottomLeft, bottomRight);
+ _viewModel.Width = 200;
+ return;
}
- }
- private void OnResetChangesClicked(object sender, EventArgs e)
- {
- BindingContext = _viewModel = new BoxViewViewModel();
+ if (double.TryParse(e.NewTextValue, NumberStyles.Float, CultureInfo.InvariantCulture, out double value) && value >= 0)
+ _viewModel.Width = value;
}
- private void OnOpacityChanged(object sender, TextChangedEventArgs e)
+ private void OnHeightChanged(object sender, TextChangedEventArgs e)
{
- if (double.TryParse(e.NewTextValue, out double value))
+ if (string.IsNullOrWhiteSpace(e.NewTextValue))
{
- if (value >= 0 && value <= 1)
- _viewModel.Opacity = value;
+ _viewModel.Height = 100;
+ return;
}
+
+ if (double.TryParse(e.NewTextValue, NumberStyles.Float, CultureInfo.InvariantCulture, out double value) && value >= 0)
+ _viewModel.Height = value;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/BoxView/BoxViewViewModel.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/BoxView/BoxViewViewModel.cs
index 8b2b0c5dfdf5..fdff85288d4d 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/BoxView/BoxViewViewModel.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/BoxView/BoxViewViewModel.cs
@@ -14,10 +14,34 @@ public class BoxViewViewModel : INotifyPropertyChanged
private bool _isRedChecked = false;
private bool _isBlueChecked = true;
private FlowDirection _flowDirection = FlowDirection.LeftToRight;
+ private bool _isRTL = false;
- private string _cornerRadiusEntryText = null;
- private string _opacityEntryText = null;
+ private bool _isGreenChecked = false;
+
+ private string _cornerRadiusEntryText = string.Empty;
+ private string _opacityEntryText = "1";
+ private string _widthEntryText = "200";
+ private string _heightEntryText = "100";
+
+ public void Reset()
+ {
+ Color = Colors.Blue;
+ Width = 200;
+ Height = 100;
+ IsVisible = true;
+ Opacity = 1.0;
+ CornerRadius = default;
+ IsRedChecked = false;
+ IsBlueChecked = true;
+ IsGreenChecked = false;
+ IsRTL = false;
+ HasShadow = false;
+ CornerRadiusEntryText = string.Empty;
+ OpacityEntryText = "1";
+ WidthEntryText = "200";
+ HeightEntryText = "100";
+ }
public string CornerRadiusEntryText
{
@@ -27,8 +51,6 @@ public string CornerRadiusEntryText
if (_cornerRadiusEntryText != value)
{
_cornerRadiusEntryText = value;
- if (double.TryParse(value, out double radius))
- CornerRadius = radius;
OnPropertyChanged();
}
}
@@ -42,8 +64,32 @@ public string OpacityEntryText
if (_opacityEntryText != value)
{
_opacityEntryText = value;
- if (double.TryParse(value, out double opacity))
- Opacity = opacity;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public string WidthEntryText
+ {
+ get => _widthEntryText;
+ set
+ {
+ if (_widthEntryText != value)
+ {
+ _widthEntryText = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public string HeightEntryText
+ {
+ get => _heightEntryText;
+ set
+ {
+ if (_heightEntryText != value)
+ {
+ _heightEntryText = value;
OnPropertyChanged();
}
}
@@ -58,7 +104,7 @@ public bool IsRedChecked
{
_isRedChecked = value;
if (value)
- Color = Colors.Red;
+ SelectColor(Colors.Red, ref _isBlueChecked, nameof(IsBlueChecked), ref _isGreenChecked, nameof(IsGreenChecked));
OnPropertyChanged();
}
}
@@ -73,11 +119,35 @@ public bool IsBlueChecked
{
_isBlueChecked = value;
if (value)
- Color = Colors.Blue;
+ SelectColor(Colors.Blue, ref _isRedChecked, nameof(IsRedChecked), ref _isGreenChecked, nameof(IsGreenChecked));
OnPropertyChanged();
}
}
}
+
+ public bool IsGreenChecked
+ {
+ get => _isGreenChecked;
+ set
+ {
+ if (_isGreenChecked != value)
+ {
+ _isGreenChecked = value;
+ if (value)
+ SelectColor(Colors.Green, ref _isRedChecked, nameof(IsRedChecked), ref _isBlueChecked, nameof(IsBlueChecked));
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private void SelectColor(Color color, ref bool other1, string other1Name, ref bool other2, string other2Name)
+ {
+ Color = color;
+ other1 = false;
+ OnPropertyChanged(other1Name);
+ other2 = false;
+ OnPropertyChanged(other2Name);
+ }
public double Opacity
{
get => _opacity;
@@ -86,7 +156,7 @@ public double Opacity
if (_opacity != value)
{
_opacity = value;
- OnPropertyChanged(nameof(Opacity));
+ OnPropertyChanged();
}
}
}
@@ -98,7 +168,7 @@ public CornerRadius CornerRadius
if (_cornerRadius != value)
{
_cornerRadius = value;
- OnPropertyChanged(nameof(CornerRadius));
+ OnPropertyChanged();
}
}
}
@@ -107,8 +177,11 @@ public bool IsVisible
get => _isVisible;
set
{
- _isVisible = value;
- OnPropertyChanged();
+ if (_isVisible != value)
+ {
+ _isVisible = value;
+ OnPropertyChanged();
+ }
}
}
private bool _hasShadow = false;
@@ -131,7 +204,7 @@ public bool HasShadow
Offset = new Point(5, 5)
}
: null;
- OnPropertyChanged(nameof(HasShadow));
+ OnPropertyChanged();
}
}
}
@@ -144,7 +217,7 @@ private set
if (_boxShadow != value)
{
_boxShadow = value;
- OnPropertyChanged(nameof(BoxShadow));
+ OnPropertyChanged();
}
}
}
@@ -152,20 +225,56 @@ private set
public Color Color
{
get => _color;
- set { _color = value; OnPropertyChanged(); }
+ set
+ {
+ if (_color != value)
+ {
+ _color = value;
+ OnPropertyChanged();
+ }
+ }
}
public double Width
{
get => _width;
- set { _width = value; OnPropertyChanged(); }
+ set
+ {
+ if (_width != value)
+ {
+ _width = value;
+ OnPropertyChanged();
+ }
+ }
}
public double Height
{
get => _height;
- set { _height = value; OnPropertyChanged(); }
+ set
+ {
+ if (_height != value)
+ {
+ _height = value;
+ OnPropertyChanged();
+ }
+ }
}
+
+ public bool IsRTL
+ {
+ get => _isRTL;
+ set
+ {
+ if (_isRTL != value)
+ {
+ _isRTL = value;
+ FlowDirection = value ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
+ OnPropertyChanged();
+ }
+ }
+ }
+
public FlowDirection FlowDirection
{
get => _flowDirection;
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CheckBox/CheckBoxControlPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CheckBox/CheckBoxControlPage.xaml
index 06d20ecc0e5f..ebcdb6c76888 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CheckBox/CheckBoxControlPage.xaml
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CheckBox/CheckBoxControlPage.xaml
@@ -3,152 +3,176 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Maui.Controls.Sample"
x:Class="Maui.Controls.Sample.CheckBoxControlPage"
- x:DataType="local:CheckBoxFeatureMatrixViewModel"
+ x:DataType="local:CheckBoxViewModel"
Title="CheckBoxFeature">
-
-
+
+
-
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
+
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CheckBox/CheckBoxControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CheckBox/CheckBoxControlPage.xaml.cs
index 870df43e85cf..86b597d6679e 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CheckBox/CheckBoxControlPage.xaml.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CheckBox/CheckBoxControlPage.xaml.cs
@@ -1,56 +1,27 @@
using System;
-using System.Windows.Input;
using Microsoft.Maui.Controls;
namespace Maui.Controls.Sample;
public partial class CheckBoxControlPage : ContentPage
{
- private CheckBoxFeatureMatrixViewModel _viewModel;
+ private CheckBoxViewModel _viewModel;
public CheckBoxControlPage()
{
InitializeComponent();
- _viewModel = new CheckBoxFeatureMatrixViewModel();
- _viewModel.SetColorCommand = new Command(OnSetColor);
+ _viewModel = new CheckBoxViewModel();
BindingContext = _viewModel;
}
- private void OnSetColor(string colorName)
+ private void ResetButton_Clicked(object sender, EventArgs e)
{
- switch (colorName)
- {
- case "Blue":
- _viewModel.Color = Colors.Blue;
- break;
- case "Green":
- _viewModel.Color = Colors.Green;
- break;
- case "Default":
- default:
- _viewModel.Color = null;
- break;
- }
- }
-
- private void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
- {
- _viewModel = new CheckBoxFeatureMatrixViewModel();
- _viewModel.SetColorCommand = new Command(OnSetColor);
- BindingContext = _viewModel;
+ _viewModel.Reset();
}
private void OnCheckBoxCheckedChanged(object sender, CheckedChangedEventArgs e)
{
_viewModel.CheckedChangedCommand.Execute(null);
}
-
-}
-
-
-// Extension of the CheckBoxFeatureMatrixViewModel to add commands for the options page
-public partial class CheckBoxFeatureMatrixViewModel
-{
- public ICommand SetColorCommand { get; set; }
}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CheckBox/CheckBoxViewModel.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CheckBox/CheckBoxViewModel.cs
index 46838a71bb7b..22010a7a63dc 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CheckBox/CheckBoxViewModel.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CheckBox/CheckBoxViewModel.cs
@@ -6,12 +6,14 @@
namespace Maui.Controls.Sample;
-public partial class CheckBoxFeatureMatrixViewModel : INotifyPropertyChanged
+public class CheckBoxViewModel : INotifyPropertyChanged
{
private bool _isChecked = true;
private Color _color = null;
private bool _isEnabled = true;
private bool _isVisible = true;
+ private bool _hasShadow = false;
+ private Shadow _shadow = null;
private string _checkedChangedStatus = string.Empty;
private bool _isEventStatusLabelVisible = false;
private string _commandStatus = string.Empty;
@@ -20,10 +22,11 @@ public partial class CheckBoxFeatureMatrixViewModel : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
- public CheckBoxFeatureMatrixViewModel()
+ public CheckBoxViewModel()
{
CheckedChangedCommand = new Command(OnCheckedChanged);
CheckBoxCommand = new Command(OnCheckBoxCommand);
+ SetColorCommand = new Command(OnSetColor);
}
public bool IsChecked
@@ -85,11 +88,8 @@ public string CheckedChangedStatus
{
if (_checkedChangedStatus != value)
{
- if (!string.IsNullOrEmpty(value))
- {
- IsEventStatusLabelVisible = true;
- }
_checkedChangedStatus = value;
+ IsEventStatusLabelVisible = !string.IsNullOrEmpty(value);
OnPropertyChanged();
}
}
@@ -110,6 +110,7 @@ public bool IsEventStatusLabelVisible
public ICommand CheckedChangedCommand { get; }
public ICommand CheckBoxCommand { get; }
+ public ICommand SetColorCommand { get; }
public string CommandParameter
{
@@ -131,11 +132,8 @@ public string CommandStatus
{
if (_commandStatus != value)
{
- if (!string.IsNullOrEmpty(value))
- {
- IsCommandStatusLabelVisible = true;
- }
_commandStatus = value;
+ IsCommandStatusLabelVisible = !string.IsNullOrEmpty(value);
OnPropertyChanged();
}
}
@@ -154,6 +152,16 @@ public bool IsCommandStatusLabelVisible
}
}
+ private void OnSetColor(string colorName)
+ {
+ Color = colorName switch
+ {
+ "Blue" => Colors.Blue,
+ "Green" => Colors.Green,
+ _ => null,
+ };
+ }
+
private void OnCheckedChanged()
{
CheckedChangedStatus = "CheckedChanged Triggered";
@@ -171,8 +179,56 @@ private void OnCheckBoxCommand(string parameter)
}
}
+ public bool HasShadow
+ {
+ get => _hasShadow;
+ set
+ {
+ if (_hasShadow != value)
+ {
+ _hasShadow = value;
+ Shadow = value
+ ? new Shadow
+ {
+ Radius = 10,
+ Opacity = 1.0f,
+ Brush = Colors.Black.AsPaint(),
+ Offset = new Point(5, 5)
+ }
+ : null;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public Shadow Shadow
+ {
+ get => _shadow;
+ set
+ {
+ if (_shadow != value)
+ {
+ _shadow = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public void Reset()
+ {
+ IsChecked = true;
+ Color = null;
+ IsEnabled = true;
+ IsVisible = true;
+ HasShadow = false;
+ CheckedChangedStatus = string.Empty;
+ CommandStatus = string.Empty;
+ CommandParameter = string.Empty;
+ }
+
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
-}
\ No newline at end of file
+}
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/ClipControlPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/ClipControlPage.xaml
new file mode 100644
index 000000000000..1b14a7b40eae
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/ClipControlPage.xaml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/ClipControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/ClipControlPage.xaml.cs
new file mode 100644
index 000000000000..095bdf956be7
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/ClipControlPage.xaml.cs
@@ -0,0 +1,52 @@
+namespace Maui.Controls.Sample;
+
+public class ClipControlPage : NavigationPage
+{
+ public ClipControlPage()
+ {
+ PushAsync(new ClipControlMainPage());
+ }
+}
+
+public partial class ClipControlMainPage : ContentPage
+{
+ public ClipControlMainPage()
+ {
+ InitializeComponent();
+ }
+
+ private void OnBorderPageClicked(object sender, EventArgs e)
+ {
+ Navigation.PushAsync(new BorderClip());
+ }
+
+ private void OnBoxViewPageClicked(object sender, EventArgs e)
+ {
+ Navigation.PushAsync(new BoxViewClip());
+ }
+
+ private void OnButtonPageClicked(object sender, EventArgs e)
+ {
+ Navigation.PushAsync(new ButtonClip());
+ }
+
+ private void OnImagePageClicked(object sender, EventArgs e)
+ {
+ Navigation.PushAsync(new ImageClip());
+ }
+
+ private void OnLabelPageClicked(object sender, EventArgs e)
+ {
+ Navigation.PushAsync(new LabelClip());
+ }
+
+ private void OnContentViewPageClicked(object sender, EventArgs e)
+ {
+ Navigation.PushAsync(new ContentViewClip());
+ }
+
+ private void OnImageButtonPageClicked(object sender, EventArgs e)
+ {
+ Navigation.PushAsync(new ImageButtonClip());
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/ClipOptionsPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/ClipOptionsPage.xaml
new file mode 100644
index 000000000000..80c57f47cb97
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/ClipOptionsPage.xaml
@@ -0,0 +1,298 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/ClipOptionsPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/ClipOptionsPage.xaml.cs
new file mode 100644
index 000000000000..e972c08a9692
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/ClipOptionsPage.xaml.cs
@@ -0,0 +1,362 @@
+using Microsoft.Maui.Controls.Shapes;
+
+namespace Maui.Controls.Sample;
+
+public partial class ClipOptionsPage : ContentPage
+{
+ private ClipViewModel _viewModel;
+#if WINDOWS
+ private Geometry _geometry;
+#endif
+ public ClipOptionsPage(ClipViewModel viewModel)
+ {
+ InitializeComponent();
+ _viewModel = viewModel;
+ BindingContext = _viewModel;
+ }
+
+ private async void ApplyButton_Clicked(object sender, EventArgs e)
+ {
+ await Navigation.PopAsync();
+#if WINDOWS
+ _viewModel.Clip = _geometry;
+#endif
+ }
+
+ private void ClipRadio_CheckedChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (e.Value && BindingContext is ClipViewModel vm && sender is RadioButton rb)
+ {
+ Geometry clip = rb.Content?.ToString() switch
+ {
+ "None" => null,
+ "Rectangle" => new RectangleGeometry(new Rect(75, 100, 150, 100)),
+ "Ellipse" => new EllipseGeometry(new Point(150, 150), 100, 65),
+ "RoundRectangle" => new RoundRectangleGeometry(new CornerRadius(50), new Rect(75, 100, 150, 100)),
+ "GeometryGroup" => new GeometryGroup
+ {
+ Children = new GeometryCollection
+ {
+ new EllipseGeometry(new Point(150, 100), 40, 25),
+ new RectangleGeometry(new Rect(75, 150, 150, 50))
+ }
+ },
+ _ => null
+ };
+#if WINDOWS
+ _geometry = clip;
+#else
+ vm.Clip = clip;
+#endif
+ }
+ }
+
+ private void PathTypeRadio_CheckedChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (e.Value && BindingContext is ClipViewModel vm && sender is RadioButton rb)
+ {
+ string pathType = rb.Content?.ToString() ?? "LineSegment";
+ Geometry pathClip = CreatePathGeometry(pathType);
+#if WINDOWS
+ _geometry = pathClip;
+#else
+ vm.Clip = pathClip;
+#endif
+ }
+ }
+
+ private PathGeometry CreatePathGeometry(string pathType)
+ {
+ return pathType switch
+ {
+ // LineSegment: Draws straight lines between points
+ "Line" => new PathGeometry
+ {
+ Figures = new PathFigureCollection
+ {
+ new PathFigure
+ {
+ StartPoint = new Point(150, 20),
+ IsClosed = true,
+ Segments = new PathSegmentCollection
+ {
+ new LineSegment { Point = new Point(280, 180) },
+ new LineSegment { Point = new Point(20, 180) }
+ }
+ }
+ }
+ },
+ // ArcSegment: Draws an elliptical arc between two points
+ "Arc" => new PathGeometry
+ {
+ Figures = new PathFigureCollection
+ {
+ new PathFigure
+ {
+ StartPoint = new Point(10, 100),
+ IsClosed = false,
+ Segments = new PathSegmentCollection
+ {
+ new ArcSegment
+ {
+ Point = new Point(290, 100),
+ Size = new Size(140, 80),
+ RotationAngle = 0,
+ IsLargeArc = true,
+ SweepDirection = SweepDirection.CounterClockwise
+ }
+ }
+ }
+ }
+ },
+ // BezierSegment: Draws a cubic Bezier curve (2 control points)
+ "Bezier" => new PathGeometry
+ {
+ Figures = new PathFigureCollection
+ {
+ new PathFigure
+ {
+ StartPoint = new Point(10, 100),
+ IsClosed = false,
+ Segments = new PathSegmentCollection
+ {
+ new BezierSegment
+ {
+ Point1 = new Point(100, 0),
+ Point2 = new Point(200, 200),
+ Point3 = new Point(290, 100)
+ }
+ }
+ }
+ }
+ },
+ // QuadraticBezierSegment: Draws a quadratic Bezier curve (1 control point)
+ "QuadraticBezier" => new PathGeometry
+ {
+ Figures = new PathFigureCollection
+ {
+ new PathFigure
+ {
+ StartPoint = new Point(10, 100),
+ IsClosed = false,
+ Segments = new PathSegmentCollection
+ {
+ new QuadraticBezierSegment
+ {
+ Point1 = new Point(150, 50),
+ Point2 = new Point(290, 100)
+ }
+ }
+ }
+ }
+ },
+ // PolyLineSegment: Draws a series of connected straight lines
+ "PolyLine" => new PathGeometry
+ {
+ Figures = new PathFigureCollection
+ {
+ new PathFigure
+ {
+ StartPoint = new Point(150, 10),
+ IsClosed = true,
+ Segments = new PathSegmentCollection
+ {
+ new PolyLineSegment
+ {
+ Points = new PointCollection
+ {
+ new Point(180, 80),
+ new Point(260, 80),
+ new Point(200, 130),
+ new Point(230, 200),
+ new Point(150, 160),
+ new Point(70, 200),
+ new Point(100, 130),
+ new Point(40, 80),
+ new Point(120, 80)
+ }
+ }
+ }
+ }
+ }
+ },
+ // PolyBezierSegment: Draws a series of connected cubic Bezier curves
+ "PolyBezier" => new PathGeometry
+ {
+ Figures = new PathFigureCollection
+ {
+ new PathFigure
+ {
+ StartPoint = new Point(10, 100),
+ IsClosed = false,
+ Segments = new PathSegmentCollection
+ {
+ new PolyBezierSegment
+ {
+ Points = new PointCollection
+ {
+ // First Bezier curve (3 points: control1, control2, end)
+ new Point(100, 50),
+ new Point(150, 50),
+ new Point(200, 100),
+ // Second Bezier curve
+ new Point(250, 150),
+ new Point(200, 150),
+ new Point(290, 100)
+ }
+ }
+ }
+ }
+ }
+ },
+ // PolyQuadraticBezierSegment: Draws a series of connected quadratic Bezier curves
+ "PolyQuadraticBezier" => new PathGeometry
+ {
+ Figures = new PathFigureCollection
+ {
+ new PathFigure
+ {
+ StartPoint = new Point(10, 100),
+ IsClosed = false,
+ Segments = new PathSegmentCollection
+ {
+ new PolyQuadraticBezierSegment
+ {
+ Points = new PointCollection
+ {
+ // First quadratic curve (2 points: control, end)
+ new Point(100, 50),
+ new Point(150, 100),
+ // Second quadratic curve
+ new Point(200, 150),
+ new Point(290, 100)
+ }
+ }
+ }
+ }
+ }
+ },
+ _ => new PathGeometry()
+ };
+ }
+
+ private void ShapeChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (!e.Value || !(sender is RadioButton rb) || !(BindingContext is ClipViewModel vm))
+ return;
+
+ vm.StrokeShape = new RoundRectangle { CornerRadius = new CornerRadius(40) };
+ }
+
+ private void OnStrokeColorClicked(object sender, EventArgs e)
+ {
+ if (sender is Button button && button.BackgroundColor != Colors.Transparent)
+ {
+ _viewModel.Stroke = button.BackgroundColor;
+ }
+ }
+
+ private void OnShadowRadioButtonCheckedChanged(object sender, CheckedChangedEventArgs e)
+ {
+ var radioButton = sender as RadioButton;
+ if (radioButton != null && radioButton.IsChecked)
+ {
+ _viewModel.Shadow = new Shadow { Brush = Colors.Violet, Radius = 20, Offset = new Point(0, 0), Opacity = 1f };
+ }
+ }
+
+ private void OnStrokeThicknessChanged(object sender, TextChangedEventArgs e)
+ {
+ if (double.TryParse(StrokeThicknessEntry.Text, out double strokeThickness))
+ {
+ _viewModel.StrokeThickness = strokeThickness;
+ }
+ }
+
+ //BoxView
+ private void OnBoxViewColorClicked(object sender, EventArgs e)
+ {
+ if (sender is Button button && button.BackgroundColor != Colors.Transparent)
+ {
+ _viewModel.Color = button.BackgroundColor;
+ }
+ }
+
+ //Button
+ private void OnImageSourceClicked(object sender, EventArgs e)
+ {
+ _viewModel.Text = null;
+ _viewModel.ImageSource = "oasis.jpg";
+ }
+
+ private void OnCornerRadiusChanged(object sender, TextChangedEventArgs e)
+ {
+ if (string.IsNullOrWhiteSpace(e.NewTextValue))
+ return;
+ if (double.TryParse(e.NewTextValue, out double result))
+ {
+ _viewModel.CornerRadius = new CornerRadius(result);
+ }
+ }
+
+ private void OnFormattedTextChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (e.Value)
+ {
+ _viewModel.FormattedText = new FormattedString
+ {
+ Spans =
+ {
+ new Span { Text = "Lorem ipsum dolor sit amet consectetur adipis elit vivamus lacinia felis eu sagittis congue nibh urna malesuada orci at fringilla quam turpis eget nunc", FontAttributes = FontAttributes.Bold },
+ new Span { Text = "consectetur adipiscing elit", FontAttributes = FontAttributes.Italic },
+ new Span { Text = "Sed do eiusmod tempor.", FontAttributes = FontAttributes.Bold }
+ }
+ };
+ }
+ }
+
+ private void OnFontSizeChanged(object sender, TextChangedEventArgs e)
+ {
+ if (!string.IsNullOrWhiteSpace(FontSizeEntry?.Text) &&
+ double.TryParse(FontSizeEntry.Text, out double size))
+ {
+ _viewModel.FontSize = size;
+ }
+ }
+
+ private void OnRotationChanged(object sender, TextChangedEventArgs e)
+ {
+ if (!string.IsNullOrWhiteSpace(RotationEntry?.Text) &&
+ double.TryParse(RotationEntry.Text, out double rotation))
+ {
+ _viewModel.ControlRotation = rotation;
+ }
+ }
+
+ private void OnScaleXChanged(object sender, TextChangedEventArgs e)
+ {
+ if (!string.IsNullOrWhiteSpace(ScaleXEntry?.Text) &&
+ double.TryParse(ScaleXEntry.Text, out double scaleX))
+ {
+ _viewModel.ControlScaleX = scaleX;
+ }
+ }
+
+ private void OnScaleYChanged(object sender, TextChangedEventArgs e)
+ {
+ if (!string.IsNullOrWhiteSpace(ScaleYEntry?.Text) &&
+ double.TryParse(ScaleYEntry.Text, out double scaleY))
+ {
+ _viewModel.ControlScaleY = scaleY;
+ }
+ }
+
+ private void OnClearClipClicked(object sender, EventArgs e)
+ {
+#if WINDOWS
+ _geometry = null;
+#else
+ _viewModel.Clip = null;
+#endif
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/ClipViewModel.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/ClipViewModel.cs
new file mode 100644
index 000000000000..c69ee7a4ce4b
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/ClipViewModel.cs
@@ -0,0 +1,208 @@
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using Microsoft.Maui.Controls.Shapes;
+namespace Maui.Controls.Sample;
+
+public class ClipViewModel : INotifyPropertyChanged
+{
+ private Color _color = Colors.Red;
+ private CornerRadius _cornerRadius;
+ private Geometry _clip = null;
+ private FormattedString _formattedText;
+ private double _fontSize = 28;
+ private string _imageSource = null;
+ private string _text = "Button";
+ private IShape _strokeShape = new Rectangle();
+ private Shadow _shadow = null;
+ private Brush _stroke = Brush.Red;
+ private double _strokeThickness = 5;
+
+
+ public Color Color
+ {
+ get => _color;
+ set
+ {
+ if (_color != value)
+ {
+ _color = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public CornerRadius CornerRadius
+ {
+ get => _cornerRadius;
+ set
+ {
+ if (_cornerRadius != value)
+ {
+ _cornerRadius = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+ public Geometry Clip
+ {
+ get => _clip;
+ set
+ {
+ if (_clip != value)
+ {
+ _clip = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public FormattedString FormattedText
+ {
+ get => _formattedText;
+ set
+ {
+ if (_formattedText != value)
+ {
+ _formattedText = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public double FontSize
+ {
+ get => _fontSize;
+ set
+ {
+ if (_fontSize != value)
+ {
+ _fontSize = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public string ImageSource
+ {
+ get => _imageSource;
+ set
+ {
+ if (_imageSource != value)
+ {
+ _imageSource = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public string Text
+ {
+ get => _text;
+ set
+ {
+ if (_text != value)
+ {
+ _text = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public IShape StrokeShape
+ {
+ get => _strokeShape;
+ set
+ {
+ if (_strokeShape != value)
+ {
+ _strokeShape = value;
+ OnPropertyChanged(nameof(StrokeShape));
+ }
+ }
+ }
+
+ public Shadow Shadow
+ {
+ get => _shadow;
+ set
+ {
+ if (_shadow != value)
+ {
+ _shadow = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public Brush Stroke
+ {
+ get => _stroke;
+ set
+ {
+ if (_stroke != value)
+ {
+ _stroke = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+ public double StrokeThickness
+ {
+ get => _strokeThickness;
+ set
+ {
+ if (_strokeThickness != value)
+ {
+ _strokeThickness = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private double _rotation;
+ public double ControlRotation
+ {
+ get => _rotation;
+ set
+ {
+ if (_rotation != value)
+ {
+ _rotation = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private double _scaleX = 1.0;
+ public double ControlScaleX
+ {
+ get => _scaleX;
+ set
+ {
+ if (_scaleX != value)
+ {
+ _scaleX = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private double _scaleY = 1.0;
+ public double ControlScaleY
+ {
+ get => _scaleY;
+ set
+ {
+ if (_scaleY != value)
+ {
+ _scaleY = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/BorderClip.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/BorderClip.xaml
new file mode 100644
index 000000000000..888bf0347c56
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/BorderClip.xaml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/BorderClip.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/BorderClip.xaml.cs
new file mode 100644
index 000000000000..fd7e8262df80
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/BorderClip.xaml.cs
@@ -0,0 +1,18 @@
+namespace Maui.Controls.Sample;
+
+public partial class BorderClip : ContentPage
+{
+ private ClipViewModel _viewModel;
+ public BorderClip()
+ {
+ InitializeComponent();
+ _viewModel = new ClipViewModel();
+ BindingContext = _viewModel;
+ }
+
+ private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
+ {
+ BindingContext = _viewModel = new ClipViewModel();
+ await Navigation.PushAsync(new ClipOptionsPage(_viewModel));
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/BoxViewClip.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/BoxViewClip.xaml
new file mode 100644
index 000000000000..60a596d12035
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/BoxViewClip.xaml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/BoxViewClip.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/BoxViewClip.xaml.cs
new file mode 100644
index 000000000000..8c32824c4d7e
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/BoxViewClip.xaml.cs
@@ -0,0 +1,18 @@
+namespace Maui.Controls.Sample;
+
+public partial class BoxViewClip : ContentPage
+{
+ private ClipViewModel _viewModel;
+ public BoxViewClip()
+ {
+ InitializeComponent();
+ _viewModel = new ClipViewModel();
+ BindingContext = _viewModel;
+ }
+
+ private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
+ {
+ BindingContext = _viewModel = new ClipViewModel();
+ await Navigation.PushAsync(new ClipOptionsPage(_viewModel));
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ButtonClip.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ButtonClip.xaml
new file mode 100644
index 000000000000..67311efc6f17
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ButtonClip.xaml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ButtonClip.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ButtonClip.xaml.cs
new file mode 100644
index 000000000000..e31ac720e44b
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ButtonClip.xaml.cs
@@ -0,0 +1,18 @@
+namespace Maui.Controls.Sample;
+
+public partial class ButtonClip : ContentPage
+{
+ private ClipViewModel _viewModel;
+ public ButtonClip()
+ {
+ InitializeComponent();
+ _viewModel = new ClipViewModel();
+ BindingContext = _viewModel;
+ }
+
+ private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
+ {
+ BindingContext = _viewModel = new ClipViewModel();
+ await Navigation.PushAsync(new ClipOptionsPage(_viewModel));
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ContentViewClip.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ContentViewClip.xaml
new file mode 100644
index 000000000000..aa9baa629859
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ContentViewClip.xaml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ContentViewClip.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ContentViewClip.xaml.cs
new file mode 100644
index 000000000000..f127e048882e
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ContentViewClip.xaml.cs
@@ -0,0 +1,18 @@
+namespace Maui.Controls.Sample;
+
+public partial class ContentViewClip : ContentPage
+{
+ private ClipViewModel _viewModel;
+ public ContentViewClip()
+ {
+ InitializeComponent();
+ _viewModel = new ClipViewModel();
+ BindingContext = _viewModel;
+ }
+
+ private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
+ {
+ BindingContext = _viewModel = new ClipViewModel();
+ await Navigation.PushAsync(new ClipOptionsPage(_viewModel));
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ImageButtonClip.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ImageButtonClip.xaml
new file mode 100644
index 000000000000..e94c076ea765
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ImageButtonClip.xaml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ImageButtonClip.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ImageButtonClip.xaml.cs
new file mode 100644
index 000000000000..d92ee74870c9
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ImageButtonClip.xaml.cs
@@ -0,0 +1,18 @@
+namespace Maui.Controls.Sample;
+
+public partial class ImageButtonClip : ContentPage
+{
+ private ClipViewModel _viewModel;
+ public ImageButtonClip()
+ {
+ InitializeComponent();
+ _viewModel = new ClipViewModel();
+ BindingContext = _viewModel;
+ }
+
+ private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
+ {
+ BindingContext = _viewModel = new ClipViewModel();
+ await Navigation.PushAsync(new ClipOptionsPage(_viewModel));
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ImageClip.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ImageClip.xaml
new file mode 100644
index 000000000000..917340af8cde
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ImageClip.xaml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ImageClip.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ImageClip.xaml.cs
new file mode 100644
index 000000000000..3a985207d817
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/ImageClip.xaml.cs
@@ -0,0 +1,18 @@
+namespace Maui.Controls.Sample;
+
+public partial class ImageClip : ContentPage
+{
+ private ClipViewModel _viewModel;
+ public ImageClip()
+ {
+ InitializeComponent();
+ _viewModel = new ClipViewModel();
+ BindingContext = _viewModel;
+ }
+
+ private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
+ {
+ BindingContext = _viewModel = new ClipViewModel();
+ await Navigation.PushAsync(new ClipOptionsPage(_viewModel));
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/LabelClip.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/LabelClip.xaml
new file mode 100644
index 000000000000..ee2fdbe46478
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/LabelClip.xaml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/LabelClip.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/LabelClip.xaml.cs
new file mode 100644
index 000000000000..648a20519968
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Clip/Controls/LabelClip.xaml.cs
@@ -0,0 +1,18 @@
+namespace Maui.Controls.Sample;
+
+public partial class LabelClip : ContentPage
+{
+ private ClipViewModel _viewModel;
+ public LabelClip()
+ {
+ InitializeComponent();
+ _viewModel = new ClipViewModel();
+ BindingContext = _viewModel;
+ }
+
+ private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
+ {
+ BindingContext = _viewModel = new ClipViewModel();
+ await Navigation.PushAsync(new ClipOptionsPage(_viewModel));
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewDynamicChanges/CollectionViewDynamicOptionsPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewDynamicChanges/CollectionViewDynamicOptionsPage.xaml
index 3275d978705e..74a63c8e2e86 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewDynamicChanges/CollectionViewDynamicOptionsPage.xaml
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewDynamicChanges/CollectionViewDynamicOptionsPage.xaml
@@ -4,16 +4,16 @@
x:Class="Maui.Controls.Sample.CollectionViewDynamicOptionsPage"
xmlns:local="clr-namespace:Maui.Controls.Sample"
Title="CollectionViewDynamicOptionsPage">
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewGrouping/GroupingOptionsPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewGrouping/GroupingOptionsPage.xaml.cs
index 07a6b3e4dedc..cd1849ba366f 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewGrouping/GroupingOptionsPage.xaml.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewGrouping/GroupingOptionsPage.xaml.cs
@@ -185,4 +185,16 @@ private void OnItemsLayoutChanged(object sender, CheckedChangedEventArgs e)
_viewModel.ItemsLayout = new GridItemsLayout(2, ItemsLayoutOrientation.Horizontal); // 2 rows
}
}
+
+ private void OnFlowDirectionChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (FlowDirectionLeftToRight.IsChecked)
+ {
+ _viewModel.FlowDirection = FlowDirection.LeftToRight;
+ }
+ else if (FlowDirectionRightToLeft.IsChecked)
+ {
+ _viewModel.FlowDirection = FlowDirection.RightToLeft;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewViewModel.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewViewModel.cs
index 4f820650a33c..5f5669a18567 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewViewModel.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewViewModel.cs
@@ -50,6 +50,7 @@ public class CollectionViewViewModel : INotifyPropertyChanged
private object _emptyView;
private object _header;
private object _footer;
+ private FlowDirection _flowDirection;
private DataTemplate _emptyViewTemplate;
private DataTemplate _headerTemplate;
private DataTemplate _footerTemplate;
@@ -85,6 +86,12 @@ public class CollectionViewViewModel : INotifyPropertyChanged
private SelectionMode _selectionMode = SelectionMode.None;
private object _selectedItem;
private ObservableCollection _selectedItems = new ObservableCollection();
+ private ScrollToPosition _scrollToPosition = ScrollToPosition.MakeVisible;
+ private string _groupName = "Fruits";
+ private int _groupIndex = 0;
+ private string _scrollToByIndexOrItem = "Index";
+ private string _scrollToItem;
+ private int _scrollToIndex = 0;
private int _selectionChangedEventCount = 0;
private string _previousSelectionText;
private string _currentSelectionText;
@@ -155,6 +162,12 @@ public object Header
set { _header = value; OnPropertyChanged(); }
}
+ public FlowDirection FlowDirection
+ {
+ get => _flowDirection;
+ set { _flowDirection = value; OnPropertyChanged(); }
+ }
+
public object Footer
{
get => _footer;
@@ -264,6 +277,84 @@ public bool IsGrouped
}
}
+ public ScrollToPosition ScrollToPosition
+ {
+ get => _scrollToPosition;
+ set
+ {
+ if (_scrollToPosition != value)
+ {
+ _scrollToPosition = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public string GroupName
+ {
+ get => _groupName;
+ set
+ {
+ if (_groupName != value)
+ {
+ _groupName = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public int GroupIndex
+ {
+ get => _groupIndex;
+ set
+ {
+ if (_groupIndex != value)
+ {
+ _groupIndex = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public string ScrollToByIndexOrItem
+ {
+ get => _scrollToByIndexOrItem;
+ set
+ {
+ if (_scrollToByIndexOrItem != value)
+ {
+ _scrollToByIndexOrItem = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public string ScrollToItem
+ {
+ get => _scrollToItem;
+ set
+ {
+ if (_scrollToItem != value)
+ {
+ _scrollToItem = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public int ScrollToIndex
+ {
+ get => _scrollToIndex;
+ set
+ {
+ if (_scrollToIndex != value)
+ {
+ _scrollToIndex = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
public object ItemsSource
{
get
@@ -585,8 +676,8 @@ private void LoadItems()
new Grouping("Fruits", new List()),
new Grouping("Vegetables", new List())
};
- AddItems(_groupedList[0], 4, "Fruits");
- AddItems(_groupedList[1], 4, "Vegetables");
+ AddItems(_groupedList[0], 20, "Fruits");
+ AddItems(_groupedList[1], 20, "Vegetables");
_observableCollection3 = new ObservableCollection();
AddItems(_observableCollection3, 15, "Fruits");
@@ -598,8 +689,8 @@ private void LoadItems()
new Grouping("Vegetables", new List())
};
- AddItems(_groupedList3[0], 12, "Fruits");
- AddItems(_groupedList3[1], 12, "Vegetables");
+ AddItems(_groupedList3[0], 25, "Fruits");
+ AddItems(_groupedList3[1], 25, "Vegetables");
_groupedList2 = new List>
{
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/EmptyView/CollectionViewEmptyViewPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/EmptyView/CollectionViewEmptyViewPage.xaml
index 4e52adfbb456..d02a2a9d2baa 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/EmptyView/CollectionViewEmptyViewPage.xaml
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/EmptyView/CollectionViewEmptyViewPage.xaml
@@ -6,21 +6,22 @@
Title="CollectionViewFeature">
+ Clicked="NavigateToOptionsPage_Clicked"
+ AutomationId="Options"/>
-
-
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/EmptyView/EmptyViewOptionsPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/EmptyView/EmptyViewOptionsPage.xaml
index 93ac2c69c63e..241a830795a2 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/EmptyView/EmptyViewOptionsPage.xaml
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/EmptyView/EmptyViewOptionsPage.xaml
@@ -242,6 +242,26 @@
AutomationId="ItemsSourceEmptyObservableCollection"/>
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/EmptyView/EmptyViewOptionsPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/EmptyView/EmptyViewOptionsPage.xaml.cs
index 4186e8905925..418f1daed905 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/EmptyView/EmptyViewOptionsPage.xaml.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/EmptyView/EmptyViewOptionsPage.xaml.cs
@@ -41,7 +41,6 @@ private void OnEmptyViewChanged(object sender, CheckedChangedEventArgs e)
grid.Children.Add(new Label
{
Text = "No Items Available(Grid View)",
- HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
TextColor = Colors.Blue
});
@@ -60,7 +59,6 @@ private void OnEmptyViewChanged(object sender, CheckedChangedEventArgs e)
Content = new Label
{
Text = "Custom Empty View (Sized)",
- HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
}
};
@@ -140,7 +138,6 @@ private void OnEmptyViewTemplateChanged(object sender, CheckedChangedEventArgs e
grid.Children.Add(new Label
{
Text = "No Template Items Available(Grid View)",
- HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
TextColor = Colors.Blue
});
@@ -163,7 +160,6 @@ private void OnEmptyViewTemplateChanged(object sender, CheckedChangedEventArgs e
Content = new Label
{
Text = "Custom EmptyViewTemplate (Sized)",
- HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
}
};
@@ -266,5 +262,17 @@ private void OnItemsLayoutChanged(object sender, CheckedChangedEventArgs e)
_viewModel.ItemsLayout = new GridItemsLayout(2, ItemsLayoutOrientation.Horizontal); // 2 rows
}
}
+
+ private void OnFlowDirectionChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (FlowDirectionLeftToRight.IsChecked)
+ {
+ _viewModel.FlowDirection = FlowDirection.LeftToRight;
+ }
+ else if (FlowDirectionRightToLeft.IsChecked)
+ {
+ _viewModel.FlowDirection = FlowDirection.RightToLeft;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/ScrollingFeature/CollectionViewScrollPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/ScrollingFeature/CollectionViewScrollPage.xaml
index 603b91f350f6..44ada31fab73 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/ScrollingFeature/CollectionViewScrollPage.xaml
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/ScrollingFeature/CollectionViewScrollPage.xaml
@@ -4,21 +4,79 @@
xmlns:local="clr-namespace:Maui.Controls.Sample"
x:Class="Maui.Controls.Sample.CollectionViewScrollPage"
Title="CollectionViewScrollPage">
-
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/ScrollingFeature/CollectionViewScrollPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/ScrollingFeature/CollectionViewScrollPage.xaml.cs
index cf1fc1fd5050..d33c1139eab4 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/ScrollingFeature/CollectionViewScrollPage.xaml.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/ScrollingFeature/CollectionViewScrollPage.xaml.cs
@@ -17,6 +17,127 @@ private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
{
BindingContext = _viewModel = new CollectionViewViewModel();
_viewModel.ItemsSourceType = ItemsSourceType.ObservableCollectionT3;
+ _viewModel.ScrollToPosition = ScrollToPosition.MakeVisible;
+ ResetScrollEventLabels();
await Navigation.PushAsync(new ScrollBehaviorOptionsPage(_viewModel));
}
+
+ public void ResetScrollEventLabels()
+ {
+#if !WINDOWS // In Windows, CollectionView automatically moves to the first item when navigating to the page, so there is no need to scroll to the first item again.
+ collectionView.ScrollTo(0, position: ScrollToPosition.Start);
+#endif
+ scrolledEventLabel.Text = "Not Fired";
+ scrollToRequestedLabel.Text = "Not Fired";
+ remainingItemsThresholdLabel.Text = "Not Fired";
+ reorderCompletedLabel.Text = "Not Fired";
+ firstIndexLabel.Text = "0";
+ centerIndexLabel.Text = "0";
+ lastIndexLabel.Text = "0";
+ indexLabel.Text = "0";
+ itemLabel.Text = "None";
+ groupIndexLabel.Text = "-1";
+ groupLabel.Text = "None";
+ }
+
+ private void OnScrolled(object sender, ItemsViewScrolledEventArgs e)
+ {
+ scrolledEventLabel.Text = "Fired";
+ firstIndexLabel.Text = e.FirstVisibleItemIndex.ToString();
+ centerIndexLabel.Text = e.CenterItemIndex.ToString();
+ lastIndexLabel.Text = e.LastVisibleItemIndex.ToString();
+ }
+
+ private void OnScrollToRequested(object sender, ScrollToRequestEventArgs e)
+ {
+ scrollToRequestedLabel.Text = "Fired";
+ indexLabel.Text = e.Index.ToString();
+ itemLabel.Text = (e.Item as CollectionViewViewModel.CollectionViewTestItem)?.Caption ?? "None";
+ groupIndexLabel.Text = e.GroupIndex.ToString();
+ groupLabel.Text = e.Group?.ToString() ?? "None";
+ }
+
+ private void OnScrollToButtonClicked(object sender, EventArgs e)
+ {
+ if (_viewModel.IsGrouped)
+ {
+ if (_viewModel.ScrollToByIndexOrItem == "Index")
+ {
+ ScrollToGroupedByIndex();
+ }
+ else
+ {
+ ScrollToGroupedByItem();
+ }
+ }
+ else
+ {
+ if (_viewModel.ScrollToByIndexOrItem == "Index")
+ {
+ ScrollToUngroupedByIndex();
+ }
+ else
+ {
+ ScrollToUngroupedByItem();
+ }
+ }
+ }
+
+ private void ScrollToGroupedByIndex()
+ {
+ if (_viewModel.ItemsSource is not List> groupedList)
+ return;
+
+ if (_viewModel.GroupIndex < 0 || _viewModel.GroupIndex >= groupedList.Count
+ || _viewModel.ScrollToIndex < 0 || _viewModel.ScrollToIndex >= groupedList[_viewModel.GroupIndex].Count)
+ return;
+
+ collectionView.ScrollTo(groupIndex: _viewModel.GroupIndex, index: _viewModel.ScrollToIndex, position: _viewModel.ScrollToPosition, animate: true);
+ }
+
+ private void ScrollToGroupedByItem()
+ {
+ if (_viewModel.ItemsSource is not List> groupedList)
+ return;
+
+ // Find the selected group
+ var selectedGroup = groupedList.FirstOrDefault(g => g.Key == _viewModel.GroupName);
+ if (selectedGroup == null)
+ return;
+
+ // Find the item in the group
+ var targetItem = selectedGroup.FirstOrDefault(item => item.Caption == _viewModel.ScrollToItem);
+ if (targetItem == null)
+ return;
+
+ collectionView.ScrollTo(item: targetItem, group: selectedGroup.Key, position: _viewModel.ScrollToPosition, animate: true);
+ }
+
+ private void ScrollToUngroupedByIndex()
+ {
+ collectionView.ScrollTo(index: _viewModel.ScrollToIndex, position: _viewModel.ScrollToPosition, animate: true);
+ }
+
+ private void ScrollToUngroupedByItem()
+ {
+ if (_viewModel.ItemsSource is not ObservableCollection items)
+ return;
+
+ // Find the item by caption
+ var targetItem = items.FirstOrDefault(x => x.Caption == _viewModel.ScrollToItem);
+ if (targetItem == null)
+ return;
+
+ collectionView.ScrollTo(item: targetItem, position: _viewModel.ScrollToPosition, animate: true);
+ }
+
+ private void OnRemainingItemsThresholdReached(object sender, EventArgs e)
+ {
+ remainingItemsThresholdLabel.Text = "Fired";
+ }
+
+ private void OnReorderCompleted(object sender, EventArgs e)
+ {
+ reorderCompletedLabel.Text = "Fired";
+ }
}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/ScrollingFeature/ScrollBehaviorOptionsPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/ScrollingFeature/ScrollBehaviorOptionsPage.xaml
index 3ff7fa3563dd..3c5ac8c9160d 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/ScrollingFeature/ScrollBehaviorOptionsPage.xaml
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/ScrollingFeature/ScrollBehaviorOptionsPage.xaml
@@ -54,7 +54,7 @@
CheckedChanged="OnItemsUpdatingScrollModeChanged"
AutomationId="ItemsUpdatingKeepLastItemInView"/>
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -160,6 +185,143 @@
AutomationId="ItemsLayoutHorizontalGrid"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/ScrollingFeature/ScrollBehaviorOptionsPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/ScrollingFeature/ScrollBehaviorOptionsPage.xaml.cs
index 766394abc621..e7d3ea64a885 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/ScrollingFeature/ScrollBehaviorOptionsPage.xaml.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/ScrollingFeature/ScrollBehaviorOptionsPage.xaml.cs
@@ -67,6 +67,21 @@ private void OnIsGroupedChanged(object sender, CheckedChangedEventArgs e)
}
}
+ private void OnGroupNameChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (!(sender is RadioButton radioButton) || !e.Value)
+ return;
+
+ if (radioButton == FruitGroup)
+ {
+ _viewModel.GroupName = "Fruits";
+ }
+ else if (radioButton == VegetableGroup)
+ {
+ _viewModel.GroupName = "Vegetables";
+ }
+ }
+
private void OnItemsLayoutChanged(object sender, CheckedChangedEventArgs e)
{
if (ItemsLayoutVerticalList.IsChecked)
@@ -86,4 +101,77 @@ private void OnItemsLayoutChanged(object sender, CheckedChangedEventArgs e)
_viewModel.ItemsLayout = new GridItemsLayout(2, ItemsLayoutOrientation.Horizontal); // 2 rows
}
}
+
+ private void OnFlowDirectionChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (FlowDirectionLeftToRight.IsChecked)
+ {
+ _viewModel.FlowDirection = FlowDirection.LeftToRight;
+ }
+ else if (FlowDirectionRightToLeft.IsChecked)
+ {
+ _viewModel.FlowDirection = FlowDirection.RightToLeft;
+ }
+ }
+ private void OnScrollToPositionChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (!(sender is RadioButton radioButton) || !e.Value)
+ return;
+
+ if (radioButton == ScrollToPositionMakeVisible)
+ _viewModel.ScrollToPosition = ScrollToPosition.MakeVisible;
+ else if (radioButton == ScrollToPositionStart)
+ _viewModel.ScrollToPosition = ScrollToPosition.Start;
+ else if (radioButton == ScrollToPositionCenter)
+ _viewModel.ScrollToPosition = ScrollToPosition.Center;
+ else if (radioButton == ScrollToPositionEnd)
+ _viewModel.ScrollToPosition = ScrollToPosition.End;
+ }
+
+ private void ScrollToIndexItemChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (!(sender is RadioButton radioButton) || !e.Value)
+ return;
+ if (radioButton == ScrollToByIndex)
+ {
+ _viewModel.ScrollToByIndexOrItem = "Index";
+ }
+ else if (radioButton == ScrollToByItem)
+ {
+ _viewModel.ScrollToByIndexOrItem = "Item";
+ }
+ }
+
+ private void ScrollToIndexEntry_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ if (int.TryParse(ScrollToIndexEntry.Text, out int index))
+ {
+ _viewModel.ScrollToIndex = index;
+ }
+ }
+
+ private void GroupIndexEntry_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ if (int.TryParse(GroupIndexEntry.Text, out int groupIndex))
+ {
+ _viewModel.GroupIndex = groupIndex;
+ }
+ }
+
+ private void ScrollToItemEntry_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ _viewModel.ScrollToItem = ScrollToItemEntry.Text;
+ }
+
+ private void OnCanReorderItemsChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (CanReorderItemsTrue.IsChecked)
+ {
+ _viewModel.CanReorderItems = true;
+ }
+ else if (CanReorderItemsFalse.IsChecked)
+ {
+ _viewModel.CanReorderItems = false;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/Selection/CollectionViewSelectionPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/Selection/CollectionViewSelectionPage.xaml
index fa292dd3d0a3..5894590e384f 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/Selection/CollectionViewSelectionPage.xaml
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/Selection/CollectionViewSelectionPage.xaml
@@ -30,6 +30,11 @@
x:Name="collectionView"
Grid.Row="0"
Grid.ColumnSpan="2"
+ FlowDirection="{Binding FlowDirection}"
+ Header="{Binding Header}"
+ Footer="{Binding Footer}"
+ HeaderTemplate="{Binding HeaderTemplate}"
+ FooterTemplate="{Binding FooterTemplate}"
ItemsSource="{Binding ItemsSource}"
ItemTemplate="{Binding ItemTemplate}"
ItemsLayout="{Binding ItemsLayout}"
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/Selection/SelectionOptionsPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/Selection/SelectionOptionsPage.xaml
index c8b72fad7638..068cb76e0d91 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/Selection/SelectionOptionsPage.xaml
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/Selection/SelectionOptionsPage.xaml
@@ -129,6 +129,115 @@
CheckedChanged="OnItemsSourceChanged"
AutomationId="ItemsSourceGroupedList"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/Selection/SelectionOptionsPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/Selection/SelectionOptionsPage.xaml.cs
index 580dd578e63a..a6001de9fa3d 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/Selection/SelectionOptionsPage.xaml.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/Selection/SelectionOptionsPage.xaml.cs
@@ -41,6 +41,139 @@ private void OnSelectionModeButtonClicked(object sender, EventArgs e)
}
}
}
+
+ private void OnFlowDirectionChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (FlowDirectionLeftToRight.IsChecked)
+ {
+ _viewModel.FlowDirection = FlowDirection.LeftToRight;
+ }
+ else if (FlowDirectionRightToLeft.IsChecked)
+ {
+ _viewModel.FlowDirection = FlowDirection.RightToLeft;
+ }
+ }
+
+ private void OnHeaderChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (HeaderNone.IsChecked)
+ {
+ _viewModel.Header = null;
+ }
+ else if (HeaderString.IsChecked)
+ {
+ _viewModel.Header = "CollectionView Header(String)";
+ }
+ else if (HeaderGrid.IsChecked)
+ {
+ Grid grid = new Grid
+ {
+ BackgroundColor = Colors.LightGray,
+ Padding = new Thickness(10)
+ };
+ grid.Children.Add(new Label
+ {
+ Text = "CollectionView Header(Grid View)",
+ FontSize = 18,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ TextColor = Colors.Blue,
+ AutomationId = "HeaderViewLabel"
+ });
+ _viewModel.Header = grid;
+ }
+ }
+
+ private void OnFooterChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (FooterNone.IsChecked)
+ {
+ _viewModel.Footer = null;
+ }
+ else if (FooterString.IsChecked)
+ {
+ _viewModel.Footer = "CollectionView Footer(String)";
+ }
+ else if (FooterGrid.IsChecked)
+ {
+ Grid grid = new Grid
+ {
+ BackgroundColor = Colors.LightGray,
+ Padding = new Thickness(10)
+ };
+ grid.Children.Add(new Label
+ {
+ Text = "CollectionView Footer(Grid View)",
+ FontSize = 18,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ TextColor = Colors.Red,
+ AutomationId = "FooterViewLabel"
+ });
+ _viewModel.Footer = grid;
+ }
+ }
+
+ private void OnHeaderTemplateChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (HeaderTemplateNone.IsChecked)
+ {
+ _viewModel.HeaderTemplate = null;
+ }
+ else if (HeaderTemplateGrid.IsChecked)
+ {
+ _viewModel.HeaderTemplate = new DataTemplate(() =>
+ {
+ Grid grid = new Grid
+ {
+ BackgroundColor = Colors.LightGray,
+ Padding = new Thickness(10)
+ };
+ grid.Children.Add(new Label
+ {
+ Text = "Header Template(Grid View)",
+ FontSize = 18,
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ TextColor = Colors.Blue,
+ AutomationId = "HeaderTemplateLabel"
+ });
+ return grid;
+ });
+ }
+ }
+
+ private void OnFooterTemplateChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (FooterTemplateNone.IsChecked)
+ {
+ _viewModel.FooterTemplate = null;
+ }
+ else if (FooterTemplateGrid.IsChecked)
+ {
+ _viewModel.FooterTemplate = new DataTemplate(() =>
+ {
+ Grid grid = new Grid
+ {
+ BackgroundColor = Colors.LightGray,
+ Padding = new Thickness(10)
+ };
+ grid.Children.Add(new Label
+ {
+ Text = "Footer Template(Grid View)",
+ FontSize = 18,
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ TextColor = Colors.Green,
+ AutomationId = "FooterTemplateLabel"
+ });
+ return grid;
+ });
+ }
+ }
+
private void OnIsGroupedChanged(object sender, CheckedChangedEventArgs e)
{
if (IsGroupedFalse.IsChecked)
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/EntryMaterial3/Material3_EntryControlPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/EntryMaterial3/Material3_EntryControlPage.xaml
new file mode 100644
index 000000000000..c3262fdf0e40
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/EntryMaterial3/Material3_EntryControlPage.xaml
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/EntryMaterial3/Material3_EntryControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/EntryMaterial3/Material3_EntryControlPage.xaml.cs
new file mode 100644
index 000000000000..0f945e3c0b1e
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/EntryMaterial3/Material3_EntryControlPage.xaml.cs
@@ -0,0 +1,125 @@
+using System.ComponentModel;
+using Microsoft.Maui.Controls;
+
+namespace Maui.Controls.Sample;
+
+public class Material3_EntryControlPage : NavigationPage
+{
+ EntryViewModel _viewModel;
+
+ public Material3_EntryControlPage()
+ {
+ _viewModel = new EntryViewModel();
+ PushAsync(new Material3_EntryControlMainPage(_viewModel));
+ }
+}
+
+public partial class Material3_EntryControlMainPage : ContentPage
+{
+ EntryViewModel _viewModel;
+
+ public Material3_EntryControlMainPage(EntryViewModel viewModel)
+ {
+ InitializeComponent();
+ _viewModel = viewModel;
+ BindingContext = _viewModel;
+ EntryControl.PropertyChanged += UpdateEntryControl;
+ }
+
+ async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
+ {
+ BindingContext = _viewModel = new EntryViewModel();
+ _viewModel.Text = "Test Entry";
+ _viewModel.Placeholder = "Enter text here";
+ _viewModel.CursorPosition = 0;
+ _viewModel.SelectionLength = 0;
+ await Navigation.PushAsync(new EntryOptionsPage(_viewModel));
+ }
+
+ void CursorPositionButton_Clicked(object sender, EventArgs e)
+ {
+ if (int.TryParse(CursorPositionEntry.Text, out int cursorPosition))
+ {
+ _viewModel.CursorPosition = cursorPosition;
+ }
+ }
+
+ void SelectionLength_Clicked(object sender, EventArgs e)
+ {
+ if (int.TryParse(SelectionLengthEntry.Text, out int selectionLength))
+ {
+ _viewModel.SelectionLength = selectionLength;
+ }
+ }
+
+ void OnUpdateCursorAndSelectionClicked(object sender, EventArgs e)
+ {
+ if (int.TryParse(CursorPositionEntry.Text, out int cursorPosition))
+ {
+ EntryControl.Focus();
+ EntryControl.CursorPosition = cursorPosition;
+
+ if (BindingContext is EntryViewModel vm)
+ vm.CursorPosition = cursorPosition;
+ }
+
+ if (int.TryParse(SelectionLengthEntry.Text, out int selectionLength))
+ {
+ EntryControl.Focus();
+ EntryControl.SelectionLength = selectionLength;
+
+ if (BindingContext is EntryViewModel vm)
+ vm.SelectionLength = selectionLength;
+ }
+ CursorPositionEntry.Text = EntryControl.CursorPosition.ToString();
+ SelectionLengthEntry.Text = EntryControl.SelectionLength.ToString();
+ }
+
+ void UpdateEntryControl(object sender, PropertyChangedEventArgs args)
+ {
+ if (args.PropertyName == Entry.CursorPositionProperty.PropertyName)
+ CursorPositionEntry.Text = EntryControl.CursorPosition.ToString();
+ else if (args.PropertyName == Entry.SelectionLengthProperty.PropertyName)
+ SelectionLengthEntry.Text = EntryControl.SelectionLength.ToString();
+ }
+
+ void EntryControl_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ string eventInfo = $"TextChanged: Old='{e.OldTextValue}', New='{e.NewTextValue}'";
+
+ if (BindingContext is EntryViewModel vm)
+ {
+ vm.TextChangedText = eventInfo;
+ }
+ }
+
+ void EntryControl_Completed(object sender, EventArgs e)
+ {
+ string eventInfo = $"Completed: Event Triggered";
+
+ if (BindingContext is EntryViewModel vm)
+ {
+ vm.CompletedText = eventInfo;
+ }
+ }
+
+ void EntryControl_Focused(object sender, FocusEventArgs e)
+ {
+ string eventInfo = $"Focused: Event Triggered";
+
+ if (BindingContext is EntryViewModel vm)
+ {
+ vm.FocusedText = eventInfo;
+ }
+ }
+
+ void EntryControl_Unfocused(object sender, FocusEventArgs e)
+ {
+ string eventInfo = $"Unfocused: Event Triggered";
+
+ if (BindingContext is EntryViewModel vm)
+ {
+ vm.UnfocusedText = eventInfo;
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/FlyoutPage/FlyoutControlPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/FlyoutPage/FlyoutControlPage.xaml
index 1427640a183e..6c35f5ee2c4a 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/FlyoutPage/FlyoutControlPage.xaml
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/FlyoutPage/FlyoutControlPage.xaml
@@ -7,7 +7,9 @@
FlyoutLayoutBehavior="{Binding FlyoutLayoutBehavior}"
IsEnabled="{Binding IsEnabled}"
FlowDirection="{Binding FlowDirection}"
- IsVisible="{Binding IsVisible }">
+ IsVisible="{Binding IsVisible }"
+ BackButtonPressed="OnBackButtonPressed"
+ IsPresentedChanged="OnIsPresentedChanged">
-
@@ -40,8 +43,8 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/FlyoutPage/FlyoutControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/FlyoutPage/FlyoutControlPage.xaml.cs
index 4ce1bcefe950..c7ac995c9b00 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/FlyoutPage/FlyoutControlPage.xaml.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/FlyoutPage/FlyoutControlPage.xaml.cs
@@ -22,6 +22,8 @@ public FlyoutControlMainPage(FlyoutPageViewModel viewModel)
BindingContext = _viewModel;
NavigationPage.SetHasNavigationBar(this, false);
_originalFlyoutPage = this.Flyout;
+ AttachFlyoutNavigationEvents(this.Flyout);
+
}
private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
{
@@ -29,6 +31,8 @@ private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
NavigatedToLabel.Text = "NavigatedTo: ";
NavigatingFromLabel.Text = "NavigatingFrom: ";
NavigatedFromLabel.Text = "NavigatedFrom: ";
+ _viewModel.IsPresentedChangedText = "Not Raised";
+ _viewModel.BackButtonPressedText = "Not Raised";
await Navigation.PushAsync(new FlyoutOptionsPage(_viewModel));
}
@@ -66,6 +70,14 @@ protected override void OnAppearing()
#endif
}
+ private void AttachFlyoutNavigationEvents(Page flyoutPage)
+ {
+ flyoutPage.NavigatedTo += OnPageNavigatedTo;
+ flyoutPage.NavigatingFrom += OnPageNavigatingFrom;
+ flyoutPage.NavigatedFrom += OnPageNavigatedFrom;
+ }
+
+
private void OnPageNavigatedTo(object sender, NavigatedToEventArgs e)
{
@@ -135,7 +147,7 @@ private async void OnGoToNewPageClicked(object sender, EventArgs e)
private void OnSetFlyout1Clicked(object sender, EventArgs e)
{
- this.Flyout = new ContentPage
+ var flyout1 = new ContentPage
{
Title = "Flyout 1",
Content = new VerticalStackLayout
@@ -156,11 +168,15 @@ private void OnSetFlyout1Clicked(object sender, EventArgs e)
}
}
};
+ AttachFlyoutNavigationEvents(flyout1);
+ this.Flyout = flyout1;
+
+
}
private void OnSetFlyout2Clicked(object sender, EventArgs e)
{
- this.Flyout = new ContentPage
+ var flyout2 = new ContentPage
{
Title = "Flyout 2",
Content = new VerticalStackLayout
@@ -181,12 +197,17 @@ private void OnSetFlyout2Clicked(object sender, EventArgs e)
}
}
};
+ AttachFlyoutNavigationEvents(flyout2);
+
+ this.Flyout = flyout2;
}
private void RestoreOriginalFlyoutPage()
{
if (_originalFlyoutPage != null)
+ {
this.Flyout = _originalFlyoutPage;
+ }
}
private async void OnSetDetail1Clicked(object sender, EventArgs e)
{
@@ -252,4 +273,23 @@ void OnOpenFlyoutClicked(object sender, EventArgs e)
{
this.IsPresented = true;
}
+
+ private void OnIsPresentedChanged(object sender, EventArgs e)
+ {
+ if (BindingContext is FlyoutPageViewModel vm)
+ {
+ vm.IsPresentedChangedText = "Raised";
+ }
+ }
+
+ private void OnBackButtonPressed(object sender, BackButtonPressedEventArgs e)
+
+ {
+ if (BindingContext is FlyoutPageViewModel vm)
+ {
+ vm.BackButtonPressedText = "Raised";
+ e.Handled = vm.ShouldHandleBackButton;
+ vm.BackButtonHandled = e.Handled.ToString();
+ }
+ }
}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/FlyoutPage/FlyoutPageViewModel.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/FlyoutPage/FlyoutPageViewModel.cs
index 89e33a004311..24edc84507fd 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/FlyoutPage/FlyoutPageViewModel.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/FlyoutPage/FlyoutPageViewModel.cs
@@ -15,6 +15,10 @@ public class FlyoutPageViewModel : INotifyPropertyChanged
private bool _isEnabled = true;
private bool _isVisible = true;
private ImageSource _iconImageSource = "coffee.png";
+ private string _isPresentedChangedText = "Not Raised";
+ private string _backButtonPressedText = "Not Raised";
+ string _backButtonHandled = "False";
+
public string Title
{
@@ -134,6 +138,47 @@ public bool IsVisible
}
}
+ public string IsPresentedChangedText
+ {
+ get => _isPresentedChangedText;
+ set
+ {
+ _isPresentedChangedText = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public string BackButtonPressedText
+ {
+ get => _backButtonPressedText;
+ set
+ {
+ _backButtonPressedText = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public string BackButtonHandled
+ {
+ get => _backButtonHandled;
+ set
+ {
+ _backButtonHandled = value;
+ OnPropertyChanged(nameof(BackButtonHandled));
+ }
+ }
+
+ bool _shouldHandleBack = false;
+ public bool ShouldHandleBackButton
+ {
+ get => _shouldHandleBack;
+ set
+ {
+ _shouldHandleBack = value;
+ OnPropertyChanged(nameof(ShouldHandleBackButton));
+ }
+ }
+
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Map/MapControlPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Map/MapControlPage.xaml
new file mode 100644
index 000000000000..997b24b6aa5e
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Map/MapControlPage.xaml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Map/MapControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Map/MapControlPage.xaml.cs
new file mode 100644
index 000000000000..fb11af2d141f
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Map/MapControlPage.xaml.cs
@@ -0,0 +1,246 @@
+using System.Collections.Specialized;
+using System.ComponentModel;
+using Microsoft.Maui.Controls.Maps;
+
+namespace Maui.Controls.Sample;
+
+public class MapControlPage : NavigationPage
+{
+ private MapViewModel _viewModel;
+ public MapControlPage()
+ {
+ _viewModel = new MapViewModel();
+ PushAsync(new MapControlMainPage(_viewModel));
+ }
+}
+
+public partial class MapControlMainPage : ContentPage
+{
+ private MapViewModel _viewModel;
+ public MapControlMainPage(MapViewModel viewModel)
+ {
+
+ InitializeComponent();
+ _viewModel = viewModel;
+ BindingContext = _viewModel;
+
+ // Set initial label text to 'Not clicked'
+ var mapLabel = this.FindByName("MapClickedLabel");
+ if (mapLabel != null)
+ mapLabel.Text = "Map Clicked: Not clicked";
+ var markerLabel = this.FindByName("MarkerClickedLabel");
+ if (markerLabel != null)
+ markerLabel.Text = "Marker Clicked: Not clicked";
+
+ // Handle MapElements collection changes manually since it's not directly bindable
+ _viewModel.MapElements.CollectionChanged += OnMapElementsCollectionChanged;
+
+ // Note: Pins collection changes are handled through ItemsSource binding
+ // Manual Pins handling is only needed when ItemsSource is null
+ _viewModel.Pins.CollectionChanged += OnPinsCollectionChanged;
+ _viewModel.PropertyChanged += OnViewModelPropertyChanged;
+
+ // ItemsSource, ItemTemplate, and ItemTemplateSelector are already bound in XAML
+ // No need for programmatic binding as it can cause conflicts
+
+ // Set initial map region to Pearl City
+ TestMap.MoveToRegion(_viewModel.InitialRegion);
+
+ // Ensure ItemTemplate and ItemTemplateSelector are synchronized with ViewModel
+ if (_viewModel.ItemTemplate != null)
+ {
+ TestMap.ItemTemplate = _viewModel.ItemTemplate;
+ }
+ if (_viewModel.ItemTemplateSelector != null)
+ {
+ TestMap.ItemTemplateSelector = _viewModel.ItemTemplateSelector;
+ }
+
+ // Subscribe to map events to sync VisibleRegion back to ViewModel
+ TestMap.PropertyChanged += OnMapPropertyChanged;
+
+ // Subscribe to MessagingCenter for label updates
+ Microsoft.Maui.Controls.MessagingCenter.Subscribe(this, "MapClickedLabelUpdate", (sender, value) =>
+ {
+ var label = this.FindByName("MapClickedLabel");
+ if (label != null)
+ label.Text = value;
+ });
+ Microsoft.Maui.Controls.MessagingCenter.Subscribe(this, "MarkerClickedLabelUpdate", (sender, value) =>
+ {
+ var label = this.FindByName("MarkerClickedLabel");
+ if (label != null)
+ label.Text = value;
+ });
+
+ TestMap.MapClicked += OnMapClicked;
+
+ }
+
+ protected override void OnDisappearing()
+ {
+ base.OnDisappearing();
+
+ // Clean up MessagingCenter subscriptions to avoid memory leaks
+ Microsoft.Maui.Controls.MessagingCenter.Unsubscribe(this, "MapClickedLabelUpdate");
+ Microsoft.Maui.Controls.MessagingCenter.Unsubscribe(this, "MarkerClickedLabelUpdate");
+ }
+
+ private void OnMapElementsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ if (e.Action == NotifyCollectionChangedAction.Add)
+ {
+ foreach (MapElement element in e.NewItems)
+ {
+ TestMap.MapElements.Add(element);
+ }
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Remove)
+ {
+ foreach (MapElement element in e.OldItems)
+ {
+ TestMap.MapElements.Remove(element);
+ }
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Reset)
+ {
+ TestMap.MapElements.Clear();
+ }
+ }
+
+ private void OnPinsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ // Only handle pins manually when ItemsSource is null
+ // When ItemsSource is set, the binding handles pin display automatically
+ if (_viewModel.ItemsSource == null)
+ {
+ if (e.Action == NotifyCollectionChangedAction.Add)
+ {
+ foreach (Pin pin in e.NewItems)
+ {
+ TestMap.Pins.Add(pin);
+ }
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Remove)
+ {
+ foreach (Pin pin in e.OldItems)
+ {
+ TestMap.Pins.Remove(pin);
+ }
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Reset)
+ {
+ TestMap.Pins.Clear();
+ }
+ }
+ }
+
+ private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(_viewModel.ItemsSource))
+ {
+ TestMap.Pins.Clear(); // Clear existing pins if any
+ TestMap.ItemsSource = _viewModel.ItemsSource;
+ }
+ else if (e.PropertyName == nameof(_viewModel.ItemTemplate))
+ {
+ // When ItemTemplate changes in ViewModel, update the actual Map
+ TestMap.ItemTemplate = _viewModel.ItemTemplate;
+ }
+ else if (e.PropertyName == nameof(_viewModel.ItemTemplateSelector))
+ {
+ TestMap.ItemTemplate = null;
+ // When ItemTemplateSelector changes in ViewModel, update the actual Map
+ TestMap.ItemTemplateSelector = _viewModel.ItemTemplateSelector;
+ }
+ else if (e.PropertyName == nameof(_viewModel.VisibleRegion))
+ {
+ // When VisibleRegion changes in ViewModel, update the actual Map
+ if (_viewModel.VisibleRegion != null)
+ {
+ TestMap.MoveToRegion(_viewModel.VisibleRegion);
+ }
+ }
+ }
+
+ private void OnMapPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ // Update ViewModel's VisibleRegion when the map's VisibleRegion changes
+ if (e.PropertyName == "VisibleRegion" && TestMap.VisibleRegion != null)
+ {
+ // Only update if it's different to avoid infinite loops
+ if (_viewModel.VisibleRegion != TestMap.VisibleRegion)
+ {
+ _viewModel.VisibleRegion = TestMap.VisibleRegion;
+ }
+ }
+ }
+
+ private Pin _lastClickPin;
+ private void OnMapClicked(object sender, Microsoft.Maui.Controls.Maps.MapClickedEventArgs e)
+ {
+ var label = this.FindByName("MapClickedLabel");
+ if (label != null)
+ label.Text = $"Map Clicked: Latitude: {e.Location.Latitude:F6}, Longitude: {e.Location.Longitude:F6}";
+
+ // Remove previous click marker if it exists
+ if (_lastClickPin != null)
+ {
+ if (TestMap.Pins.Contains(_lastClickPin))
+ TestMap.Pins.Remove(_lastClickPin);
+ _lastClickPin = null;
+ }
+
+ // Add a new pin at the clicked location
+ var clickPin = new Pin
+ {
+ Label = "Clicked Location",
+ Location = e.Location,
+ Address = $"Lat: {e.Location.Latitude:F6}, Lng: {e.Location.Longitude:F6}",
+ Type = PinType.Place
+ };
+ // Attach MarkerClicked and InfoWindowClicked events
+ clickPin.MarkerClicked += OnPinMarkerClicked;
+ clickPin.InfoWindowClicked += OnPinInfoWindowClicked;
+ TestMap.Pins.Add(clickPin);
+ _lastClickPin = clickPin;
+ }
+ private void OnPinMarkerClicked(object sender, PinClickedEventArgs e)
+ {
+ var label = this.FindByName("MarkerClickedLabel");
+ if (label != null)
+ label.Text = "Marker Clicked: Pin tapped (info window will show)";
+ e.HideInfoWindow = false;
+ }
+
+ private void OnPinInfoWindowClicked(object sender, PinClickedEventArgs e)
+ {
+ var label = this.FindByName("MarkerClickedLabel");
+ if (label != null)
+ label.Text = "Marker Clicked: Info window tapped (info window will hide)";
+ e.HideInfoWindow = true;
+ }
+
+ public void ResetClickedLocationPinAndLabel()
+ {
+ // Remove the clicked location pin if it exists
+ if (_lastClickPin != null)
+ {
+ if (TestMap.Pins.Contains(_lastClickPin))
+ TestMap.Pins.Remove(_lastClickPin);
+ _lastClickPin = null;
+ }
+ // Reset the labels to 'Not clicked'
+ var mapLabel = this.FindByName("MapClickedLabel");
+ if (mapLabel != null)
+ mapLabel.Text = "Map Clicked: Not clicked";
+ var markerLabel = this.FindByName("MarkerClickedLabel");
+ if (markerLabel != null)
+ markerLabel.Text = "Marker Clicked: Not clicked";
+ }
+ private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
+ {
+ // Use the existing view model instead of creating a new one
+ await Navigation.PushAsync(new MapOptionsPage(_viewModel));
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Map/MapOptionsPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Map/MapOptionsPage.xaml
new file mode 100644
index 000000000000..4356b3737e5e
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Map/MapOptionsPage.xaml
@@ -0,0 +1,281 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Map/MapOptionsPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Map/MapOptionsPage.xaml.cs
new file mode 100644
index 000000000000..cf084ee0ed8d
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Map/MapOptionsPage.xaml.cs
@@ -0,0 +1,527 @@
+using Microsoft.Maui.Controls.Maps;
+using Microsoft.Maui.Maps;
+using System.ComponentModel;
+namespace Maui.Controls.Sample;
+
+public partial class MapOptionsPage : ContentPage
+{
+ private MapViewModel _viewModel;
+
+ public MapOptionsPage(MapViewModel viewModel)
+ {
+ InitializeComponent();
+ _viewModel = viewModel;
+ BindingContext = _viewModel;
+
+ // Set up a basic ItemTemplate for demonstration
+ // Templates are now set via UI controls - SetItemTemplateButton_Clicked and SetTemplateSelectorButton_Clicked
+
+ // Initialize radio buttons based on current MapType
+ UpdateRadioButtonsFromMapType();
+
+ // Subscribe to checkbox events
+ IsShowingUserCheckBox.CheckedChanged += OnIsShowingUserCheckBoxChanged;
+
+ // Subscribe to VisibleRegion property changes to update the UI
+ _viewModel.PropertyChanged += OnViewModelPropertyChanged;
+
+ // Initialize the VisibleRegion display
+ UpdateVisibleRegionDisplay();
+ }
+
+ private void UpdateRadioButtonsFromMapType()
+ {
+ switch (_viewModel.MapType)
+ {
+ case MapType.Street:
+ StreetRadioButton.IsChecked = true;
+ break;
+ case MapType.Satellite:
+ SatelliteRadioButton.IsChecked = true;
+ break;
+ case MapType.Hybrid:
+ HybridRadioButton.IsChecked = true;
+ break;
+ }
+ }
+
+ private void MapTypeRadioButton_CheckedChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (e.Value) // Only respond to checked events, not unchecked
+ {
+ if (sender == StreetRadioButton)
+ _viewModel.MapType = MapType.Street;
+ else if (sender == SatelliteRadioButton)
+ _viewModel.MapType = MapType.Satellite;
+ else if (sender == HybridRadioButton)
+ _viewModel.MapType = MapType.Hybrid;
+ }
+ }
+
+ private void OnIsShowingUserCheckBoxChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (e.Value)
+ {
+ // Add current location pin when IsShowingUser is checked
+ AddCurrentLocationPin();
+ }
+ else
+ {
+ // Remove current location pin when IsShowingUser is unchecked
+ RemoveCurrentLocationPin();
+ }
+ }
+
+ private void AddCurrentLocationPin()
+ {
+ // Check if current location pin already exists
+ var existingCurrentLocationPin = _viewModel.Pins.FirstOrDefault(p => p.Label == "Current Location");
+ if (existingCurrentLocationPin == null)
+ {
+ var currentLocationPin = new Pin
+ {
+ Label = "Current Location",
+ Address = "Pearl City, Hawaii (Current Location)",
+ Type = PinType.Generic,
+ Location = MapViewModel.PearlCityLocation
+ };
+
+ var placeInfo = new PlaceInfo
+ {
+ Name = "Current Location",
+ Description = "Pearl City, Hawaii (Current Location)",
+ Location = MapViewModel.PearlCityLocation,
+ Type = PlaceType.Tourist
+ };
+
+ _viewModel.Pins.Add(currentLocationPin);
+ _viewModel.Places.Add(placeInfo);
+ }
+ }
+
+ private void RemoveCurrentLocationPin()
+ {
+ // Find and remove the current location pin
+ var currentLocationPin = _viewModel.Pins.FirstOrDefault(p => p.Label == "Current Location");
+ if (currentLocationPin != null)
+ {
+ _viewModel.Pins.Remove(currentLocationPin);
+ }
+ }
+
+ private void ApplyButton_Clicked(object sender, EventArgs e)
+ {
+ Navigation.PopAsync();
+ }
+
+ private void AddPinButton_Clicked(object sender, EventArgs e)
+ {
+ // Count only numbered pins (Pin1, Pin2, etc.), exclude "Current Location" pin
+ var numberedPinsCount = _viewModel.Pins.Count(p => p.Label != "Current Location");
+ // Maximum of 10 numbered pins allowed
+ if (numberedPinsCount >= 10)
+ {
+ return; // Don't add more than 10 numbered pins
+ }
+
+ // Predefined pin locations around Pearl City, Hawaii
+ var pinLocations = new[]
+ {
+ // Pin 1: Pearl City, Hawaii
+ new { Lat = 21.3933, Lng = -157.9751, Name = "Pearl City", Description = "Suburb, shopping, residential, schools, parks" , Type = PlaceType.Tourist },
+ // Pin 2: Waipahu, Hawaii
+ new { Lat = 21.3986, Lng = -158.0097, Name = "Waipahu", Description = "Historic sugar town, diverse community" , Type = PlaceType.Restaurant },
+ // Pin 3: Aiea, Hawaii
+ new { Lat = 21.3649, Lng = -157.9634, Name = "Aiea", Description = "Residential, Pearl Harbor views, mall" , Type = PlaceType.Restaurant },
+ // Pin 4: Waikele, Hawaii
+ new { Lat = 21.4513, Lng = -158.0147, Name = "Waikele", Description = "Outlet shopping, golf, residential area" , Type = PlaceType.Tourist },
+ // Pin 5: Halawa, Hawaii
+ new { Lat = 21.3847, Lng = -157.9261, Name = "Halawa", Description = "Aloha Stadium, neighborhoods, valley, events", Type = PlaceType.Restaurant },
+ // Pin 6: Mililani, Hawaii
+ new { Lat = 21.4644, Lng = -158.0411, Name = "Mililani", Description = "Planned community, schools, parks, shopping" , Type = PlaceType.Tourist },
+ // Pin 7: Ewa Beach, Hawaii
+ new { Lat = 21.3408, Lng = -158.0061, Name = "Ewa Beach", Description = "Coastal, golf, growing, residential, beaches" , Type = PlaceType.Restaurant },
+ // Pin 8: Waimalu, Hawaii
+ new { Lat = 21.3247, Lng = -157.9772, Name = "Waimalu", Description = "Small community, shopping, residential, eateries", Type = PlaceType.Tourist },
+ // Pin 9: Kapolei, Hawaii
+ new { Lat = 21.3142, Lng = -158.0397, Name = "Kapolei", Description = "Second city, business, shopping, growth", Type = PlaceType.Restaurant },
+ // Pin 10: Makakilo, Hawaii
+ new { Lat = 21.3350, Lng = -158.0550, Name = "Makakilo", Description = "Hillside, residential, views, breezy, quiet", Type = PlaceType.Tourist },
+ };
+
+ // Get the next pin location based on numbered pins count
+ var pinData = pinLocations[numberedPinsCount];
+
+ var pin = new Pin
+ {
+ Label = pinData.Name,
+ Address = $"{pinData.Description}",
+ Type = PinType.Place,
+ Location = new Location(pinData.Lat, pinData.Lng)
+ };
+
+ var placeInfo = new PlaceInfo
+ {
+ Name = pinData.Name,
+ Description = "Pin Added from ItemsSource",
+ Location = new Location(pinData.Lat, pinData.Lng),
+ Type = pinData.Type
+ };
+
+ _viewModel.Places.Add(placeInfo);
+ _viewModel.Pins.Add(pin);
+ _viewModel.UserAddedPinCount++;
+ }
+
+ // Shape selection for Map Elements
+ private string _selectedShape = "Polyline"; // Default
+
+ private void ShapeRadioButton_CheckedChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (e.Value && sender is RadioButton rb && rb.Content is string content)
+ {
+ _selectedShape = content;
+
+ // If Polygon is selected, render the polygon immediately
+ if (_selectedShape == "Polygon")
+ {
+ // Remove any existing polygon
+ var polygons = _viewModel.MapElements.Where(el => el is Polygon).ToList();
+ foreach (var poly in polygons)
+ _viewModel.MapElements.Remove(poly);
+
+ // Add the polygon connecting all 9 locations
+ var destinations = new[]
+ {
+ new Location(21.3649, -157.9634), // Pin3
+ new Location(21.3986, -158.0097), // Pin2
+
+ new Location(21.4513, -158.0147), // Pin4
+ new Location(21.3847, -157.9261), // Pin5
+ };
+ var polygon = new Polygon
+ {
+ StrokeColor = Colors.Blue,
+ StrokeWidth = 3,
+ FillColor = Color.FromArgb("#330000FF")
+ };
+ foreach (var loc in destinations)
+ polygon.Geopath.Add(loc);
+ _viewModel.MapElements.Add(polygon);
+ }
+ else
+ {
+ // Remove any existing polygon if another shape is selected
+ var polygons = _viewModel.MapElements.Where(el => el is Polygon).ToList();
+ foreach (var poly in polygons)
+ _viewModel.MapElements.Remove(poly);
+ }
+ }
+ }
+
+ private void AddElementButton_Clicked(object sender, EventArgs e)
+ {
+ // Use the same destinations for all shapes
+ var destinations = new[]
+ {
+ new { Name = "Pin2", Location = new Location(21.3986, -158.0097), Color = Colors.Red, Type = PlaceType.Restaurant },
+ new { Name = "Pin3", Location = new Location(21.3649, -157.9634), Color = Colors.Green, Type = PlaceType.Tourist },
+ new { Name = "Pin4", Location = new Location(21.4513, -158.0147), Color = Colors.Orange, Type = PlaceType.Restaurant },
+ new { Name = "Pin5", Location = new Location(21.3847, -157.9261), Color = Colors.Purple, Type = PlaceType.Tourist },
+ new { Name = "Pin6", Location = new Location(21.4644, -158.0411), Color = Colors.Brown, Type = PlaceType.Restaurant },
+ new { Name = "Pin7", Location = new Location(21.3408, -158.0061), Color = Colors.Pink, Type = PlaceType.Tourist },
+ new { Name = "Pin8", Location = new Location(21.3247, -157.9772), Color = Colors.Navy, Type = PlaceType.Restaurant },
+ new { Name = "Pin9", Location = new Location(21.3142, -158.0397), Color = Colors.Teal, Type = PlaceType.Tourist },
+ new { Name = "Pin10", Location = new Location(21.3350, -158.0550), Color = Colors.Maroon, Type = PlaceType.Restaurant }
+ };
+
+ if (_selectedShape == "Polyline")
+ {
+ // Add up to 9 polylines, one per click
+ int polylineCount = _viewModel.MapElements.Count(e => e is Polyline);
+ if (polylineCount >= 9)
+ return;
+ var destination = destinations[polylineCount];
+ var polyline = new Polyline
+ {
+ StrokeColor = destination.Color,
+ StrokeWidth = 4
+ };
+ polyline.Geopath.Add(MapViewModel.PearlCityLocation);
+ var startLat = MapViewModel.PearlCityLocation.Latitude;
+ var startLng = MapViewModel.PearlCityLocation.Longitude;
+ var endLat = destination.Location.Latitude;
+ var endLng = destination.Location.Longitude;
+ var midLat1 = startLat + (endLat - startLat) * 0.33;
+ var midLng1 = startLng + (endLng - startLng) * 0.33;
+ polyline.Geopath.Add(new Location(midLat1, midLng1));
+ var midLat2 = startLat + (endLat - startLat) * 0.67;
+ var midLng2 = startLng + (endLng - startLng) * 0.67;
+ polyline.Geopath.Add(new Location(midLat2, midLng2));
+ polyline.Geopath.Add(destination.Location);
+ _viewModel.MapElements.Add(polyline);
+ }
+ else if (_selectedShape == "Circle")
+ {
+ // Add up to 9 circles, one per click
+ int circleCount = _viewModel.MapElements.Count(e => e is Circle && ((Circle)e).Radius.Kilometers == 2);
+ if (circleCount >= 9)
+ return;
+ var destination = destinations[circleCount];
+ var circle = new Circle
+ {
+ Center = destination.Location,
+ Radius = Distance.FromKilometers(2),
+ StrokeColor = destination.Color,
+ StrokeWidth = 2,
+ FillColor = Color.FromArgb("#3300FF00")
+ };
+ _viewModel.MapElements.Add(circle);
+ }
+ else if (_selectedShape == "Polygon")
+ {
+ bool polygonExists = _viewModel.MapElements.Any(e => e is Polygon);
+ if (polygonExists)
+ return;
+
+ var polygon = new Polygon
+ {
+ StrokeColor = Colors.Blue,
+ StrokeWidth = 3,
+ FillColor = Color.FromArgb("#330000FF")
+ };
+ polygon.Geopath.Add(MapViewModel.PearlCityLocation);
+ polygon.Geopath.Add(destinations[0].Location);
+ polygon.Geopath.Add(destinations[1].Location);
+ polygon.Geopath.Add(destinations[3].Location);
+
+ _viewModel.MapElements.Add(polygon);
+ }
+ }
+ private void SetItemsSourceButton_Clicked(object sender, EventArgs e)
+ {
+ _viewModel.Pins.Clear(); // Clear any existing pins
+ _viewModel.ItemsSource = _viewModel.Places;
+ }
+
+ private void ClearItemsSourceButton_Clicked(object sender, EventArgs e)
+ {
+ // Clear the ItemsSource to disable data templating
+ // This will revert to manual pin management
+ _viewModel.ItemsSource = null;
+ }
+
+ private void SetItemTemplateButton_Clicked(object sender, EventArgs e)
+ {
+ // Create a sample DataTemplate using a Pin to render place info
+ var pinTemplate = new DataTemplate(() =>
+ {
+ var pin = new Pin
+ {
+ Type = PinType.SearchResult
+ };
+ pin.SetBinding(Pin.LocationProperty, new Binding("Location"));
+ pin.SetBinding(Pin.LabelProperty, new Binding("Name"));
+ pin.SetBinding(Pin.AddressProperty, new Binding("Description"));
+
+ return pin;
+ });
+
+ _viewModel.ItemTemplate = pinTemplate;
+ }
+
+ private void ClearItemTemplateButton_Clicked(object sender, EventArgs e)
+ {
+ // Clear the ItemTemplate
+ _viewModel.ItemTemplate = null;
+ }
+
+ private void SetTemplateSelectorButton_Clicked(object sender, EventArgs e)
+ {
+ // Create sample templates for the selector using Grid with red background
+ var restaurantTemplate = new DataTemplate(() =>
+ {
+ var pin = new Pin
+ {
+ Type = PinType.Place,
+ Address = "Restaurant Pin",
+ };
+ pin.SetBinding(Pin.LocationProperty, new Binding("Location"));
+ pin.SetBinding(Pin.LabelProperty, new Binding("Name"));
+ return pin;
+ });
+
+ var touristTemplate = new DataTemplate(() =>
+ {
+ var pin = new Pin
+ {
+ Type = PinType.SearchResult,
+ Address = "Tourist Pin"
+ };
+ pin.SetBinding(Pin.LocationProperty, new Binding("Location"));
+ pin.SetBinding(Pin.LabelProperty, new Binding("Name"));
+
+ return pin;
+ });
+
+ // Setup ItemTemplateSelector
+ var templateSelector = new SamplePinTemplateSelector
+ {
+ RestaurantTemplate = restaurantTemplate,
+ TouristTemplate = touristTemplate
+ };
+
+ _viewModel.ItemTemplateSelector = templateSelector;
+
+ // Note: ItemsSource must be set separately via "Set ItemsSource" button
+ // Template selector only works when ItemsSource is explicitly set by the user
+ }
+
+ private void ClearTemplateSelectorButton_Clicked(object sender, EventArgs e)
+ {
+ // Clear the ItemTemplateSelector
+ _viewModel.ItemTemplateSelector = null;
+ }
+
+ private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(_viewModel.VisibleRegion))
+ {
+ UpdateVisibleRegionDisplay();
+ }
+ }
+
+ private void UpdateVisibleRegionDisplay()
+ {
+ // RegionDetailsLabel is commented out in XAML, so no UI update needed
+ // This method can be used for debugging or future implementation
+ if (_viewModel.VisibleRegion != null)
+ {
+ var region = _viewModel.VisibleRegion;
+ // Could log or store region info for debugging:
+ // $"Lat: {region.Center.Latitude:F4}, Lng: {region.Center.Longitude:F4}, LatDeg: {region.LatitudeDegrees:F4}, LngDeg: {region.LongitudeDegrees:F4}"
+ }
+ }
+
+ private void ZoomInButton_Clicked(object sender, EventArgs e)
+ {
+ // Only allow zoom if IsZoomEnabled is checked
+ if (!_viewModel.IsZoomEnabled)
+ {
+ return; // Don't zoom if zoom is disabled
+ }
+
+ // Zoom in by reducing the radius by half
+ if (_viewModel.VisibleRegion != null)
+ {
+ var currentRegion = _viewModel.VisibleRegion;
+ var newRadius = Distance.FromMeters(currentRegion.Radius.Meters / 2);
+ _viewModel.VisibleRegion = MapSpan.FromCenterAndRadius(currentRegion.Center, newRadius);
+ }
+ }
+
+ private void ZoomOutButton_Clicked(object sender, EventArgs e)
+ {
+ // Only allow zoom if IsZoomEnabled is checked
+ if (!_viewModel.IsZoomEnabled)
+ {
+ return; // Don't zoom if zoom is disabled
+ }
+
+ // Zoom out by doubling the radius
+ if (_viewModel.VisibleRegion != null)
+ {
+ var currentRegion = _viewModel.VisibleRegion;
+ var newRadius = Distance.FromMeters(currentRegion.Radius.Meters * 2);
+ _viewModel.VisibleRegion = MapSpan.FromCenterAndRadius(currentRegion.Center, newRadius);
+ }
+ }
+
+ private void ShowAllPinsButton_Clicked(object sender, EventArgs e)
+ {
+ // Calculate a region that encompasses all pins
+ if (_viewModel.Pins.Any())
+ {
+ var pins = _viewModel.Pins.ToList();
+
+ // Find the bounds of all pins
+ var minLat = pins.Min(p => p.Location.Latitude);
+ var maxLat = pins.Max(p => p.Location.Latitude);
+ var minLng = pins.Min(p => p.Location.Longitude);
+ var maxLng = pins.Max(p => p.Location.Longitude);
+
+ // Calculate center point
+ var centerLat = (minLat + maxLat) / 2;
+ var centerLng = (minLng + maxLng) / 2;
+ var center = new Location(centerLat, centerLng);
+
+ // Calculate the distance to encompass all pins with some padding
+ var latDelta = Math.Abs(maxLat - minLat);
+ var lngDelta = Math.Abs(maxLng - minLng);
+ var maxDelta = Math.Max(latDelta, lngDelta);
+
+ // Add 20% padding and ensure minimum radius
+ var radiusKm = Math.Max(maxDelta * 111 * 0.6, 1); // 111 km per degree, 20% padding
+ var radius = Distance.FromKilometers(radiusKm);
+
+ _viewModel.VisibleRegion = MapSpan.FromCenterAndRadius(center, radius);
+ }
+ else
+ {
+ // If no pins, just reset to initial state
+ ResetToInitialButton_Clicked(sender, e);
+ }
+ }
+
+ private void ResetToInitialButton_Clicked(object sender, EventArgs e)
+ {
+ // Reset all properties to their original state
+
+ // Reset boolean properties to their default values
+ _viewModel.IsShowingUser = false;
+ _viewModel.IsScrollEnabled = false;
+ _viewModel.IsTrafficEnabled = false;
+ _viewModel.IsZoomEnabled = false;
+ _viewModel.IsVisible = true;
+
+ // Reset MapType to default
+ _viewModel.MapType = MapType.Street;
+
+ // Reset Map Element radio button to Polyline
+ _selectedShape = "Polyline";
+
+ // Clear all pins and reset user pin count (from ClearPinsButton functionality)
+ _viewModel.Pins.Clear();
+ _viewModel.UserAddedPinCount = 0;
+ _viewModel.Places.Clear();
+
+ // Clear all map elements (from ClearElementsButton functionality)
+ _viewModel.MapElements.Clear();
+
+ // Reset template properties
+ _viewModel.ItemsSource = null;
+ _viewModel.ItemTemplate = null;
+ _viewModel.ItemTemplateSelector = null;
+
+ // Reset visible region to initial region
+ _viewModel.VisibleRegion = _viewModel.InitialRegion;
+
+ // Update radio buttons to reflect the reset MapType
+ UpdateRadioButtonsFromMapType();
+
+ // Also reset the clicked location pin and label on the map page
+ var nav = this.Navigation;
+ if (nav != null && nav.NavigationStack.Count > 0)
+ {
+ // Find the MapControlMainPage in the navigation stack
+ foreach (var page in nav.NavigationStack)
+ {
+ if (page is Maui.Controls.Sample.MapControlMainPage mainPage)
+ {
+ mainPage.ResetClickedLocationPinAndLabel();
+ break;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Map/MapViewModel.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Map/MapViewModel.cs
new file mode 100644
index 000000000000..a7d28c6d5d3e
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Map/MapViewModel.cs
@@ -0,0 +1,243 @@
+using System.Collections;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using Microsoft.Maui.Maps;
+using Microsoft.Maui.Controls.Maps;
+using System.Windows.Input;
+
+namespace Maui.Controls.Sample;
+
+public class SamplePinTemplateSelector : DataTemplateSelector
+{
+ public DataTemplate RestaurantTemplate { get; set; }
+ public DataTemplate TouristTemplate { get; set; }
+
+ protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
+ {
+ if (item is PlaceInfo placeInfo)
+ {
+ return placeInfo.Type == PlaceType.Tourist ? TouristTemplate : RestaurantTemplate;
+ }
+ return RestaurantTemplate;
+ }
+}
+
+public class MapViewModel : INotifyPropertyChanged
+{
+ public static readonly Location PearlCityLocation = new Location(21.3933, -157.9751);
+ private bool _isShowingUser = false;
+ private bool _isScrollEnabled = false;
+ private bool _isTrafficEnabled;
+ private bool _isZoomEnabled = false;
+ private bool _isVisible = true;
+ private MapType _mapType = MapType.Street;
+ private ObservableCollection _pins = new();
+ private ObservableCollection _mapElements = new();
+ private IEnumerable _itemsSource;
+ private DataTemplate _itemTemplate;
+ private DataTemplateSelector _itemTemplateSelector;
+ private MapSpan _initialRegion;
+ private MapSpan _visibleRegion;
+ private ICommand _mapClickedCommand;
+ private int _userAddedPinCount = 0;
+
+ public ObservableCollection Places { get; set; } = new();
+
+ public MapViewModel()
+ {
+
+ _initialRegion = MapSpan.FromCenterAndRadius(
+ PearlCityLocation,
+ Distance.FromMiles(5)
+ );
+
+ // Initialize visible region to the same as initial region
+ _visibleRegion = _initialRegion;
+
+ // Start with ItemsSource cleared (null) - user must click "Set ItemsSource" button to enable data templating
+ // This allows manual pin management by default, and data templating only when explicitly requested
+ _itemsSource = null;
+
+ // Note: IsShowingUser is set to false by default
+ // No pins are added initially - pins will only appear when user clicks "Add Pin"
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public bool IsShowingUser
+ {
+ get => _isShowingUser;
+ set
+ {
+ _isShowingUser = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public bool IsScrollEnabled
+ {
+ get => _isScrollEnabled;
+ set
+ {
+ _isScrollEnabled = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public bool IsTrafficEnabled
+ {
+ get => _isTrafficEnabled;
+ set
+ {
+ _isTrafficEnabled = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public bool IsZoomEnabled
+ {
+ get => _isZoomEnabled;
+ set
+ {
+ _isZoomEnabled = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public bool IsVisible
+ {
+ get => _isVisible;
+ set
+ {
+ _isVisible = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public MapType MapType
+ {
+ get => _mapType;
+ set
+ {
+ _mapType = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public ObservableCollection Pins
+ {
+ get => _pins;
+ set
+ {
+ _pins = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public int UserAddedPinCount
+ {
+ get => _userAddedPinCount;
+ set
+ {
+ _userAddedPinCount = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public ObservableCollection MapElements
+ {
+ get => _mapElements;
+ set
+ {
+ _mapElements = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public IEnumerable ItemsSource
+ {
+ get => _itemsSource;
+ set
+ {
+ _itemsSource = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public DataTemplate ItemTemplate
+ {
+ get => _itemTemplate;
+ set
+ {
+ _itemTemplate = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public DataTemplateSelector ItemTemplateSelector
+ {
+ get => _itemTemplateSelector;
+ set
+ {
+ _itemTemplateSelector = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public List MapTypeOptions { get; } = new List
+ {
+ MapType.Street,
+ MapType.Satellite,
+ MapType.Hybrid
+ };
+
+ public MapSpan InitialRegion
+ {
+ get => _initialRegion;
+ set
+ {
+ _initialRegion = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public MapSpan VisibleRegion
+ {
+ get => _visibleRegion;
+ set
+ {
+ _visibleRegion = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public ICommand MapClickedCommand
+ {
+ get => _mapClickedCommand;
+ set
+ {
+ _mapClickedCommand = value;
+ OnPropertyChanged();
+ }
+ }
+
+ protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+}
+
+public class PlaceInfo
+{
+ public string Name { get; set; } = string.Empty;
+ public string Description { get; set; } = string.Empty;
+ public Location Location { get; set; } = new Location();
+ public PlaceType Type { get; set; }
+}
+
+public enum PlaceType
+{
+ Restaurant,
+ Tourist,
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/RefreshView/RefreshViewControlPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/RefreshView/RefreshViewControlPage.xaml
index c17554fcde90..ad6548c7456d 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/RefreshView/RefreshViewControlPage.xaml
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/RefreshView/RefreshViewControlPage.xaml
@@ -13,7 +13,7 @@
@@ -40,7 +40,8 @@
IsRefreshing="{Binding IsRefreshing}"
RefreshColor="{Binding RefreshColor}"
Shadow="{Binding Shadow}"
- AutomationId="RefreshView">
+ AutomationId="RefreshView"
+ Refreshing="OnRefreshViewRefreshing">
+
+
+
+
-
\ No newline at end of file
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/RefreshView/RefreshViewControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/RefreshView/RefreshViewControlPage.xaml.cs
index 113d58a8b22a..6f3048e91b1a 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/RefreshView/RefreshViewControlPage.xaml.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/RefreshView/RefreshViewControlPage.xaml.cs
@@ -50,7 +50,7 @@ private void SetScrollViewContent()
// Set binding immediately
boxView.SetBinding(BoxView.ColorProperty, "BoxViewColor");
- var RefreshView = new RefreshView
+ var refreshView = new RefreshView
{
AutomationId = "RefreshView",
Content = new ScrollView
@@ -59,22 +59,24 @@ private void SetScrollViewContent()
}
};
- RefreshView.SetBinding(RefreshView.CommandProperty, "Command");
- RefreshView.SetBinding(RefreshView.CommandParameterProperty, "CommandParameter");
- RefreshView.SetBinding(RefreshView.FlowDirectionProperty, "FlowDirection");
- RefreshView.SetBinding(RefreshView.IsEnabledProperty, "IsEnabled");
- RefreshView.SetBinding(RefreshView.IsVisibleProperty, "IsVisible");
- RefreshView.SetBinding(RefreshView.IsRefreshingProperty, "IsRefreshing");
- RefreshView.SetBinding(RefreshView.RefreshColorProperty, "RefreshColor");
- RefreshView.SetBinding(RefreshView.ShadowProperty, "Shadow");
+ refreshView.Refreshing += OnRefreshViewRefreshing;
- RefreshViewContainer.Children.Add(RefreshView);
+ refreshView.SetBinding(RefreshView.CommandProperty, "Command");
+ refreshView.SetBinding(RefreshView.CommandParameterProperty, "CommandParameter");
+ refreshView.SetBinding(RefreshView.FlowDirectionProperty, "FlowDirection");
+ refreshView.SetBinding(RefreshView.IsEnabledProperty, "IsEnabled");
+ refreshView.SetBinding(RefreshView.IsVisibleProperty, "IsVisible");
+ refreshView.SetBinding(RefreshView.IsRefreshingProperty, "IsRefreshing");
+ refreshView.SetBinding(RefreshView.RefreshColorProperty, "RefreshColor");
+ refreshView.SetBinding(RefreshView.ShadowProperty, "Shadow");
+
+ RefreshViewContainer.Children.Add(refreshView);
}
private void SetCollectionViewContent()
{
RefreshViewContainer.Children.Clear();
- var RefreshView = new RefreshView
+ var refreshView = new RefreshView
{
AutomationId = "RefreshView",
Content = new CollectionView
@@ -99,16 +101,19 @@ private void SetCollectionViewContent()
}
};
- RefreshView.SetBinding(RefreshView.CommandProperty, "Command");
- RefreshView.SetBinding(RefreshView.CommandParameterProperty, "CommandParameter");
- RefreshView.SetBinding(RefreshView.FlowDirectionProperty, "FlowDirection");
- RefreshView.SetBinding(RefreshView.IsEnabledProperty, "IsEnabled");
- RefreshView.SetBinding(RefreshView.IsVisibleProperty, "IsVisible");
- RefreshView.SetBinding(RefreshView.IsRefreshingProperty, "IsRefreshing");
- RefreshView.SetBinding(RefreshView.RefreshColorProperty, "RefreshColor");
- RefreshView.SetBinding(RefreshView.ShadowProperty, "Shadow");
+ refreshView.Refreshing += OnRefreshViewRefreshing;
+
- RefreshViewContainer.Children.Add(RefreshView);
+ refreshView.SetBinding(RefreshView.CommandProperty, "Command");
+ refreshView.SetBinding(RefreshView.CommandParameterProperty, "CommandParameter");
+ refreshView.SetBinding(RefreshView.FlowDirectionProperty, "FlowDirection");
+ refreshView.SetBinding(RefreshView.IsEnabledProperty, "IsEnabled");
+ refreshView.SetBinding(RefreshView.IsVisibleProperty, "IsVisible");
+ refreshView.SetBinding(RefreshView.IsRefreshingProperty, "IsRefreshing");
+ refreshView.SetBinding(RefreshView.RefreshColorProperty, "RefreshColor");
+ refreshView.SetBinding(RefreshView.ShadowProperty, "Shadow");
+
+ RefreshViewContainer.Children.Add(refreshView);
}
private void OnScrollViewContentClicked(object sender, EventArgs e)
@@ -120,4 +125,12 @@ private void OnCollectionViewContentClicked(object sender, EventArgs e)
{
SetCollectionViewContent();
}
-}
\ No newline at end of file
+
+ private void OnRefreshViewRefreshing(object sender, EventArgs e)
+{
+ if (BindingContext is RefreshViewViewModel vm)
+ {
+ vm.RefreshEventStatusText = "Raised";
+ }
+}
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/RefreshView/RefreshViewViewModel.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/RefreshView/RefreshViewViewModel.cs
index 9b2de2030e84..da087a1325d4 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/RefreshView/RefreshViewViewModel.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/RefreshView/RefreshViewViewModel.cs
@@ -184,8 +184,23 @@ public string RefreshStatusText
}
}
+ private string _refreshEventStatusText = "Not Raised";
+
+ public string RefreshEventStatusText
+ {
+ get => _refreshEventStatusText;
+ set
+ {
+ if (_refreshEventStatusText != value)
+ {
+ _refreshEventStatusText = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
-}
\ No newline at end of file
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/SearchBarMaterial3/Material3_SearchBarControlPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/SearchBarMaterial3/Material3_SearchBarControlPage.xaml
new file mode 100644
index 000000000000..3d359676756a
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/SearchBarMaterial3/Material3_SearchBarControlPage.xaml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/SearchBarMaterial3/Material3_SearchBarControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/SearchBarMaterial3/Material3_SearchBarControlPage.xaml.cs
new file mode 100644
index 000000000000..d9061a77eda6
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/SearchBarMaterial3/Material3_SearchBarControlPage.xaml.cs
@@ -0,0 +1,94 @@
+namespace Maui.Controls.Sample;
+
+public class Material3_SearchBarControlPage : NavigationPage
+{
+ private SearchBarViewModel _viewModel;
+ public Material3_SearchBarControlPage()
+ {
+ _viewModel = new SearchBarViewModel();
+ PushAsync(new SearchBarMaterial3ControlMainPage(_viewModel));
+ }
+}
+
+public partial class SearchBarMaterial3ControlMainPage : ContentPage
+{
+ private SearchBarViewModel _viewModel;
+
+ public SearchBarMaterial3ControlMainPage(SearchBarViewModel viewModel)
+ {
+ InitializeComponent();
+ _viewModel = viewModel;
+ BindingContext = _viewModel;
+ }
+
+ private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
+ {
+ BindingContext = _viewModel = new SearchBarViewModel();
+ ReInitializeSearchBar();
+ await Navigation.PushAsync(new SearchBarOptionsPage(_viewModel));
+ }
+
+ private void ReInitializeSearchBar()
+ {
+ SearchBarGrid.Children.Clear();
+
+ var searchBar = new SearchBar
+ {
+ AutomationId = "SearchBar",
+ };
+
+ searchBar.SetBinding(SearchBar.CancelButtonColorProperty, nameof(SearchBarViewModel.CancelButtonColor));
+ searchBar.SetBinding(SearchBar.CharacterSpacingProperty, nameof(SearchBarViewModel.CharacterSpacing));
+ searchBar.SetBinding(SearchBar.CursorPositionProperty, nameof(SearchBarViewModel.CursorPosition));
+ searchBar.SetBinding(SearchBar.FlowDirectionProperty, nameof(SearchBarViewModel.FlowDirection));
+ searchBar.SetBinding(SearchBar.FontAttributesProperty, nameof(SearchBarViewModel.FontAttributes));
+ searchBar.SetBinding(SearchBar.FontAutoScalingEnabledProperty, nameof(SearchBarViewModel.FontAutoScalingEnabled));
+ searchBar.SetBinding(SearchBar.FontFamilyProperty, nameof(SearchBarViewModel.FontFamily));
+ searchBar.SetBinding(SearchBar.FontSizeProperty, nameof(SearchBarViewModel.FontSize));
+ searchBar.SetBinding(SearchBar.HorizontalTextAlignmentProperty, nameof(SearchBarViewModel.HorizontalTextAlignment));
+ searchBar.SetBinding(SearchBar.IsEnabledProperty, nameof(SearchBarViewModel.IsEnabled));
+ searchBar.SetBinding(SearchBar.IsVisibleProperty, nameof(SearchBarViewModel.IsVisible));
+ searchBar.SetBinding(SearchBar.ShadowProperty, nameof(SearchBarViewModel.Shadow));
+ searchBar.SetBinding(SearchBar.IsReadOnlyProperty, nameof(SearchBarViewModel.IsReadOnly));
+ searchBar.SetBinding(SearchBar.IsSpellCheckEnabledProperty, nameof(SearchBarViewModel.IsSpellCheckEnabled));
+ searchBar.SetBinding(SearchBar.IsTextPredictionEnabledProperty, nameof(SearchBarViewModel.IsTextPredictionEnabled));
+ searchBar.SetBinding(SearchBar.KeyboardProperty, nameof(SearchBarViewModel.Keyboard));
+ searchBar.SetBinding(SearchBar.MaxLengthProperty, nameof(SearchBarViewModel.MaxLength));
+ searchBar.SetBinding(SearchBar.PlaceholderProperty, nameof(SearchBarViewModel.Placeholder));
+ searchBar.SetBinding(SearchBar.PlaceholderColorProperty, nameof(SearchBarViewModel.PlaceholderColor));
+ searchBar.SetBinding(SearchBar.SelectionLengthProperty, nameof(SearchBarViewModel.SelectionLength));
+ searchBar.SetBinding(SearchBar.TextProperty, nameof(SearchBarViewModel.Text));
+ searchBar.SetBinding(SearchBar.TextColorProperty, nameof(SearchBarViewModel.TextColor));
+ searchBar.SetBinding(SearchBar.TextTransformProperty, nameof(SearchBarViewModel.TextTransform));
+ searchBar.SetBinding(SearchBar.VerticalTextAlignmentProperty, nameof(SearchBarViewModel.VerticalTextAlignment));
+ searchBar.SetBinding(SearchBar.SearchCommandProperty, nameof(SearchBarViewModel.SearchCommand));
+ searchBar.SetBinding(SearchBar.SearchCommandParameterProperty, nameof(SearchBarViewModel.Text));
+
+ searchBar.SearchButtonPressed += OnSearchButtonPressed;
+ searchBar.TextChanged += OnTextChanged;
+
+ NewTextChangedLabel.Text = string.Empty;
+ OldTextChangedLabel.Text = string.Empty;
+ SearchButtonPressedLabel.Text = "No";
+
+ SearchBarGrid.Children.Add(searchBar);
+ }
+
+ private void OnSearchButtonPressed(object sender, EventArgs e)
+ {
+ var searchBar = sender as SearchBar;
+ if (searchBar != null && !string.IsNullOrEmpty(searchBar.Text))
+ {
+ SearchButtonPressedLabel.Text = "Yes";
+ }
+ }
+
+ private void OnTextChanged(object sender, TextChangedEventArgs e)
+ {
+ if (!string.IsNullOrEmpty(e.NewTextValue))
+ {
+ NewTextChangedLabel.Text = e.NewTextValue;
+ OldTextChangedLabel.Text = e.OldTextValue;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellFeaturePage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellFeaturePage.xaml
index 0e7b6d3c220e..831cbfd0fad4 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellFeaturePage.xaml
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellFeaturePage.xaml
@@ -17,5 +17,15 @@
HorizontalOptions="Center"
AutomationId="ShellTabbedButton"
WidthRequest="400"/>
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellFeaturePage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellFeaturePage.xaml.cs
index 2086a1b9ada1..8aebc1021207 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellFeaturePage.xaml.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellFeaturePage.xaml.cs
@@ -5,9 +5,8 @@ namespace Maui.Controls.Sample;
public class ShellFeaturePage : NavigationPage
{
- public ShellFeaturePage()
+ public ShellFeaturePage() : base(new ShellFeatureMainPage())
{
- PushAsync(new ShellFeatureMainPage());
}
}
@@ -27,4 +26,14 @@ private void OnShellTabbedButtonClicked(object sender, EventArgs e)
{
Application.Current.MainPage = new ShellTabbedControlPage();
}
+
+ private void OnShellPageButtonClicked(object sender, EventArgs e)
+ {
+ this.Window.Page = new ShellControlPage();
+ }
+
+ private void OnShellNavigationButtonClicked(object sender, EventArgs e)
+ {
+ this.Window.Page = new ShellNavigationControlPage();
+ }
}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellNavigation/ShellNavigationControlPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellNavigation/ShellNavigationControlPage.xaml
new file mode 100644
index 000000000000..63bbfc043528
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellNavigation/ShellNavigationControlPage.xaml
@@ -0,0 +1,903 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellNavigation/ShellNavigationControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellNavigation/ShellNavigationControlPage.xaml.cs
new file mode 100644
index 000000000000..97913b1628c4
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellNavigation/ShellNavigationControlPage.xaml.cs
@@ -0,0 +1,807 @@
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Maui.Controls;
+namespace Maui.Controls.Sample
+{
+ public partial class ShellNavigationControlPage : Shell
+ {
+ readonly ShellViewModel _viewModel;
+ string _previousPageTitle = "null";
+ public ShellNavigationControlPage()
+ {
+ _viewModel = new ShellViewModel();
+ BindingContext = _viewModel;
+ InitializeComponent();
+ Routing.RegisterRoute("detail1", typeof(DetailPage1));
+ Routing.RegisterRoute("detail2", typeof(DetailPage2));
+ Routing.RegisterRoute("detail1/subdetail", typeof(SubDetailPage)); // Contextual route from Detail1
+ Routing.RegisterRoute("detail2/subdetail", typeof(SubDetailPage)); // Contextual route from Detail2
+
+ // Register navigation test pages
+ Routing.RegisterRoute("navtest1", typeof(NavigationTestPage1));
+ Routing.RegisterRoute("navtest2", typeof(NavigationTestPage2));
+ Routing.RegisterRoute("navtest3", typeof(NavigationTestPage3));
+
+ // Pass data demo routes
+ Routing.RegisterRoute("querysender", typeof(QuerySenderPage));
+ Routing.RegisterRoute("querydetail", typeof(QueryDataDetailPage));
+ Routing.RegisterRoute("queryintermediate", typeof(QueryIntermediatePage));
+
+ this.Navigating += OnShellNavigating;
+ this.Navigated += OnShellNavigated;
+
+ }
+ public ShellViewModel ViewModel => _viewModel;
+ void UpdateCurrentState()
+ {
+ var shell = Shell.Current;
+ if (shell != null)
+ {
+ _viewModel.CurrentState = shell.CurrentState?.Location?.ToString() ?? "Not Set";
+ _viewModel.CurrentPage = shell.CurrentPage?.Title ?? "Not Set";
+ _viewModel.CurrentItem = shell.CurrentItem?.Title ?? "Not Set";
+ _viewModel.ShellCurrent = shell.GetType().Name;
+ }
+ }
+ void UpdatePage2State()
+ {
+ UpdatePageLabels(Page2CurrentStateLabel, Page2CurrentPageLabel, Page2CurrentItemLabel, Page2ShellCurrentLabel, Page2ContentPage);
+
+ // Update Tab.Stack info
+ var shell = Shell.Current;
+ var section = shell?.CurrentItem?.CurrentItem;
+ if (section != null)
+ {
+ var stack = section.Stack;
+ _viewModel.TabStackInfo = $"Count={stack.Count}: {string.Join(", ", stack.Select(p => p?.Title ?? "null"))}";
+ }
+ }
+ void UpdatePage3C1State()
+ {
+ UpdatePageLabels(Page3C1CurrentStateLabel, Page3C1CurrentPageLabel, Page3C1CurrentItemLabel, Page3C1ShellCurrentLabel, Page3C1ContentPage);
+ }
+ void UpdatePage3C2State()
+ {
+ UpdatePageLabels(Page3C2CurrentStateLabel, Page3C2CurrentPageLabel, Page3C2CurrentItemLabel, Page3C2ShellCurrentLabel, Page3C2ContentPage);
+ }
+ void UpdatePage2TabBState()
+ {
+ UpdatePageLabels(Page2TabBCurrentStateLabel, Page2TabBCurrentPageLabel, Page2TabBCurrentItemLabel, Page2TabBShellCurrentLabel, Page2TabBPage);
+ }
+ void UpdatePageLabels(Label stateLabel, Label pageLabel, Label itemLabel, Label shellLabel, ContentPage page)
+ {
+ var shell = Shell.Current;
+ if (shell != null)
+ {
+ stateLabel.Text = shell.CurrentState?.Location?.ToString() ?? "Not Set";
+ pageLabel.Text = shell.CurrentPage?.Title ?? "Not Set";
+ itemLabel.Text = shell.CurrentItem?.Title ?? "Not Set";
+ shellLabel.Text = shell.GetType().Name;
+ page.BindingContext = _viewModel;
+ }
+ }
+ public void OnIconOverrideClicked(object sender, EventArgs e)
+ {
+ if (sender is Button btn)
+ {
+ if (btn.Text == "None")
+ _viewModel.IconOverride = string.Empty;
+ else
+ _viewModel.IconOverride = btn.Text;
+ }
+ }
+ void OnToggleIsEnabled(object sender, EventArgs e)
+ {
+ _viewModel.IsEnabled = !_viewModel.IsEnabled;
+ }
+ void OnToggleIsVisible(object sender, EventArgs e)
+ {
+ _viewModel.IsVisible = !_viewModel.IsVisible;
+ }
+ async void OnNavigateToDetail1Clicked(object sender, EventArgs e)
+ {
+ await Shell.Current.GoToAsync("detail1");
+ }
+ async void OnNavigateToDetail2Clicked(object sender, EventArgs e)
+ {
+ await Shell.Current.GoToAsync("detail2");
+ }
+ async void OnGoToMainClicked(object sender, EventArgs e)
+ {
+ await Shell.Current.GoToAsync("//main/MainContent");
+ }
+ async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
+ {
+ await Navigation.PushAsync(new ShellNavigationOptionsPage(_viewModel));
+ }
+ void OnShellNavigating(object sender, ShellNavigatingEventArgs e)
+ {
+ _previousPageTitle = Shell.Current?.CurrentPage?.Title ?? "null";
+ _viewModel.NavigatingCurrent = Shell.Current?.CurrentPage?.Title ?? "null";
+ _viewModel.NavigatingSource = e.Source.ToString();
+ _viewModel.NavigatingTarget = e.Target?.Location?.ToString() ?? "null";
+ _viewModel.NavigatingCanCancel = e.CanCancel.ToString();
+ _viewModel.NavigatingCancelled = e.Cancelled.ToString();
+
+ if (_viewModel.CancelNavigation && e.CanCancel)
+ {
+ e.Cancel();
+ _viewModel.NavigatingCancelled = e.Cancelled.ToString();
+ return;
+ }
+
+ if (_viewModel.EnableDeferral && e.CanCancel)
+ {
+ var deferral = e.GetDeferral();
+ _viewModel.DeferralStatus = "Deferring...";
+ _ = HandleDeferralAsync(deferral);
+ }
+ }
+
+ async Task HandleDeferralAsync(ShellNavigatingDeferral deferral)
+ {
+ try
+ {
+ await Task.Delay(2000);
+ _viewModel.DeferralStatus = "Deferral completed";
+ deferral.Complete();
+ }
+ catch (Exception ex)
+ {
+ _viewModel.DeferralStatus = $"Deferral error";
+ System.Diagnostics.Debug.WriteLine($"Deferral failed: {ex.Message}");
+ }
+ }
+ void OnShellNavigated(object sender, ShellNavigatedEventArgs e)
+ {
+ _viewModel.NavigatedCurrent = Shell.Current?.CurrentPage?.Title ?? "null";
+ _viewModel.NavigatedPrevious = _previousPageTitle;
+ _viewModel.NavigatedSource = e.Source.ToString();
+ UpdateCurrentState();
+ UpdatePage2State();
+ UpdatePage2TabBState();
+ UpdatePage3C1State();
+ UpdatePage3C2State();
+ }
+ protected override void OnNavigating(ShellNavigatingEventArgs args)
+ {
+ base.OnNavigating(args);
+ _viewModel.OverrideNavigatingStatus = $"Source={args.Source}, Target={args.Target?.Location}";
+ }
+ protected override void OnNavigated(ShellNavigatedEventArgs args)
+ {
+ base.OnNavigated(args);
+ _viewModel.OverrideNavigatedStatus = $"Source={args.Source}, Previous={args.Previous?.Location}";
+ }
+ bool _routeRegistered = true;
+ async void OnToggleRouteClicked(object sender, EventArgs e)
+ {
+ var btn = (Button)sender;
+ if (_routeRegistered)
+ {
+ Routing.UnRegisterRoute("detail2");
+ try
+ { await Shell.Current.GoToAsync("detail2"); _viewModel.RouteStatus = "Still works"; }
+ catch (Exception ex) { _viewModel.RouteStatus = "Unregistered"; System.Diagnostics.Debug.WriteLine($"Expected: {ex.Message}"); }
+ btn.Text = "Register Route";
+ _routeRegistered = false;
+ }
+ else
+ {
+ Routing.RegisterRoute("detail2", typeof(DetailPage2));
+ try
+ { await Shell.Current.GoToAsync("detail2"); _viewModel.RouteStatus = "Registered"; }
+ catch (Exception ex) { _viewModel.RouteStatus = $"{ex.Message}"; }
+ btn.Text = "Unregister Route";
+ _routeRegistered = true;
+ }
+ }
+ void OnResetClicked(object sender, EventArgs e)
+ {
+ _viewModel.TextOverride = string.Empty;
+ _viewModel.IconOverride = string.Empty;
+ _viewModel.IsEnabled = true;
+ _viewModel.IsVisible = true;
+ _viewModel.CommandParameter = string.Empty;
+ _viewModel.CommandExecuted = string.Empty;
+ _viewModel.CurrentState = "Not Set";
+ _viewModel.CurrentPage = "Not Set";
+ _viewModel.CurrentItem = "Not Set";
+ _viewModel.ShellCurrent = "Not Set";
+ _viewModel.NavigatingCurrent = string.Empty;
+ _viewModel.NavigatingSource = string.Empty;
+ _viewModel.NavigatingTarget = string.Empty;
+ _viewModel.NavigatingCanCancel = string.Empty;
+ _viewModel.NavigatingCancelled = string.Empty;
+ _viewModel.NavigatedCurrent = string.Empty;
+ _viewModel.NavigatedPrevious = string.Empty;
+ _viewModel.NavigatedSource = string.Empty;
+ _viewModel.RouteStatus = string.Empty;
+ _viewModel.CancelNavigation = false;
+ _viewModel.EnableDeferral = false;
+ _viewModel.DeferralStatus = string.Empty;
+ _viewModel.OverrideNavigatingStatus = string.Empty;
+ _viewModel.OverrideNavigatedStatus = string.Empty;
+ _viewModel.TabStackInfo = string.Empty;
+
+ // Restore Shell-level route state to initial state
+ if (!_routeRegistered)
+ {
+ Routing.RegisterRoute("detail2", typeof(DetailPage2));
+ _routeRegistered = true;
+ }
+ ToggleRouteButton.Text = "Unregister Route";
+ }
+ void OnToggleCancelNavigation(object sender, EventArgs e)
+ {
+ _viewModel.CancelNavigation = !_viewModel.CancelNavigation;
+ }
+ void OnToggleEnableDeferral(object sender, EventArgs e)
+ {
+ _viewModel.EnableDeferral = !_viewModel.EnableDeferral;
+ }
+ async void OnOpenPassDataDemoClicked(object sender, EventArgs e)
+ {
+ await Shell.Current.GoToAsync("querysender");
+ }
+ }
+ public class ShellDetailBasePage : ContentPage
+ {
+ readonly string _prefix;
+ Label _currentStateLabel;
+ Label _currentPageLabel;
+ Label _currentItemLabel;
+ Label _shellCurrentLabel;
+ Label _commandExecutedLabel;
+ public ShellDetailBasePage(string title, string prefix)
+ {
+ Title = title;
+ AutomationId = $"{prefix}Page";
+ _prefix = prefix;
+ var behavior = new BackButtonBehavior();
+ behavior.SetBinding(BackButtonBehavior.TextOverrideProperty, "TextOverride");
+ behavior.SetBinding(BackButtonBehavior.IconOverrideProperty, "IconOverride");
+ behavior.SetBinding(BackButtonBehavior.IsEnabledProperty, "IsEnabled");
+ behavior.SetBinding(BackButtonBehavior.IsVisibleProperty, "IsVisible");
+ behavior.SetBinding(BackButtonBehavior.CommandProperty, "Command");
+ behavior.SetBinding(BackButtonBehavior.CommandParameterProperty, "CommandParameter");
+ Shell.SetBackButtonBehavior(this, behavior);
+ BuildUI();
+ this.Appearing += OnPageAppearing;
+ }
+ void BuildUI()
+ {
+ _currentStateLabel = new Label { FontSize = 12, AutomationId = $"{_prefix}CurrentStateLabel" };
+ _currentPageLabel = new Label { FontSize = 12, AutomationId = $"{_prefix}CurrentPageLabel" };
+ _currentItemLabel = new Label { FontSize = 12, AutomationId = $"{_prefix}CurrentItemLabel" };
+ _shellCurrentLabel = new Label { FontSize = 12, AutomationId = $"{_prefix}ShellCurrentLabel" };
+ _commandExecutedLabel = new Label { FontSize = 12, AutomationId = $"{_prefix}CommandExecutedLabel" };
+ var identityLabel = new Label
+ {
+ Text = Title,
+ FontSize = 14,
+ FontAttributes = FontAttributes.Bold,
+ Margin = new Thickness(10, 8, 10, 4),
+ AutomationId = $"{_prefix}PageIdentityLabel"
+ };
+ var goBackButton = ShellNavHelper.CreateNavButton("Go Back", "..", $"{_prefix}GoBackButton");
+ var contextualNavButton = ShellNavHelper.CreateNavButton("Navigate SubDetail", "subdetail", $"{_prefix}ContextualNavButton");
+ var grid = new Grid
+ {
+ Padding = 10,
+ RowSpacing = 4,
+ ColumnSpacing = 10,
+ RowDefinitions = { new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto) },
+ ColumnDefinitions = { new ColumnDefinition(GridLength.Star), new ColumnDefinition(GridLength.Star) }
+ };
+ AddRow(grid, 0, "CurrentState:", _currentStateLabel);
+ AddRow(grid, 1, "CurrentPage:", _currentPageLabel);
+ AddRow(grid, 2, "CurrentItem:", _currentItemLabel);
+ AddRow(grid, 3, "Shell.Current:", _shellCurrentLabel);
+ AddRow(grid, 4, "CommandExecuted:", _commandExecutedLabel);
+ Grid.SetRow(goBackButton, 5);
+ Grid.SetColumn(goBackButton, 0);
+ Grid.SetColumnSpan(goBackButton, 2);
+ grid.Children.Add(goBackButton);
+ Grid.SetRow(contextualNavButton, 6);
+ Grid.SetColumn(contextualNavButton, 0);
+ Grid.SetColumnSpan(contextualNavButton, 2);
+ grid.Children.Add(contextualNavButton);
+ Content = new ScrollView
+ {
+ Content = new VerticalStackLayout
+ {
+ Spacing = 4,
+ Children = { identityLabel, grid }
+ }
+ };
+ }
+ static void AddRow(Grid grid, int row, string labelText, Label valueLabel)
+ {
+ var label = new Label { Text = labelText, FontSize = 12 };
+ Grid.SetRow(label, row);
+ Grid.SetColumn(label, 0);
+ Grid.SetRow(valueLabel, row);
+ Grid.SetColumn(valueLabel, 1);
+ grid.Children.Add(label);
+ grid.Children.Add(valueLabel);
+ }
+ void OnPageAppearing(object sender, EventArgs e)
+ {
+ UpdateState();
+ Shell.Current?.Navigated += OnShellNavigatedUpdateState;
+ }
+
+ void OnShellNavigatedUpdateState(object sender, ShellNavigatedEventArgs e)
+ {
+ if (Shell.Current?.CurrentPage == this)
+ UpdateState();
+ }
+
+ void UpdateState()
+ {
+ var shell = Shell.Current;
+ if (shell != null)
+ {
+ _currentStateLabel.Text = shell.CurrentState?.Location?.ToString() ?? "Not Set";
+ _currentPageLabel.Text = shell.CurrentPage?.Title ?? "Not Set";
+ _currentItemLabel.Text = shell.CurrentItem?.Title ?? "Not Set";
+ _shellCurrentLabel.Text = shell.GetType().Name;
+ if (shell is ShellNavigationControlPage controlPage)
+ {
+ var vm = controlPage.ViewModel;
+ _commandExecutedLabel.Text = vm.CommandExecuted;
+ BindingContext = vm;
+ }
+ }
+ }
+
+ protected override void OnDisappearing()
+ {
+ base.OnDisappearing();
+ Shell.Current?.Navigated -= OnShellNavigatedUpdateState;
+ }
+ }
+ public class DetailPage1 : ShellDetailBasePage
+ {
+ public DetailPage1() : base("DetailPage1", "Detail1")
+ {
+ var stackLayout = (Content as ScrollView)?.Content as VerticalStackLayout;
+ if (stackLayout == null)
+ return;
+ var absBtn = ShellNavHelper.CreateNavButton("Absolute to Page2", "//page2", "Detail1AbsoluteButton");
+ var relBtn = ShellNavHelper.CreateNavButton("Relative to NavTest1", "navtest1", "Detail1RelativeButton");
+ stackLayout.Children.Add(absBtn);
+ stackLayout.Children.Add(relBtn);
+ }
+ }
+
+ public class DetailPage2 : ShellDetailBasePage
+ {
+ public DetailPage2() : base("DetailPage2", "Detail2")
+ {
+ var stackLayout = (Content as ScrollView)?.Content as VerticalStackLayout;
+ if (stackLayout == null)
+ return;
+ var absToPage2Btn = ShellNavHelper.CreateNavButton("Absolute to Page2", "//page2", "Detail2AbsoluteButton");
+ var backBtn = ShellNavHelper.CreateNavButton("Go Back", "..", "Detail2BackButton");
+ stackLayout.Children.Add(absToPage2Btn);
+ stackLayout.Children.Add(backBtn);
+ }
+ }
+ public class SubDetailPage : ContentPage
+ {
+ public SubDetailPage()
+ {
+ Title = "SubDetailPage";
+ AutomationId = "SubDetailPage";
+ var currentRouteLabel = new Label { FontSize = 12, AutomationId = "SubDetailCurrentRouteLabel" };
+ var sourceContextLabel = new Label { FontSize = 12, AutomationId = "SubDetailSourceContextLabel" };
+ var identityLabel = new Label
+ {
+ Text = "SubDetail Page",
+ FontSize = 14,
+ FontAttributes = FontAttributes.Bold,
+ Margin = new Thickness(10, 8, 10, 4),
+ AutomationId = "SubDetailPageIdentityLabel"
+ };
+ var goBackButton = ShellNavHelper.CreateNavButton("Go Back", "..", "SubDetailGoBackButton");
+ this.Appearing += (s, e) =>
+ {
+ UpdateLabels(currentRouteLabel, sourceContextLabel);
+ Shell.Current?.Navigated += OnNavigated;
+ };
+ this.Disappearing += (s, e) =>
+ {
+ Shell.Current?.Navigated -= OnNavigated;
+ };
+
+ void OnNavigated(object sender, ShellNavigatedEventArgs e)
+ {
+ if (Shell.Current?.CurrentPage == this)
+ UpdateLabels(currentRouteLabel, sourceContextLabel);
+ }
+
+ void UpdateLabels(Label routeLabel, Label contextLabel)
+ {
+ var shell = Shell.Current;
+ var location = shell?.CurrentState?.Location?.ToString() ?? "unknown";
+ routeLabel.Text = location;
+ contextLabel.Text = location.Contains("detail1", StringComparison.Ordinal) ? "Contextual from: detail1"
+ : location.Contains("detail2", StringComparison.Ordinal) ? "Contextual from: detail2"
+ : "Unknown context";
+ }
+ Content = new ScrollView
+ {
+ Content = new VerticalStackLayout
+ {
+ Spacing = 0,
+ Children =
+ {
+ identityLabel,
+ new Grid
+ {
+ Padding = 10,
+ RowSpacing = 4,
+ ColumnSpacing = 10,
+ RowDefinitions = { new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto) },
+ ColumnDefinitions = { new ColumnDefinition(GridLength.Star), new ColumnDefinition(GridLength.Star) },
+ Children =
+ {
+ CreateLabel("Current Route:", 0, 0),
+ SetGrid(currentRouteLabel, 0, 1),
+ CreateLabel("Source Context:", 1, 0),
+ SetGrid(sourceContextLabel, 1, 1),
+ SetGrid(goBackButton, 2, 0, 2)
+ }
+ }
+ }
+ }
+ };
+ }
+ static Label CreateLabel(string text, int row, int col, int colSpan = 1, bool bold = false)
+ {
+ var label = new Label { Text = text, FontSize = 12 };
+ if (bold)
+ label.FontAttributes = FontAttributes.Bold;
+ Grid.SetRow(label, row);
+ Grid.SetColumn(label, col);
+ if (colSpan > 1)
+ Grid.SetColumnSpan(label, colSpan);
+ return label;
+ }
+ static View SetGrid(View view, int row, int col, int colSpan = 1)
+ {
+ Grid.SetRow(view, row);
+ Grid.SetColumn(view, col);
+ if (colSpan > 1)
+ Grid.SetColumnSpan(view, colSpan);
+ return view;
+ }
+ }
+ public class NavigationTestPage1 : ContentPage
+ {
+ public NavigationTestPage1()
+ {
+ Title = "NavTest1";
+ AutomationId = "NavigationTestPage1";
+ Content = new ScrollView
+ {
+ Content = new VerticalStackLayout
+ {
+ Padding = 10,
+ Spacing = 10,
+ Children =
+ {
+ new Label { Text = "NavTest 1", FontSize = 14, FontAttributes = FontAttributes.Bold, AutomationId = "NavTest1PageIdentityLabel" },
+ ShellNavHelper.CreateNavButton("Go Back", "..", "NavTest1BackButton"),
+ ShellNavHelper.CreateNavButton("Navigate to NavTest2", "navtest2", "NavTest1ToNavTest2Button")
+ }
+ }
+ };
+ }
+ }
+ public class NavigationTestPage2 : ContentPage
+ {
+ public NavigationTestPage2()
+ {
+ Title = "NavTest2";
+ AutomationId = "NavigationTestPage2";
+ Content = new ScrollView
+ {
+ Content = new VerticalStackLayout
+ {
+ Padding = 10,
+ Spacing = 10,
+ Children =
+ {
+ new Label { Text = "NavTest 2", FontSize = 14, FontAttributes = FontAttributes.Bold, AutomationId = "NavTest2PageIdentityLabel" },
+ ShellNavHelper.CreateNavButton("Back and Forward", "../navtest3", "NavTest2BackForwardButton"),
+ ShellNavHelper.CreateNavButton("Simple Back", "..", "NavTest2BackButton")
+ }
+ }
+ };
+ }
+ }
+ public class NavigationTestPage3 : ContentPage
+ {
+ public NavigationTestPage3()
+ {
+ Title = "NavTest3";
+ AutomationId = "NavigationTestPage3";
+ Content = new ScrollView
+ {
+ Content = new VerticalStackLayout
+ {
+ Padding = 10,
+ Spacing = 10,
+ Children =
+ {
+ new Label { Text = "NavTest 3", FontSize = 14, FontAttributes = FontAttributes.Bold, AutomationId = "NavTest3PageIdentityLabel" },
+ ShellNavHelper.CreateNavButton("Back 2 Levels", "../..", "NavTest3MultiBackButton")
+ }
+ }
+ };
+ }
+ }
+ // ── Pass Data: QuerySenderPage ────────────────────────────────────────────
+ public class QuerySenderPage : ContentPage, IQueryAttributable
+ {
+ readonly Label _backValueLabel;
+ readonly Entry _nameEntry;
+ readonly Entry _locationEntry;
+ string _backValue;
+
+ public string BackValue
+ {
+ get => _backValue;
+ set
+ {
+ _backValue = value;
+ _backValueLabel.Text = value ?? "(none)";
+ }
+ }
+
+ public void ApplyQueryAttributes(IDictionary query)
+ {
+ if (query.TryGetValue("backvalue", out var val))
+ BackValue = val?.ToString();
+ }
+
+ public QuerySenderPage()
+ {
+ Title = "QuerySenderPage";
+ AutomationId = "QuerySenderPage";
+
+ _nameEntry = new Entry { Text = "Hello World", FontSize = 12, HeightRequest = 35, AutomationId = "QuerySendNameEntry" };
+ _locationEntry = new Entry { Text = "Savannah", FontSize = 12, HeightRequest = 35, AutomationId = "QuerySendLocationEntry" };
+ _backValueLabel = new Label { FontSize = 12, Text = "(none)", AutomationId = "QueryBackValueLabel" };
+
+ var identityLabel = new Label
+ {
+ Text = "Query Sender",
+ FontSize = 14,
+ FontAttributes = FontAttributes.Bold,
+ Margin = new Thickness(10, 8, 10, 4),
+ AutomationId = "QuerySenderPageIdentityLabel"
+ };
+
+ var sendStringBtn = MakeButton("Send ?name= (string param)", "QuerySendStringButton");
+ sendStringBtn.Clicked += async (s, e) =>
+ await Shell.Current.GoToAsync($"querydetail?name={Uri.EscapeDataString(_nameEntry.Text ?? string.Empty)}");
+
+ var sendMultiBtn = MakeButton("Send ?name=&location= (multi param)", "QuerySendMultiParamButton");
+ sendMultiBtn.Clicked += async (s, e) =>
+ await Shell.Current.GoToAsync($"querydetail?name={Uri.EscapeDataString(_nameEntry.Text ?? string.Empty)}&location={Uri.EscapeDataString(_locationEntry.Text ?? string.Empty)}");
+
+ var sendDictBtn = MakeButton("Send Dictionary", "QuerySendDictButton");
+ sendDictBtn.Clicked += async (s, e) =>
+ await Shell.Current.GoToAsync("querydetail", new Dictionary { ["name"] = _nameEntry.Text ?? string.Empty });
+
+ var sendSingleUseBtn = MakeButton("Send SingleUse Params", "QuerySendSingleUseButton");
+ sendSingleUseBtn.Clicked += async (s, e) =>
+ await Shell.Current.GoToAsync("querydetail", new ShellNavigationQueryParameters { ["name"] = _nameEntry.Text ?? string.Empty });
+
+ var goBackBtn = ShellNavHelper.CreateNavButton("Go Back", "..", "QuerySenderGoBackButton");
+
+ var grid = new Grid
+ {
+ Padding = 10,
+ RowSpacing = 4,
+ ColumnSpacing = 10,
+ RowDefinitions = { new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto) },
+ ColumnDefinitions = { new ColumnDefinition(GridLength.Star), new ColumnDefinition(GridLength.Star) }
+ };
+
+ void AddLabelRow(int row, string labelText, View valueView)
+ {
+ var lbl = new Label { Text = labelText, FontSize = 12, VerticalOptions = LayoutOptions.Center };
+ Grid.SetRow(lbl, row);
+ Grid.SetColumn(lbl, 0);
+ Grid.SetRow(valueView, row);
+ Grid.SetColumn(valueView, 1);
+ grid.Children.Add(lbl);
+ grid.Children.Add(valueView);
+ }
+
+ AddLabelRow(0, "Name to Send:", _nameEntry);
+ AddLabelRow(1, "Location to Send:", _locationEntry);
+ AddLabelRow(2, "Back Value:", _backValueLabel);
+ foreach (var (btn, row) in new (Button, int)[] { (sendStringBtn, 3), (sendMultiBtn, 4), (sendDictBtn, 5), (sendSingleUseBtn, 6), (goBackBtn, 7) })
+ {
+ Grid.SetRow(btn, row);
+ Grid.SetColumn(btn, 0);
+ Grid.SetColumnSpan(btn, 2);
+ grid.Children.Add(btn);
+ }
+
+ Content = new ScrollView { Content = new VerticalStackLayout { Spacing = 4, Children = { identityLabel, grid } } };
+ }
+
+ static Button MakeButton(string text, string automationId) => new Button
+ {
+ Text = text,
+ FontSize = 11,
+ HeightRequest = 35,
+ Padding = new Thickness(8, 0),
+ Margin = new Thickness(10, 4),
+ HorizontalOptions = LayoutOptions.Fill,
+ AutomationId = automationId
+ };
+ }
+
+ // ── Pass Data: QueryDataDetailPage ────────────────────────────────────────
+ public class QueryDataDetailPage : ContentPage, IQueryAttributable
+ {
+ readonly Label _attributeNameLabel;
+ readonly Label _attributeLocationLabel;
+ readonly Label _iqaNameLabel;
+ readonly Label _iqaCallCountLabel;
+ int _iqaCallCount;
+
+ public QueryDataDetailPage()
+ {
+ Title = "QueryDataDetail";
+ AutomationId = "QueryDataDetailPage";
+
+ _attributeNameLabel = new Label { FontSize = 12, Text = "(not set)", AutomationId = "QueryPropertyReceivedLabel" };
+ _attributeLocationLabel = new Label { FontSize = 12, Text = "(not set)", AutomationId = "QueryPropertyLocationLabel" };
+ _iqaNameLabel = new Label { FontSize = 12, Text = "(not set)", AutomationId = "IQueryAttributableReceivedLabel" };
+ _iqaCallCountLabel = new Label { FontSize = 12, Text = "0", AutomationId = "DictAppliedCountLabel" };
+
+ var identityLabel = new Label
+ {
+ Text = "Query Data Detail",
+ FontSize = 14,
+ FontAttributes = FontAttributes.Bold,
+ Margin = new Thickness(10, 8, 10, 4),
+ AutomationId = "QueryDataDetailPageIdentityLabel"
+ };
+
+ var goBackBtn = ShellNavHelper.CreateNavButton("Go Back", "..", "QueryDetailGoBackButton");
+ var goBackWithDataBtn = new Button
+ {
+ Text = "Go Back with Data",
+ FontSize = 11,
+ HeightRequest = 35,
+ Padding = new Thickness(8, 0),
+ Margin = new Thickness(10, 4),
+ HorizontalOptions = LayoutOptions.Fill,
+ AutomationId = "QueryDetailGoBackWithDataButton"
+ };
+ goBackWithDataBtn.Clicked += async (s, e) => await Shell.Current.GoToAsync("..?backvalue=ReturnedData");
+
+ // Navigates forward without passing data — Dictionary data will re-apply on return (persistence demo)
+ var goToIntermediateBtn = new Button
+ {
+ Text = "Go to Intermediate (no data)",
+ FontSize = 11,
+ HeightRequest = 35,
+ Padding = new Thickness(8, 0),
+ Margin = new Thickness(10, 4),
+ HorizontalOptions = LayoutOptions.Fill,
+ AutomationId = "QueryDetailGoToIntermediateButton"
+ };
+ goToIntermediateBtn.Clicked += async (s, e) => await Shell.Current.GoToAsync("queryintermediate");
+
+ var grid = new Grid
+ {
+ Padding = 10,
+ RowSpacing = 4,
+ ColumnSpacing = 10,
+ RowDefinitions = { new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto) },
+ ColumnDefinitions = { new ColumnDefinition(GridLength.Star), new ColumnDefinition(GridLength.Star) }
+ };
+
+ void AddRow(int row, string labelText, Label valueLabel)
+ {
+ var lbl = new Label { Text = labelText, FontSize = 12 };
+ Grid.SetRow(lbl, row);
+ Grid.SetColumn(lbl, 0);
+ Grid.SetRow(valueLabel, row);
+ Grid.SetColumn(valueLabel, 1);
+ grid.Children.Add(lbl);
+ grid.Children.Add(valueLabel);
+ }
+
+ AddRow(0, "[QueryProp] name:", _attributeNameLabel);
+ AddRow(1, "[QueryProp] location:", _attributeLocationLabel);
+ AddRow(2, "IQA name:", _iqaNameLabel);
+ AddRow(3, "IQA call count:", _iqaCallCountLabel);
+ foreach (var (btn, row) in new (Button, int)[] { (goBackBtn, 4), (goBackWithDataBtn, 5), (goToIntermediateBtn, 6) })
+ {
+ Grid.SetRow(btn, row);
+ Grid.SetColumn(btn, 0);
+ Grid.SetColumnSpan(btn, 2);
+ grid.Children.Add(btn);
+ }
+
+ Content = new ScrollView { Content = new VerticalStackLayout { Spacing = 4, Children = { identityLabel, grid } } };
+ }
+
+ public void ApplyQueryAttributes(IDictionary query)
+ {
+ if (query.TryGetValue("name", out var nameVal))
+ {
+ _iqaCallCount++;
+ _iqaCallCountLabel.Text = _iqaCallCount.ToString();
+ var name = nameVal?.ToString() ?? "(null)";
+ _attributeNameLabel.Text = name;
+ _iqaNameLabel.Text = name;
+ }
+
+ if (query.TryGetValue("location", out var locVal))
+ _attributeLocationLabel.Text = locVal?.ToString() ?? "(null)";
+ }
+ }
+
+ // ── Pass Data: QueryIntermediatePage ─────────────────────────────────────
+ public class QueryIntermediatePage : ContentPage
+ {
+ public QueryIntermediatePage()
+ {
+ Title = "QueryIntermediate";
+ AutomationId = "QueryIntermediatePage";
+ Content = new ScrollView
+ {
+ Content = new VerticalStackLayout
+ {
+ Padding = 10,
+ Spacing = 8,
+ Children =
+ {
+ new Label { Text = "Intermediate Page", FontSize = 14, FontAttributes = FontAttributes.Bold, AutomationId = "QueryIntermediatePageIdentityLabel" },
+ new Label { Text = "No data was passed here.\nGo Back to see Dictionary data re-applied on the detail page.", FontSize = 12, LineBreakMode = LineBreakMode.WordWrap },
+ ShellNavHelper.CreateNavButton("Go Back", "..", "QueryIntermediateGoBackButton")
+ }
+ }
+ };
+ }
+ }
+
+ static class ShellNavHelper
+ {
+ public static Button CreateNavButton(string text, string route, string automationId)
+ {
+ var btn = new Button
+ {
+ Text = text,
+ FontSize = 12,
+ HeightRequest = 35,
+ Padding = new Thickness(8, 0),
+ Margin = new Thickness(10, 4),
+ HorizontalOptions = LayoutOptions.Fill,
+ AutomationId = automationId
+ };
+ btn.Clicked += async (s, e) =>
+ {
+ try
+ { await Shell.Current.GoToAsync(route); }
+ catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Navigation failed: {ex.Message}"); }
+ };
+ return btn;
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellNavigation/ShellNavigationOptionsPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellNavigation/ShellNavigationOptionsPage.xaml
new file mode 100644
index 000000000000..9f841e776984
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellNavigation/ShellNavigationOptionsPage.xaml
@@ -0,0 +1,248 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellNavigation/ShellNavigationOptionsPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellNavigation/ShellNavigationOptionsPage.xaml.cs
new file mode 100644
index 000000000000..2bbb34ce0bf5
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellNavigation/ShellNavigationOptionsPage.xaml.cs
@@ -0,0 +1,273 @@
+using System.Linq;
+using Microsoft.Maui.Controls;
+namespace Maui.Controls.Sample
+{
+ public partial class ShellNavigationOptionsPage : ContentPage
+ {
+ readonly ShellViewModel _viewModel;
+ readonly List _insertedPages = new();
+ int _insertedPageCount;
+ public ShellNavigationOptionsPage(ShellViewModel viewModel, List existingInsertedPages = null, int existingInsertedPageCount = 0)
+ {
+ _viewModel = viewModel;
+ if (existingInsertedPages != null)
+ {
+ _insertedPages.AddRange(existingInsertedPages);
+ _insertedPageCount = existingInsertedPageCount;
+ }
+ BindingContext = _viewModel;
+ InitializeComponent();
+ this.Appearing += OnPageAppearing;
+ }
+ void OnPageAppearing(object sender, System.EventArgs e)
+ {
+ UpdateStateLabels();
+ Shell.Current?.Navigated += OnShellNavigated;
+ }
+ void OnShellNavigated(object sender, ShellNavigatedEventArgs e)
+ {
+ if (Shell.Current?.CurrentPage == this)
+ UpdateStateLabels();
+ }
+ void UpdateStateLabels()
+ {
+ var shell = Shell.Current;
+ if (shell != null)
+ {
+ _viewModel.CurrentState = shell.CurrentState?.Location?.ToString() ?? "Not Set";
+ _viewModel.CurrentPage = shell.CurrentPage?.Title ?? "Not Set";
+ _viewModel.CurrentItem = shell.CurrentItem?.Title ?? "Not Set";
+ _viewModel.ShellCurrent = shell.GetType().Name;
+ }
+ UpdateNavigationStackDisplay();
+ }
+ protected override void OnDisappearing()
+ {
+ base.OnDisappearing();
+ Shell.Current?.Navigated -= OnShellNavigated;
+ }
+ void UpdateNavigationStackDisplay()
+ {
+ var shell = Shell.Current;
+ var section = shell?.CurrentItem?.CurrentItem;
+ if (section != null)
+ {
+ // section.Stack[0] is null in Shell (root placeholder).
+ // Resolve it to the root ContentPage title via ShellContent.Content.
+ var rootTitle = (section.CurrentItem?.Content as Page)?.Title ?? "Root";
+ var stack = section.Stack;
+ TabStackLabel.Text = $"Count={stack.Count}: {string.Join(", ", stack.Select(p => p?.Title ?? rootTitle))}";
+ var navStack = Navigation.NavigationStack;
+ GetNavStackLabel.Text = $"Count={navStack.Count}: {string.Join(", ", navStack.Select(p => p?.Title ?? rootTitle))}";
+ }
+ else
+ {
+ TabStackLabel.Text = "N/A";
+ GetNavStackLabel.Text = "N/A";
+ }
+ }
+ async void OnPushClicked(object sender, System.EventArgs e)
+ {
+ var stack = Navigation.NavigationStack;
+ int optionsIndex = -1;
+ for (int i = 0; i < stack.Count; i++)
+ if (ReferenceEquals(stack[i], this)) { optionsIndex = i; break; }
+ var subPageNumber = optionsIndex >= 0 ? stack.Count - optionsIndex : 1;
+ var pageTitle = $"SubPage{subPageNumber}";
+ var subPage = new ContentPage
+ {
+ Title = pageTitle,
+ AutomationId = $"OptionsSubPage{subPageNumber}",
+ };
+ var subTabStackLabel = new Label { FontSize = 11, AutomationId = $"SubPage{subPageNumber}TabStackLabel" };
+ var subNavStackLabel = new Label { FontSize = 11, AutomationId = $"SubPage{subPageNumber}NavStackLabel" };
+ subPage.Content = new ScrollView
+ {
+ Content = new VerticalStackLayout
+ {
+ Padding = 10,
+ Spacing = 8,
+ Children =
+ {
+ new Label { Text = $"Sub Page {subPageNumber}", FontSize = 14, FontAttributes = FontAttributes.Bold, AutomationId = $"OptionsSubPage{subPageNumber}IdentityLabel" },
+ CreateEventDisplay(),
+ CreateStackDisplay(subTabStackLabel, subNavStackLabel),
+ CreateSubPageButtons()
+ }
+ }
+ };
+ subPage.Appearing += (s2, e2) => UpdateStackLabels(subPage, subTabStackLabel, subNavStackLabel);
+ subPage.BindingContext = _viewModel;
+ await Navigation.PushAsync(subPage);
+ // Update the new SubPage's stack labels immediately after push so they
+ // reflect the correct count before Appearing fires (fixes "not updated on push").
+ UpdateStackLabels(subPage, subTabStackLabel, subNavStackLabel);
+ UpdateNavigationStackDisplay();
+ }
+ async void OnPopClicked(object sender, System.EventArgs e)
+ {
+ if (Navigation.NavigationStack.Count > 1)
+ {
+ await Navigation.PopAsync();
+ }
+ }
+ async void OnPopToRootClicked(object sender, System.EventArgs e)
+ {
+ await Navigation.PopToRootAsync();
+ }
+ void OnInsertClicked(object sender, System.EventArgs e)
+ {
+ _insertedPageCount++;
+ var pageTitle = $"InsertedPage{_insertedPageCount}";
+ var goBackBtn = new Button
+ {
+ Text = "Go Back",
+ FontSize = 12,
+ HeightRequest = 35,
+ AutomationId = $"InsertedPage{_insertedPageCount}GoBackButton"
+ };
+ goBackBtn.Clicked += async (s, ev) => await Shell.Current.GoToAsync("..");
+ var goToOptionsBtn = new Button
+ {
+ Text = "Go to Options Page",
+ FontSize = 12,
+ HeightRequest = 35,
+ AutomationId = $"InsertedPage{_insertedPageCount}GoToOptionsButton"
+ };
+ var insTabStackLabel = new Label { FontSize = 11, AutomationId = $"InsertedPage{_insertedPageCount}TabStackLabel" };
+ var insNavStackLabel = new Label { FontSize = 11, AutomationId = $"InsertedPage{_insertedPageCount}NavStackLabel" };
+ var insertedPage = new ContentPage
+ {
+ Title = pageTitle,
+ AutomationId = $"InsertedPage{_insertedPageCount}",
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 10,
+ Children =
+ {
+ new Label { Text = $"Inserted Page {_insertedPageCount}", FontSize = 14, FontAttributes = FontAttributes.Bold, AutomationId = $"InsertedPage{_insertedPageCount}IdentityLabel" },
+ CreateStackDisplay(insTabStackLabel, insNavStackLabel),
+ goToOptionsBtn,
+ goBackBtn
+ }
+ }
+ };
+ insertedPage.Appearing += (s2, e2) => UpdateStackLabels(insertedPage, insTabStackLabel, insNavStackLabel);
+ goToOptionsBtn.Clicked += async (s, ev) => await insertedPage.Navigation.PushAsync(new ShellNavigationOptionsPage(_viewModel, _insertedPages, _insertedPageCount));
+ _insertedPages.Add(insertedPage);
+ Navigation.InsertPageBefore(insertedPage, this);
+ UpdateNavigationStackDisplay();
+ }
+ void OnRemoveClicked(object sender, System.EventArgs e)
+ {
+ for (int i = _insertedPages.Count - 1; i >= 0; i--)
+ {
+ if (Navigation.NavigationStack.Contains(_insertedPages[i]))
+ {
+ Navigation.RemovePage(_insertedPages[i]);
+ _insertedPages.RemoveAt(i);
+ break;
+ }
+ }
+ if (_insertedPages.Count == 0)
+ _insertedPageCount = 0;
+ UpdateNavigationStackDisplay();
+ }
+ VerticalStackLayout CreateEventDisplay()
+ {
+ var layout = new VerticalStackLayout { Spacing = 3 };
+ layout.Children.Add(new Label { Text = "NAVIGATING EVENT", FontSize = 13, FontAttributes = FontAttributes.Bold, TextColor = Colors.DarkBlue });
+ AddBoundLabel(layout, "Current:", "NavigatingCurrent", "SubNavigatingCurrentLabel");
+ AddBoundLabel(layout, "Source:", "NavigatingSource", "SubNavigatingSourceLabel");
+ AddBoundLabel(layout, "Target:", "NavigatingTarget", "SubNavigatingTargetLabel");
+ AddBoundLabel(layout, "CanCancel:", "NavigatingCanCancel", "SubNavigatingCanCancelLabel");
+ AddBoundLabel(layout, "Cancelled:", "NavigatingCancelled", "SubNavigatingCancelledLabel");
+ layout.Children.Add(new BoxView { HeightRequest = 1, Color = Colors.Gray, Margin = new Thickness(0, 4) });
+ layout.Children.Add(new Label { Text = "NAVIGATED EVENT", FontSize = 13, FontAttributes = FontAttributes.Bold, TextColor = Colors.DarkGreen });
+ AddBoundLabel(layout, "Current:", "NavigatedCurrent", "SubNavigatedCurrentLabel");
+ AddBoundLabel(layout, "Previous:", "NavigatedPrevious", "SubNavigatedPreviousLabel");
+ AddBoundLabel(layout, "Source:", "NavigatedSource", "SubNavigatedSourceLabel");
+ return layout;
+ }
+ static void AddBoundLabel(VerticalStackLayout layout, string title, string bindingPath, string automationId)
+ {
+ var row = new HorizontalStackLayout { Spacing = 5 };
+ row.Children.Add(new Label { Text = title, FontSize = 11, WidthRequest = 80 });
+ var valueLabel = new Label { FontSize = 11, AutomationId = automationId };
+ valueLabel.SetBinding(Label.TextProperty, bindingPath);
+ row.Children.Add(valueLabel);
+ layout.Children.Add(row);
+ }
+ Grid CreateSubPageButtons()
+ {
+ var pushBtn = new Button { Text = "Push Deeper", FontSize = 11, HeightRequest = 35, AutomationId = "SubPushDeeperButton" };
+ pushBtn.Clicked += OnPushClicked;
+ var popBtn = new Button { Text = "Pop (Go Back)", FontSize = 11, HeightRequest = 35, AutomationId = "SubPopButton" };
+ popBtn.Clicked += async (s, e) => await Navigation.PopAsync();
+ var popToRootBtn = new Button { Text = "PopToRoot", FontSize = 11, HeightRequest = 35, AutomationId = "SubPopToRootButton" };
+ popToRootBtn.Clicked += async (s, e) => await Navigation.PopToRootAsync();
+ var grid = new Grid
+ {
+ ColumnDefinitions = { new ColumnDefinition(GridLength.Star), new ColumnDefinition(GridLength.Star) },
+ RowDefinitions = { new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto) },
+ RowSpacing = 4,
+ ColumnSpacing = 4
+ };
+ Grid.SetRow(pushBtn, 0);
+ Grid.SetColumn(pushBtn, 0);
+ Grid.SetRow(popBtn, 0);
+ Grid.SetColumn(popBtn, 1);
+ Grid.SetRow(popToRootBtn, 1);
+ Grid.SetColumn(popToRootBtn, 0);
+ Grid.SetColumnSpan(popToRootBtn, 2);
+ grid.Children.Add(pushBtn);
+ grid.Children.Add(popBtn);
+ grid.Children.Add(popToRootBtn);
+ return grid;
+ }
+ static Grid CreateStackDisplay(Label tabStackLabel, Label navStackLabel)
+ {
+ var grid = new Grid
+ {
+ ColumnDefinitions = { new ColumnDefinition(new GridLength(100)), new ColumnDefinition(GridLength.Star) },
+ RowDefinitions = { new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto) },
+ RowSpacing = 2,
+ ColumnSpacing = 8
+ };
+ var tabLabel = new Label { Text = "TabStack:", FontSize = 12 };
+ Grid.SetRow(tabLabel, 0);
+ Grid.SetColumn(tabLabel, 0);
+ Grid.SetRow(tabStackLabel, 0);
+ Grid.SetColumn(tabStackLabel, 1);
+ var navLabel = new Label { Text = "NavStack:", FontSize = 12 };
+ Grid.SetRow(navLabel, 1);
+ Grid.SetColumn(navLabel, 0);
+ Grid.SetRow(navStackLabel, 1);
+ Grid.SetColumn(navStackLabel, 1);
+ grid.Children.Add(tabLabel);
+ grid.Children.Add(tabStackLabel);
+ grid.Children.Add(navLabel);
+ grid.Children.Add(navStackLabel);
+ return grid;
+ }
+ void UpdateStackLabels(Page page, Label tabStackLabel, Label navStackLabel)
+ {
+ var shell = Shell.Current;
+ var section = shell?.CurrentItem?.CurrentItem;
+ if (section != null)
+ {
+ var rootTitle = (section.CurrentItem?.Content as Page)?.Title ?? "Root";
+ var stack = section.Stack;
+ tabStackLabel.Text = $"Count={stack.Count}: {string.Join(", ", stack.Select(p => p?.Title ?? rootTitle))}";
+ var navStack = page.Navigation.NavigationStack;
+ navStackLabel.Text = $"Count={navStack.Count}: {string.Join(", ", navStack.Select(p => p?.Title ?? rootTitle))}";
+ }
+ else
+ {
+ tabStackLabel.Text = "N/A";
+ navStackLabel.Text = "N/A";
+ }
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellPage/ShellControlPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellPage/ShellControlPage.xaml
new file mode 100644
index 000000000000..a9f877131b20
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellPage/ShellControlPage.xaml
@@ -0,0 +1,220 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellPage/ShellControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellPage/ShellControlPage.xaml.cs
new file mode 100644
index 000000000000..76d9fab2bb31
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellPage/ShellControlPage.xaml.cs
@@ -0,0 +1,203 @@
+using Microsoft.Maui.Controls;
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Maui.Controls.Sample;
+
+public partial class ShellControlPage : Shell
+{
+ private ShellViewModel _viewModel;
+ public ShellContent HomePage => homePage;
+ private const string NotAnimatedRoute = "shell-feature/notanimated";
+ private const string AnimatedRoute = "shell-feature/animated";
+ private const string ModalRoute = "shell-feature/modal";
+ private const string ModalAnimatedRoute = "shell-feature/modalanimated";
+ private const string ModalNotAnimatedRoute = "shell-feature/modalnotanimated";
+
+
+ public ShellControlPage()
+ {
+ InitializeComponent();
+ _viewModel = new ShellViewModel();
+ BindingContext = _viewModel;
+ Routing.RegisterRoute(NotAnimatedRoute, typeof(NotAnimatedPresentationPage));
+ Routing.RegisterRoute(AnimatedRoute, typeof(AnimatedPresentationPage));
+ Routing.RegisterRoute(ModalRoute, typeof(ModalPresentationPage));
+ Routing.RegisterRoute(ModalAnimatedRoute, typeof(ModalAnimatedPresentationPage));
+ Routing.RegisterRoute(ModalNotAnimatedRoute, typeof(ModalNotAnimatedPresentationPage));
+ }
+ async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
+ {
+ BindingContext = _viewModel = new ShellViewModel();
+ await Navigation.PushAsync(new ShellOptionsPage(_viewModel));
+ }
+ void OnNavBarAnimTrueClicked(object sender, EventArgs e)
+ {
+ Shell.SetNavBarVisibilityAnimationEnabled(this, true);
+ }
+ void OnNavBarAnimFalseClicked(object sender, EventArgs e)
+ {
+ Shell.SetNavBarVisibilityAnimationEnabled(this, false);
+ }
+ void OnNavBarVisibleClicked(object sender, EventArgs e)
+ {
+ Shell.SetNavBarIsVisible(this, true);
+ }
+ void OnNavBarHiddenClicked(object sender, EventArgs e)
+ {
+ Shell.SetNavBarIsVisible(this, false);
+ }
+ void OnNavBarHasShadowTrueClicked(object sender, EventArgs e)
+ {
+ Shell.SetNavBarHasShadow(this, true);
+ }
+ void OnNavBarHasShadowFalseClicked(object sender, EventArgs e)
+ {
+ Shell.SetNavBarHasShadow(this, false);
+ }
+ private async void OnGoToNotAnimated(object sender, EventArgs e)
+ => await Shell.Current.GoToAsync(NotAnimatedRoute);
+
+ private async void OnGoToAnimated(object sender, EventArgs e)
+ => await Shell.Current.GoToAsync(AnimatedRoute);
+
+ private async void OnGoToModal(object sender, EventArgs e)
+ => await Shell.Current.GoToAsync(ModalRoute);
+
+ private async void OnGoToModalAnimated(object sender, EventArgs e)
+ => await Shell.Current.GoToAsync(ModalAnimatedRoute);
+
+ private async void OnGoToModalNotAnimated(object sender, EventArgs e)
+ => await Shell.Current.GoToAsync(ModalNotAnimatedRoute);
+ private async void OnGoToHomeClicked(object sender, EventArgs e)
+ {
+ await Shell.Current.GoToAsync("//home");
+ }
+ private async void OnGoBackClicked(object sender, EventArgs e)
+ {
+ await Shell.Current.GoToAsync("..");
+ }
+ private void SetTitleView()
+ {
+ var grid = new Grid
+ {
+ ColumnDefinitions =
+ {
+ new ColumnDefinition { Width = GridLength.Auto },
+ new ColumnDefinition { Width = GridLength.Auto }
+ },
+ VerticalOptions = LayoutOptions.Center
+ };
+ var image = new Image
+ {
+ Source = "dotnet_bot.png",
+ WidthRequest = 28,
+ HeightRequest = 28,
+ HorizontalOptions = LayoutOptions.Start,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ var label = new Label
+ {
+ Text = "Shell TitleView",
+ Margin = new Thickness(30, 0, 0, 0),
+ VerticalOptions = LayoutOptions.Center,
+ FontAttributes = FontAttributes.Bold,
+ TextColor = Colors.Black
+ };
+
+ Grid.SetColumn(label, 1);
+
+ grid.Add(image);
+ grid.Add(label);
+
+ Shell.SetTitleView(this, grid);
+ }
+ private void RemoveTitleView()
+ {
+ Shell.SetTitleView(this, null);
+ }
+ private void OnShowTitleViewClicked(object sender, EventArgs e)
+ {
+ SetTitleView();
+ }
+ private void OnHideTitleViewClicked(object sender, EventArgs e)
+ {
+ RemoveTitleView();
+ }
+
+ private abstract class PresentationModePage : ContentPage
+ {
+ protected PresentationModePage(string title, PresentationMode mode)
+ {
+ Title = title;
+ BackgroundColor = Colors.White;
+ Shell.SetPresentationMode(this, mode);
+
+ var backButton = new Button
+ {
+ Text = "Go Back",
+ AutomationId = "GoBackButton",
+ BackgroundColor = Color.FromArgb("F5F5F5"),
+ TextColor = Colors.Black
+ };
+ backButton.Clicked += async (_, __) => await Shell.Current.GoToAsync("..");
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 20,
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ Children =
+ {
+ new Label { Text = title, HorizontalOptions = LayoutOptions.Center },
+ backButton
+ }
+ };
+ }
+ }
+
+ private sealed class NotAnimatedPresentationPage : PresentationModePage
+ {
+ public NotAnimatedPresentationPage()
+ : base("NotAnimated Page", PresentationMode.NotAnimated)
+ {
+ }
+ }
+
+ private sealed class AnimatedPresentationPage : PresentationModePage
+ {
+ public AnimatedPresentationPage()
+ : base("Animated Page", PresentationMode.Animated)
+ {
+ }
+ }
+
+ private sealed class ModalPresentationPage : PresentationModePage
+ {
+ public ModalPresentationPage()
+ : base("Modal Page", PresentationMode.Modal)
+ {
+ }
+ }
+
+ private sealed class ModalAnimatedPresentationPage : PresentationModePage
+ {
+ public ModalAnimatedPresentationPage()
+ : base("ModalAnimated Page", PresentationMode.ModalAnimated)
+ {
+ }
+ }
+
+ private sealed class ModalNotAnimatedPresentationPage : PresentationModePage
+ {
+ public ModalNotAnimatedPresentationPage()
+ : base("ModalNotAnimated Page", PresentationMode.ModalNotAnimated)
+ {
+ }
+ }
+
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellPage/ShellOptionsPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellPage/ShellOptionsPage.xaml
new file mode 100644
index 000000000000..bb9b14342d4d
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellPage/ShellOptionsPage.xaml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellPage/ShellOptionsPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellPage/ShellOptionsPage.xaml.cs
new file mode 100644
index 000000000000..b81921abefea
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellPage/ShellOptionsPage.xaml.cs
@@ -0,0 +1,122 @@
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Graphics;
+using System;
+
+namespace Maui.Controls.Sample
+{
+ public partial class ShellOptionsPage : ContentPage
+ {
+ private readonly ShellViewModel _viewModel;
+
+ public ShellOptionsPage(ShellViewModel viewModel)
+ {
+ InitializeComponent();
+ _viewModel = viewModel;
+ BindingContext = _viewModel;
+ }
+
+ private async void OnApplyClicked(object sender, EventArgs e)
+ {
+ await Navigation.PopAsync();
+ }
+
+ // BackgroundColor
+ void OnBackgroundColorClicked(object sender, EventArgs e)
+ {
+ if (sender is Button button)
+ {
+ _viewModel.BackgroundColor = button.Text switch
+ {
+ "SkyBlue" => Colors.SkyBlue,
+ "LightGreen" => Colors.LightGreen,
+ "White" => Colors.White,
+ _ => _viewModel.BackgroundColor
+ };
+ }
+ }
+
+ // ForegroundColor
+ void OnForegroundColorClicked(object sender, EventArgs e)
+ {
+ if (sender is Button button)
+ {
+ _viewModel.ForegroundColor = button.Text switch
+ {
+ "Brown" => Colors.Brown,
+ "Magenta" => Colors.Magenta,
+ "Purple" => Colors.Purple,
+ _ => _viewModel.ForegroundColor
+ };
+ }
+ }
+
+ // TitleColor
+ void OnTitleColorClicked(object sender, EventArgs e)
+ {
+ if (sender is Button button)
+ {
+ _viewModel.TitleColor = button.Text switch
+ {
+ "Red" => Colors.Red,
+ "Green" => Colors.Green,
+ "Navy" => Colors.Navy,
+ _ => _viewModel.TitleColor
+ };
+ }
+ }
+
+ // DisabledColor
+ void OnDisabledColorClicked(object sender, EventArgs e)
+ {
+ if (sender is Button button)
+ {
+ _viewModel.DisabledColor = button.Text switch
+ {
+ "Gold" => Colors.Gold,
+ "Violet" => Colors.Violet,
+ "Silver" => Colors.Silver,
+ _ => _viewModel.DisabledColor
+ };
+ }
+ }
+
+ // UnselectedColor
+ void OnUnselectedColorClicked(object sender, EventArgs e)
+ {
+ if (sender is Button button)
+ {
+ _viewModel.UnselectedColor = button.Text switch
+ {
+ "Maroon" => Colors.Maroon,
+ "Blue" => Colors.Blue,
+ "Indigo" => Colors.Indigo,
+ _ => _viewModel.UnselectedColor
+ };
+ }
+ }
+ private void OnIsEnabledChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (sender is RadioButton radioButton && e.Value)
+ {
+ _viewModel.IsEnabled = radioButton.Content.ToString() == "True";
+ }
+ }
+
+ private void OnIsVisibleChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (sender is RadioButton radioButton && e.Value)
+ {
+ _viewModel.IsVisible = radioButton.Content.ToString() == "True";
+ }
+ }
+
+ private void OnFlowDirectionChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (sender is RadioButton radioButton && e.Value)
+ {
+ _viewModel.FlowDirection = radioButton.Content?.ToString() == "LTR" ? FlowDirection.LeftToRight : FlowDirection.RightToLeft;
+ }
+ }
+
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellViewModel.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellViewModel.cs
index 212040a7af09..dd2c5728974c 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellViewModel.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Shell/ShellViewModel.cs
@@ -1,6 +1,7 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
+using System.Windows.Input;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;
@@ -37,8 +38,41 @@ public class ShellViewModel : INotifyPropertyChanged
private Color _tabBarTitleColor;
private Color _tabBarUnselectedColor;
private bool _tabBarIsVisible = true;
+ private string _textOverride = string.Empty;
+ private string _iconOverride = string.Empty;
private bool _isEnabled = true;
private bool _isVisible = true;
+ private string _commandParameter = string.Empty;
+ private string _commandExecuted = string.Empty;
+ private string _currentState = "Not Set";
+ private string _currentPage = "Not Set";
+ private string _currentItem = "Not Set";
+ private string _shellCurrent = "Not Set";
+ private string _navigatingCurrent = string.Empty;
+ private string _navigatingSource = string.Empty;
+ private string _navigatingTarget = string.Empty;
+ private string _navigatingCanCancel = string.Empty;
+ private string _navigatingCancelled = string.Empty;
+ private string _navigatedCurrent = string.Empty;
+ private string _navigatedPrevious = string.Empty;
+ private string _navigatedSource = string.Empty;
+ private string _routeStatus = string.Empty;
+ private bool _cancelNavigation;
+ private bool _enableDeferral;
+ private string _deferralStatus = string.Empty;
+ private string _overrideNavigatingStatus = string.Empty;
+ private string _overrideNavigatedStatus = string.Empty;
+ private string _tabStackInfo = string.Empty;
+ readonly Command _command;
+ private Color _backgroundColor;
+ private Color _foregroundColor;
+ private Color _titleColor;
+ private Color _disabledColor;
+ private Color _unselectedColor;
+ private bool _navBarVisible = true;
+ private bool _navBarHasShadow = true;
+ private bool _navBarVisibilityAnimationEnabled = true;
+ private PresentationMode _presentationMode = PresentationMode.Animated;
public FlyoutDisplayOptions FlyoutDisplayOptions
{
@@ -243,30 +277,6 @@ public bool TabBarIsVisible
}
}
}
- public bool IsEnabled
- {
- get => _isEnabled;
- set
- {
- if (_isEnabled != value)
- {
- _isEnabled = value;
- OnPropertyChanged();
- }
- }
- }
- public bool IsVisible
- {
- get => _isVisible;
- set
- {
- if (_isVisible != value)
- {
- _isVisible = value;
- OnPropertyChanged();
- }
- }
- }
public ShellViewModel()
{
ItemTemplate = new DataTemplate(() =>
@@ -288,8 +298,212 @@ public ShellViewModel()
label.SetBinding(Label.TextProperty, "Text");
return label;
});
+
+ _command = new Command(
+ execute: param =>
+ {
+ CommandExecuted = param is string s && !string.IsNullOrEmpty(s)
+ ? $"Executed: {s}"
+ : "Executed";
+ Shell.Current?.GoToAsync("..");
+ },
+ canExecute: _ => _isEnabled);
+ }
+ public ICommand Command => _command;
+ public string TextOverride
+ {
+ get => _textOverride;
+ set { if (_textOverride != value) { _textOverride = value; OnPropertyChanged(); } }
}
+ public string IconOverride
+ {
+ get => _iconOverride;
+ set { if (_iconOverride != value) { _iconOverride = value; OnPropertyChanged(); } }
+ }
+ public bool IsEnabled
+ {
+ get => _isEnabled;
+ set
+ {
+ if (_isEnabled != value)
+ {
+ _isEnabled = value;
+ OnPropertyChanged();
+ OnPropertyChanged(nameof(IsEnabledText));
+ _command.ChangeCanExecute();
+ }
+ }
+ }
+ public string IsEnabledText => $"IsEnabled: {_isEnabled}";
+ public bool IsVisible
+ {
+ get => _isVisible;
+ set { if (_isVisible != value) { _isVisible = value; OnPropertyChanged(); OnPropertyChanged(nameof(IsVisibleText)); } }
+ }
+ public string IsVisibleText => $"IsVisible: {_isVisible}";
+ public string CommandParameter
+ {
+ get => _commandParameter;
+ set { if (_commandParameter != value) { _commandParameter = value; OnPropertyChanged(); } }
+ }
+ public string CommandExecuted
+ {
+ get => _commandExecuted;
+ set { if (_commandExecuted != value) { _commandExecuted = value; OnPropertyChanged(); } }
+ }
+ public string CurrentState
+ {
+ get => _currentState;
+ set { if (_currentState != value) { _currentState = value; OnPropertyChanged(); } }
+ }
+ public string CurrentPage
+ {
+ get => _currentPage;
+ set { if (_currentPage != value) { _currentPage = value; OnPropertyChanged(); } }
+ }
+ public string CurrentItem
+ {
+ get => _currentItem;
+ set { if (_currentItem != value) { _currentItem = value; OnPropertyChanged(); } }
+ }
+ public string ShellCurrent
+ {
+ get => _shellCurrent;
+ set { if (_shellCurrent != value) { _shellCurrent = value; OnPropertyChanged(); } }
+ }
+ public string NavigatingCurrent
+ {
+ get => _navigatingCurrent;
+ set { if (_navigatingCurrent != value) { _navigatingCurrent = value; OnPropertyChanged(); } }
+ }
+ public string NavigatingSource
+ {
+ get => _navigatingSource;
+ set { if (_navigatingSource != value) { _navigatingSource = value; OnPropertyChanged(); } }
+ }
+ public string NavigatingTarget
+ {
+ get => _navigatingTarget;
+ set { if (_navigatingTarget != value) { _navigatingTarget = value; OnPropertyChanged(); } }
+ }
+ public string NavigatingCanCancel
+ {
+ get => _navigatingCanCancel;
+ set { if (_navigatingCanCancel != value) { _navigatingCanCancel = value; OnPropertyChanged(); } }
+ }
+ public string NavigatingCancelled
+ {
+ get => _navigatingCancelled;
+ set { if (_navigatingCancelled != value) { _navigatingCancelled = value; OnPropertyChanged(); } }
+ }
+ public string NavigatedCurrent
+ {
+ get => _navigatedCurrent;
+ set { if (_navigatedCurrent != value) { _navigatedCurrent = value; OnPropertyChanged(); } }
+ }
+ public string NavigatedPrevious
+ {
+ get => _navigatedPrevious;
+ set { if (_navigatedPrevious != value) { _navigatedPrevious = value; OnPropertyChanged(); } }
+ }
+ public string NavigatedSource
+ {
+ get => _navigatedSource;
+ set { if (_navigatedSource != value) { _navigatedSource = value; OnPropertyChanged(); } }
+ }
+ public string RouteStatus
+ {
+ get => _routeStatus;
+ set { if (_routeStatus != value) { _routeStatus = value; OnPropertyChanged(); } }
+ }
+ public bool CancelNavigation
+ {
+ get => _cancelNavigation;
+ set { if (_cancelNavigation != value) { _cancelNavigation = value; OnPropertyChanged(); OnPropertyChanged(nameof(CancelNavigationText)); } }
+ }
+ public string CancelNavigationText => $"CancelNav: {_cancelNavigation}";
+ public bool EnableDeferral
+ {
+ get => _enableDeferral;
+ set { if (_enableDeferral != value) { _enableDeferral = value; OnPropertyChanged(); OnPropertyChanged(nameof(EnableDeferralText)); } }
+ }
+ public string EnableDeferralText => $"Deferral: {_enableDeferral}";
+ public string DeferralStatus
+ {
+ get => _deferralStatus;
+ set { if (_deferralStatus != value) { _deferralStatus = value; OnPropertyChanged(); } }
+ }
+ public string OverrideNavigatingStatus
+ {
+ get => _overrideNavigatingStatus;
+ set { if (_overrideNavigatingStatus != value) { _overrideNavigatingStatus = value; OnPropertyChanged(); } }
+ }
+ public string OverrideNavigatedStatus
+ {
+ get => _overrideNavigatedStatus;
+ set { if (_overrideNavigatedStatus != value) { _overrideNavigatedStatus = value; OnPropertyChanged(); } }
+ }
+ public string TabStackInfo
+ {
+ get => _tabStackInfo;
+ set { if (_tabStackInfo != value) { _tabStackInfo = value; OnPropertyChanged(); } }
+ }
+
+ public Color BackgroundColor
+ {
+ get => _backgroundColor;
+ set { _backgroundColor = value; OnPropertyChanged(); }
+ }
+
+ public Color ForegroundColor
+ {
+ get => _foregroundColor;
+ set { _foregroundColor = value; OnPropertyChanged(); }
+ }
+
+ public Color TitleColor
+ {
+ get => _titleColor;
+ set { _titleColor = value; OnPropertyChanged(); }
+ }
+
+ public Color DisabledColor
+ {
+ get => _disabledColor;
+ set { _disabledColor = value; OnPropertyChanged(); }
+ }
+
+ public Color UnselectedColor
+ {
+ get => _unselectedColor;
+ set { _unselectedColor = value; OnPropertyChanged(); }
+ }
+
+ public bool NavBarIsVisible
+ {
+ get => _navBarVisible;
+ set { _navBarVisible = value; OnPropertyChanged(); }
+ }
+
+ public bool NavBarHasShadow
+ {
+ get => _navBarHasShadow;
+ set { _navBarHasShadow = value; OnPropertyChanged(); }
+ }
+
+ public bool NavBarVisibilityAnimationEnabled
+ {
+ get => _navBarVisibilityAnimationEnabled;
+ set { _navBarVisibilityAnimationEnabled = value; OnPropertyChanged(); }
+ }
+
+ public PresentationMode PresentationMode
+ {
+ get => _presentationMode;
+ set { _presentationMode = value; OnPropertyChanged(); }
+ }
+
public event PropertyChangedEventHandler PropertyChanged;
- private void OnPropertyChanged([CallerMemberName] string name = "")
+ void OnPropertyChanged([CallerMemberName] string name = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Slider/SliderControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Slider/SliderControlPage.xaml.cs
index 3011d2bfddd8..11c03e39d434 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Slider/SliderControlPage.xaml.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Slider/SliderControlPage.xaml.cs
@@ -10,9 +10,6 @@ public class SliderControlPage : NavigationPage
public SliderControlPage()
{
_viewModel = new SliderViewModel();
-#if ANDROID
- BarTextColor = Colors.White;
-#endif
PushAsync(new SliderControlMainPage(_viewModel));
}
}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Slider/SliderOptionsPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Slider/SliderOptionsPage.xaml
index 08ee64982ede..982b462ed893 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Slider/SliderOptionsPage.xaml
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Slider/SliderOptionsPage.xaml
@@ -67,7 +67,10 @@
-
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Slider/SliderOptionsPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Slider/SliderOptionsPage.xaml.cs
index e35f116ca57f..df5162357759 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Slider/SliderOptionsPage.xaml.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Slider/SliderOptionsPage.xaml.cs
@@ -100,5 +100,10 @@ private void ThumbImageSourceButton_Clicked(object sender, EventArgs e)
{
_viewModel.ThumbImageSource = "coffee.png";
}
+
+ private void ThumbImageResetButton_Clicked(object sender, EventArgs e)
+ {
+ _viewModel.ThumbImageSource = null;
+ }
}
}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Stepper/StepperControlPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Stepper/StepperControlPage.xaml
index b686c52f5a67..355a4a6f35a5 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Stepper/StepperControlPage.xaml
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Stepper/StepperControlPage.xaml
@@ -6,78 +6,121 @@
x:DataType="local:StepperViewModel"
Title="StepperFeature">
-
-
-
+
+
+
-
+
-
-
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Stepper/StepperControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Stepper/StepperControlPage.xaml.cs
index 9317cece85c3..5cc74230e720 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Stepper/StepperControlPage.xaml.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Stepper/StepperControlPage.xaml.cs
@@ -30,6 +30,16 @@ public StepperControlMainPage()
private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
{
BindingContext = _viewModel = new StepperViewModel();
+ _viewModel.ValueChangedText = "Not Raised";
await Navigation.PushAsync(new StepperFeaturePage(_viewModel));
}
-}
\ No newline at end of file
+ private void StepperControl_ValueChanged(object sender, ValueChangedEventArgs e)
+ {
+ if (BindingContext is StepperViewModel vm)
+ {
+ vm.ValueChangedText = "Raised";
+ vm.OldValue = e.OldValue;
+ vm.NewValue = e.NewValue;
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Stepper/StepperViewModel.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Stepper/StepperViewModel.cs
index c0a1e027c57d..764f25e516c0 100644
--- a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Stepper/StepperViewModel.cs
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Stepper/StepperViewModel.cs
@@ -11,8 +11,12 @@ public class StepperViewModel : INotifyPropertyChanged
private double _value = 0;
private bool _isEnabled = true;
private bool _isVisible = true;
+ private double _oldValue;
+ private double _newValue;
+
private FlowDirection _flowDirection = FlowDirection.LeftToRight;
+
public double Minimum
{
get => _minimum;
@@ -37,6 +41,13 @@ public double Value
set => SetProperty(ref _value, value);
}
+ private string _valueChangedText = "Not Raised";
+ public string ValueChangedText
+ {
+ get => _valueChangedText;
+ set => SetProperty(ref _valueChangedText, value);
+ }
+
public bool IsEnabled
{
get => _isEnabled;
@@ -55,6 +66,19 @@ public FlowDirection FlowDirection
set => SetProperty(ref _flowDirection, value);
}
+ public double OldValue
+ {
+ get => _oldValue;
+ set => SetProperty(ref _oldValue, value);
+ }
+
+ public double NewValue
+ {
+ get => _newValue;
+ set => SetProperty(ref _newValue, value);
+ }
+
+
public event PropertyChangedEventHandler PropertyChanged;
protected void SetProperty(ref T backingStore, T value, [CallerMemberName] string propertyName = "")
@@ -65,4 +89,4 @@ protected void SetProperty(ref T backingStore, T value, [CallerMemberName] st
backingStore = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
-}
\ No newline at end of file
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Triggers/TriggersControlPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Triggers/TriggersControlPage.xaml
new file mode 100644
index 000000000000..a8bf6b1e5d98
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Triggers/TriggersControlPage.xaml
@@ -0,0 +1,579 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Triggers/TriggersControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Triggers/TriggersControlPage.xaml.cs
new file mode 100644
index 000000000000..cb355c08416a
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Triggers/TriggersControlPage.xaml.cs
@@ -0,0 +1,127 @@
+namespace Maui.Controls.Sample;
+
+public class TriggersControlPage : NavigationPage
+{
+ private TriggersViewModel _viewModel;
+
+ public TriggersControlPage()
+ {
+ _viewModel = new TriggersViewModel();
+ PushAsync(new TriggersControlMainPage(_viewModel));
+ }
+}
+
+public partial class TriggersControlMainPage : ContentPage
+{
+ private TriggersViewModel _viewModel;
+ private bool _isReturningFromOptions = false;
+
+ public TriggersControlMainPage(TriggersViewModel viewModel)
+ {
+ InitializeComponent();
+ _viewModel = viewModel;
+ BindingContext = _viewModel;
+
+ // Display platform information
+ UpdatePlatformLabel();
+ // Display orientation information
+ UpdateOrientationLabel();
+ }
+
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+
+ // Reset entry values and focus only when returning from options page
+ if (_isReturningFromOptions)
+ {
+ // Reset view model and clear entry focus
+ // OnAppearing is called after the page is visible, so UI is already ready
+ _viewModel.Reset();
+ ClearAndUnfocusAllEntries();
+ _isReturningFromOptions = false;
+ }
+
+ // Update orientation when page appears
+ UpdateOrientationLabel();
+
+ // Subscribe to orientation changes
+ DeviceDisplay.Current.MainDisplayInfoChanged += OnMainDisplayInfoChanged;
+
+ // Re-subscribe to size changes (may have been unsubscribed in OnDisappearing)
+ this.SizeChanged += OnPageSizeChanged;
+ }
+
+ private void ClearAndUnfocusAllEntries()
+ {
+ // Force focus to content page first to ensure entries lose focus
+ this.Focus();
+
+ // Directly clear text and unfocus all entry controls
+ if (propertyTriggerEntry != null)
+ {
+ propertyTriggerEntry.Text = string.Empty;
+ propertyTriggerEntry.Unfocus();
+ }
+ if (dataEntry != null)
+ {
+ dataEntry.Text = string.Empty;
+ dataEntry.Unfocus();
+ }
+ if (numericEntry != null)
+ {
+ numericEntry.Text = string.Empty;
+ numericEntry.Unfocus();
+ }
+ if (emailEntry != null)
+ {
+ emailEntry.Text = string.Empty;
+ emailEntry.Unfocus();
+ }
+ if (phoneEntry != null)
+ {
+ phoneEntry.Text = string.Empty;
+ phoneEntry.Unfocus();
+ }
+ }
+
+ protected override void OnDisappearing()
+ {
+ base.OnDisappearing();
+
+ // Unsubscribe from orientation changes
+ DeviceDisplay.Current.MainDisplayInfoChanged -= OnMainDisplayInfoChanged;
+
+ // Unsubscribe from size changes
+ this.SizeChanged -= OnPageSizeChanged;
+ }
+
+ private void OnPageSizeChanged(object sender, EventArgs e)
+ {
+ // Update window size label for adaptive trigger demo
+ windowSizeLabel.Text = $"Window size: {Width:F0} x {Height:F0} (Width threshold: 600)";
+ }
+
+ private void OnMainDisplayInfoChanged(object sender, DisplayInfoChangedEventArgs e)
+ {
+ // Update orientation label when device orientation changes
+ UpdateOrientationLabel();
+ }
+
+ private void UpdatePlatformLabel()
+ {
+ platformLabel.Text = $"Running on: {DeviceInfo.Platform}";
+ }
+
+ private void UpdateOrientationLabel()
+ {
+ var orientation = DeviceDisplay.Current.MainDisplayInfo.Orientation;
+ orientationLabel.Text = $"Current orientation: {orientation}";
+ }
+
+ private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
+ {
+ _isReturningFromOptions = true;
+ await Navigation.PushAsync(new TriggersOptionsPage(_viewModel));
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Triggers/TriggersOptionsPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Triggers/TriggersOptionsPage.xaml
new file mode 100644
index 000000000000..b874394a3dcf
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Triggers/TriggersOptionsPage.xaml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Triggers/TriggersOptionsPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Triggers/TriggersOptionsPage.xaml.cs
new file mode 100644
index 000000000000..a2c58b1b9ab1
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Triggers/TriggersOptionsPage.xaml.cs
@@ -0,0 +1,29 @@
+namespace Maui.Controls.Sample;
+
+public partial class TriggersOptionsPage : ContentPage
+{
+ private TriggersViewModel _viewModel;
+
+ public TriggersOptionsPage(TriggersViewModel viewModel)
+ {
+ InitializeComponent();
+ _viewModel = viewModel;
+ BindingContext = _viewModel;
+ }
+
+ private void ApplyButton_Clicked(object sender, EventArgs e)
+ {
+ Navigation.PopAsync();
+ }
+
+ private void OnTriggerTypeButtonClicked(object sender, EventArgs e)
+ {
+ if (sender is Button button && button.CommandParameter is string triggerTypeString)
+ {
+ if (Enum.TryParse(triggerTypeString, out var triggerType))
+ {
+ _viewModel.SelectedTriggerType = triggerType;
+ }
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Triggers/TriggersViewModel.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Triggers/TriggersViewModel.cs
new file mode 100644
index 000000000000..2f3f2fffd0e4
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/Triggers/TriggersViewModel.cs
@@ -0,0 +1,246 @@
+using System;
+using System.ComponentModel;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+
+namespace Maui.Controls.Sample;
+
+public class TriggersViewModel : INotifyPropertyChanged
+{
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private TriggerType _selectedTriggerType = TriggerType.PropertyTrigger;
+ private bool _isToggled = false;
+ private bool _isChecked = false;
+ private string _dataEntryText = string.Empty;
+ private string _emailEntryText = string.Empty;
+ private string _phoneEntryText = string.Empty;
+
+ public TriggerType SelectedTriggerType
+ {
+ get => _selectedTriggerType;
+ set
+ {
+ if (_selectedTriggerType != value)
+ {
+ _selectedTriggerType = value;
+ OnPropertyChanged();
+ OnPropertyChanged(nameof(ShowPropertyTrigger));
+ OnPropertyChanged(nameof(ShowDataTrigger));
+ OnPropertyChanged(nameof(ShowEventTrigger));
+ OnPropertyChanged(nameof(ShowMultiTrigger));
+ OnPropertyChanged(nameof(ShowEnterExitActions));
+ OnPropertyChanged(nameof(ShowStateTrigger));
+ OnPropertyChanged(nameof(ShowAdaptiveTrigger));
+ OnPropertyChanged(nameof(ShowCompareStateTrigger));
+ OnPropertyChanged(nameof(ShowDeviceStateTrigger));
+ OnPropertyChanged(nameof(ShowOrientationStateTrigger));
+ }
+ }
+ }
+
+ public bool IsToggled
+ {
+ get => _isToggled;
+ set
+ {
+ if (_isToggled != value)
+ {
+ _isToggled = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public bool IsChecked
+ {
+ get => _isChecked;
+ set
+ {
+ if (_isChecked != value)
+ {
+ _isChecked = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public string DataEntryText
+ {
+ get => _dataEntryText;
+ set
+ {
+ if (_dataEntryText != value)
+ {
+ _dataEntryText = value;
+ OnPropertyChanged();
+ OnPropertyChanged(nameof(IsSaveButtonEnabled));
+ }
+ }
+ }
+
+ public string EmailEntryText
+ {
+ get => _emailEntryText;
+ set
+ {
+ if (_emailEntryText != value)
+ {
+ _emailEntryText = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public string PhoneEntryText
+ {
+ get => _phoneEntryText;
+ set
+ {
+ if (_phoneEntryText != value)
+ {
+ _phoneEntryText = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public bool IsSaveButtonEnabled => !string.IsNullOrEmpty(DataEntryText);
+ public bool ShowPropertyTrigger => SelectedTriggerType == TriggerType.PropertyTrigger;
+ public bool ShowDataTrigger => SelectedTriggerType == TriggerType.DataTrigger;
+ public bool ShowEventTrigger => SelectedTriggerType == TriggerType.EventTrigger;
+ public bool ShowMultiTrigger => SelectedTriggerType == TriggerType.MultiTrigger;
+ public bool ShowEnterExitActions => SelectedTriggerType == TriggerType.EnterExitActions;
+ public bool ShowStateTrigger => SelectedTriggerType == TriggerType.StateTrigger;
+ public bool ShowAdaptiveTrigger => SelectedTriggerType == TriggerType.AdaptiveTrigger;
+ public bool ShowCompareStateTrigger => SelectedTriggerType == TriggerType.CompareStateTrigger;
+ public bool ShowDeviceStateTrigger => SelectedTriggerType == TriggerType.DeviceStateTrigger;
+ public bool ShowOrientationStateTrigger => SelectedTriggerType == TriggerType.OrientationStateTrigger;
+
+ public void Reset()
+ {
+ IsToggled = false;
+ IsChecked = false;
+ DataEntryText = string.Empty;
+ EmailEntryText = string.Empty;
+ PhoneEntryText = string.Empty;
+ }
+
+ protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+}
+
+public enum TriggerType
+{
+ PropertyTrigger,
+ DataTrigger,
+ EventTrigger,
+ MultiTrigger,
+ EnterExitActions,
+ StateTrigger,
+ AdaptiveTrigger,
+ CompareStateTrigger,
+ DeviceStateTrigger,
+ OrientationStateTrigger
+}
+
+///
+/// Trigger action that performs a fade animation on visual elements
+///
+public class FadeTriggerAction : TriggerAction
+{
+ public int StartsFrom { get; set; }
+
+ protected override void Invoke(VisualElement sender)
+ {
+ sender.Animate("FadeTriggerAction", new Animation((d) =>
+ {
+ var val = StartsFrom == 1 ? d : 1 - d;
+ sender.BackgroundColor = Color.FromRgb(1, val, 1);
+ }),
+ length: 1000, // milliseconds
+ easing: Easing.Linear);
+ }
+}
+
+///
+/// Trigger action that validates numeric input and changes text color based on validity
+///
+public class NumericValidationTriggerAction : TriggerAction
+{
+ protected override void Invoke(Entry entry)
+ {
+ double result;
+ bool isValid = double.TryParse(entry.Text, out result);
+ entry.TextColor = isValid ? Colors.Black : Colors.Red;
+ entry.BackgroundColor = isValid ? Colors.SkyBlue : Colors.Yellow;
+ }
+}
+
+///
+/// Value converter that inverts a boolean value
+///
+public class InverseBooleanConverter : IValueConverter
+{
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is bool boolValue)
+ {
+ return !boolValue;
+ }
+ return false;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is bool boolValue)
+ {
+ return !boolValue;
+ }
+ return false;
+ }
+}
+
+///
+/// Value converter that returns true if a string is not null or empty
+///
+public class StringNotNullOrEmptyConverter : IValueConverter
+{
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return !string.IsNullOrEmpty(value as string);
+ }
+
+ ///
+ /// Two-way binding is not supported. Returns as a default.
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return Binding.DoNothing;
+ }
+}
+
+///
+/// Value converter that returns the length of a string
+///
+public class StringLengthConverter : IValueConverter
+{
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is string text)
+ {
+ return text.Length;
+ }
+ return 0;
+ }
+
+ ///
+ /// Two-way binding is not supported. Returns as a default.
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return Binding.DoNothing;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMButton/VisualStateManagerButtonPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMButton/VisualStateManagerButtonPage.xaml
new file mode 100644
index 000000000000..4ed6751ea722
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMButton/VisualStateManagerButtonPage.xaml
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMButton/VisualStateManagerButtonPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMButton/VisualStateManagerButtonPage.xaml.cs
new file mode 100644
index 000000000000..a81ada579092
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMButton/VisualStateManagerButtonPage.xaml.cs
@@ -0,0 +1,83 @@
+namespace Maui.Controls.Sample;
+
+public partial class VisualStateManagerButtonPage : ContentPage
+{
+ public VisualStateManagerButtonPage()
+ {
+ InitializeComponent();
+ VisualStateManager.GoToState(DemoButton, "Normal");
+ }
+
+ void OnDemoButtonPressed(object sender, EventArgs e)
+ {
+ if (!DemoButton.IsEnabled)
+ return;
+ VisualStateManager.GoToState(DemoButton, "Pressed");
+ StateLabel.Text = "State: Pressed";
+ DemoButton.Text = "Pressed";
+ }
+
+ void OnDemoButtonReleased(object sender, EventArgs e)
+ {
+ if (!DemoButton.IsEnabled)
+ return;
+ VisualStateManager.GoToState(DemoButton, "Normal");
+ StateLabel.Text = "State: Normal/Released";
+ DemoButton.Text = "Normal/Released";
+ }
+
+ void OnToggleButtonDisabled(object sender, EventArgs e)
+ {
+ DemoButton.IsEnabled = !DemoButton.IsEnabled;
+ ButtonDisableButton.Text = DemoButton.IsEnabled ? "Disable" : "Enable";
+ if (!DemoButton.IsEnabled)
+ {
+ VisualStateManager.GoToState(DemoButton, "Disabled");
+ StateLabel.Text = "State: Disabled";
+ DemoButton.Text = "Disabled";
+ }
+ else
+ {
+ VisualStateManager.GoToState(DemoButton, "Normal");
+ StateLabel.Text = "State: Normal";
+ DemoButton.Text = "Normal";
+ }
+ }
+
+ void OnResetButtonState(object sender, EventArgs e)
+ {
+ DemoButton.IsEnabled = true;
+ VisualStateManager.GoToState(DemoButton, "Normal");
+ ButtonDisableButton.Text = "Disable";
+ StateLabel.Text = "State: Normal";
+ DemoButton.Text = "Press Me";
+ }
+
+ void OnDemoButtonPointerEntered(object sender, PointerEventArgs e)
+ {
+ if (!DemoButton.IsEnabled)
+ return;
+ VisualStateManager.GoToState(DemoButton, "PointerOver");
+ StateLabel.Text = "State: PointerOver Enter";
+ DemoButton.Text = "PointerOver Enter";
+ }
+
+ void OnDemoButtonPointerExited(object sender, PointerEventArgs e)
+ {
+ if (!DemoButton.IsEnabled)
+ return;
+ VisualStateManager.GoToState(DemoButton, "Normal");
+ StateLabel.Text = "State: PointerOver Exit";
+ DemoButton.Text = "PointerOver Exit";
+ }
+
+ void OnButtonFocused(object sender, FocusEventArgs e)
+ {
+ if (!DemoButton.IsEnabled)
+ return;
+ VisualStateManager.GoToState(DemoButton, "Focused");
+ StateLabel.Text = "State: Focused";
+ DemoButton.Text = "Focused";
+ }
+}
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMCheckBox/VisualStateManagerCheckBoxPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMCheckBox/VisualStateManagerCheckBoxPage.xaml
new file mode 100644
index 000000000000..936550847517
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMCheckBox/VisualStateManagerCheckBoxPage.xaml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMCheckBox/VisualStateManagerCheckBoxPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMCheckBox/VisualStateManagerCheckBoxPage.xaml.cs
new file mode 100644
index 000000000000..8dc63932e476
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMCheckBox/VisualStateManagerCheckBoxPage.xaml.cs
@@ -0,0 +1,51 @@
+namespace Maui.Controls.Sample;
+
+public partial class VisualStateManagerCheckBoxPage : ContentPage
+{
+ public VisualStateManagerCheckBoxPage()
+ {
+ InitializeComponent();
+ VisualStateManager.GoToState(VSMCheckBox, "Normal");
+ CheckBoxState.Text = "State: Normal";
+ }
+
+ void OnCheckBoxCheckedChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (!VSMCheckBox.IsEnabled)
+ {
+ CheckBoxState.Text = "State: Disabled";
+ return;
+ }
+
+ var state = e.Value ? "Checked" : "Unchecked";
+ VisualStateManager.GoToState(VSMCheckBox, state);
+ CheckBoxState.Text = $"State: {state}";
+ }
+
+ void OnToggleCheckBoxDisabled(object sender, EventArgs e)
+ {
+ VSMCheckBox.IsEnabled = !VSMCheckBox.IsEnabled;
+ CheckBoxDisableButton.Text = VSMCheckBox.IsEnabled ? "Disable" : "Enable";
+ if (!VSMCheckBox.IsEnabled)
+ {
+ VisualStateManager.GoToState(VSMCheckBox, "Disabled");
+ CheckBoxState.Text = "State: Disabled";
+ }
+ else
+ {
+ var state = VSMCheckBox.IsChecked ? "Checked" : "Unchecked";
+ VisualStateManager.GoToState(VSMCheckBox, state);
+ CheckBoxState.Text = $"State: {state}";
+ }
+ }
+
+ void OnResetCheckBox(object sender, EventArgs e)
+ {
+ VSMCheckBox.IsEnabled = true;
+ VSMCheckBox.IsChecked = false;
+ CheckBoxDisableButton.Text = "Disable";
+ VisualStateManager.GoToState(VSMCheckBox, "Normal");
+ CheckBoxState.Text = "State: Normal";
+ }
+}
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMCollectionViewPage/VSMCollectionViewViewModel.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMCollectionViewPage/VSMCollectionViewViewModel.cs
new file mode 100644
index 000000000000..89986fae6308
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMCollectionViewPage/VSMCollectionViewViewModel.cs
@@ -0,0 +1,28 @@
+using System.Collections.ObjectModel;
+
+namespace Maui.Controls.Sample;
+
+public class VSMCollectionViewItem
+{
+ public string Name { get; set; } = string.Empty;
+}
+
+public class VSMCollectionViewViewModel
+{
+ public ObservableCollection Items { get; }
+
+ public VSMCollectionViewViewModel()
+ {
+ Items = new ObservableCollection
+ {
+ new VSMCollectionViewItem { Name = "Banana" },
+ new VSMCollectionViewItem { Name = "Cherry" },
+ new VSMCollectionViewItem { Name = "Date" },
+ new VSMCollectionViewItem { Name = "Elderberry" },
+ new VSMCollectionViewItem { Name = "Fig" },
+ new VSMCollectionViewItem { Name = "Grape" },
+ new VSMCollectionViewItem { Name = "Honeydew" }
+ };
+ }
+}
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMCollectionViewPage/VisualStateManagerCollectionViewPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMCollectionViewPage/VisualStateManagerCollectionViewPage.xaml
new file mode 100644
index 000000000000..9837805f4529
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMCollectionViewPage/VisualStateManagerCollectionViewPage.xaml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMCollectionViewPage/VisualStateManagerCollectionViewPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMCollectionViewPage/VisualStateManagerCollectionViewPage.xaml.cs
new file mode 100644
index 000000000000..673992f896d3
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMCollectionViewPage/VisualStateManagerCollectionViewPage.xaml.cs
@@ -0,0 +1,140 @@
+using System.Collections.ObjectModel;
+
+namespace Maui.Controls.Sample;
+
+public partial class VisualStateManagerCollectionViewPage : ContentPage
+{
+ private readonly VSMCollectionViewViewModel _viewModel;
+ public VisualStateManagerCollectionViewPage()
+ {
+ InitializeComponent();
+ _viewModel = new VSMCollectionViewViewModel();
+ BindingContext = _viewModel;
+ }
+
+ void OnCollectionSelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (!MyCollectionView.IsEnabled)
+ {
+ CVState.Text = "State: Disabled";
+ return;
+ }
+
+ var hasSelection = e.CurrentSelection != null && e.CurrentSelection.Count > 0;
+ VisualStateManager.GoToState(MyCollectionView, hasSelection ? "Selected" : "Normal");
+ CVState.Text = hasSelection ? $"State: Selected ({e.CurrentSelection.Count})" : "State: Normal";
+ }
+
+ void OnToggleCollectionViewDisabled(object sender, EventArgs e)
+ {
+ MyCollectionView.IsEnabled = !MyCollectionView.IsEnabled;
+ CVToggleButton?.Text = MyCollectionView.IsEnabled ? "Disable" : "Enable";
+ CVState.Text = MyCollectionView.IsEnabled
+ ? (GetSelectedCount() > 0 ? $"State: Selected ({GetSelectedCount()})" : "State: Normal")
+ : "State: Disabled";
+ }
+
+ void OnResetCollectionView(object sender, EventArgs e)
+ {
+ MyCollectionView.IsEnabled = true;
+ MyCollectionView.SelectedItem = null;
+ if (MyCollectionView.SelectionMode == SelectionMode.Multiple)
+ {
+ MyCollectionView.SelectedItems?.Clear();
+ }
+ CVToggleButton.Text = "Disable";
+ VisualStateManager.GoToState(MyCollectionView, "Normal");
+ CVState.Text = "State: Normal";
+ }
+
+ void OnItemPointerEntered(object sender, PointerEventArgs e)
+ {
+ if (!MyCollectionView.IsEnabled)
+ {
+ CVState.Text = "State: Disabled";
+ return;
+ }
+
+ if (sender is VisualElement ve)
+ {
+ VisualStateManager.GoToState(ve, "PointerOver");
+ CVState.Text = "State: PointerOver";
+ }
+ }
+
+ void OnItemPointerExited(object sender, PointerEventArgs e)
+ {
+ if (!MyCollectionView.IsEnabled)
+ {
+ CVState.Text = "State: Disabled";
+ return;
+ }
+
+ if (sender is VisualElement ve)
+ {
+ var bc = ve.BindingContext;
+ var isSelected = IsItemSelected(bc);
+ VisualStateManager.GoToState(ve, isSelected ? "Selected" : "Normal");
+ CVState.Text = GetSelectedCount() > 0 ? $"State: Selected ({GetSelectedCount()})" : "State: Normal";
+ }
+ }
+
+ int GetSelectedCount()
+ {
+ if (MyCollectionView.SelectionMode == SelectionMode.Multiple)
+ return MyCollectionView.SelectedItems?.Count ?? 0;
+ return MyCollectionView.SelectedItem != null ? 1 : 0;
+ }
+
+ bool IsItemSelected(object item)
+ {
+ if (item is null)
+ return false;
+ if (MyCollectionView.SelectionMode == SelectionMode.Multiple)
+ return MyCollectionView.SelectedItems?.Contains(item) == true;
+ return Equals(MyCollectionView.SelectedItem, item);
+
+ }
+
+ void OnSelectItem(object sender, EventArgs e)
+ {
+ if (!MyCollectionView.IsEnabled)
+ {
+ CVState.Text = "State: Disabled";
+ return;
+ }
+ if (_viewModel.Items is ObservableCollection items && items.Count > 0)
+ {
+ if (MyCollectionView.SelectionMode == SelectionMode.Multiple)
+ {
+ if (MyCollectionView.SelectedItems?.Contains(items[0]) != true)
+ {
+ MyCollectionView.SelectedItems?.Add(items[0]);
+ }
+ }
+ else
+ {
+ MyCollectionView.SelectedItem = items[0];
+ }
+ }
+ VisualStateManager.GoToState(MyCollectionView, "Selected");
+ CVState.Text = $"State: Selected ({GetSelectedCount()})";
+ }
+
+ void OnSetNormal(object sender, EventArgs e)
+ {
+ if(!MyCollectionView.IsEnabled)
+ {
+ CVState.Text = "State: Disabled";
+ return;
+ }
+ MyCollectionView.SelectedItem = null;
+ if (MyCollectionView.SelectionMode == SelectionMode.Multiple)
+ {
+ MyCollectionView.SelectedItems?.Clear();
+ }
+ VisualStateManager.GoToState(MyCollectionView, "Normal");
+ CVState.Text = "State: Normal/Unselected";
+ }
+}
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMEntry/VisualStateManagerEntryPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMEntry/VisualStateManagerEntryPage.xaml
new file mode 100644
index 000000000000..58704606962a
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMEntry/VisualStateManagerEntryPage.xaml
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMEntry/VisualStateManagerEntryPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMEntry/VisualStateManagerEntryPage.xaml.cs
new file mode 100644
index 000000000000..0be6ddbc6287
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMEntry/VisualStateManagerEntryPage.xaml.cs
@@ -0,0 +1,131 @@
+using System.Text.RegularExpressions;
+
+namespace Maui.Controls.Sample;
+
+public partial class VisualStateManagerEntryPage : ContentPage
+{
+ bool _completedFired;
+
+ public VisualStateManagerEntryPage()
+ {
+ InitializeComponent();
+
+ VisualStateManager.GoToState(VSMEntry, "Normal");
+ VisualStateManager.GoToState(ValidationEntry, "Invalid");
+ }
+
+ void OnFocusEntry(object sender, EventArgs e)
+ {
+ if (!VSMEntry.IsEnabled)
+ return;
+ VisualStateManager.GoToState(VSMEntry, "Focused");
+ StateEntryLabel.Text = "State: Focused";
+ VSMEntry.Focus();
+ }
+
+ void OnNormalEntry(object sender, EventArgs e)
+ {
+ if (!VSMEntry.IsEnabled)
+ return;
+ VisualStateManager.GoToState(VSMEntry, "Normal");
+ StateEntryLabel.Text = "State: Normal/Unfocused";
+ VSMEntry.Unfocus();
+ }
+
+ void OnEntryDisabled(object sender, EventArgs e)
+ {
+ VSMEntry.IsEnabled = !VSMEntry.IsEnabled;
+ DisableEntryButton.Text = VSMEntry.IsEnabled ? "Disable" : "Enable";
+
+ if (!VSMEntry.IsEnabled)
+ {
+ VisualStateManager.GoToState(VSMEntry, "Disabled");
+ StateEntryLabel.Text = "State: Disabled";
+ }
+ else
+ {
+ VisualStateManager.GoToState(VSMEntry, "Normal");
+ StateEntryLabel.Text = "State: Normal";
+ }
+ }
+
+ void OnEntryCompleted(object sender, EventArgs e)
+ {
+ if (!VSMEntry.IsEnabled)
+ return;
+ _completedFired = true;
+ VisualStateManager.GoToState(VSMEntry, "Completed");
+ StateEntryLabel.Text = "State: Completed";
+ }
+
+ void OnEntryFocused(object sender, FocusEventArgs e)
+ {
+ if (!VSMEntry.IsEnabled)
+ return;
+ VSMEntry.Focus();
+ VisualStateManager.GoToState(VSMEntry, "Focused");
+ StateEntryLabel.Text = "State: Focused";
+ }
+
+ void OnEntryUnfocused(object sender, FocusEventArgs e)
+ {
+ if (_completedFired)
+ {
+ _completedFired = false;
+ return;
+ }
+
+ if (VSMEntry.IsEnabled)
+ {
+ VisualStateManager.GoToState(VSMEntry, "Normal");
+ VSMEntry.Unfocus();
+ StateEntryLabel.Text = "State: Normal/Unfocused";
+ }
+ }
+
+ void OnResetEntry(object sender, EventArgs e)
+ {
+ _completedFired = false;
+ VSMEntry.IsEnabled = true;
+ DisableEntryButton.Text = "Disable";
+ VisualStateManager.GoToState(VSMEntry, "Normal");
+ StateEntryLabel.Text = "State: Normal";
+ VSMEntry.Text = string.Empty;
+ }
+
+ // ---------------- Entry2 (Valid / Invalid) ----------------
+ void OnValidationEntryTextChanged(object sender, TextChangedEventArgs e)
+ {
+ ValidateEntryText(e.NewTextValue);
+ }
+
+ void OnValidateEntry(object sender, EventArgs e)
+ {
+ ValidateEntryText(ValidationEntry.Text);
+ }
+
+ void OnValidateEntryReset(object sender, EventArgs e)
+ {
+ ValidationEntry.Text = string.Empty;
+ VisualStateManager.GoToState(ValidationEntry, "Invalid");
+ ValidationEntryLabel.Text = "State: Invalid";
+ }
+
+ void ValidateEntryText(string text)
+ {
+ bool isValid = Regex.IsMatch(text ?? string.Empty,
+ @"^[2-9]\d{2}-\d{3}-\d{4}$");
+
+ if (isValid)
+ {
+ VisualStateManager.GoToState(ValidationEntry, "Valid");
+ ValidationEntryLabel.Text = "State: Valid";
+ }
+ else
+ {
+ VisualStateManager.GoToState(ValidationEntry, "Invalid");
+ ValidationEntryLabel.Text = "State: Invalid";
+ }
+ }
+}
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMLabel/VisualStateManagerLabelPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMLabel/VisualStateManagerLabelPage.xaml
new file mode 100644
index 000000000000..16a2322ed073
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMLabel/VisualStateManagerLabelPage.xaml
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMLabel/VisualStateManagerLabelPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMLabel/VisualStateManagerLabelPage.xaml.cs
new file mode 100644
index 000000000000..c95f13f0f3ac
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMLabel/VisualStateManagerLabelPage.xaml.cs
@@ -0,0 +1,50 @@
+namespace Maui.Controls.Sample;
+
+public partial class VisualStateManagerLabelPage : ContentPage
+{
+ bool _labelSelected;
+ bool _labelDisabled;
+
+ public VisualStateManagerLabelPage()
+ {
+ InitializeComponent();
+ VisualStateManager.GoToState(SelectableLabelContainer, "Normal");
+ }
+ void OnToggleLabelSelected(object sender, EventArgs e)
+ {
+ if (_labelDisabled)
+ return;
+ _labelSelected = !_labelSelected;
+ var state = _labelSelected ? "Selected" : "Normal";
+ VisualStateManager.GoToState(SelectableLabelContainer, state);
+ LabelState.Text = $"State: {state}";
+ }
+
+ void OnToggleLabelDisabled(object sender, EventArgs e)
+ {
+ _labelDisabled = !_labelDisabled;
+ string state;
+ LabelDisableButton.Text = _labelDisabled ? "Enable" : "Disable";
+ if (_labelDisabled)
+ {
+ state = "Disabled";
+ VisualStateManager.GoToState(SelectableLabelContainer, state);
+ }
+ else
+ {
+ state = _labelSelected ? "Selected" : "Normal";
+ VisualStateManager.GoToState(SelectableLabelContainer, state);
+ }
+ LabelState.Text = $"State: {state}";
+ }
+
+ void OnResetLabel(object sender, EventArgs e)
+ {
+ _labelSelected = false;
+ _labelDisabled = false;
+ LabelDisableButton.Text = "Disable";
+ VisualStateManager.GoToState(SelectableLabelContainer, "Normal");
+ LabelState.Text = "State: Normal";
+ }
+}
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMSlider/VisualStateManagerSliderPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMSlider/VisualStateManagerSliderPage.xaml
new file mode 100644
index 000000000000..c58aa7bf49c9
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMSlider/VisualStateManagerSliderPage.xaml
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMSlider/VisualStateManagerSliderPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMSlider/VisualStateManagerSliderPage.xaml.cs
new file mode 100644
index 000000000000..5c7bbefa1022
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMSlider/VisualStateManagerSliderPage.xaml.cs
@@ -0,0 +1,94 @@
+namespace Maui.Controls.Sample;
+
+public partial class VisualStateManagerSliderPage : ContentPage
+{
+ bool _isResetting = false;
+
+ public VisualStateManagerSliderPage()
+ {
+ InitializeComponent();
+ VisualStateManager.GoToState(VSMSlider, "Normal");
+ }
+
+ void OnFocusSlider(object sender, EventArgs e)
+ {
+ if (!VSMSlider.IsEnabled)
+ return;
+ if (!VSMSlider.IsFocused)
+ {
+ VSMSlider.Focus();
+ VisualStateManager.GoToState(VSMSlider, "Focused");
+ VSMSlider.Value = 65;
+ SliderState.Text = $"State: Focused | Value: {VSMSlider.Value:0}";
+ }
+ }
+
+ void OnUnfocusSlider(object sender, EventArgs e)
+ {
+ if (!VSMSlider.IsEnabled)
+ return;
+
+ VSMSlider.Unfocus();
+ VisualStateManager.GoToState(VSMSlider, "Normal");
+ SliderState.Text = $"State: Normal/Unfocused | Value: {VSMSlider.Value:0}";
+ }
+
+ void OnToggleSliderDisabled(object sender, EventArgs e)
+ {
+ VSMSlider.IsEnabled = !VSMSlider.IsEnabled;
+ SliderDisableButton.Text = VSMSlider.IsEnabled ? "Disable" : "Enable";
+ if (!VSMSlider.IsEnabled)
+ {
+ VisualStateManager.GoToState(VSMSlider, "Disabled");
+ SliderState.Text = $"State: Disabled | Value: {VSMSlider.Value:0}";
+ }
+ else
+ {
+ VisualStateManager.GoToState(VSMSlider, "Normal");
+ SliderState.Text = $"State: Normal | Value: {VSMSlider.Value:0}";
+ }
+ }
+
+ void OnResetSlider(object sender, EventArgs e)
+ {
+ _isResetting = true;
+ VSMSlider.IsEnabled = true;
+ SliderDisableButton.Text = "Disable";
+ VSMSlider.Value = 50;
+ VisualStateManager.GoToState(VSMSlider, "Normal");
+ SliderState.Text = $"State: Normal | Value: {VSMSlider.Value:0}";
+ _isResetting = false;
+ }
+
+ void OnSliderFocused(object sender, FocusEventArgs e)
+ {
+ VSMSlider.Focus();
+ VisualStateManager.GoToState(VSMSlider, "Focused");
+ SliderState.Text = $"State: Focused | Value: {VSMSlider.Value:0}";
+ }
+
+ void OnSliderUnfocused(object sender, FocusEventArgs e)
+ {
+ VisualStateManager.GoToState(VSMSlider, "Normal");
+ SliderState.Text = $"State: Normal/Unfocused | Value: {VSMSlider.Value:0}";
+ }
+
+ void OnSliderValueChanged(object sender, ValueChangedEventArgs e)
+ {
+ if (_isResetting)
+ {
+ VisualStateManager.GoToState(VSMSlider, "Normal");
+ SliderState.Text = $"State: Normal | Value: {e.NewValue:0}";
+ return;
+ }
+ if (!VSMSlider.IsEnabled)
+ {
+ VisualStateManager.GoToState(VSMSlider, "Disabled");
+ SliderState.Text = $"State: Disabled | Value: {e.NewValue:0}";
+ return;
+ }
+ VisualStateManager.GoToState(VSMSlider, "Focused");
+ SliderState.Text = $"State: Focused | Value: {e.NewValue:0}";
+ }
+}
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMSwitch/VisualStateManagerSwitchPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMSwitch/VisualStateManagerSwitchPage.xaml
new file mode 100644
index 000000000000..a031bb2ff956
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMSwitch/VisualStateManagerSwitchPage.xaml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMSwitch/VisualStateManagerSwitchPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMSwitch/VisualStateManagerSwitchPage.xaml.cs
new file mode 100644
index 000000000000..a58e4caa96ba
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VSMSwitch/VisualStateManagerSwitchPage.xaml.cs
@@ -0,0 +1,48 @@
+namespace Maui.Controls.Sample;
+
+public partial class VisualStateManagerSwitchPage : ContentPage
+{
+ public VisualStateManagerSwitchPage()
+ {
+ InitializeComponent();
+ VisualStateManager.GoToState(VSMSwitch, "Normal");
+ }
+
+ void OnSwitchToggled(object sender, ToggledEventArgs e)
+ {
+ if (!VSMSwitch.IsEnabled)
+ {
+ SwitchState.Text = "State: Disabled";
+ return;
+ }
+ var state = e.Value ? "On" : "Off";
+ VisualStateManager.GoToState(VSMSwitch, state);
+ SwitchState.Text = $"State: {state}";
+ }
+
+ void OnToggleSwitchDisabled(object sender, EventArgs e)
+ {
+ VSMSwitch.IsEnabled = !VSMSwitch.IsEnabled;
+ SwitchDisableButton.Text = VSMSwitch.IsEnabled ? "Disable" : "Enable";
+ if (!VSMSwitch.IsEnabled)
+ {
+ VisualStateManager.GoToState(VSMSwitch, "Disabled");
+ SwitchState.Text = "State: Disabled";
+ }
+ else
+ {
+ var state = VSMSwitch.IsToggled ? "On" : "Off";
+ VisualStateManager.GoToState(VSMSwitch, state);
+ SwitchState.Text = $"State: {state}";
+ }
+ }
+
+ void OnResetSwitch(object sender, EventArgs e)
+ {
+ VSMSwitch.IsEnabled = true;
+ SwitchDisableButton.Text = "Disable";
+ VisualStateManager.GoToState(VSMSwitch, "Normal");
+ VSMSwitch.IsToggled = false;
+ SwitchState.Text = "State: Normal";
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VisualStateManagerFeaturePage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VisualStateManagerFeaturePage.xaml
new file mode 100644
index 000000000000..d91ba3a66c0e
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VisualStateManagerFeaturePage.xaml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VisualStateManagerFeaturePage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VisualStateManagerFeaturePage.xaml.cs
new file mode 100644
index 000000000000..744ebcadcf6e
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualStateManager/VisualStateManagerFeaturePage.xaml.cs
@@ -0,0 +1,52 @@
+namespace Maui.Controls.Sample;
+
+public class VisualStateManagerFeaturePage : NavigationPage
+{
+ public VisualStateManagerFeaturePage()
+ {
+ PushAsync(new VisualStateManagerFeatureMainPage());
+ }
+}
+public partial class VisualStateManagerFeatureMainPage : ContentPage
+{
+ public VisualStateManagerFeatureMainPage()
+ {
+ InitializeComponent();
+ }
+
+ private async void OnVSMButtonClicked (object sender, EventArgs e)
+ {
+ await Navigation.PushAsync(new VisualStateManagerButtonPage ());
+ }
+
+ private async void OnVSMCheckBoxClicked (object sender, EventArgs e)
+ {
+ await Navigation.PushAsync(new VisualStateManagerCheckBoxPage ());
+ }
+
+ private async void OnVSMCollectionViewClicked (object sender, EventArgs e)
+ {
+ await Navigation.PushAsync(new VisualStateManagerCollectionViewPage ());
+ }
+
+ private async void OnVSMEntryClicked (object sender, EventArgs e)
+ {
+ await Navigation.PushAsync(new VisualStateManagerEntryPage ());
+ }
+
+ private async void OnVSMLabelClicked (object sender, EventArgs e)
+ {
+ await Navigation.PushAsync(new VisualStateManagerLabelPage ());
+ }
+
+ private async void OnVSMSliderClicked (object sender, EventArgs e)
+ {
+ await Navigation.PushAsync(new VisualStateManagerSliderPage ());
+ }
+
+ private async void OnVSMSwitchClicked (object sender, EventArgs e)
+ {
+ await Navigation.PushAsync(new VisualStateManagerSwitchPage ());
+ }
+}
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualTransform/VisualTransformControlPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualTransform/VisualTransformControlPage.xaml
new file mode 100644
index 000000000000..e496e108fafd
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualTransform/VisualTransformControlPage.xaml
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualTransform/VisualTransformControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualTransform/VisualTransformControlPage.xaml.cs
new file mode 100644
index 000000000000..79167725c0aa
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualTransform/VisualTransformControlPage.xaml.cs
@@ -0,0 +1,30 @@
+namespace Maui.Controls.Sample;
+
+public partial class VisualTransformControlPage : NavigationPage
+{
+
+ private VisualTransformViewModel _viewModel;
+
+ public VisualTransformControlPage()
+ {
+ _viewModel = new VisualTransformViewModel();
+ PushAsync(new VisualTransformControlMainPage(_viewModel));
+ }
+}
+
+public partial class VisualTransformControlMainPage : ContentPage
+{
+ private VisualTransformViewModel _viewModel;
+
+ public VisualTransformControlMainPage(VisualTransformViewModel viewModel)
+ {
+ InitializeComponent();
+ _viewModel = viewModel;
+ BindingContext = _viewModel;
+ }
+
+ private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e)
+ {
+ await Navigation.PushAsync(new VisualTransformOptionsPage(_viewModel));
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualTransform/VisualTransformOptionsPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualTransform/VisualTransformOptionsPage.xaml
new file mode 100644
index 000000000000..b221efae5a23
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualTransform/VisualTransformOptionsPage.xaml
@@ -0,0 +1,247 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualTransform/VisualTransformOptionsPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualTransform/VisualTransformOptionsPage.xaml.cs
new file mode 100644
index 000000000000..6d14a44eb14e
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualTransform/VisualTransformOptionsPage.xaml.cs
@@ -0,0 +1,21 @@
+namespace Maui.Controls.Sample
+{
+ public partial class VisualTransformOptionsPage : ContentPage
+ {
+ private VisualTransformViewModel _viewModel;
+
+ public VisualTransformOptionsPage(VisualTransformViewModel viewModel)
+ {
+ InitializeComponent();
+ _viewModel = viewModel;
+ BindingContext = _viewModel;
+ }
+
+ private void ApplyButton_Clicked(object sender, EventArgs e)
+ {
+ Navigation.PopAsync();
+ }
+
+
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualTransform/VisualTransformViewModel.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualTransform/VisualTransformViewModel.cs
new file mode 100644
index 000000000000..3189afbccb46
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/VisualTransform/VisualTransformViewModel.cs
@@ -0,0 +1,218 @@
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Windows.Input;
+
+namespace Maui.Controls.Sample;
+
+public class VisualTransformViewModel : INotifyPropertyChanged
+{
+ public event PropertyChangedEventHandler PropertyChanged;
+ // Core Transformation Properties
+ private double _rotation = 0.0;
+ private double _rotationX = 0.0;
+ private double _rotationY = 0.0;
+ private double _scale = 1.0;
+ private double _scaleX = 1.0;
+ private double _scaleY = 1.0;
+ private bool _isVisible = true;
+
+ // Additional Related Properties
+ private double _translationX = 0.0;
+ private double _translationY = 0.0;
+ private double _anchorX = 0.5;
+ private double _anchorY = 0.5;
+
+ // Core Transformation Properties
+ public bool IsVisible
+ {
+ get => _isVisible;
+ set
+ {
+ if (_isVisible != value)
+ {
+ _isVisible = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+ private bool _hasShadow = false;
+ private Shadow _boxShadow = null;
+ public bool HasShadow
+ {
+ get => _hasShadow;
+ set
+ {
+ if (_hasShadow != value)
+ {
+ _hasShadow = value;
+ // Minimal shadow that shouldn't interfere with layout
+ BoxShadow = value
+ ? new Shadow
+ {
+ Radius = 2,
+ Opacity = 0.9f,
+ Brush = new SolidColorBrush(Colors.Gray),
+ Offset = new Point(10, 10) // Visible offset to make shadow apparent in screenshots
+ }
+ : null;
+ OnPropertyChanged(nameof(HasShadow));
+ }
+ }
+ }
+ public Shadow BoxShadow
+ {
+ get => _boxShadow;
+ private set
+ {
+ if (_boxShadow != value)
+ {
+ _boxShadow = value;
+ OnPropertyChanged(nameof(BoxShadow));
+ }
+ }
+ }
+ public double Rotation
+ {
+ get => _rotation;
+ set
+ {
+ if (_rotation != value)
+ {
+ _rotation = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+ public double RotationX
+ {
+ get => _rotationX;
+ set
+ {
+ if (_rotationX != value)
+ {
+ _rotationX = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+ public double RotationY
+ {
+ get => _rotationY;
+ set
+ {
+ if (_rotationY != value)
+ {
+ _rotationY = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+ public double Scale
+ {
+ get => _scale;
+ set
+ {
+ if (_scale != value)
+ {
+ _scale = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+ public double ScaleX
+ {
+ get => _scaleX;
+ set
+ {
+ if (_scaleX != value)
+ {
+ _scaleX = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+ public double ScaleY
+ {
+ get => _scaleY;
+ set
+ {
+ if (_scaleY != value)
+ {
+ _scaleY = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ // Additional Related Properties
+ public double TranslationX
+ {
+ get => _translationX;
+ set
+ {
+ if (_translationX != value)
+ {
+ _translationX = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+ public double TranslationY
+ {
+ get => _translationY;
+ set
+ {
+ if (_translationY != value)
+ {
+ _translationY = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+ public double AnchorX
+ {
+ get => _anchorX;
+ set
+ {
+ if (_anchorX != value)
+ {
+ _anchorX = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+ public double AnchorY
+ {
+ get => _anchorY;
+ set
+ {
+ if (_anchorY != value)
+ {
+ _anchorY = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ // Reset Command
+ public ICommand ResetCommand => new Command(Reset);
+ private void Reset()
+ {
+ Rotation = 0.0;
+ RotationX = 0.0;
+ RotationY = 0.0;
+ Scale = 1.0;
+ ScaleX = 1.0;
+ ScaleY = 1.0;
+ TranslationX = 0.0;
+ TranslationY = 0.0;
+ AnchorX = 0.5;
+ AnchorY = 0.5;
+ IsVisible = true;
+ HasShadow = false;
+ }
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue10509.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue10509.cs
new file mode 100644
index 000000000000..4335b0111402
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue10509.cs
@@ -0,0 +1,82 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 10509, "Query parameter is missing after navigation", PlatformAffected.Android)]
+public class Issue10509 : Shell
+{
+ public Issue10509()
+ {
+ Routing.RegisterRoute(nameof(Issue10509Page1), typeof(Issue10509Page1));
+ Routing.RegisterRoute(nameof(Issue10509Page2), typeof(Issue10509Page2));
+
+ Items.Add(new ShellContent
+ {
+ Title = "First Page",
+ Route = nameof(Issue10509Page1),
+ ContentTemplate = new DataTemplate(typeof(Issue10509Page1))
+ });
+
+ Items.Add(new ShellContent
+ {
+ Title = "Second Page",
+ Route = nameof(Issue10509Page2),
+ ContentTemplate = new DataTemplate(typeof(Issue10509Page2))
+ });
+ }
+}
+
+public class Issue10509Page1 : ContentPage
+{
+ public Issue10509Page1()
+ {
+ var navigateBtn = new Button
+ {
+ Text = "Navigate to Page 2",
+ HorizontalOptions = LayoutOptions.Center,
+ AutomationId = "Page1Button"
+ };
+ navigateBtn.Clicked += NavigateBtn_Clicked;
+
+ Content = new VerticalStackLayout
+ {
+ VerticalOptions = LayoutOptions.Center,
+ Children = { navigateBtn }
+ };
+ }
+
+ private async void NavigateBtn_Clicked(object sender, EventArgs e)
+ {
+ await Shell.Current.GoToAsync($"//{nameof(Issue10509Page2)}?{Issue10509Page2.NavigationDataParam}=Passed");
+ }
+}
+
+[QueryProperty(nameof(NavigationData), NavigationDataParam)]
+public class Issue10509Page2 : ContentPage
+{
+ public const string NavigationDataParam = "NavigationDataParam";
+
+ private Label dataLabel;
+
+ public Issue10509Page2()
+ {
+ dataLabel = new Label
+ {
+ AutomationId = "Page2Label",
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ Content = new VerticalStackLayout
+ {
+ VerticalOptions = LayoutOptions.Center,
+ Children = { dataLabel }
+ };
+ }
+
+ public string NavigationData { get; set; }
+
+ protected override void OnNavigatedTo(NavigatedToEventArgs args)
+ {
+ base.OnNavigatedTo(args);
+ dataLabel.Text = $"Navigation data: {NavigationData ?? "Missed"}";
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue11677.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue11677.cs
new file mode 100644
index 000000000000..efc6de421774
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue11677.cs
@@ -0,0 +1,47 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 11677, "[iOS][maccatalyst] SearchBar BackgroundColor is black when set to transparent", PlatformAffected.iOS)]
+public class Issue11677 : ContentPage
+{
+ public Issue11677()
+ {
+ Content = new VerticalStackLayout
+ {
+ Children =
+ {
+ new Label
+ {
+ Text = "SearchBar Transparent BackgroundColor",
+ FontSize = 16,
+ TextColor = Colors.Black,
+ AutomationId = "Label"
+ },
+ new SearchBar
+ {
+ BackgroundColor = Colors.Transparent,
+ },
+ new Label
+ {
+ Text = "SearchBar with null background",
+ FontSize = 16,
+ TextColor = Colors.Black,
+ },
+ new SearchBar
+ {
+ BackgroundColor = null,
+ },
+ new Label
+ {
+ Text = "SearchBar with default background",
+ FontSize = 16,
+ TextColor = Colors.Black,
+ },
+ new SearchBar
+ {
+ Background = Brush.Default,
+ }
+ }
+ };
+
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue11812.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue11812.cs
new file mode 100644
index 000000000000..81a34923d5e4
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue11812.cs
@@ -0,0 +1,65 @@
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 11812, "Setting Content of ContentView through style would crash on parent change", PlatformAffected.Android)]
+public class Issue11812 : ContentPage
+{
+ ContentView _mainContentView;
+ public Issue11812()
+ {
+ var button = new Button
+ {
+ Text = "Add ContentView",
+ HorizontalOptions = LayoutOptions.Center,
+ AutomationId = "ChangeInnerContent"
+ };
+ button.Clicked += OnButtonClicked;
+
+ _mainContentView = new ContentView();
+ _mainContentView.Content = new Issue11812InnerContentView();
+
+ var layout = new VerticalStackLayout
+ {
+ Spacing = 25,
+ Padding = new Thickness(30, 0),
+ VerticalOptions = LayoutOptions.Center,
+ Children =
+ {
+ button,
+ _mainContentView
+ }
+ };
+
+ Content = layout;
+ }
+ private void OnButtonClicked(object sender, EventArgs e)
+ {
+ _mainContentView.Content = new Issue11812InnerContentView() { BackgroundColor = Colors.Red };
+ }
+}
+
+public class Issue11812InnerContentView : ContentView
+{
+ public Issue11812InnerContentView()
+ {
+ Application.Current.Resources ??= new ResourceDictionary();
+
+ if (!Application.Current.Resources.ContainsKey("SubContentStyle"))
+ {
+ Application.Current.Resources["SubContentStyle"] = new Style(typeof(ContentView))
+ {
+ Setters =
+ {
+ new Setter
+ {
+ Property = ContentView.ContentProperty,
+ Value = new Label { Text = "SubContent" }
+ }
+ }
+ };
+ }
+ Style = (Style)Application.Current.Resources["SubContentStyle"];
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue12131.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue12131.cs
new file mode 100644
index 000000000000..333272103b1b
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue12131.cs
@@ -0,0 +1,51 @@
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 12131, "RefreshView - CollectionView sizing not working correctly inside VerticalStackLayout", PlatformAffected.Android)]
+ public class Issue12131 : ContentPage
+ {
+ public Issue12131()
+ {
+ var items = Enumerable.Range(1, 20).Select(i => $"Item {i}").ToList();
+
+ var collectionView = new CollectionView
+ {
+ AutomationId = "CollectionView12131",
+ ItemTemplate = new DataTemplate(() =>
+ {
+ var label = new Label();
+ label.SetBinding(Label.TextProperty, ".");
+ return label;
+ }),
+ ItemsSource = items
+ };
+
+ var refreshView = new RefreshView
+ {
+ AutomationId = "RefreshView12131",
+ Content = collectionView
+ };
+
+ var sizeLabel = new Label
+ {
+ AutomationId = "SizeLabel12131",
+ Text = "Waiting..."
+ };
+
+ refreshView.SizeChanged += (s, e) =>
+ {
+ sizeLabel.Text = $"Height={refreshView.Height:F0}";
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 10,
+ Children =
+ {
+ new Label { Text = "CollectionView wrapped within RefreshView." },
+ sizeLabel,
+ refreshView
+ }
+ };
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue12324.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue12324.cs
new file mode 100644
index 000000000000..d966cf9760f6
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue12324.cs
@@ -0,0 +1,34 @@
+using AndroidSpecific = Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;
+namespace Controls.TestCases.HostApp.Issues;
+
+[Issue(IssueTracker.Github, 12324, "Tabbedpage should not have visual bug", PlatformAffected.Android)]
+
+public class Issue12324 : TabbedPage
+{
+ public Issue12324()
+ {
+ // Set tabs to bottom placement on Android
+ AndroidSpecific.TabbedPage.SetToolbarPlacement(this, AndroidSpecific.ToolbarPlacement.Bottom);
+ BarBackground = new RadialGradientBrush
+ {
+ GradientStops = new GradientStopCollection
+ {
+ new GradientStop { Color = Color.FromArgb("#1AFFFF00"), Offset = 0.0f },
+ new GradientStop { Color = Colors.Green, Offset = 1.0f }
+ }
+ };
+
+ Children.Add(new ContentPage
+ {
+ IconImageSource = "groceries.png",
+ Title = "Tab 1",
+ Content = new VerticalStackLayout
+ {
+ Children = {
+ new Label { AutomationId = "Label12324", HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center, Text = "Welcome to .NET MAUI!"
+ }
+ }
+ }
+ });
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue13258.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue13258.cs
new file mode 100644
index 000000000000..ade96442da2e
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue13258.cs
@@ -0,0 +1,51 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 13258, "MAUI Slider thumb image is big on android", PlatformAffected.Android)]
+public class Issue13258 : TestContentPage
+{
+ protected override void Init()
+ {
+ StackLayout rootLayout = new StackLayout() { Spacing = 10, Padding = 10 };
+
+ Label slider1DescriptionLabel = CreateLabel("Slider with Thumb Image");
+ Slider slider1 = CreateSlider("avatar.png");
+
+ Label slider2DescriptionLabel = CreateLabel("Thumb Image will be set to coffee.png at run time");
+ Slider slider2 = CreateSlider();
+
+ Label slider3DescriptionLabel = CreateLabel("Thumb Image will be set to null at run time");
+ Slider slider3 = CreateSlider("shopping_cart.png");
+
+ Button button = new Button() { Text = "Change Thumb Image", AutomationId = "ToggleImageBtn" };
+ button.Clicked += (s, e) => ToggleThumbImages(slider2, slider3);
+
+ rootLayout.Children.Add(slider1DescriptionLabel);
+ rootLayout.Children.Add(slider1);
+
+ rootLayout.Children.Add(slider2DescriptionLabel);
+ rootLayout.Children.Add(slider2);
+
+ rootLayout.Children.Add(slider3DescriptionLabel);
+ rootLayout.Children.Add(slider3);
+
+ rootLayout.Children.Add(button);
+
+ Content = rootLayout;
+ }
+
+ Label CreateLabel(string text)
+ {
+ return new Label { Text = text };
+ }
+
+ Slider CreateSlider(string thumbImageSource = null)
+ {
+ return new Slider { ThumbImageSource = thumbImageSource };
+ }
+
+ private void ToggleThumbImages(Slider slider2, Slider slider3)
+ {
+ slider2.ThumbImageSource = "coffee.png";
+ slider3.ThumbImageSource = null;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue14364.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue14364.cs
new file mode 100644
index 000000000000..62a449b327b7
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue14364.cs
@@ -0,0 +1,50 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 14364, "Control size properties are not available during Loaded event", PlatformAffected.Android)]
+public class Issue14364 : Shell
+{
+ public Issue14364()
+ {
+ ShellContent shellContent = new ShellContent
+ {
+ ContentTemplate = new DataTemplate(typeof(Issue14364Page)),
+ Route = "Issue14364Page"
+ };
+
+ Items.Add(shellContent);
+ }
+}
+
+public class Issue14364Page : ContentPage
+{
+ public Issue14364Page()
+ {
+ var grid = new Grid
+ {
+ HeightRequest = 300,
+ WidthRequest = 200
+ };
+
+ var label = new Label
+ {
+ Text = "Size",
+ AutomationId = "labelSize"
+ };
+
+ label.Loaded += Label_Loaded;
+
+ grid.Children.Add(label);
+ Content = grid;
+ }
+
+ void Label_Loaded(object sender, EventArgs e)
+ {
+ if (sender is Label label)
+ {
+ var h1 = label.Height;
+ var h2 = label.Width;
+
+ label.Text = $"Height: {h1}, Width: {h2}";
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue14566.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue14566.cs
new file mode 100644
index 000000000000..08f38ea47c4a
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue14566.cs
@@ -0,0 +1,49 @@
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 14566, "SearchBar IsEnabled property not functioning", PlatformAffected.Android | PlatformAffected.iOS | PlatformAffected.macOS)]
+ public class Issue14566 : TestContentPage
+ {
+ const string SearchBar = "SearchBar";
+ const string ResultText = "ResultText";
+ const string CheckResultButton = "CheckResultButton";
+ const string Success = "Success";
+ const string Failure = "Failure";
+
+ protected override void Init()
+ {
+ var layout = new StackLayout
+ {
+ VerticalOptions = LayoutOptions.Center,
+ Padding = new Thickness(30, 0),
+ Spacing = 25
+ };
+
+ var searchBar = new SearchBar
+ {
+ AutomationId = SearchBar,
+ Placeholder = "Search Placeholder",
+ IsEnabled = false
+ };
+ var searchText = new Label();
+
+ searchBar.TextChanged += (sender, e) =>
+ {
+ // Handle text changed event
+ searchText.Text = $"{e.NewTextValue}";
+ };
+
+ var result = new Label() { AutomationId = ResultText };
+ var checkResultButton = new Button() { AutomationId = CheckResultButton, Text = "Check Result" };
+ checkResultButton.Clicked += (sender, e) =>
+ {
+ result.Text = string.IsNullOrWhiteSpace(searchBar.Text) ? Success : Failure;
+ };
+
+ layout.Children.Add(searchBar);
+ layout.Children.Add(checkResultButton);
+ layout.Children.Add(result);
+
+ Content = layout;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue15280.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue15280.cs
new file mode 100644
index 000000000000..8d7a9f42e97e
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue15280.cs
@@ -0,0 +1,70 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 15280, "Swipe gestures attached to rotated controls are rotated on iOS", PlatformAffected.iOS)]
+public partial class Issue15280 : ContentPage
+{
+ Image _botImage;
+ Label _directionLabel;
+ public Issue15280()
+ {
+ Title = "Swipe Gesture Rotation Test";
+ Padding = 20;
+
+ _botImage = new Image
+ {
+ AutomationId = "RotatedImage",
+ Source = "dotnet_bot.png",
+ HeightRequest = 300,
+ WidthRequest = 300,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ BackgroundColor = Colors.LightBlue
+ };
+
+ var upSwipe = new SwipeGestureRecognizer { Direction = SwipeDirection.Up, Threshold = 20 };
+ var downSwipe = new SwipeGestureRecognizer { Direction = SwipeDirection.Down, Threshold = 20 };
+ var leftSwipe = new SwipeGestureRecognizer { Direction = SwipeDirection.Left, Threshold = 20 };
+ var rightSwipe = new SwipeGestureRecognizer { Direction = SwipeDirection.Right, Threshold = 20 };
+
+ upSwipe.Swiped += SwipeGestureRecognizer_Swiped;
+ downSwipe.Swiped += SwipeGestureRecognizer_Swiped;
+ leftSwipe.Swiped += SwipeGestureRecognizer_Swiped;
+ rightSwipe.Swiped += SwipeGestureRecognizer_Swiped;
+
+ _botImage.GestureRecognizers.Add(upSwipe);
+ _botImage.GestureRecognizers.Add(downSwipe);
+ _botImage.GestureRecognizers.Add(leftSwipe);
+ _botImage.GestureRecognizers.Add(rightSwipe);
+
+ _directionLabel = new Label
+ {
+ AutomationId = "DirectionLabel",
+ Text = "Try swiping Left on the image",
+ FontSize = 18,
+ HorizontalOptions = LayoutOptions.Center,
+ HorizontalTextAlignment = TextAlignment.Center,
+ TextColor = Colors.Blue
+ };
+
+ var mainLayout = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Children =
+ {
+ _botImage,
+ _directionLabel
+ }
+ };
+
+ var grid = new Grid();
+ grid.Children.Add(mainLayout);
+ Content = grid;
+ }
+
+ void SwipeGestureRecognizer_Swiped(object sender, SwipedEventArgs e)
+ {
+ _directionLabel.Text = $"Swiped: {e.Direction.ToString().ToUpperInvariant()}";
+
+ _botImage.Rotation += 90;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue15559.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue15559.xaml
new file mode 100644
index 000000000000..c58eeeb7b416
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue15559.xaml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue15559.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue15559.xaml.cs
new file mode 100644
index 000000000000..96d4f3bc950a
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue15559.xaml.cs
@@ -0,0 +1,11 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 15559, "[iOS] Vertical layout content of label is truncated when a width request is set in the layout", PlatformAffected.iOS | PlatformAffected.macOS)]
+
+public partial class Issue15559 : ContentPage
+{
+ public Issue15559()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue16522.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue16522.xaml
new file mode 100644
index 000000000000..6719b7468fe0
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue16522.xaml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue16522.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue16522.xaml.cs
new file mode 100644
index 000000000000..69d890caa760
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue16522.xaml.cs
@@ -0,0 +1,12 @@
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 16522, "[Android] Tabs briefly display wrong background color when navigating between flyout items", PlatformAffected.Android)]
+ public partial class Issue16522 : Shell
+ {
+ public Issue16522()
+ {
+ InitializeComponent();
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue17323.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue17323.cs
new file mode 100644
index 000000000000..17e8b18f41bb
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue17323.cs
@@ -0,0 +1,40 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 17323, "Arabic text flows RTL on Android in MAUI, but flows LTR on Windows", PlatformAffected.Android)]
+public class Issue17323 : TestContentPage
+{
+ protected override void Init()
+ {
+ VerticalStackLayout _verticalStackLayout = new VerticalStackLayout();
+ GraphicsView _graphicsView = new GraphicsView
+ {
+ Drawable = new Issue17323_Drawable(),
+ HeightRequest = 300,
+ WidthRequest = 350,
+ };
+
+ Label label = new Label
+ {
+ AutomationId = "TextLabel",
+ Text = "The Arabic text should be drawn from left to right on Android for consistent behavior with Windows, iOS, and macOS."
+ };
+
+ _verticalStackLayout.Add(_graphicsView);
+ _verticalStackLayout.Add(label);
+ Content = _verticalStackLayout;
+ }
+}
+
+
+public class Issue17323_Drawable : IDrawable
+{
+ public void Draw(ICanvas canvas, RectF dirtyRect)
+ {
+ canvas.StrokeColor = Colors.Red;
+ canvas.StrokeSize = 5;
+ canvas.FontSize = 18;
+ canvas.DrawRectangle(dirtyRect);
+ canvas.DrawString("Hello World", dirtyRect.X + 5, dirtyRect.Y + 5, dirtyRect.Width - 10, dirtyRect.Height - 160, HorizontalAlignment.Left, VerticalAlignment.Center);
+ canvas.DrawString("مرحبا بالعالم", dirtyRect.X + 5, dirtyRect.Y + 20, dirtyRect.Width - 10, dirtyRect.Height - 160, HorizontalAlignment.Left, VerticalAlignment.Center);
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue17550.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue17550.cs
new file mode 100644
index 000000000000..bedbc5ef0a79
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue17550.cs
@@ -0,0 +1,57 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 17550, "Changing Shell.NavBarIsVisible does not update the nav bar on Mac / iOS",
+ PlatformAffected.All)]
+public class Issue17550 : TestShell
+{
+ Button _toggleButton;
+
+ protected override void Init()
+ {
+ // Create a toggle button similar to the one in SandboxShell.xaml
+ _toggleButton = new Button
+ {
+ Text = Shell.GetNavBarIsVisible(this) ? "Hide NavBar" : "Show NavBar",
+ HeightRequest = 50,
+ WidthRequest = 150,
+ AutomationId = "NavBarToggleButton",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ _toggleButton.Clicked += OnNavBarToggleButtonClicked;
+
+ // Add the button to a content page in the shell
+ Items.Add(new ShellContent
+ {
+ Content = new ContentPage
+ {
+ Title = "Home",
+ Content = new StackLayout
+ {
+ Children =
+ {
+ new Label
+ {
+ Text = "Toggle NavBar visibility",
+ HorizontalOptions = LayoutOptions.Center
+ },
+ _toggleButton
+ },
+ VerticalOptions = LayoutOptions.Center
+ }
+ }
+ });
+ }
+
+ void OnNavBarToggleButtonClicked(object sender, EventArgs e)
+ {
+ // Toggle the NavBar visibility
+ bool isCurrentlyVisible = Shell.GetNavBarIsVisible(this);
+ bool newVisibility = !isCurrentlyVisible;
+ Shell.SetNavBarIsVisible(this, newVisibility);
+
+ // Update the button text to reflect the new state
+ _toggleButton.Text = newVisibility ? "Hide NavBar" : "Show NavBar";
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue17664.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue17664.cs
new file mode 100644
index 000000000000..707e06ad5aa2
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue17664.cs
@@ -0,0 +1,120 @@
+using System.Collections.ObjectModel;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 17664, "Incorrect ItemsViewScrolledEventArgs in CollectionView when IsGrouped is set to true", PlatformAffected.iOS)]
+public class Issue17664 : ContentPage
+{
+ CollectionView _collectionView;
+ Label descriptionLabel;
+ ObservableCollection _groupedItems;
+
+ public Issue17664()
+ {
+ Button scrollButton = new Button
+ {
+ AutomationId = "Issue17664ScrollBtn",
+ Text = "Scroll to Category C, Item #2"
+ };
+ scrollButton.Clicked += ScrollButton_Clicked;
+
+ descriptionLabel = new Label
+ {
+ AutomationId = "Issue17664DescriptionLabel",
+ Text = "Use the button above to scroll the CollectionView.",
+ FontSize = 14,
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ _collectionView = new CollectionView
+ {
+ IsGrouped = true,
+ GroupHeaderTemplate = new DataTemplate(() =>
+ {
+ Label label = new Label
+ {
+ FontAttributes = FontAttributes.Bold,
+ BackgroundColor = Colors.LightGray,
+ Padding = 10
+ };
+
+ label.SetBinding(Label.TextProperty, "Name");
+ return label;
+ }),
+ ItemTemplate = new DataTemplate(() =>
+ {
+ Label textLabel = new Label
+ {
+ FontAttributes = FontAttributes.Bold,
+ Padding = 30
+ };
+
+ textLabel.SetBinding(Label.TextProperty, ".");
+ return textLabel;
+ })
+ };
+
+ _collectionView.Scrolled += (s, e) =>
+ {
+ var flatItems = _groupedItems.SelectMany(group => group).ToList();
+ if (e.LastVisibleItemIndex >= 0 && e.LastVisibleItemIndex < flatItems.Count)
+ {
+ descriptionLabel.Text = flatItems[e.LastVisibleItemIndex];
+ }
+ };
+
+ List categories = new List { "Category A", "Category B", "Category C" };
+
+ _groupedItems = new ObservableCollection();
+
+ foreach (var category in categories)
+ {
+ List items = new List();
+
+ for (int i = 0; i < 5; i++)
+ {
+ items.Add($"{category} item #{i}");
+ }
+
+ _groupedItems.Add(new Issue17664_ItemModelGroup(category, items));
+ }
+
+ _collectionView.ItemsSource = _groupedItems;
+
+ Grid grid = new Grid
+ {
+ RowSpacing = 10,
+ Padding = 10,
+ RowDefinitions =
+ {
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Star }
+ }
+ };
+
+ grid.Add(scrollButton, 0, 0);
+ grid.Add(descriptionLabel, 0, 1);
+ grid.Add(_collectionView, 0, 2);
+
+ Content = grid;
+ }
+
+ private void ScrollButton_Clicked(object sender, EventArgs e)
+ {
+ var targetGroup = _groupedItems.FirstOrDefault(group => group.Name == "Category C");
+ var targetItem = targetGroup.FirstOrDefault(item => item == "Category C item #2");
+
+ _collectionView.ScrollTo(targetItem, targetGroup, ScrollToPosition.End);
+ }
+}
+
+public class Issue17664_ItemModelGroup : ObservableCollection
+{
+ public string Name { get; set; }
+
+ public Issue17664_ItemModelGroup(string name, IEnumerable items) : base(items)
+ {
+ Name = name;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue18011.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue18011.cs
new file mode 100644
index 000000000000..eea896e3aa0f
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue18011.cs
@@ -0,0 +1,54 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 18011,
+ "RadioButton TextColor for plain Content not working on iOS when Label styles present", PlatformAffected.iOS)]
+public class Issue18011 : ContentPage
+{
+ ResourceDictionary _styleDictionary;
+
+ public Issue18011()
+ {
+ // Add an implicit Label style to Application resources to simulate global App.xaml styles.
+ // The bug only reproduces when global Label styles exist that set TextColor.
+ _styleDictionary = new ResourceDictionary();
+ _styleDictionary.Add(new Style(typeof(Label))
+ {
+ Setters =
+ {
+ new Setter { Property = Label.TextColorProperty, Value = Colors.Green }
+ }
+ });
+
+ Application.Current.Resources.MergedDictionaries.Add(_styleDictionary);
+
+ Content = new VerticalStackLayout
+ {
+ Padding = new Thickness(20),
+ Spacing = 20,
+ Children =
+ {
+ new RadioButton
+ {
+ AutomationId = "RadioButtonWithTextColor",
+ Content = "Red RadioButton Text",
+ TextColor = Colors.Red,
+ FontSize = 20,
+ IsChecked = true
+ },
+ new RadioButton
+ {
+ AutomationId = "RadioButtonDefault",
+ Content = "Default RadioButton Text",
+ FontSize = 20
+ }
+ }
+ };
+ }
+
+ protected override void OnDisappearing()
+ {
+ base.OnDisappearing();
+ // Clean up global styles to avoid affecting other tests
+ Application.Current?.Resources?.MergedDictionaries?.Remove(_styleDictionary);
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue18668.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue18668.xaml
new file mode 100644
index 000000000000..fb0e8db1a23f
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue18668.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue18668.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue18668.xaml.cs
new file mode 100644
index 000000000000..f1a3ac837655
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue18668.xaml.cs
@@ -0,0 +1,17 @@
+namespace Maui.Controls.Sample.Issues
+{
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ [Issue(IssueTracker.Github, 18668, "Visual state change for disabled RadioButton", PlatformAffected.All)]
+ public partial class Issue18668 : ContentPage
+ {
+ public Issue18668()
+ {
+ InitializeComponent();
+ }
+
+ private void ButtonClicked(object sender, EventArgs e)
+ {
+ radioButton.IsEnabled = false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue18679.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue18679.cs
new file mode 100644
index 000000000000..abc153b6b488
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue18679.cs
@@ -0,0 +1,100 @@
+using Font = Microsoft.Maui.Graphics.Font;
+
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 18679, "Canvas.GetStringSize() is not consistent with actual string size in GraphicsView", PlatformAffected.All)]
+ public class Issue18679 : TestContentPage
+ {
+ const string ShortText = "ShortText";
+ const string LongText = "CiaomondorowfdskleCiaomondorowfdsk";
+ const string MultiLineText = "HELLO, WORLD!\nCiao mondo row 2\nGuten tag!?àèìòù@";
+
+ protected override void Init()
+ {
+ var layout = new VerticalStackLayout
+ {
+ Spacing = 10,
+ };
+ var label = new Label
+ {
+ Text = "The drawn text should not overflow the bounding rectangle.",
+ FontSize = 16,
+ AutomationId = "18679DescriptionLabel",
+ };
+ layout.Add(label);
+
+ // Test Case 1: Single long line (basic case)
+ layout.Add(CreateDrawable("Test Case 1: Short text", ShortText));
+
+ // Test Case 2: Multi-line text
+ layout.Add(CreateDrawable("Test Case 2: Multi-line Text", MultiLineText));
+
+ // Test Case 3: Unicode/non-Latin text
+ layout.Add(CreateDrawable("Test Case 3: Long Text", LongText));
+
+ Content = layout;
+ }
+
+ private Border CreateDrawable(string testName, string text)
+ {
+ var graphicsView = new GraphicsView
+ {
+ HeightRequest = 150,
+ Drawable = new Issue18679_Drawable(text, testName)
+ };
+
+ return new Border
+ {
+ Content = graphicsView,
+ Stroke = Colors.LightGray,
+ StrokeThickness = 1,
+ };
+ }
+ }
+
+ public class Issue18679_Drawable : IDrawable
+ {
+ readonly string _text;
+ readonly string _label;
+ static readonly Font Font = Font.Default;
+ const int FontSize = 20;
+
+
+ public Issue18679_Drawable(string text, string label)
+ {
+ _text = text;
+ _label = label;
+ }
+
+ public void Draw(ICanvas canvas, RectF dirtyRect)
+ {
+ canvas.SaveState();
+
+ // Draw label
+ canvas.FontSize = 14;
+ canvas.Font = Font;
+ canvas.FontColor = Colors.Blue;
+ canvas.DrawString(_label, 10, 10, dirtyRect.Width - 20, 20,
+ HorizontalAlignment.Left, VerticalAlignment.Top);
+
+ // Set up for text measurement
+ canvas.FontSize = FontSize;
+ canvas.Font = Font;
+
+ // Get text size and create bounds
+ var stringSize = canvas.GetStringSize(_text, Font, FontSize);
+
+ // Draw the actual text first
+ canvas.FontColor = Colors.Black;
+ canvas.DrawString(_text, 2, 40, dirtyRect.Width, dirtyRect.Height,
+ HorizontalAlignment.Left, VerticalAlignment.Top);
+
+ // Draw the measured bounds rectangle
+ canvas.StrokeColor = Colors.Red;
+ canvas.StrokeSize = 1;
+ canvas.DrawRectangle(2, 40, stringSize.Width, stringSize.Height);
+
+ canvas.RestoreState();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue18933.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue18933.cs
new file mode 100644
index 000000000000..d71ddac37e74
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue18933.cs
@@ -0,0 +1,76 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 18933, "ContentView Background Color Not Cleared When Set to Null", PlatformAffected.Android | PlatformAffected.iOS)]
+public class Issue18933 : ContentPage
+{
+ ContentView contentView;
+ ContentView contentViewWithBackground;
+
+ public Issue18933()
+ {
+ Label contentLabel = new Label
+ {
+ Text = "ContentView - Test BackgroundColor removal",
+ Padding = 20
+ };
+
+ Label backgroundContentLabel = new Label
+ {
+ Text = "ContentView - Test Background removal",
+ Padding = 20
+ };
+
+ contentView = new ContentView
+ {
+ BackgroundColor = Colors.Purple,
+ Content = contentLabel
+ };
+
+ contentViewWithBackground = new ContentView
+ {
+ Background = Colors.Purple,
+ Content = backgroundContentLabel
+ };
+
+ Button clearBackgroundBtn = new Button
+ {
+ AutomationId = "clearBgBtn",
+ Text = "Set ContentView Background to Null"
+ };
+ clearBackgroundBtn.Clicked += Button_Clicked;
+
+ Button setBackgroundBtn = new Button
+ {
+ AutomationId = "setBgBtn",
+ Text = "Set ContentView Background"
+ };
+ setBackgroundBtn.Clicked += SetBackground_Clicked;
+
+ VerticalStackLayout mainLayout = new VerticalStackLayout
+ {
+ Spacing = 10,
+ Padding = 20,
+ Children =
+ {
+ contentView,
+ contentViewWithBackground,
+ clearBackgroundBtn,
+ setBackgroundBtn,
+ }
+ };
+
+ Content = mainLayout;
+ }
+
+ void Button_Clicked(object sender, EventArgs e)
+ {
+ contentView.BackgroundColor = null;
+ contentViewWithBackground.Background = null;
+ }
+
+ void SetBackground_Clicked(object sender, EventArgs e)
+ {
+ contentView.BackgroundColor = Colors.Purple;
+ contentViewWithBackground.Background = Colors.Purple;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue19219.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue19219.cs
new file mode 100644
index 000000000000..e15b03feddcf
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue19219.cs
@@ -0,0 +1,61 @@
+using System.Windows.Input;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 19219, "[Android, iOS, macOS] Shell SearchHandler Command Not Executed on Item Selection", PlatformAffected.All)]
+public class Issue19219Shell : Shell
+{
+ public Issue19219Shell()
+ {
+ this.FlyoutBehavior = FlyoutBehavior.Flyout;
+
+ var shellContent = new ShellContent
+ {
+ Title = "Home",
+ Route = "MainPage",
+ Content = new Issue19219() { Title = "Home" }
+ };
+
+ Items.Add(shellContent);
+ }
+
+ class Issue19219 : ContentPage
+ {
+ public Issue19219()
+ {
+ Label label = new Label
+ {
+ Text = "SearchHandler command will execute when tap on item",
+ AutomationId = "SearchHandlerLabel"
+ };
+
+ var cities = new List { "Los Angeles", "New York", "London", "Sydney", "Toronto", "Chicago", "Melbourne", "Vancouver",
+ "Manchester", "Birmingham", "San Francisco", "Dublin", "Auckland", "Glasgow", "Perth", "Houston",
+ "Seattle", "Cape Town", "Ottawa", "Brisbane", "Boston", "Phoenix", "Washington", "Edinburgh" };
+ var searchHandler = new SearchHandler
+ {
+ AutomationId = "searchBar",
+ Placeholder = "SearchHandler",
+ Command = (new Command(() =>
+ {
+ label.Text = "SearchHandler Command Executed when tap on item";
+ })),
+ ShowsResults = true,
+ };
+ searchHandler.SetBinding(SearchHandler.ItemsSourceProperty, new Binding
+ {
+ Source = cities,
+ Mode = BindingMode.OneWay
+ });
+
+ Shell.SetSearchHandler(this, searchHandler);
+
+ Grid grid = new Grid
+ {
+ Children = { label }
+ };
+
+ Content = grid;
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue19676.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue19676.cs
new file mode 100644
index 000000000000..4d4ecdb8f0e8
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue19676.cs
@@ -0,0 +1,45 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 19676, "Android Switch Control Thumb Shadow missing when ThumbColor matches background", PlatformAffected.Android)]
+public class Issue19676 : ContentPage
+{
+ public Issue19676()
+ {
+ BackgroundColor = Color.FromArgb("#F2F5F8");
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 20,
+ Children =
+ {
+ new Label
+ {
+ Text = "Switch with ThumbColor matching background",
+ FontSize = 16,
+ FontAttributes = FontAttributes.Bold,
+ },
+ new Label
+ {
+ Text = "The switch thumb should be visible with a shadow, even though ThumbColor matches the background.",
+ FontSize = 14,
+ },
+ new Switch
+ {
+ IsToggled = true,
+ ThumbColor = Color.FromArgb("#F2F5F8"),
+ OnColor = Colors.Red,
+ AutomationId = "TestSwitch",
+ Margin = new Thickness(10, 20, 15, 0),
+ VerticalOptions = LayoutOptions.Center
+ },
+ new Label
+ {
+ Text = "Expected: Switch thumb is visible with shadow",
+ FontSize = 14,
+ TextColor = Colors.Gray,
+ }
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue20596.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue20596.cs
new file mode 100644
index 000000000000..493c1977c192
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue20596.cs
@@ -0,0 +1,49 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 20596, "[Android] Button with corner radius shadow broken on Android device", PlatformAffected.Android | PlatformAffected.UWP)]
+public class Issue20596 : ContentPage
+{
+ public Issue20596()
+ {
+ var button = new Button
+ {
+ TextColor = Colors.Black,
+ HeightRequest = 200,
+ WidthRequest = 200,
+ BackgroundColor = Colors.Green,
+ CornerRadius = 20,
+ Shadow = new Shadow { Radius = 10 }
+ };
+
+ var imageButton = new ImageButton
+ {
+ HeightRequest = 200,
+ WidthRequest = 200,
+ BackgroundColor = Colors.Red,
+ CornerRadius = 20,
+ Shadow = new Shadow { Radius = 10 }
+ };
+
+ var updateButton = new Button
+ {
+ Text = "Update corner radius",
+ HeightRequest = 50,
+ AutomationId = "UpdateCornerRadiusButton"
+ };
+ updateButton.Clicked += (sender, e) =>
+ {
+ button.CornerRadius = 100;
+ imageButton.CornerRadius = 100;
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Children =
+ {
+ button,
+ imageButton,
+ updateButton
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue20855.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue20855.cs
new file mode 100644
index 000000000000..21ab0b104b34
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue20855.cs
@@ -0,0 +1,86 @@
+using System.Collections.ObjectModel;
+using Microsoft.Maui.Layouts;
+
+namespace Maui.Controls.Sample.Issues
+{
+
+ [Issue(IssueTracker.Github, 20855, "Grouped CollectionView items not rendered properly on Android, works on Windows", PlatformAffected.Android)]
+ public class Issue20855 : TestContentPage
+ {
+
+ protected override void Init()
+ {
+ ObservableCollection ItemGroups = new ObservableCollection();
+ var group1 = new Issue20855ItemGroup { Name = "Group 1" };
+ group1.Add(new Issue20855Item { Name = "Item 1", CreateDate = new DateTime(2025, 2, 20) });
+ group1.Add(new Issue20855Item { Name = "Item 2", CreateDate = new DateTime(2025, 2, 19) });
+ ItemGroups.Add(group1);
+ var group2 = new Issue20855ItemGroup { Name = "Group 2" };
+ group2.Add(new Issue20855Item { Name = "Item 3", CreateDate = new DateTime(2025, 2, 18) });
+ group2.Add(new Issue20855Item { Name = "Item 4", CreateDate = new DateTime(2025, 2, 17) });
+ ItemGroups.Add(group2);
+
+ var collectionView = new CollectionView
+ {
+ ItemsSource = ItemGroups,
+ IsGrouped = true,
+ ItemSizingStrategy = ItemSizingStrategy.MeasureFirstItem
+ };
+ collectionView.GroupHeaderTemplate = new DataTemplate(() =>
+ {
+ var label = new Label
+ {
+ FontSize = 18,
+ BackgroundColor = Colors.DarkGray,
+ TextColor = Colors.White,
+ Padding = new Thickness(10, 0)
+ };
+ label.SetBinding(Label.TextProperty, "Name");
+
+ return label;
+ });
+ collectionView.ItemTemplate = new DataTemplate(() =>
+ {
+ var flexLayout = new FlexLayout
+ {
+ JustifyContent = FlexJustify.SpaceBetween,
+ AlignItems = FlexAlignItems.Center,
+ HeightRequest = 100
+ };
+
+ var nameLabel = new Label
+ {
+ Padding = new Thickness(8, 0),
+ HeightRequest = 80
+ };
+ nameLabel.SetBinding(Label.TextProperty, "Name");
+
+ var dateLabel = new Label
+ {
+ Padding = new Thickness(8, 0),
+ HeightRequest = 85
+ };
+ dateLabel.SetBinding(Label.TextProperty, new Binding("CreateDate", stringFormat: "{0:MMM d}"));
+
+ flexLayout.Children.Add(nameLabel);
+ flexLayout.Children.Add(dateLabel);
+
+ return flexLayout;
+ });
+ var grid = new Grid();
+ grid.Children.Add(collectionView);
+ Content = grid;
+ }
+ }
+
+ public class Issue20855Item
+ {
+ public string Name { get; set; }
+ public DateTime CreateDate { get; set; }
+ }
+
+ public class Issue20855ItemGroup : ObservableCollection
+ {
+ public string Name { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue20922.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue20922.cs
new file mode 100644
index 000000000000..f6b28ce63039
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue20922.cs
@@ -0,0 +1,30 @@
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 20922, "Shadow Doesn't Work on Grid in scroll view on Android", PlatformAffected.Android)]
+ public class Issue20922 : ContentPage
+ {
+ public Issue20922()
+ {
+ var scrollView = new ScrollView();
+
+ var grid = new Grid
+ {
+ HeightRequest = 200,
+ WidthRequest = 200,
+ AutomationId = "Grid",
+ BackgroundColor = Colors.Red
+ };
+
+ var shadow = new Shadow
+ {
+ Radius = 20,
+ Brush = new SolidColorBrush(Colors.Gray),
+ Offset = new Point(1, 50)
+ };
+
+ grid.Shadow = shadow;
+ scrollView.Content = grid;
+ Content = scrollView;
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue21037.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue21037.cs
new file mode 100644
index 000000000000..f45591cef0f9
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue21037.cs
@@ -0,0 +1,137 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 21037, "Switching to an existing page with SetTitleView used in Flyout menu causes crash on Windows", PlatformAffected.UWP)]
+public class Issue21037 : FlyoutPage
+{
+ static Issue21037 flyoutPageInstance;
+
+ public Issue21037()
+ {
+ flyoutPageInstance = this;
+ var menuPage = new _21037MenuPage();
+ var homePage = new NavigationPage(new _21037MainPage());
+ var secondPage = new NavigationPage(new _21037SecondPage());
+
+ Flyout = menuPage;
+ Detail = homePage;
+ FlyoutLayoutBehavior = FlyoutLayoutBehavior.Popover;
+
+ menuPage.MenuItemSelected += (sender, page) =>
+ {
+ Detail = page switch
+ {
+ "Home" => homePage,
+ "Second" => secondPage,
+ _ => homePage
+ };
+ IsPresented = false;
+ };
+ }
+
+ public class _21037MenuPage : ContentPage
+ {
+ public event EventHandler MenuItemSelected;
+
+ public _21037MenuPage()
+ {
+ Title = "Menu";
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 10,
+ Children =
+ {
+ new Button
+ {
+ Text = "Home Page",
+ AutomationId = "HomeButton",
+ Command = new Command(() => MenuItemSelected?.Invoke(this, "Home"))
+ },
+ new Button
+ {
+ Text = "Second Page",
+ AutomationId = "SecondButton",
+ Command = new Command(() => MenuItemSelected?.Invoke(this, "Second"))
+ }
+ }
+ };
+ }
+ }
+
+ public class _21037MainPage : ContentPage
+ {
+ public _21037MainPage()
+ {
+ Title = "Home";
+
+ var titleView = new Button
+ {
+ Text = "Main Page Title",
+ HeightRequest = 44,
+ WidthRequest = 200,
+ AutomationId = "MainTitleButton"
+ };
+ NavigationPage.SetTitleView(this, titleView);
+
+ var openMenuButton = new Button
+ {
+ Text = "Open Menu",
+ AutomationId = "OpenMenuButton"
+ };
+ openMenuButton.Clicked += (s, e) => flyoutPageInstance.IsPresented = true;
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 10,
+ Children =
+ {
+ new Label
+ {
+ Text = "This is the Home Page with Button TitleView",
+ AutomationId = "HomeLabel"
+ },
+ openMenuButton
+ }
+ };
+ }
+ }
+
+ public class _21037SecondPage : ContentPage
+ {
+ public _21037SecondPage()
+ {
+ Title = "Second";
+
+ var titleView = new Slider
+ {
+ HeightRequest = 44,
+ WidthRequest = 300,
+ AutomationId = "SecondTitleSlider"
+ };
+ NavigationPage.SetTitleView(this, titleView);
+
+ var openMenuButton = new Button
+ {
+ Text = "Open Menu",
+ AutomationId = "OpenMenuButton"
+ };
+ openMenuButton.Clicked += (s, e) => flyoutPageInstance.IsPresented = true;
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 10,
+ Children =
+ {
+ new Label
+ {
+ Text = "This is Page 2 with Slider TitleView",
+ AutomationId = "SecondLabel"
+ },
+ openMenuButton
+ }
+ };
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue21646.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue21646.cs
new file mode 100644
index 000000000000..937a8bd9fd83
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue21646.cs
@@ -0,0 +1,82 @@
+using Microsoft.Maui.Controls;
+using System;
+
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 21646, "Flyout icon should remain visible when a page is pushed onto a NavigationPage with the back button disabled.", PlatformAffected.iOS | PlatformAffected.Android, issueTestNumber: 1)]
+ public partial class Issue21646 : FlyoutPage
+ {
+ public Issue21646()
+ {
+ FlyoutLayoutBehavior = FlyoutLayoutBehavior.Popover;
+ var flyoutPage = new ContentPage
+ {
+ Title = "OK",
+ BackgroundColor = Colors.LightGray,
+ Content = new StackLayout
+ {
+ Padding = new Thickness(20),
+ Children =
+ {
+ new Label { Text = "Flyout Menu", FontSize = 20 },
+ new Button { Text = "Go to Home", Command = new Command(GoToHomePage) }
+ }
+ }
+ };
+
+ var navigateButton = new Button { Text = "Go to Next Page", AutomationId="NavigateToNextPageButton" };
+ navigateButton.Clicked += OnNavigateButtonClicked;
+
+ var detailPage = new NavigationPage(new ContentPage
+ {
+ Content = new StackLayout
+ {
+ Children =
+ {
+ new Label { Text = "Welcome to Home Page!", FontSize = 24 },
+ navigateButton
+ }
+ }
+ });
+
+ Flyout = flyoutPage;
+ Detail = detailPage;
+ }
+
+ private async void OnNavigateButtonClicked(object sender, EventArgs e)
+ {
+ if (Detail is NavigationPage navPage)
+ {
+ await PushPageNoBackAsync(navPage, new ContentPage
+ {
+ Title = "Second Page",
+ Content = new StackLayout
+ {
+ Children = { new Label { Text = "This is the second page!", AutomationId="SecondPageLabel" } }
+ }
+ });
+ }
+ }
+
+ public static async Task PushPageNoBackAsync(NavigationPage np, Page p)
+ {
+ NavigationPage.SetHasBackButton(p, false);
+ await np.PushAsync(p);
+ }
+
+ private void GoToHomePage()
+ {
+ if (Detail is NavigationPage navPage)
+ {
+ Detail = new NavigationPage(new ContentPage
+ {
+ Content = new StackLayout
+ {
+ Children = { new Label { Text = "Welcome Back to Home!" } }
+ }
+ });
+ IsPresented = false;
+ }
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue21646_ShellFlyout.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue21646_ShellFlyout.cs
new file mode 100644
index 000000000000..abc8fb244e87
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue21646_ShellFlyout.cs
@@ -0,0 +1,96 @@
+using Microsoft.Maui.Controls;
+using System;
+
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 21646, "Flyout icon should remain visible when a page is pushed onto a ShellPage with the back button disabled.", PlatformAffected.iOS | PlatformAffected.Android, issueTestNumber: 2)]
+ public class Issue21646_ShellFlyout : Shell
+ {
+ public Issue21646_ShellFlyout()
+ {
+ FlyoutHeader = new StackLayout
+ {
+ Padding = new Thickness(20),
+ BackgroundColor = Colors.LightGray,
+ Children =
+ {
+ new Label
+ {
+ Text = "Flyout Menu",
+ FontSize = 20
+ }
+ }
+ };
+
+ Items.Add(new ShellContent
+ {
+ Title = "Home",
+ Route = "home",
+ Content = new HomePage()
+ });
+
+ Items.Add(new ShellContent
+ {
+ Title = "Second Page",
+ Route = "second",
+ Content = new SecondPage()
+ });
+ }
+ }
+
+ public class HomePage : ContentPage
+ {
+ public HomePage()
+ {
+ Routing.RegisterRoute("second", typeof(SecondPage));
+
+ Content = new StackLayout
+ {
+ Children =
+ {
+ new Label
+ {
+ Text = "Welcome to Home Page!",
+ FontSize = 24
+ },
+ new Button
+ {
+ Text = "Go to Next Page",
+ Command = new Command(async () => await OnNavigateButtonClicked()),
+ AutomationId = "NavigateToNextPageButton"
+ }
+ }
+ };
+ }
+
+ private async Task OnNavigateButtonClicked()
+ {
+ await PushPageNoBackAsync(new SecondPage());
+ }
+
+ public static async Task PushPageNoBackAsync(Page p)
+ {
+ Shell.SetBackButtonBehavior(p, new BackButtonBehavior { IsVisible = false });
+ await Shell.Current.Navigation.PushAsync(p);
+ }
+ }
+
+ public class SecondPage : ContentPage
+ {
+ public SecondPage()
+ {
+ Content = new StackLayout
+ {
+ Children =
+ {
+ new Label
+ {
+ Text = "This is the second page!",
+ FontSize = 24,
+ AutomationId = "SecondPageLabel"
+ }
+ }
+ };
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue21828.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue21828.cs
new file mode 100644
index 000000000000..bebd1b221aad
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue21828.cs
@@ -0,0 +1,64 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 21828, "Flyout icon disappears after root page replacement and pop", PlatformAffected.iOS)]
+public class Issue21828 : FlyoutPage
+{
+ public Issue21828()
+ {
+ Detail = new NavigationPage(new Issue21828Page1());
+ Flyout = new ContentPage
+ {
+ Title = "Flyout Page",
+ IconImageSource = "menu_icon.png",
+ Content = new Label
+ {
+ Text = "This is flyout",
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center
+ }
+ };
+ FlyoutLayoutBehavior = FlyoutLayoutBehavior.Popover;
+ }
+}
+
+public class Issue21828Page1 : ContentPage
+{
+ public Issue21828Page1()
+ {
+ Title = "Start Page";
+ var insertAndPopButton = new Button { Text = "Insert Page Before and Pop", AutomationId = "InsertAndPopButton" };
+ insertAndPopButton.Clicked += InsertAndPopButton_Clicked;
+ Content = new StackLayout
+ {
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ Children = { insertAndPopButton }
+ };
+ }
+
+ private async void InsertAndPopButton_Clicked(object sender, EventArgs e)
+ {
+ if (Parent is NavigationPage navPage)
+ {
+ navPage.Navigation.InsertPageBefore(new Issue21828Page2(), navPage.RootPage);
+ await navPage.Navigation.PopAsync();
+ }
+ }
+}
+
+public class Issue21828Page2 : ContentPage
+{
+ public Issue21828Page2()
+ {
+ Title = "End Page";
+ Content = new StackLayout
+ {
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ Children =
+ {
+ new Label { Text = "Flyout icon is visible", AutomationId = "Page2Label" },
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue22060.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue22060.cs
new file mode 100644
index 000000000000..1395598b0f43
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue22060.cs
@@ -0,0 +1,33 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 22060, "Flyout icon and content page title disappeared after focusing on the search handler", PlatformAffected.iOS)]
+public class Issue22060Shell : Shell
+{
+ public Issue22060Shell()
+ {
+ this.FlyoutBehavior = FlyoutBehavior.Flyout;
+
+ var shellContent = new ShellContent
+ {
+ Title = "Home",
+ Route = "MainPage",
+ Content = new Issue22060() { Title = "Home" }
+ };
+
+ Items.Add(shellContent);
+ }
+
+ class Issue22060 : ContentPage
+ {
+ public Issue22060()
+ {
+ Shell.SetSearchHandler(this, new SearchHandler
+ {
+ AutomationId = "searchHandler",
+ Placeholder = "SearchHandler",
+ });
+
+ this.SetBinding(TitleProperty, new Binding("Title", source: Shell.Current));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue22565.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue22565.cs
new file mode 100644
index 000000000000..92cce32fb134
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue22565.cs
@@ -0,0 +1,50 @@
+namespace Controls.TestCases.HostApp.Issues;
+
+[Issue(IssueTracker.Github, 22565, "A disabled Picker prevents the parent container's GestureRecognizer from being triggered", PlatformAffected.Android)]
+public class Issue22565 : ContentPage
+{
+ Label descriptionLabel;
+ public Issue22565()
+ {
+ VerticalStackLayout verticalStackLayout = new VerticalStackLayout
+ {
+ Padding = new Thickness(30, 0),
+ Spacing = 25
+ };
+
+ Grid grid = new Grid
+ {
+ Padding = 20,
+ BackgroundColor = Colors.DarkRed
+ };
+
+ Picker picker = new Picker
+ {
+ AutomationId = "DisabledPicker",
+ BackgroundColor = Colors.Gray,
+ IsEnabled = false
+ };
+
+ TapGestureRecognizer tapGesture = new TapGestureRecognizer();
+ tapGesture.Tapped += OnCounterClicked;
+
+ grid.GestureRecognizers.Add(tapGesture);
+ grid.Children.Add(picker);
+
+ descriptionLabel = new Label
+ {
+ AutomationId = "22565DescriptionLabel",
+ Text = "The test passes if the disabled Picker does not intercept the parent container's GestureRecognizer; otherwise, it fails.",
+ };
+
+ verticalStackLayout.Children.Add(grid);
+ verticalStackLayout.Children.Add(descriptionLabel);
+
+ Content = verticalStackLayout;
+ }
+
+ private void OnCounterClicked(object sender, EventArgs e)
+ {
+ descriptionLabel.Text = "Parent Gesture recognizer triggered";
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue22887.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue22887.cs
new file mode 100644
index 000000000000..3861fd4d025c
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue22887.cs
@@ -0,0 +1,33 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 22887, "Microsoft.Maui.Controls.ImageSource.FromFile fails in iOS when image in subfolder")]
+public class Issue22887 : TestContentPage
+{
+ protected override void Init()
+ {
+ Content = CreateContent();
+ }
+
+ VerticalStackLayout CreateContent()
+ {
+ var image = new Image
+ {
+ Source = ImageSource.FromFile("subfolder/dotnet_bot_red.png"),
+ AutomationId = "ImageView"
+ };
+
+ return new VerticalStackLayout()
+ {
+ Padding = new Thickness(20),
+ Children =
+ {
+ image,
+ new Label
+ {
+ Text = "If the image is displayed, the test has passed.",
+ AutomationId = "descriptionLabel"
+ }
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue22938.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue22938.cs
new file mode 100644
index 000000000000..e332732e6e37
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue22938.cs
@@ -0,0 +1,92 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 22938, "Keyboard focus does not shift to a newly opened modal page", PlatformAffected.All)]
+public class Issue22938 : ContentPage
+{
+ public Issue22938()
+ {
+ var clickCountLabel = new Label
+ {
+ Text = "0",
+ AutomationId = "ClickCountLabel",
+ FontSize = 24
+ };
+
+ var mainPageButton = new Button
+ {
+ Text = "Click Me",
+ AutomationId = "MainPageButton",
+ Command = new Command(() =>
+ {
+ int count = int.Parse(clickCountLabel.Text);
+ clickCountLabel.Text = (count + 1).ToString();
+ })
+ };
+
+ var openModalButton = new Button
+ {
+ Text = "Open Modal",
+ AutomationId = "OpenModalButton",
+ Command = new Command(async () =>
+ {
+ // Use semi-transparent background to match the reproduction scenario.
+ // This causes the underlying page to remain in the visual tree
+ // (ModalNavigationManager does not call RemovePage for non-default backgrounds).
+ var modalPage = new ContentPage
+ {
+ BackgroundColor = Color.FromArgb("#40808080"),
+ Content = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Padding = new Thickness(30),
+ VerticalOptions = LayoutOptions.Center,
+ Children =
+ {
+ new Label
+ {
+ Text = "Modal Page",
+ AutomationId = "ModalPageLabel",
+ FontSize = 24,
+ HorizontalOptions = LayoutOptions.Center
+ },
+ new Entry
+ {
+ Placeholder = "Focus target on modal",
+ AutomationId = "ModalEntry"
+ },
+ new Button
+ {
+ Text = "Close Modal",
+ AutomationId = "CloseModalButton",
+ Command = new Command(async () =>
+ {
+ await Navigation.PopModalAsync();
+ })
+ }
+ }
+ }
+ };
+
+ await Navigation.PushModalAsync(modalPage);
+ })
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Padding = new Thickness(30),
+ VerticalOptions = LayoutOptions.Center,
+ Children =
+ {
+ new Label
+ {
+ Text = "Main Page - Issue 22938",
+ FontSize = 24
+ },
+ mainPageButton,
+ clickCountLabel,
+ openModalButton
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue23014.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue23014.xaml
new file mode 100644
index 000000000000..8b985af6d674
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue23014.xaml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue23014.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue23014.xaml.cs
new file mode 100644
index 000000000000..1dc7624d3d8a
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue23014.xaml.cs
@@ -0,0 +1,35 @@
+using System.Collections.ObjectModel;
+
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 23014, "App crashes when calling ItemsView.ScrollTo on unloaded CollectionView", PlatformAffected.All)]
+ public partial class Issue23014 : ContentPage
+ {
+ public Issue23014()
+ {
+ InitializeComponent();
+ BindingContext = new Issue23014ViewModel();
+ }
+
+ private void OnRemoveAndScrollToClicked(object sender, EventArgs e)
+ {
+ Stack.Remove(ItemList);
+
+ try
+ {
+ ItemList.ScrollTo(0);
+ ItemList.ScrollTo("Foo");
+ StatusLabel.Text = "Success";
+ }
+ catch (Exception ex)
+ {
+ StatusLabel.Text = $"Fail: {ex.Message}";
+ }
+ }
+ }
+
+ public class Issue23014ViewModel
+ {
+ public ObservableCollection Items { get; } = ["Foo", "Bar", "Baz", "Goo"];
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue23377.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue23377.cs
new file mode 100644
index 000000000000..b75b4f219ca6
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue23377.cs
@@ -0,0 +1,57 @@
+namespace Maui.Controls.Sample.Issues;
+[Issue(IssueTracker.Github, 23377, "Horizontal Item spacing in collectionView", PlatformAffected.UWP)]
+public class Issue23377 : TestContentPage
+{
+ public List Items { get; set; }
+ private Button button;
+ private CollectionView collectionView;
+
+ protected override void Init()
+ {
+ Items = new List
+ {
+ "Item 1", "Item 2", "Item 3", "Item 4",
+ "Item 5", "Item 6", "Item 7", "Item 8", "Item 9"
+ };
+ BindingContext = this;
+
+ var VerticalStackLayout = new VerticalStackLayout();
+
+ button = new Button
+ {
+ Text = "Change Item Space",
+ AutomationId = "ChangeItemSpace"
+ };
+
+ button.Clicked += Button_Clicked;
+
+ collectionView = new CollectionView
+ {
+ ItemsSource = Items,
+ Margin = new Thickness(100),
+ ItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Horizontal),
+ HeightRequest = 200,
+ HorizontalScrollBarVisibility = ScrollBarVisibility.Never
+ };
+
+ collectionView.ItemTemplate = new DataTemplate(() =>
+ {
+ var label = new Label();
+ label.SetBinding(Label.TextProperty, ".");
+ return label;
+ });
+
+ VerticalStackLayout.Children.Add(collectionView);
+ VerticalStackLayout.Children.Add(button);
+
+ Content = VerticalStackLayout;
+ }
+
+ void Button_Clicked(object sender, EventArgs e)
+ {
+ if (collectionView.ItemsLayout is LinearItemsLayout linearItemsLayout)
+ {
+ linearItemsLayout.ItemSpacing = 80;
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue23832.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue23832.cs
new file mode 100644
index 000000000000..e721b4997e10
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue23832.cs
@@ -0,0 +1,53 @@
+using Microsoft.Maui.Graphics.Platform;
+using System.Reflection;
+using IImage = Microsoft.Maui.Graphics.IImage;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 23832, "Some HEIC photos are upside down after using PlatformImage.Resize", PlatformAffected.Android | PlatformAffected.iOS)]
+public class Issue23832 : ContentPage
+{
+ public Issue23832()
+ {
+ VerticalStackLayout stackLayout = new VerticalStackLayout
+ {
+ Padding = new Thickness(20),
+ Spacing = 25,
+ };
+
+ Label labelResize = new Label
+ {
+ AutomationId = "DrawableLabel",
+ Text = "The test passes if the image is displayed correctly without being upside down.",
+ };
+
+ GraphicsView graphicsView = new GraphicsView
+ {
+ Drawable = new Issue23832_Drawable(),
+ HeightRequest = 300,
+ WidthRequest = 400,
+ };
+
+ stackLayout.Children.Add(graphicsView);
+ stackLayout.Children.Add(labelResize);
+ Content = stackLayout;
+ }
+}
+
+internal class Issue23832_Drawable : IDrawable
+{
+ public void Draw(ICanvas canvas, RectF dirtyRect)
+ {
+ IImage image;
+ Assembly assembly = GetType().GetTypeInfo().Assembly;
+ using (Stream stream = assembly.GetManifestResourceStream("Controls.TestCases.HostApp.Resources.Images.img_0111.heic"))
+ {
+ image = PlatformImage.FromStream(stream);
+ }
+
+ if (image is not null)
+ {
+ canvas.DrawImage(image, 0, 0, 300, 300);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue24252.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue24252.cs
new file mode 100644
index 000000000000..0c930527b5ca
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue24252.cs
@@ -0,0 +1,179 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 24252, "Overlapping gesture recognizers should only fire on the topmost child view on Windows", PlatformAffected.UWP)]
+public class Issue24252 : ContentPage
+{
+ public Issue24252()
+ {
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 15,
+ Children =
+ {
+ CreatePanSection(),
+ CreateSwipeSection(),
+ CreatePinchSection()
+ }
+ };
+ }
+
+ static View CreatePanSection()
+ {
+ var statusLabel = new Label { Text = "None", AutomationId = "PanStatusLabel" };
+
+ var parent = new Grid
+ {
+ BackgroundColor = Colors.LightBlue,
+ WidthRequest = 250,
+ HeightRequest = 150,
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ var parentPan = new PanGestureRecognizer();
+ parentPan.PanUpdated += (s, e) =>
+ {
+ if (e.StatusType == GestureStatus.Started)
+ {
+ statusLabel.Text = "Parent triggered";
+ }
+ };
+ parent.GestureRecognizers.Add(parentPan);
+
+ var child = new Image
+ {
+ Source = "dotnet_bot.png",
+ WidthRequest = 120,
+ HeightRequest = 100,
+ BackgroundColor = Colors.Orange,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ AutomationId = "PanChildBox"
+ };
+
+ var childPan = new PanGestureRecognizer();
+ childPan.PanUpdated += (s, e) =>
+ {
+ if (e.StatusType == GestureStatus.Started)
+ {
+ statusLabel.Text = "Child triggered";
+ }
+ };
+ child.GestureRecognizers.Add(childPan);
+
+ parent.Children.Add(child);
+
+ return new VerticalStackLayout
+ {
+ Spacing = 5,
+ Children =
+ {
+ new Label { Text = "Pan: Drag on orange child", FontAttributes = FontAttributes.Bold },
+ statusLabel,
+ parent
+ }
+ };
+ }
+
+ static View CreateSwipeSection()
+ {
+ var statusLabel = new Label { Text = "None", AutomationId = "SwipeStatusLabel" };
+
+ var parent = new Grid
+ {
+ BackgroundColor = Colors.LightGreen,
+ WidthRequest = 250,
+ HeightRequest = 150,
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ var parentSwipe = new SwipeGestureRecognizer { Direction = SwipeDirection.Right };
+ parentSwipe.Swiped += (s, e) => statusLabel.Text = "Parent triggered";
+ parent.GestureRecognizers.Add(parentSwipe);
+
+ var child = new Image
+ {
+ Source = "dotnet_bot.png",
+ WidthRequest = 120,
+ HeightRequest = 100,
+ BackgroundColor = Colors.Orange,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ AutomationId = "SwipeChildBox"
+ };
+
+ var childSwipe = new SwipeGestureRecognizer { Direction = SwipeDirection.Right };
+ childSwipe.Swiped += (s, e) => statusLabel.Text = "Child triggered";
+ child.GestureRecognizers.Add(childSwipe);
+
+ parent.Children.Add(child);
+
+ return new VerticalStackLayout
+ {
+ Spacing = 5,
+ Children =
+ {
+ new Label { Text = "Swipe: Swipe right on orange child", FontAttributes = FontAttributes.Bold },
+ statusLabel,
+ parent
+ }
+ };
+ }
+
+ static View CreatePinchSection()
+ {
+ var statusLabel = new Label { Text = "None", AutomationId = "PinchStatusLabel" };
+
+ var parent = new Grid
+ {
+ BackgroundColor = Colors.LightCoral,
+ WidthRequest = 250,
+ HeightRequest = 150,
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ var parentPinch = new PinchGestureRecognizer();
+ parentPinch.PinchUpdated += (s, e) =>
+ {
+ if (e.Status == GestureStatus.Started)
+ {
+ statusLabel.Text = "Parent triggered";
+ }
+ };
+ parent.GestureRecognizers.Add(parentPinch);
+
+ var child = new Image
+ {
+ Source = "dotnet_bot.png",
+ WidthRequest = 150,
+ HeightRequest = 150,
+ BackgroundColor = Colors.Orange,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ AutomationId = "PinchChildBox"
+ };
+
+ var childPinch = new PinchGestureRecognizer();
+ childPinch.PinchUpdated += (s, e) =>
+ {
+ if (e.Status == GestureStatus.Started)
+ {
+ statusLabel.Text = "Child triggered";
+ }
+ };
+ child.GestureRecognizers.Add(childPinch);
+
+ parent.Children.Add(child);
+
+ return new VerticalStackLayout
+ {
+ Spacing = 5,
+ Children =
+ {
+ new Label { Text = "Pinch: Pinch on orange child", FontAttributes = FontAttributes.Bold },
+ statusLabel,
+ parent
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue25081.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue25081.cs
new file mode 100644
index 000000000000..f0a680aebaad
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue25081.cs
@@ -0,0 +1,71 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 25081, "[Windows] The flyout icon and background appear awkward when enabled alongside a TitleBar", PlatformAffected.UWP)]
+public class Issue25081 : TestShell
+{
+ protected override void Init()
+ {
+ FlyoutBehavior = FlyoutBehavior.Flyout;
+
+ var content = new Issue25081ContentPage();
+
+ Items.Add(new ShellContent
+ {
+ Title = "Home",
+ Route = "MainPage",
+ Content = content
+ });
+ }
+}
+
+public class Issue25081ContentPage : ContentPage
+{
+ TitleBar customTitleBar;
+
+ public Issue25081ContentPage()
+ {
+ customTitleBar = new TitleBar
+ {
+ Title = "MauiApp1",
+ Subtitle = "Welcome to .NET MAUI",
+ HeightRequest = 32,
+ BackgroundColor = Colors.YellowGreen
+ };
+
+ var button = new Button
+ {
+ Text = "Change Background color",
+ AutomationId = "ColorChangeButton",
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ };
+
+ button.Clicked += (sender, e) =>
+ {
+ customTitleBar.BackgroundColor = Colors.Cyan;
+ };
+
+ var verticalStackLayout = new VerticalStackLayout()
+ {
+ Spacing = 20,
+ Padding = new Thickness(20),
+ };
+
+ verticalStackLayout.Add(button);
+
+ Content = verticalStackLayout;
+ }
+
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+ if (Window is not null)
+ {
+ Window.TitleBar = customTitleBar;
+ }
+ else if (Shell.Current?.Window is not null)
+ {
+ Shell.Current.Window.TitleBar = customTitleBar;
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue25093.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue25093.cs
new file mode 100644
index 000000000000..7630d67a2327
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue25093.cs
@@ -0,0 +1,73 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 25093, "[iOS] TintColor on UIButton image no longer working when button made visible", PlatformAffected.iOS)]
+public class Issue25093 : ContentPage
+{
+ public Issue25093()
+ {
+ var tintedButton = new Button
+ {
+ AutomationId = "TintedButton",
+ ImageSource = "red.png",
+ BackgroundColor = Colors.Black,
+ WidthRequest = 200,
+ HeightRequest = 200
+ };
+
+ var statusLabel = new Label
+ {
+ AutomationId = "StatusLabel",
+ Text = "Waiting"
+ };
+
+ var applyTintButton = new Button
+ {
+ Text = "Apply Tint and Resize",
+ AutomationId = "ApplyTintButton"
+ };
+
+ applyTintButton.Clicked += (s, e) =>
+ {
+#if IOS || MACCATALYST
+ if (tintedButton.Handler?.PlatformView is UIKit.UIButton platformButton)
+ {
+ // Simulate IconTintColorBehavior: set AlwaysTemplate + tint color
+ var image = platformButton.ImageView?.Image?.ImageWithRenderingMode(UIKit.UIImageRenderingMode.AlwaysTemplate);
+ if (image is not null)
+ {
+ platformButton.SetImage(image, UIKit.UIControlState.Normal);
+ platformButton.TintColor = UIKit.UIColor.Green;
+ platformButton.ImageView.TintColor = UIKit.UIColor.Green;
+ }
+
+ // Verify rendering mode is preserved after resize triggers ResizeImageIfNecessary
+ void OnSizeChanged(object sender, EventArgs args)
+ {
+ tintedButton.SizeChanged -= OnSizeChanged;
+ var currentMode = platformButton.ImageView?.Image?.RenderingMode;
+ statusLabel.Text = currentMode == UIKit.UIImageRenderingMode.AlwaysTemplate
+ ? "PASS"
+ : $"FAIL: RenderingMode is {currentMode}";
+ }
+
+ tintedButton.SizeChanged += OnSizeChanged;
+
+ // Shrink to trigger ResizeImageIfNecessary during the next measure pass
+ tintedButton.WidthRequest = 100;
+ tintedButton.HeightRequest = 100;
+ }
+ else
+ {
+ statusLabel.Text = "PASS";
+ }
+#else
+ statusLabel.Text = "PASS";
+#endif
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Children = { applyTintButton, tintedButton, statusLabel }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue25233.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue25233.xaml
new file mode 100644
index 000000000000..ab115de3d951
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue25233.xaml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue25233.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue25233.xaml.cs
new file mode 100644
index 000000000000..3d1e12aa78a4
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue25233.xaml.cs
@@ -0,0 +1,17 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 25233, "CollectionView with SwipeView items behaves strangely", PlatformAffected.All)]
+
+public partial class Issue25233 : ContentPage
+{
+ public Issue25233()
+ {
+ InitializeComponent();
+ cv.ItemsSource = Enumerable.Range(1, 15).Select(i => $"Item{i}");
+ }
+
+ private void Button_Clicked(object sender, EventArgs e)
+ {
+ cv.ScrollTo(14, position: ScrollToPosition.End, animate: false);
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue25502.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue25502.cs
deleted file mode 100644
index 708e68ed2e6a..000000000000
--- a/src/Controls/tests/TestCases.HostApp/Issues/Issue25502.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-namespace Maui.Controls.Sample.Issues
-{
-
- [Issue(IssueTracker.Github, 25502, "Gray Line Appears on the Right Side of GraphicsView with Decimal WidthRequest on iOS Platform", PlatformAffected.iOS)]
- public class Issue25502 : ContentPage
- {
- public Issue25502()
- {
- GraphicsView graphicsView = new GraphicsView
- {
- AutomationId = "GraphicsView",
- HeightRequest = 200.25,
- WidthRequest = 248.25,
- BackgroundColor = Colors.White,
- Margin = new Thickness(20),
- };
- graphicsView.Drawable = new GraphicsDrawable(graphicsView);
-
- Button changeColorButton = new Button
- {
- Text = "Click to change Color",
- AutomationId = "ChangeColorButton"
- };
-
- changeColorButton.Clicked += ChangeColorButton_Clicked;
-
- Content = new StackLayout
- {
- Spacing = 10,
- Children =
- {
- graphicsView,
- changeColorButton
- }
- };
-
- void ChangeColorButton_Clicked(object sender, EventArgs e)
- {
- graphicsView.BackgroundColor = Colors.Yellow;
- }
- }
-
- public class GraphicsDrawable : IDrawable
- {
- GraphicsView _graphicsView;
- public GraphicsDrawable(GraphicsView graphicsView)
- {
- _graphicsView = graphicsView;
- }
-
- public void Draw(ICanvas canvas, RectF dirtyRect)
- {
- canvas.FillColor = _graphicsView.BackgroundColor;
- canvas.FillRectangle(new RectF(dirtyRect.X, dirtyRect.Y + 40, dirtyRect.Width - 40, dirtyRect.Height - 40));
- }
- }
- }
-}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue25728.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue25728.cs
new file mode 100644
index 000000000000..a4a729597313
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue25728.cs
@@ -0,0 +1,52 @@
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 25728, "Java.Lang.IllegalArgumentException when clearing Entry text with StringFormat binding on Android", PlatformAffected.Android)]
+public class Issue25728 : ContentPage
+{
+ public Issue25728()
+ {
+ var vm = new Issue25728ViewModel();
+
+ var entry = new Entry
+ {
+ Keyboard = Keyboard.Numeric,
+ AutomationId = "FloatEntry"
+ };
+ entry.SetBinding(Entry.TextProperty, new Binding(nameof(Issue25728ViewModel.FloatValue), stringFormat: "{0:F2}"));
+
+ BindingContext = vm;
+
+ Content = new VerticalStackLayout
+ {
+ Padding = new Thickness(30, 0),
+ Spacing = 25,
+ Children = { entry }
+ };
+ }
+
+ public class Issue25728ViewModel : INotifyPropertyChanged
+ {
+ float _floatValue;
+
+ public float FloatValue
+ {
+ get => _floatValue;
+ set
+ {
+ if (!Equals(_floatValue, value))
+ {
+ _floatValue = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue25921.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue25921.cs
new file mode 100644
index 000000000000..5d79bbc8de17
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue25921.cs
@@ -0,0 +1,46 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 25921, "[Windows] Setting BackgroundColor for Slider updates the Maximum Track Color", PlatformAffected.UWP)]
+public class Issue25921 : ContentPage
+{
+ public Issue25921()
+ {
+ var layout = new VerticalStackLayout();
+ layout.VerticalOptions = LayoutOptions.Center;
+ layout.Spacing = 20;
+ var slider = new Slider()
+ {
+ WidthRequest = 300,
+ Maximum = 100,
+ Minimum = 0,
+ Value = 50,
+ BackgroundColor = Colors.Yellow,
+ MaximumTrackColor = Colors.Red,
+ MinimumTrackColor = Colors.Fuchsia,
+ };
+
+ var secondSlider = new Slider()
+ {
+ WidthRequest = 300,
+ Maximum = 100,
+ Minimum = 0,
+ Value = 50,
+ Background = Colors.Grey,
+ MaximumTrackColor = Colors.SpringGreen,
+ MinimumTrackColor = Colors.BlueViolet,
+ };
+
+ var button = new Button()
+ {
+ Text = "Change Background Color",
+ AutomationId = "ColorChangeButton",
+ Command = new Command(() => secondSlider.Background = Colors.Salmon)
+ };
+
+ layout.Children.Add(slider);
+ layout.Children.Add(secondSlider);
+ layout.Children.Add(button);
+
+ Content = layout;
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue26158.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue26158.cs
new file mode 100644
index 000000000000..e82df5463e9c
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue26158.cs
@@ -0,0 +1,23 @@
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 26158, "SelectionLength property not applied when an entry is focused", PlatformAffected.Android | PlatformAffected.iOS)]
+ public class Issue26158 : ContentPage
+ {
+ public Issue26158()
+ {
+ Entry entry = new Entry()
+ {
+ AutomationId = "entry",
+ Text = "Microsoft Maui Entry",
+ };
+
+ entry.Focused += (s, e) =>
+ {
+ entry.CursorPosition = 0;
+ entry.SelectionLength = 4;
+ };
+
+ Content = entry;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue26187.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue26187.cs
index 764d78a25afe..18530870e834 100644
--- a/src/Controls/tests/TestCases.HostApp/Issues/Issue26187.cs
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue26187.cs
@@ -15,8 +15,6 @@ public class CollectionViewSelectedItemNullPage : ContentPage
{
public ObservableCollection Items { get; set; }
- public string SelectedItem { get; set; }
-
public CollectionViewSelectedItemNullPage()
{
Items = new ObservableCollection
@@ -27,7 +25,6 @@ public CollectionViewSelectedItemNullPage()
"Item 4",
"Item 5"
};
- SelectedItem = Items.LastOrDefault();
var cv = new CollectionView
{
SelectionMode = SelectionMode.Single,
@@ -51,20 +48,16 @@ public CollectionViewSelectedItemNullPage()
};
cv.SetBinding(CollectionView.ItemsSourceProperty, new Binding(nameof(Items)));
- // cv.SetBinding(CollectionView.SelectedItemProperty, new Binding(nameof(SelectedItem)));
Content = cv;
-
BindingContext = this;
- // cv.SelectedItem = SelectedItem;
-
cv.SelectionChanged += CollectionView_SelectionChanged;
}
- async void CollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ void CollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.CurrentSelection.FirstOrDefault() is string issue)
{
- await Navigation.PushAsync(new NewPage(issue));
+ _ = Navigation.PushAsync(new NewPage(issue));
}
// Clear Selection
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue26864.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue26864.cs
new file mode 100644
index 000000000000..e17d1912444d
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue26864.cs
@@ -0,0 +1,39 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 26864, "Shell Content Title Not Rendering in Full-Screen Mode on Mac Catalyst", PlatformAffected.macOS)]
+public class Issue26864 : Shell
+{
+ public Issue26864()
+ {
+ var tabBar = new TabBar
+ {
+ AutomationId = "TabBar"
+ };
+ var tab = new Tab
+ {
+ Title = "Nested Tabs",
+ AutomationId = "tabbar"
+ };
+ var homeShellContent = new ShellContent
+ {
+ Title = "Home",
+ Content = new ContentPage
+ {
+ BackgroundColor = Microsoft.Maui.Graphics.Colors.White
+ }
+ };
+ var settingsShellContent = new ShellContent
+ {
+ Title = "Settings",
+ Content = new ContentPage
+ {
+ BackgroundColor = Microsoft.Maui.Graphics.Colors.White
+ }
+ };
+ tab.Items.Add(homeShellContent);
+ tab.Items.Add(settingsShellContent);
+ tabBar.Items.Add(tab);
+ this.Items.Add(tabBar);
+ }
+
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue2708.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue2708.xaml
new file mode 100644
index 000000000000..9e21c5aa290a
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue2708.xaml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue2708.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue2708.xaml.cs
new file mode 100644
index 000000000000..619184ce1b8a
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue2708.xaml.cs
@@ -0,0 +1,64 @@
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 2708, "Prevent tabs from being removed during modal navigation", PlatformAffected.Android)]
+ public partial class Issue2708 : TabbedPage
+ {
+ public Issue2708()
+ {
+ InitializeComponent();
+ }
+
+ async void OnOpenModalClicked(object sender, EventArgs e)
+ {
+ await Navigation.PushModalAsync(new Issue2708Modal());
+ }
+ }
+
+ public class Issue2708Modal : ContentPage
+ {
+ public Issue2708Modal()
+ {
+ Title = "Modal Page";
+
+ // Use a semi-transparent background so tabs can be verified as visible behind the modal
+ BackgroundColor = Color.FromArgb("#80000000");
+
+ var layout = new StackLayout
+ {
+ Padding = new Thickness(20),
+ BackgroundColor = Colors.White,
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ WidthRequest = 300,
+ Children =
+ {
+ new Label
+ {
+ Text = "This is a modal page",
+ FontSize = 18,
+ HorizontalOptions = LayoutOptions.Center
+ },
+ new Label
+ {
+ Text = "The tabs should still be visible behind this modal on Android",
+ FontSize = 14,
+ HorizontalOptions = LayoutOptions.Center
+ },
+ new Button
+ {
+ Text = "Close Modal",
+ AutomationId = "CloseModalButton"
+ }
+ }
+ };
+
+ var closeButton = layout.Children.OfType().First();
+ closeButton.Clicked += async (sender, e) =>
+ {
+ await Navigation.PopModalAsync();
+ };
+
+ Content = layout;
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue27143.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue27143.cs
new file mode 100644
index 000000000000..232cd577b45c
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue27143.cs
@@ -0,0 +1,48 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 27143, "Not trigger OnNavigatedTo method when hide the navi bar and using swipe", PlatformAffected.iOS)]
+public class Issue27143NavigationPage : NavigationPage
+{
+ public Issue27143NavigationPage() : base(new Issue27143()) { }
+
+ class Issue27143 : ContentPage
+ {
+ int _navigatedToEventTriggersCount;
+
+ Label _label;
+
+ public Issue27143()
+ {
+ ContentPage subpage = new ContentPage()
+ {
+ Content = new Label()
+ {
+ Text = "Hello from the other side!",
+ }
+ };
+
+ SetHasNavigationBar(this, false);
+
+ _label = new Label()
+ {
+ AutomationId = "navigatedToEventTriggersCountLabel",
+ };
+
+ Content = new VerticalStackLayout()
+ {
+ new Button()
+ {
+ Text = "Click to navigate",
+ AutomationId = "button",
+ Command = new Command(() => Window!.Page!.Navigation.PushAsync(subpage, false))
+ },
+ _label
+ };
+ }
+
+ protected override void OnNavigatedTo(NavigatedToEventArgs args)
+ {
+ _label.Text = $"NavigatedTo event triggers count: {++_navigatedToEventTriggersCount}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue27377.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue27377.xaml
new file mode 100644
index 000000000000..b617bb7999e7
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue27377.xaml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue27377.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue27377.xaml.cs
new file mode 100644
index 000000000000..6aeae4d615de
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue27377.xaml.cs
@@ -0,0 +1,15 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 27377, "SwipeView: SwipeItem.IconImageSource.FontImageSource color value not honored", PlatformAffected.iOS)]
+public partial class Issue27377 : ContentPage
+{
+ public Issue27377()
+ {
+ InitializeComponent();
+ }
+
+ private void Button_Clicked(object sender, EventArgs e)
+ {
+ swipeView.Open(OpenSwipeItem.RightItems, true);
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue27427.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue27427.cs
new file mode 100644
index 000000000000..dbe02680d7f6
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue27427.cs
@@ -0,0 +1,100 @@
+using Microsoft.Maui.Layouts;
+
+namespace Maui.Controls.Sample.Issues
+{
+
+ [Issue(IssueTracker.Github, 27427, "iOS SearchBar ignores WidthRequest and HeightRequest property values", PlatformAffected.iOS | PlatformAffected.macOS)]
+ public class Issue27427 : TestContentPage
+ {
+ protected override void Init()
+ {
+ StackLayout mainLayout = new StackLayout
+ {
+ Spacing = 5,
+ };
+
+ VerticalStackLayout verticalLayout = new VerticalStackLayout
+ {
+ Spacing = 10,
+ Padding = new Thickness(20, 0),
+ Children =
+ {
+ new SearchBar { HeightRequest = 150, BackgroundColor = Colors.LightGray, Placeholder = "Vertical Layout SearchBar" }
+ }
+ };
+
+ HorizontalStackLayout horizontalLayout = new HorizontalStackLayout
+ {
+ Spacing = 10,
+ Padding = new Thickness(20, 0),
+ Children =
+ {
+ new SearchBar
+ {
+ WidthRequest = 300,
+ BackgroundColor = Colors.LightBlue,
+ Placeholder = "Horizontal Layout SearchBar"
+ }
+ }
+ };
+
+ AbsoluteLayout absoluteLayout = new AbsoluteLayout
+ {
+ Padding = new Thickness(20, 0),
+ Children =
+ {
+ new SearchBar
+ {
+ WidthRequest = 350,
+ BackgroundColor = Colors.LightGreen,
+ Placeholder = "Absolute Layout SearchBar"
+ }
+ }
+ };
+
+ Grid gridLayout = new Grid
+ {
+ Padding = new Thickness(20, 0),
+ RowDefinitions =
+ {
+ new RowDefinition(),
+ },
+ ColumnDefinitions =
+ {
+ new ColumnDefinition { Width = GridLength.Star },
+ new ColumnDefinition { Width = GridLength.Star }
+ }
+ };
+
+ SearchBar gridSearchBar1 = new SearchBar
+ {
+ HeightRequest = 80,
+ BackgroundColor = Colors.LightYellow,
+ Placeholder = "Grid SearchBar 1"
+ };
+ Grid.SetRow(gridSearchBar1, 0);
+ Grid.SetColumn(gridSearchBar1, 0);
+
+ SearchBar gridSearchBar2 = new SearchBar
+ {
+ WidthRequest = 150,
+ BackgroundColor = Colors.LightYellow,
+ Placeholder = "Grid SearchBar 2"
+ };
+ Grid.SetRow(gridSearchBar2, 0);
+ Grid.SetColumn(gridSearchBar2, 1);
+
+ gridLayout.Add(gridSearchBar1);
+ gridLayout.Add(gridSearchBar2);
+
+ mainLayout.Children.Add(new Label { Text = "SearchBar Explicit Size Dimensions", AutomationId = "SearchBarDimensionsHeaderLabel" });
+ mainLayout.Children.Add(verticalLayout);
+ mainLayout.Children.Add(horizontalLayout);
+ mainLayout.Children.Add(absoluteLayout);
+ mainLayout.Children.Add(gridLayout);
+
+ Content = mainLayout;
+ }
+ }
+}
+
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue27646.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue27646.cs
new file mode 100644
index 000000000000..8f45e6f492c0
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue27646.cs
@@ -0,0 +1,99 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 27646, "AdaptiveTrigger not firing when changing window width programmatically only", PlatformAffected.UWP)]
+public class Issue27646 : ContentPage
+{
+ Label indicatorLabel;
+ Label statusLabel;
+
+ public Issue27646()
+ {
+ var stackLayout = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 10
+ };
+
+ var instructionLabel = new Label
+ {
+ Text = "Click button to resize window. Label text should change at 600px window width.",
+ AutomationId = "InstructionLabel"
+ };
+ stackLayout.Add(instructionLabel);
+
+ statusLabel = new Label
+ {
+ Text = "Window width: Unknown",
+ AutomationId = "StatusLabel",
+ FontAttributes = FontAttributes.Bold
+ };
+ stackLayout.Add(statusLabel);
+
+ var resizeButton = new Button
+ {
+ Text = "Resize Window",
+ AutomationId = "ResizeButton"
+ };
+ resizeButton.Clicked += OnResizeClicked;
+ stackLayout.Add(resizeButton);
+
+ indicatorLabel = new Label
+ {
+ Text = "Initial",
+ WidthRequest = 200,
+ HeightRequest = 100,
+ HorizontalTextAlignment = TextAlignment.Center,
+ VerticalTextAlignment = TextAlignment.Center,
+ AutomationId = "IndicatorLabel"
+ };
+ stackLayout.Add(indicatorLabel);
+
+ Content = stackLayout;
+
+ VisualStateManager.SetVisualStateGroups(indicatorLabel, new VisualStateGroupList
+ {
+ new VisualStateGroup
+ {
+ Name = "WindowWidthStates",
+ States =
+ {
+ new VisualState
+ {
+ Name = "Narrow",
+ StateTriggers =
+ {
+ new AdaptiveTrigger { MinWindowWidth = 0 }
+ },
+ Setters =
+ {
+ new Setter { Property = Label.TextProperty, Value = "Narrow Window" }
+ }
+ },
+ new VisualState
+ {
+ Name = "Wide",
+ StateTriggers =
+ {
+ new AdaptiveTrigger { MinWindowWidth = 600 }
+ },
+ Setters =
+ {
+ new Setter { Property = Label.TextProperty, Value = "Wide Window" }
+ }
+ }
+ }
+ }
+ });
+ }
+
+ void OnResizeClicked(object sender, EventArgs e)
+ {
+ if (Window is not null)
+ {
+ double newWidth = Window.Width >= 600 ? 550 : 650;
+ Window.Width = newWidth;
+
+ statusLabel.Text = $"Window width: {newWidth}px";
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue27799.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue27799.cs
new file mode 100644
index 000000000000..d4ea9ff7f751
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue27799.cs
@@ -0,0 +1,74 @@
+namespace Maui.Controls.Sample.Issues
+{
+
+ [Issue(IssueTracker.Github, 27799, "[iOS] OnAppearing and OnNavigatedTo does not work when using extended Tabbar", PlatformAffected.iOS)]
+
+ public class Issue27799 : TestShell
+ {
+ private static int _onNavigatedToCount = 0;
+ private static int _onAppearingCount = 0;
+
+ protected override void Init()
+ {
+ Routing.RegisterRoute(nameof(Tab6Subpage), typeof(Tab6Subpage));
+ AddBottomTab("tab1");
+ AddBottomTab(new Tab2(), "tab2");
+ AddBottomTab("tab3");
+ AddBottomTab("tab4");
+ AddBottomTab("tab5");
+ AddBottomTab(new Tab6(), "Tab6");
+ }
+
+ class Tab2 : ContentPage
+ {
+ Label _onNavigatedToCountLabel;
+ Label _onAppearingCountLabel;
+ public Tab2()
+ {
+ _onNavigatedToCountLabel = new Label { AutomationId = "OnNavigatedToCountLabel", Text = $"OnNavigatedTo: {_onNavigatedToCount}" };
+ _onAppearingCountLabel = new Label { AutomationId = "OnAppearingCountLabel", Text = $"OnAppearing: {_onAppearingCount}" };
+ Content = new StackLayout
+ {
+ Children =
+ {
+ _onNavigatedToCountLabel,
+ _onAppearingCountLabel
+ }
+ };
+ }
+
+ protected override void OnNavigatedTo(NavigatedToEventArgs e)
+ {
+ _onNavigatedToCount++;
+ _onNavigatedToCountLabel.Text = $"OnNavigatedTo: {_onNavigatedToCount}";
+ }
+
+ protected override void OnAppearing()
+ {
+ _onAppearingCount++;
+ _onAppearingCountLabel.Text = $"OnAppearing: {_onAppearingCount}";
+ }
+ }
+
+ class Tab6 : ContentPage
+ {
+ public Tab6()
+ {
+ Content = new Button
+ {
+ Text = "Go to subpage6",
+ AutomationId = "GoToSubpage6Button",
+ Command = new Command(async () => await Current.GoToAsync(nameof(Tab6Subpage)))
+ };
+ }
+ protected override void OnNavigatedTo(NavigatedToEventArgs e) => _onNavigatedToCount++;
+ protected override void OnAppearing() => _onAppearingCount++;
+ }
+
+ class Tab6Subpage : ContentPage
+ {
+ protected override void OnNavigatedTo(NavigatedToEventArgs e) => _onNavigatedToCount++;
+ protected override void OnAppearing() => _onAppearingCount++;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue27800.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue27800.cs
new file mode 100644
index 000000000000..a629156f04bb
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue27800.cs
@@ -0,0 +1,72 @@
+#if ANDROID || IOS
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 27800, "Shell.BackButtonBehavior does not work when using extended Tabbar", PlatformAffected.iOS)]
+public class Issue27800 : TestShell
+{
+
+ protected override void Init()
+ {
+ Routing.RegisterRoute(nameof(Tab6DetailPage), typeof(Tab6DetailPage));
+ AddBottomTab("tab1");
+ AddBottomTab("tab2");
+ AddBottomTab("tab3");
+ AddBottomTab("tab4");
+ AddBottomTab("tab5");
+ AddBottomTab(new Tab6(), "tab6");
+ }
+
+ class Tab6 : ContentPage
+ {
+ Label _onNavigatedToCountLabel;
+ Label _onAppearingCountLabel;
+ int _onNavigatedToCount;
+ int _onAppearingCount;
+
+ public Tab6()
+ {
+ _onNavigatedToCountLabel = new Label { AutomationId = "OnNavigatedToCountLabel", Text = $"OnNavigatedTo: {_onNavigatedToCount}" };
+ _onAppearingCountLabel = new Label { AutomationId = "OnAppearingCountLabel", Text = $"OnAppearing: {_onAppearingCount}" };
+ Content = new StackLayout
+ {
+ Children =
+ {
+ _onNavigatedToCountLabel,
+ _onAppearingCountLabel,
+ new Button
+ {
+ AutomationId = "button",
+ Text = "Tap to navigate to a detail page",
+ Command = new Command(() => Shell.Current.GoToAsync(nameof(Tab6DetailPage)))
+ }
+ }
+ };
+ }
+
+ protected override void OnNavigatedTo(NavigatedToEventArgs e)
+ {
+ _onNavigatedToCount++;
+ _onNavigatedToCountLabel.Text = $"OnNavigatedTo: {_onNavigatedToCount}";
+ }
+
+ protected override void OnAppearing()
+ {
+ _onAppearingCount++;
+ _onAppearingCountLabel.Text = $"OnAppearing: {_onAppearingCount}";
+ }
+ }
+
+
+ class Tab6DetailPage : ContentPage
+ {
+ public Tab6DetailPage()
+ {
+ Shell.SetBackButtonBehavior(this, new BackButtonBehavior
+ {
+ TextOverride = "Go Back",
+ Command = new Command(() => Shell.Current.GoToAsync(".."))
+ });
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue27846.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue27846.cs
new file mode 100644
index 000000000000..22d761ae8709
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue27846.cs
@@ -0,0 +1,20 @@
+namespace Maui.Controls.Sample.Issues
+{
+
+ [Issue(IssueTracker.Github, 27846, "[iOS] More tab doesn't respect shell nav bar customization", PlatformAffected.iOS)]
+
+ public class Issue27846 : TestShell
+ {
+ protected override void Init()
+ {
+ SetBackgroundColor(this, Colors.Green);
+ AddBottomTab("tab1");
+ AddBottomTab("tab2");
+ AddBottomTab("tab3");
+ AddBottomTab("tab4");
+ AddBottomTab("tab5");
+ AddBottomTab("tab6");
+ AddBottomTab("tab7");
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue28101.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue28101.cs
new file mode 100644
index 000000000000..1d831acfacba
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue28101.cs
@@ -0,0 +1,54 @@
+using Microsoft.Maui.Platform;
+
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 28101, "CollectionView Footer Becomes Scrollable When EmptyView is Active on Android", PlatformAffected.Android)]
+ public class Issue28101 : TestContentPage
+ {
+ protected override void Init()
+ {
+ Grid grid = new Grid();
+ CollectionView collectionView = new CollectionView
+ {
+ EmptyView = new Label
+ {
+ Padding = new Thickness(20, 5, 5, 5),
+ Text = "Empty"
+ }
+ };
+
+ collectionView.HeaderTemplate = new DataTemplate(() =>
+ {
+ Grid gridHeader = new Grid();
+
+ Label labelHeader = new Label
+ {
+ FontSize = 20,
+ BackgroundColor = Colors.HotPink,
+ Text = "This Is A Header"
+ };
+
+ gridHeader.Children.Add(labelHeader);
+ return gridHeader;
+ });
+
+ collectionView.FooterTemplate = new DataTemplate(() =>
+ {
+ Grid gridFooter = new Grid();
+
+ Label labelFooter = new Label
+ {
+ Text = "This Is A Footer",
+ BackgroundColor = Colors.HotPink,
+ FontSize = 20,
+ };
+
+ gridFooter.Children.Add(labelFooter);
+ return gridFooter;
+ });
+
+ grid.Children.Add(collectionView);
+ Content = grid;
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue28321.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue28321.xaml
new file mode 100644
index 000000000000..f260185ef03a
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue28321.xaml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue28321.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue28321.xaml.cs
new file mode 100644
index 000000000000..726b7042923d
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue28321.xaml.cs
@@ -0,0 +1,38 @@
+using System.Collections.ObjectModel;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 28321, "CV RemainingItemsThresholdReachedCommand fires on initial data load", PlatformAffected.Android)]
+public partial class Issue28321 : ContentPage
+{
+ public readonly record struct Data(string Text, string AutomationId);
+ public Issue28321()
+ {
+ InitializeComponent();
+ BindingContext = new Issue28321ViewModel();
+ }
+
+ public class Issue28321ViewModel : ViewModel
+ {
+
+ public ObservableCollection Items { get; set; }
+ public Command LoadMoreItemsCommand { get; }
+ public Issue28321ViewModel()
+ {
+ LoadMoreItemsCommand = new Command(() =>
+ {
+ int size = Items.Count;
+ for (int i = size; i < size + 10; i++)
+ Items.Add($"Item{i}");
+ });
+ _ = LoadItems();
+ }
+
+ async Task LoadItems()
+ {
+ await Task.Delay(1000);
+ Items = [.. Enumerable.Range(0, 4).Select(x => $"Item{x}").ToList()];
+ OnPropertyChanged(nameof(Items));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue28656.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue28656.xaml
new file mode 100644
index 000000000000..9c0b043948a7
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue28656.xaml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue28656.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue28656.xaml.cs
new file mode 100644
index 000000000000..fab8587413a1
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue28656.xaml.cs
@@ -0,0 +1,51 @@
+using System.Collections.ObjectModel;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 28656, "CollectionView CollectionViewHandler2 does not change ItemsLayout on DataTrigger", PlatformAffected.iOS)]
+public partial class Issue28656 : ContentPage
+{
+ public Issue28656()
+ {
+ InitializeComponent();
+ BindingContext = new Issue28656ViewModel();
+ }
+
+ public partial class Issue28656ViewModel : ViewModel
+ {
+ private ItemsLayout _itemsLayout = new GridItemsLayout(2, ItemsLayoutOrientation.Vertical);
+ public ItemsLayout ItemsLayout
+ {
+ get => _itemsLayout;
+ set
+ {
+ _itemsLayout = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private bool _isGridLayout = true;
+ public bool IsGridLayout
+ {
+ get => _isGridLayout;
+ set
+ {
+ _isGridLayout = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public ObservableCollection Items => [.. Enumerable.Range(0, 100).Select(i => $"Item {i}")];
+
+ public Command ChangeLayoutCommand { get; }
+
+ public Issue28656ViewModel()
+ {
+ ChangeLayoutCommand = new Command(() =>
+ {
+ IsGridLayout = !IsGridLayout;
+ ItemsLayout = IsGridLayout ? new GridItemsLayout(2, ItemsLayoutOrientation.Vertical) : new LinearItemsLayout(ItemsLayoutOrientation.Vertical);
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue28901.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue28901.cs
new file mode 100644
index 000000000000..903ce3291cf3
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue28901.cs
@@ -0,0 +1,49 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 28901, "[Windows] Switch control is not sizing properly", PlatformAffected.UWP)]
+public class Issue28901 : ContentPage
+{
+ public Issue28901()
+ {
+ var switchControl = new Switch
+ {
+ IsToggled = true,
+ HorizontalOptions = LayoutOptions.End,
+ AutomationId = "SwitchControl"
+ };
+
+ var switchControl2 = new Switch();
+
+ var label = new Label
+ {
+ Text = "Switch Control",
+ VerticalOptions = LayoutOptions.Center,
+ };
+
+ var settingsLabel = new Label
+ {
+ Text = "Change View",
+ VerticalOptions = LayoutOptions.Center,
+ };
+
+ var horizontalStackLayout = new HorizontalStackLayout
+ {
+ Children =
+ {
+ label,
+ switchControl2,
+ settingsLabel
+ },
+ };
+
+ Content = new VerticalStackLayout
+ {
+ VerticalOptions = LayoutOptions.Center,
+ Children =
+ {
+ switchControl,
+ horizontalStackLayout,
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue28968.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue28968.cs
new file mode 100644
index 000000000000..091b75ff98e9
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue28968.cs
@@ -0,0 +1,69 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 28968, "[iOS] ActivityIndicator IsRunning ignores IsVisible when set to true", PlatformAffected.iOS)]
+public class Issue28968 : ContentPage
+{
+ public Issue28968()
+ {
+ var activityIndicator = new ActivityIndicator
+ {
+ IsVisible = false,
+ AutomationId = "MauiActivityIndicator"
+ };
+
+ var statusLabel = new Label
+ {
+ Text = "Waiting",
+ AutomationId = "StatusLabel"
+ };
+
+ var setRunningButton = new Button
+ {
+ Text = "Set IsRunning = true",
+ AutomationId = "SetRunningButton",
+ Command = new Command(() =>
+ {
+ activityIndicator.IsRunning = true;
+
+ // Check the native platform hidden state after a delay to allow
+ // Draw/LayoutSubviews to execute on iOS
+ Dispatcher.DispatchDelayed(TimeSpan.FromMilliseconds(500), () =>
+ {
+ bool nativeHidden = IsNativeViewHidden(activityIndicator);
+ statusLabel.Text = nativeHidden ? "HIDDEN" : "VISIBLE";
+ });
+ })
+ };
+
+ Content = new VerticalStackLayout
+ {
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ Children =
+ {
+ activityIndicator,
+ setRunningButton,
+ statusLabel
+ }
+ };
+ }
+
+ static bool IsNativeViewHidden(ActivityIndicator indicator)
+ {
+ var handler = indicator.Handler;
+ if (handler?.PlatformView is null)
+ return true;
+
+#if IOS || MACCATALYST
+ if (handler.PlatformView is UIKit.UIView nativeView)
+ return nativeView.Hidden;
+#elif ANDROID
+ if (handler.PlatformView is Android.Views.View nativeView)
+ return nativeView.Visibility != Android.Views.ViewStates.Visible;
+#elif WINDOWS
+ if (handler.PlatformView is Microsoft.UI.Xaml.UIElement nativeView)
+ return nativeView.Visibility != Microsoft.UI.Xaml.Visibility.Visible;
+#endif
+ return true;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue29036.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue29036.cs
new file mode 100644
index 000000000000..84a3e76ce241
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue29036.cs
@@ -0,0 +1,59 @@
+using static Microsoft.Maui.Controls.Button;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 29036, "Button RTL text and image overlap", PlatformAffected.iOS)]
+public class Issue29036 : ContentPage
+{
+ public Issue29036()
+ {
+ Content = new VerticalStackLayout
+ {
+ WidthRequest = 400,
+ Children =
+ {
+ new Button
+ {
+ AutomationId = "button",
+ FlowDirection = FlowDirection.RightToLeft,
+ BackgroundColor = Colors.LightGreen,
+ Text = "This is a Regular Button",
+ FontSize = 24,
+ ImageSource = "dotnet_bot.png",
+ TextColor = Colors.Black,
+ VerticalOptions = LayoutOptions.Center
+ },
+ new Button
+ {
+ BackgroundColor = Colors.LightGreen,
+ Text = "This is a Regular Button",
+ FontSize = 24,
+ ImageSource = "dotnet_bot.png",
+ TextColor = Colors.Black,
+ VerticalOptions = LayoutOptions.Center
+ },
+ new Button
+ {
+ FlowDirection = FlowDirection.RightToLeft,
+ ContentLayout = new ButtonContentLayout(ButtonContentLayout.ImagePosition.Right, 10),
+ BackgroundColor = Colors.LightGreen,
+ Text = "This is a Regular Button",
+ FontSize = 24,
+ ImageSource = "dotnet_bot.png",
+ TextColor = Colors.Black,
+ VerticalOptions = LayoutOptions.Center
+ },
+ new Button
+ {
+ BackgroundColor = Colors.LightGreen,
+ ContentLayout = new ButtonContentLayout(ButtonContentLayout.ImagePosition.Right, 10),
+ Text = "This is a Regular Button",
+ FontSize = 24,
+ ImageSource = "dotnet_bot.png",
+ TextColor = Colors.Black,
+ VerticalOptions = LayoutOptions.Center
+ }
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue29141.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue29141.cs
new file mode 100644
index 000000000000..68e999f0b5e7
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue29141.cs
@@ -0,0 +1,418 @@
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 29141, "[iOS] Group Header/Footer Repeated for All Items When IsGrouped is True for ObservableCollection in CollectionView", PlatformAffected.Android)]
+public class Issue29141 : ContentPage
+{
+ Issue29141CollectionViewViewModel _viewModel;
+ CollectionView _collectionView;
+ RadioButton _groupHeaderTemplateNone;
+ RadioButton _groupHeaderTemplateGrid;
+ RadioButton _groupFooterTemplateNone;
+ RadioButton _groupFooterTemplateGrid;
+ RadioButton _isGroupedFalse;
+ RadioButton _isGroupedTrue;
+ Button _switchToStringCollectionButton;
+ Button _addItemButton;
+
+ public Issue29141()
+ {
+ _viewModel = new Issue29141CollectionViewViewModel();
+ BindingContext = _viewModel;
+
+ // Create CollectionView
+ _collectionView = new CollectionView
+ {
+ AutomationId = "collectionView"
+ };
+ _collectionView.SetBinding(CollectionView.ItemsSourceProperty, "ItemsSource");
+ _collectionView.SetBinding(CollectionView.ItemTemplateProperty, "ItemTemplate");
+ _collectionView.SetBinding(CollectionView.IsGroupedProperty, "IsGrouped");
+ _collectionView.SetBinding(CollectionView.GroupHeaderTemplateProperty, "GroupHeaderTemplate");
+ _collectionView.SetBinding(CollectionView.GroupFooterTemplateProperty, "GroupFooterTemplate");
+
+ // Create GroupHeaderTemplate radio buttons
+ _groupHeaderTemplateNone = new RadioButton
+ {
+ IsChecked = true,
+ Content = "None",
+ FontSize = 11,
+ GroupName = "GroupHeaderTemplateGroup",
+ AutomationId = "GroupHeaderTemplateNone"
+ };
+ _groupHeaderTemplateNone.CheckedChanged += OnGroupHeaderTemplateChanged;
+
+ _groupHeaderTemplateGrid = new RadioButton
+ {
+ Content = "View",
+ FontSize = 11,
+ GroupName = "GroupHeaderTemplateGroup",
+ AutomationId = "GroupHeaderTemplateGrid"
+ };
+ _groupHeaderTemplateGrid.CheckedChanged += OnGroupHeaderTemplateChanged;
+
+ // Create GroupFooterTemplate radio buttons
+ _groupFooterTemplateNone = new RadioButton
+ {
+ IsChecked = true,
+ Content = "None",
+ FontSize = 11,
+ GroupName = "GroupFooterTemplateGroup",
+ AutomationId = "GroupFooterTemplateNone"
+ };
+ _groupFooterTemplateNone.CheckedChanged += OnGroupFooterTemplateChanged;
+
+ _groupFooterTemplateGrid = new RadioButton
+ {
+ Content = "View",
+ FontSize = 11,
+ GroupName = "GroupFooterTemplateGroup",
+ AutomationId = "GroupFooterTemplateGrid"
+ };
+ _groupFooterTemplateGrid.CheckedChanged += OnGroupFooterTemplateChanged;
+
+ // Create IsGrouped radio buttons
+ _isGroupedFalse = new RadioButton
+ {
+ Content = "False",
+ IsChecked = true,
+ FontSize = 11,
+ AutomationId = "IsGroupedFalse"
+ };
+ _isGroupedFalse.CheckedChanged += OnIsGroupedChanged;
+
+ _isGroupedTrue = new RadioButton
+ {
+ Content = "True",
+ FontSize = 11,
+ AutomationId = "IsGroupedTrue"
+ };
+ _isGroupedTrue.CheckedChanged += OnIsGroupedChanged;
+
+ _switchToStringCollectionButton = new Button
+ {
+ Text = "Use String Collection",
+ AutomationId = "SwitchToStringCollection"
+ };
+ _switchToStringCollectionButton.Clicked += (s, e) => _viewModel.SwitchToStringItems();
+
+ _addItemButton = new Button
+ {
+ Text = "Add Item",
+ AutomationId = "AddItemButton"
+ };
+ _addItemButton.Clicked += (s, e) => _viewModel.AddItem();
+
+ // Build the UI
+ var controlsLayout = new StackLayout
+ {
+ Padding = new Thickness(10),
+ Spacing = 10,
+ Children =
+ {
+ new Label
+ {
+ Text = "GroupHeaderTemplate:",
+ FontAttributes = FontAttributes.Bold,
+ FontSize = 12
+ },
+ new StackLayout
+ {
+ Orientation = StackOrientation.Horizontal,
+ Children = { _groupHeaderTemplateNone, _groupHeaderTemplateGrid }
+ },
+ new Label
+ {
+ Text = "GroupFooterTemplate:",
+ FontAttributes = FontAttributes.Bold,
+ FontSize = 12
+ },
+ new StackLayout
+ {
+ Orientation = StackOrientation.Horizontal,
+ Children = { _groupFooterTemplateNone, _groupFooterTemplateGrid }
+ },
+ new Label
+ {
+ Text = "IsGrouped:",
+ FontSize = 12,
+ FontAttributes = FontAttributes.Bold
+ },
+ new StackLayout
+ {
+ Orientation = StackOrientation.Horizontal,
+ Children = { _isGroupedFalse, _isGroupedTrue }
+ },
+ new Label
+ {
+ Text = "Scenarios:",
+ FontSize = 12,
+ FontAttributes = FontAttributes.Bold
+ },
+ _switchToStringCollectionButton,
+ _addItemButton
+ }
+ };
+
+ var scrollView = new ScrollView
+ {
+ Content = controlsLayout
+ };
+
+ Content = new Grid
+ {
+ Padding = new Thickness(10),
+ RowDefinitions =
+ {
+ new RowDefinition { Height = new GridLength(1, GridUnitType.Star) },
+ new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }
+ },
+ Children =
+ {
+ _collectionView,
+ scrollView
+ }
+ };
+
+ Grid.SetRow(_collectionView, 0);
+ Grid.SetRow(scrollView, 1);
+ }
+
+ void OnGroupHeaderTemplateChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (_groupHeaderTemplateNone.IsChecked)
+ {
+ _viewModel.GroupHeaderTemplate = null;
+ }
+ else if (_groupHeaderTemplateGrid.IsChecked)
+ {
+ _viewModel.GroupHeaderTemplate = new DataTemplate(() =>
+ {
+ return new Grid
+ {
+ BackgroundColor = Colors.LightGray,
+ Padding = new Thickness(10),
+ Children =
+ {
+ new Label
+ {
+ Text = "Group Header Template (Grid View)",
+ FontSize = 18,
+ AutomationId = "GroupHeaderTemplate",
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ TextColor = Colors.Green
+ }
+ }
+ };
+ });
+ }
+ }
+
+ void OnGroupFooterTemplateChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (_groupFooterTemplateNone.IsChecked)
+ {
+ _viewModel.GroupFooterTemplate = null;
+ }
+ else if (_groupFooterTemplateGrid.IsChecked)
+ {
+ _viewModel.GroupFooterTemplate = new DataTemplate(() =>
+ {
+ return new Grid
+ {
+ BackgroundColor = Colors.LightGray,
+ Padding = new Thickness(10),
+ Children =
+ {
+ new Label
+ {
+ Text = "Group Footer Template (Grid View)",
+ FontSize = 18,
+ AutomationId = "GroupFooterTemplate",
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ TextColor = Colors.Red
+ }
+ }
+ };
+ });
+ }
+ }
+
+ void OnIsGroupedChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (_isGroupedFalse.IsChecked)
+ {
+ _viewModel.IsGrouped = false;
+ }
+ else if (_isGroupedTrue.IsChecked)
+ {
+ _viewModel.IsGrouped = true;
+ }
+ }
+}
+
+public class Issue29141CollectionViewViewModel : INotifyPropertyChanged
+{
+ DataTemplate _groupHeaderTemplate;
+ DataTemplate _groupFooterTemplate;
+ DataTemplate _itemTemplate;
+ bool _isGrouped = false;
+ ObservableCollection _observableCollection;
+ ObservableCollection _stringCollection;
+ bool _useStringItems;
+ DataTemplate _stringItemTemplate;
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public Issue29141CollectionViewViewModel()
+ {
+ LoadItems();
+
+ _stringCollection = new ObservableCollection { "String A", "String B", "String C" };
+ _stringItemTemplate = new DataTemplate(() =>
+ {
+ var label = new Label
+ {
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center
+ };
+ label.SetBinding(Label.TextProperty, ".");
+ return label;
+ });
+
+ ItemTemplate = new DataTemplate(() =>
+ {
+ var stackLayout = new StackLayout
+ {
+ Padding = new Thickness(10),
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ var label = new Label
+ {
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center
+ };
+ label.SetBinding(Label.TextProperty, "Caption");
+ stackLayout.Children.Add(label);
+ return stackLayout;
+ });
+
+ GroupHeaderTemplate = new DataTemplate(() =>
+ {
+ var stackLayout = new StackLayout
+ {
+ BackgroundColor = Colors.LightGray
+ };
+ var label = new Label
+ {
+ FontAttributes = FontAttributes.Bold,
+ FontSize = 18
+ };
+ label.SetBinding(Label.TextProperty, "Key");
+ stackLayout.Children.Add(label);
+ return stackLayout;
+ });
+ }
+
+ void LoadItems()
+ {
+ _observableCollection = new ObservableCollection
+ {
+ new Issue29141ItemModel { Caption = "Item 1" },
+ new Issue29141ItemModel { Caption = "Item 2" },
+ new Issue29141ItemModel { Caption = "Item 3" }
+ };
+ }
+
+ public void SwitchToStringItems()
+ {
+ _useStringItems = true;
+ ItemTemplate = _stringItemTemplate;
+ OnPropertyChanged(nameof(ItemsSource));
+ }
+
+ public void AddItem()
+ {
+ if (_useStringItems)
+ {
+ _stringCollection.Add($"String {_stringCollection.Count + 1}");
+ }
+ else
+ {
+ _observableCollection.Add(new Issue29141ItemModel { Caption = $"Item {_observableCollection.Count + 1}" });
+ }
+ }
+
+ public DataTemplate GroupHeaderTemplate
+ {
+ get => _groupHeaderTemplate;
+ set { _groupHeaderTemplate = value; OnPropertyChanged(); }
+ }
+
+ public DataTemplate GroupFooterTemplate
+ {
+ get => _groupFooterTemplate;
+ set { _groupFooterTemplate = value; OnPropertyChanged(); }
+ }
+
+ public DataTemplate ItemTemplate
+ {
+ get => _itemTemplate;
+ set { _itemTemplate = value; OnPropertyChanged(); }
+ }
+
+ public bool IsGrouped
+ {
+ get => _isGrouped;
+ set { _isGrouped = value; OnPropertyChanged(); }
+ }
+
+ public object ItemsSource
+ {
+ get => _useStringItems ? (object)_stringCollection : _observableCollection;
+ }
+
+ protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ if (propertyName == nameof(IsGrouped))
+ {
+ OnPropertyChanged(nameof(ItemsSource));
+ }
+
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+}
+
+internal class Issue29141Grouping : List
+{
+ public TKey Key { get; }
+
+ public Issue29141Grouping(TKey key, List items) : base(items)
+ {
+ Key = key;
+ }
+
+ public override string ToString()
+ {
+ return Key?.ToString() ?? base.ToString();
+ }
+}
+
+internal class Issue29141ItemModel
+{
+ public string Caption { get; set; }
+
+ public override string ToString()
+ {
+ return !string.IsNullOrEmpty(Caption) ? Caption : base.ToString();
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue29192.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue29192.cs
new file mode 100644
index 000000000000..29b091182801
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue29192.cs
@@ -0,0 +1,48 @@
+using System.Collections.ObjectModel;
+
+namespace Maui.Controls.Sample.Issues;
+[Issue(IssueTracker.Github, 29192, "[Android] CollectionView MeasureFirstItem ItemSizingStrategy Not Applied in Horizontal Layouts", PlatformAffected.Android)]
+public class Issue29192 : ContentPage
+{
+ public Issue29192()
+ {
+ CollectionView collectionView = new CollectionView();
+
+ var layout = new Grid();
+ var bindingContext = new Issue29192ViewModel();
+
+ collectionView.BindingContext = bindingContext;
+ collectionView.AutomationId = "CollectionView";
+ collectionView.ItemSizingStrategy = ItemSizingStrategy.MeasureFirstItem;
+ collectionView.ItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Horizontal);
+
+ collectionView.ItemTemplate = new DataTemplate(() =>
+ {
+ var label = new Label();
+ label.SetBinding(Label.TextProperty, ".");
+ return label;
+ });
+
+ // Set the ItemsSource binding
+ collectionView.SetBinding(CollectionView.ItemsSourceProperty, "Items");
+ layout.Children.Add(collectionView);
+
+ Content = layout;
+ }
+}
+
+
+public class Issue29192ViewModel
+{
+ public ObservableCollection Items { get; set; }
+
+ public Issue29192ViewModel()
+ {
+ Items = new ObservableCollection
+ {
+ "Short text.",
+ "This is a long text; it should be wrapped to avoid truncation or overflow.",
+ "This is very long text."
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue29394.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue29394.cs
new file mode 100644
index 000000000000..f030585e7a07
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue29394.cs
@@ -0,0 +1,68 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 29394, "On Android Shadows should not be rendered over fully transparent areas of drawn shapes", PlatformAffected.Android)]
+public class Issue29394 : TestContentPage
+{
+ protected override void Init()
+ {
+ var verticalStackLayout = new VerticalStackLayout()
+ {
+ Padding = new Thickness(30, 0),
+ Spacing = 25,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ var graphicsView = new GraphicsView()
+ {
+ HeightRequest = 500,
+ WidthRequest = 400,
+ Drawable = new Issue29394_Drawable()
+ };
+ var label = new Label()
+ {
+ Text = "This is a test for Shadows should not be rendered over fully transparent areas of drawn shapes.",
+ AutomationId = "label",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ verticalStackLayout.Children.Add(graphicsView);
+ verticalStackLayout.Children.Add(label);
+ Content = verticalStackLayout;
+ }
+}
+
+public class Issue29394_Drawable : IDrawable
+{
+ Rect fillRect = new Rect(0, 0, 300, 300);
+
+ public void Draw(ICanvas canvas, RectF dirtyRect)
+ {
+ canvas.SaveState();
+
+ float centerX = (dirtyRect.Width - (float)fillRect.Width) / 2;
+ float centerY = (dirtyRect.Height - (float)fillRect.Height) / 2;
+ var outerRect = new RectF(centerX, centerY, (float)fillRect.Width, (float)fillRect.Height);
+
+ canvas.SetFillPaint(Colors.Transparent.AsPaint(), outerRect);
+ canvas.SetShadow(new SizeF(0, 15), 4, Color.FromArgb("#59000000"));
+ canvas.FillEllipse(outerRect.X, outerRect.Y, outerRect.Width, outerRect.Height);
+
+ float arcSize = 250f;
+ float arcX = outerRect.X + (outerRect.Width - arcSize) / 2;
+ float arcY = outerRect.Y + (outerRect.Height - arcSize) / 2;
+
+ canvas.FillColor = Colors.Blue;
+ canvas.FillCircle(arcX + arcSize / 2, arcY + arcSize / 2, 15);
+ canvas.DrawArc(arcX, arcY, arcSize, arcSize, 45, 90, true, false);
+
+ canvas.StrokeColor = Colors.Transparent;
+ canvas.StrokeSize = 2;
+ canvas.DrawLine(0, 25, dirtyRect.Width, 25);
+
+ canvas.FontColor = Colors.Transparent;
+ canvas.DrawString("Shadow should not be Rendered", dirtyRect.Width / 2, 10, HorizontalAlignment.Center);
+
+ canvas.RestoreState();
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue29428.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue29428.cs
new file mode 100644
index 000000000000..a1eaea9b4766
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue29428.cs
@@ -0,0 +1,106 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 29428, "Shell flyout navigation fires NavigatedTo before Loaded event", PlatformAffected.iOS | PlatformAffected.Android)]
+public class Issue29428 : Shell
+{
+ public Issue29428()
+ {
+ var mainPage = new ContentPage
+ {
+ Title = "Main Page",
+ Content = new StackLayout
+ {
+ Padding = new Thickness(20),
+ Children =
+ {
+ new Label
+ {
+ Text = "This is the main page. Use the flyout to navigate to Page 2.",
+ AutomationId = "MainPageLabel"
+ }
+ }
+ }
+ };
+
+ var secondPage = new EventOrderTestPage29428();
+ Items.Add(new FlyoutItem
+ {
+ Title = "Main Page",
+ Items =
+ {
+ new ShellSection
+ {
+ Items = { new ShellContent { Content = mainPage } }
+ }
+ }
+ });
+
+ Items.Add(new FlyoutItem
+ {
+ Title = "Page 2",
+ Items =
+ {
+ new ShellSection
+ {
+ Items = { new ShellContent { Content = secondPage } }
+ }
+ }
+ });
+ }
+
+ public class EventOrderTestPage29428 : ContentPage
+ {
+ private readonly Label _eventOrderLabel;
+
+ public EventOrderTestPage29428()
+ {
+ Title = "SecondPage";
+ AutomationId = "SecondPage";
+ _eventOrderLabel = new Label
+ {
+ AutomationId = "EventOrderLabel",
+ FontSize = 16,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ Content = new StackLayout
+ {
+ Padding = new Thickness(20),
+ Children =
+ {
+ new Label
+ {
+ Text = "Page 2 - Event Order Test",
+ FontSize = 20,
+ HorizontalOptions = LayoutOptions.Center
+ },
+ _eventOrderLabel
+ }
+ };
+
+ Loaded += OnLoaded;
+ NavigatedTo += OnNavigatedTo;
+ }
+
+ private void OnLoaded(object sender, EventArgs e)
+ {
+ _eventOrderLabel.Text = string.IsNullOrEmpty(_eventOrderLabel.Text)
+ ? "Loaded called first"
+ : $"{_eventOrderLabel.Text} then Loaded called";
+ }
+
+ private void OnNavigatedTo(object sender, NavigatedToEventArgs e)
+ {
+ _eventOrderLabel.Text = string.IsNullOrEmpty(_eventOrderLabel.Text)
+ ? "NavigatedTo called first"
+ : $"{_eventOrderLabel.Text} then NavigatedTo called";
+ }
+
+ protected override void OnDisappearing()
+ {
+ _eventOrderLabel.Text = string.Empty;
+ base.OnDisappearing();
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue29484.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue29484.cs
new file mode 100644
index 000000000000..8082763bdd2f
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue29484.cs
@@ -0,0 +1,82 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 29484, "CollectionView Selected state does not work on the selected item when combined with PointerOver", PlatformAffected.UWP | PlatformAffected.macOS)]
+public class Issue29484 : TestContentPage
+{
+ protected override void Init()
+ {
+ Style pointerOverSelectedStyle = CreatePointerOverSelectedItemStyle();
+ DataTemplate pointerOverSelectedItemTemplate = CreateItemTemplate(pointerOverSelectedStyle);
+
+ Grid grid = new Grid
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ RowDefinitions =
+ {
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Star },
+ }
+ };
+
+ grid.Add(new Label { Text = "PointerOverAndSelectedState" }, 0, 0);
+ grid.Add(InitializeCollectionView(pointerOverSelectedItemTemplate), 0, 1);
+ Content = grid;
+ }
+
+ CollectionView InitializeCollectionView(DataTemplate itemTemplate)
+ {
+ return new CollectionView
+ {
+ AutomationId = "CollectionView",
+ ItemTemplate = itemTemplate,
+ SelectionMode = SelectionMode.Single,
+ ItemsSource = new List { "Item 1", "Item 2", "Item 3", "Item 4" }
+ };
+ }
+
+ DataTemplate CreateItemTemplate(Style style)
+ {
+ return new DataTemplate(() =>
+ {
+ Label label = new Label { Style = style };
+ label.SetBinding(Label.TextProperty, ".");
+ return label;
+ });
+ }
+
+ Style CreatePointerOverSelectedItemStyle()
+ {
+ return new Style(typeof(Label))
+ {
+ Setters =
+ {
+ new Setter { Property = BackgroundColorProperty, Value = Colors.Transparent },
+ new Setter
+ {
+ Property = VisualStateManager.VisualStateGroupsProperty,
+ Value = CreateVisualState()
+ }
+ }
+ };
+ }
+
+ VisualStateGroupList CreateVisualState()
+ {
+ VisualStateGroupList groupList = new VisualStateGroupList();
+ VisualStateGroup commonStates = new VisualStateGroup { Name = "CommonStates" };
+
+ VisualState normalState = new VisualState { Name = "Normal" };
+ VisualState pointerOverState = new VisualState { Name = "PointerOver" };
+ VisualState selectedState = new VisualState { Name = "Selected" };
+
+ pointerOverState.Setters.Add(new Setter { Property = BackgroundColorProperty, Value = Colors.DarkTurquoise });
+ selectedState.Setters.Add(new Setter { Property = BackgroundColorProperty, Value = Colors.DarkBlue });
+
+ commonStates.States.Add(pointerOverState);
+ commonStates.States.Add(selectedState);
+ commonStates.States.Add(normalState);
+
+ groupList.Add(commonStates);
+ return groupList;
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue29729.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue29729.cs
new file mode 100644
index 000000000000..d8577e8fcb81
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue29729.cs
@@ -0,0 +1,53 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 29729, "RadioButton TextTransform Property Does Not Apply on Android and Windows Platforms", PlatformAffected.All)]
+public class Issue29729 : ContentPage
+{
+ RadioButton radioButton;
+
+ public Issue29729()
+ {
+ radioButton = new RadioButton
+ {
+ Content = "HelloWorld",
+ AutomationId = "radioButton"
+ };
+
+ var button = new Button
+ {
+ Text = "Lower",
+ AutomationId = "LowerCaseButton"
+ };
+ button.Clicked += OnLowerButtonClicked;
+
+ var button1 = new Button
+ {
+ Text = "Upper",
+ AutomationId = "UpperCaseButton"
+ };
+
+ button1.Clicked += OnUpperButtonClicked;
+
+ var layout = new VerticalStackLayout
+ {
+ Children =
+ {
+ radioButton,
+ button,
+ button1
+ }
+ };
+
+ Content = layout;
+ }
+
+ void OnLowerButtonClicked(object sender, EventArgs e)
+ {
+ radioButton.TextTransform = TextTransform.Lowercase;
+ }
+
+ void OnUpperButtonClicked(object sender, EventArgs e)
+ {
+ radioButton.TextTransform = TextTransform.Uppercase;
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue29764.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue29764.cs
new file mode 100644
index 000000000000..e2ec526e3d3d
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue29764.cs
@@ -0,0 +1,56 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 29764, "Shadows disappearing permanently in Android and Windows after Label opacity is at any time set to 0", PlatformAffected.All)]
+public class Issue29764 : ContentPage
+{
+ public Issue29764()
+ {
+ var mainLayout = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Padding = new Thickness(20)
+ };
+ Content = mainLayout;
+
+ var shadowLabel = new Label
+ {
+ Text = "HELLO",
+ FontSize = 80,
+ Shadow = new Shadow { Brush = Colors.DarkRed, Radius = 10, Offset = new Point(0, 4) },
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ VisualElement labelToAnimate = shadowLabel;
+ labelToAnimate.Opacity = 0;
+
+ var mainButton = new Button
+ {
+ AutomationId = "MainButton",
+ Text = "Update Label Opacity",
+ };
+ mainButton.Clicked += (sender, e) =>
+ {
+ var timer = Application.Current.Dispatcher.CreateTimer();
+ double deltaTime = 1 / 60.0;
+ double fadeDuration = 0.5;
+
+ timer.Tick += delegate (object s, EventArgs e)
+ {
+ if (labelToAnimate.Opacity < 1)
+ {
+ labelToAnimate.Opacity += deltaTime / fadeDuration;
+ }
+ if (labelToAnimate.Opacity > 1)
+ {
+ labelToAnimate.Opacity = 1;
+ }
+ };
+
+ timer.Interval = TimeSpan.FromSeconds(deltaTime);
+ timer.Start();
+ };
+
+ mainLayout.Add(mainButton);
+ mainLayout.Add(shadowLabel);
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue29921.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue29921.cs
new file mode 100644
index 000000000000..95c10469bde4
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue29921.cs
@@ -0,0 +1,63 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 29921, "Flyout icon not replaced after root page change", PlatformAffected.iOS)]
+public class Issue29921 : FlyoutPage
+{
+ public Issue29921()
+ {
+ Detail = new NavigationPage(new Issue29921Page1());
+ Flyout = new ContentPage
+ {
+ Title = "Flyout Page",
+ IconImageSource = "menu_icon.png",
+ Content = new Label
+ {
+ Text = "This is flyout",
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center
+ }
+ };
+ FlyoutLayoutBehavior = FlyoutLayoutBehavior.Popover;
+ }
+}
+
+public class Issue29921Page1 : ContentPage
+{
+ public Issue29921Page1()
+ {
+ Title = "Page 1";
+ var insertBeforeButton = new Button { Text = "Insert Page Before", AutomationId = "InsertPageButton" };
+ insertBeforeButton.Clicked += InsertBefore_Clicked;
+ Content = new StackLayout
+ {
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ Children = { insertBeforeButton }
+ };
+ }
+
+ private void InsertBefore_Clicked(object sender, EventArgs e)
+ {
+ if (Parent is NavigationPage navPage)
+ {
+ navPage.Navigation.InsertPageBefore(new Issue29921Page2(), navPage.RootPage);
+ }
+ }
+}
+
+public class Issue29921Page2 : ContentPage
+{
+ public Issue29921Page2()
+ {
+ Title = "Page 2";
+ Content = new StackLayout
+ {
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ Children =
+ {
+ new Label { Text = "Flyout icon is visible", AutomationId = "Page2Label" },
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue29930.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue29930.cs
new file mode 100644
index 000000000000..98a55a9719ee
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue29930.cs
@@ -0,0 +1,62 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 29930, "[Windows] Setting a ContentView with a content of StaticResource Style Causes a System.Runtime.InteropServices.COMException.", PlatformAffected.UWP)]
+public class Issue29930 : ContentPage
+{
+ ContentView _mainContentView;
+ public Issue29930()
+ {
+ var button = new Button
+ {
+ Text = "Add ContentView",
+ HorizontalOptions = LayoutOptions.Center,
+ AutomationId = "ChangeInnerContent"
+ };
+ button.Clicked += OnButtonClicked;
+
+ _mainContentView = new ContentView();
+ _mainContentView.Content = new Issue29930InnerContentView();
+
+ var layout = new VerticalStackLayout
+ {
+ Spacing = 25,
+ Padding = new Thickness(30, 0),
+ VerticalOptions = LayoutOptions.Center,
+ Children =
+ {
+ button,
+ _mainContentView
+ }
+ };
+
+ Content = layout;
+ }
+ private void OnButtonClicked(object sender, EventArgs e)
+ {
+ _mainContentView.Content = new Issue29930InnerContentView() { BackgroundColor = Colors.Red };
+ }
+}
+
+public class Issue29930InnerContentView : ContentView
+{
+ public Issue29930InnerContentView()
+ {
+ Application.Current.Resources ??= new ResourceDictionary();
+
+ if (!Application.Current.Resources.ContainsKey("SubContentStyle"))
+ {
+ Application.Current.Resources["SubContentStyle"] = new Style(typeof(ContentView))
+ {
+ Setters =
+ {
+ new Setter
+ {
+ Property = ContentView.ContentProperty,
+ Value = new Label { Text = "SubContent" }
+ }
+ }
+ };
+ }
+ Style = (Style)Application.Current.Resources["SubContentStyle"];
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue30004.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue30004.cs
new file mode 100644
index 000000000000..f0f13b54101f
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue30004.cs
@@ -0,0 +1,20 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 30004, "FontImageSource not center-aligned inside Image control", PlatformAffected.UWP)]
+public class Issue30004 : ContentPage
+{
+ public Issue30004()
+ {
+ Content = new StackLayout()
+ {
+ Children =
+ {
+ new Label(){Text = "FontAwesome", AutomationId="FontImage", HorizontalOptions = LayoutOptions.Center, FontSize = 20, Margin = new Thickness(0, 20, 0, 20)},
+ new Image() { Source = new FontImageSource() { FontFamily = "FA", Glyph = "\xf7a4", Color = Colors.Black, Size = 50}, Margin = 4, Background= Colors.Red, WidthRequest=100, HeightRequest=100, HorizontalOptions = LayoutOptions.Center},
+
+ new Label(){Text = "ionicons", HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center, FontSize = 20, Margin = new Thickness(0, 20, 0, 20)},
+ new Image() { Source = new FontImageSource() { FontFamily = "Ion",Glyph = "\uf47e", Color = Colors.Black, Size = 50}, Background= Colors.Red,WidthRequest=100, HeightRequest=100, HorizontalOptions = LayoutOptions.Center},
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue30363.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue30363.cs
new file mode 100644
index 000000000000..51ea9b0bc779
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue30363.cs
@@ -0,0 +1,60 @@
+using System.Collections.ObjectModel;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 30363, "[iOS] CollectionView does not clear selection when SelectedItem is set to null", PlatformAffected.iOS)]
+public class Issue30363 : ContentPage
+{
+ public Issue30363()
+ {
+ ObservableCollection Items = new ObservableCollection
+ {
+ "Item 1",
+ "Item 2",
+ "Item 3",
+ "Item 4",
+ "Item 5"
+ };
+
+
+ var collectionView = new CollectionView
+ {
+ ItemsSource = Items,
+ SelectionMode = SelectionMode.Single,
+ ItemTemplate = new DataTemplate(() =>
+ {
+ var label = new Label
+ {
+ FontSize = 24,
+ HorizontalOptions = LayoutOptions.FillAndExpand,
+ VerticalOptions = LayoutOptions.Center,
+ AutomationId = "cvItem"
+ };
+ label.SetBinding(Label.TextProperty, ".");
+
+ return new Grid
+ {
+ Children = { label }
+ };
+ })
+ };
+
+ collectionView.SelectionChanged += (sender, e) =>
+ {
+ if (e.CurrentSelection.Count > 0)
+ {
+ var selectedItem = e.CurrentSelection[0] as string;
+ if (selectedItem != null)
+ {
+ // Deselect the item
+ collectionView.SelectedItem = null;
+ }
+ }
+ };
+
+ var grid = new Grid();
+ grid.Children.Add(collectionView);
+
+ Content = grid;
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue30837.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue30837.cs
new file mode 100644
index 000000000000..521b904ac2b4
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue30837.cs
@@ -0,0 +1,35 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 30837, "[iOS] TimePicker AM/PM frequently changes when the app is closed and reopened", PlatformAffected.iOS)]
+public class Issue30837 : ContentPage
+{
+ public Issue30837()
+ {
+ var label = new Label
+ {
+ Text = "Select Time:",
+ FontSize = 16,
+ AutomationId = "TimePickerLabel",
+ Margin = new Thickness(0, 0, 0, 5)
+ };
+
+ var timePicker = new TimePicker
+ {
+ Time = new TimeSpan(12, 0, 0),
+ Format = "hh:mm tt"
+ };
+
+ var layout = new StackLayout
+ {
+ Padding = new Thickness(20),
+ Children =
+ {
+ label,
+ timePicker
+ }
+ };
+
+ Content = layout;
+ }
+
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue30888.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue30888.cs
new file mode 100644
index 000000000000..a18f079a14aa
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue30888.cs
@@ -0,0 +1,57 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 30888, "Flyout page toolbar items not rendered on iOS", PlatformAffected.iOS | PlatformAffected.macOS)]
+public class Issue30888 : TestFlyoutPage
+{
+ protected override void Init()
+ {
+ var flyoutPage = new ContentPage
+ {
+ Title = "Flyout",
+ BackgroundColor = Colors.Blue
+ };
+
+ flyoutPage.ToolbarItems.Add(new ToolbarItem
+ {
+ Text = "FlyoutItem",
+ });
+
+ flyoutPage.Content = new Grid
+ {
+ Children = {
+ new Label
+ {
+ Text = "Flyout Content",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ },
+ }
+ };
+
+ var detailPage = new ContentPage
+ {
+ Title = "Detail",
+ };
+
+ detailPage.ToolbarItems.Add(new ToolbarItem
+ {
+ Text = "DetailItem",
+ });
+
+ detailPage.Content = new Grid
+ {
+ Children = {
+ new Label
+ {
+ Text = "Detail Content",
+ AutomationId = "DetailContent",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ },
+ }
+ };
+
+ Flyout = flyoutPage;
+ Detail = new NavigationPage(detailPage);
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue31109.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue31109.cs
new file mode 100644
index 000000000000..a75211187f51
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue31109.cs
@@ -0,0 +1,78 @@
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 31109, "FlexLayout items with Dynamic Width are not updating correctly on orientation change or scroll in Android", PlatformAffected.Android)]
+ public class Issue31109 : TestContentPage
+ {
+ FlexLayout _flexLayout;
+ Label _statusLabel;
+ double _currentWidth = 100;
+
+ protected override void Init()
+ {
+ _flexLayout = new FlexLayout
+ {
+ Direction = Microsoft.Maui.Layouts.FlexDirection.Row,
+ Wrap = Microsoft.Maui.Layouts.FlexWrap.Wrap,
+ BackgroundColor = Colors.LightGray,
+ };
+
+ var item1 = CreateItem("Item1", 100);
+ var item2 = CreateItem("Item2", 150);
+ var item3 = CreateItem("Item3", 200);
+
+ _flexLayout.Add(item1);
+ _flexLayout.Add(item2);
+ _flexLayout.Add(item3);
+
+ _statusLabel = new Label
+ {
+ AutomationId = "StatusLabel",
+ Text = "Width: 100, 150, 200",
+ };
+
+ var changeButton = new Button
+ {
+ Text = "Change Widths",
+ AutomationId = "ChangeWidthsButton",
+ };
+ changeButton.Clicked += OnChangeWidths;
+
+ Content = new ScrollView
+ {
+ Content = new VerticalStackLayout
+ {
+ _statusLabel,
+ changeButton,
+ _flexLayout,
+ }
+ };
+ }
+
+ BoxView CreateItem(string automationId, double width)
+ {
+ return new BoxView
+ {
+ AutomationId = automationId,
+ WidthRequest = width,
+ HeightRequest = 50,
+ Color = Colors.CornflowerBlue,
+ Margin = 5,
+ };
+ }
+
+ void OnChangeWidths(object sender, EventArgs e)
+ {
+ _currentWidth += 50;
+
+ foreach (var child in _flexLayout.Children)
+ {
+ if (child is BoxView box)
+ {
+ box.WidthRequest = _currentWidth;
+ }
+ }
+
+ _statusLabel.Text = $"Width: {_currentWidth}";
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue31121.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue31121.cs
new file mode 100644
index 000000000000..22ec3b39f8f9
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue31121.cs
@@ -0,0 +1,97 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 31121, "[iOS, Mac] TabbedPage FlowDirection Property Renders Opposite Layout Direction When Set via ViewModel Binding", PlatformAffected.iOS | PlatformAffected.macOS)]
+public class Issue31121 : TestTabbedPage
+{
+ Issue31121_ViewModel _viewModel;
+ protected override void Init()
+ {
+ _viewModel = new Issue31121_ViewModel();
+ BindingContext = _viewModel;
+ this.SetBinding(FlowDirectionProperty, new Binding(nameof(Issue31121_ViewModel.FlowDirection)));
+ Children.Add(new Issue31121_FirstTab(_viewModel));
+ Children.Add(new Issue31121_SecondTab());
+ }
+}
+
+public class Issue31121_FirstTab : ContentPage
+{
+ public Issue31121_FirstTab(Issue31121_ViewModel vm)
+ {
+ Title = "FlowDirection Test";
+ BindingContext = vm;
+
+ var leftToRightButton = new Button
+ {
+ Text = "Set LeftToRight",
+ BackgroundColor = Colors.Blue,
+ TextColor = Colors.White
+ };
+ leftToRightButton.AutomationId = "LeftToRightButton";
+ leftToRightButton.Clicked += (s, e) =>
+ {
+ vm.FlowDirection = FlowDirection.LeftToRight;
+ };
+
+ var rightToLeftButton = new Button
+ {
+ Text = "Set RightToLeft",
+ BackgroundColor = Colors.Green,
+ TextColor = Colors.White
+ };
+ rightToLeftButton.AutomationId = "RightToLeftButton";
+ rightToLeftButton.Clicked += (s, e) =>
+ {
+ vm.FlowDirection = FlowDirection.RightToLeft;
+ };
+
+ var currentDirectionLabel = new Label
+ {
+ Text = "Current FlowDirection will be shown here"
+ };
+ currentDirectionLabel.SetBinding(Label.TextProperty, new Binding(nameof(Issue31121_ViewModel.FlowDirection), stringFormat: "Current: {0}"));
+
+ var instructionLabel = new Label
+ {
+ Text = "Test FlowDirection binding on TabbedPage. The tabs should move to reflect the FlowDirection change.",
+ FontSize = 14
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Children =
+ {
+ instructionLabel,
+ currentDirectionLabel,
+ leftToRightButton,
+ rightToLeftButton
+ }
+ };
+ }
+}
+
+public class Issue31121_SecondTab : ContentPage
+{
+ public Issue31121_SecondTab()
+ {
+ Title = "Second Tab";
+ }
+}
+
+public class Issue31121_ViewModel : BindableObject
+{
+ FlowDirection _flowDirection = FlowDirection.RightToLeft;
+ public FlowDirection FlowDirection
+ {
+ get => _flowDirection;
+ set
+ {
+ if (_flowDirection != value)
+ {
+ _flowDirection = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue31140.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue31140.cs
new file mode 100644
index 000000000000..1c8b931fdf55
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue31140.cs
@@ -0,0 +1,66 @@
+using System.Collections.ObjectModel;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 31140, "Setting both IndicatorSize and Shadow properties on IndicatorView causes some dots to be invisible", PlatformAffected.iOS | PlatformAffected.macOS)]
+public class Issue31140 : ContentPage
+{
+ public Issue31140()
+ {
+ var verticalStackLayout = new VerticalStackLayout();
+ var carouselItems = new ObservableCollection
+ {
+ "Item 0",
+ "Item 1",
+ "Item 2",
+ };
+
+ CarouselView carouselView = new CarouselView
+ {
+ ItemsSource = carouselItems,
+ AutomationId = "carouselview",
+ HeightRequest = 300,
+ ItemTemplate = new DataTemplate(() =>
+ {
+ var grid = new Grid
+ {
+ Padding = 10
+ };
+
+ var label = new Label
+ {
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ FontSize = 18,
+ };
+ label.SetBinding(Label.TextProperty, ".");
+ label.SetBinding(Label.AutomationIdProperty, ".");
+
+ grid.Children.Add(label);
+ return grid;
+ }),
+ HorizontalOptions = LayoutOptions.Fill,
+ };
+
+ var indicatorView = new IndicatorView
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ IndicatorSize = 30,
+ SelectedIndicatorColor = Colors.Blue,
+ IndicatorColor = Colors.Orange,
+ };
+ indicatorView.Shadow = new Shadow
+ {
+ Radius = 10,
+ Opacity = 0.7f,
+ Brush = Colors.Black,
+ Offset = new Point(5, 5)
+ };
+ carouselView.IndicatorView = indicatorView;
+
+ verticalStackLayout.Children.Add(carouselView);
+ verticalStackLayout.Children.Add(indicatorView);
+ Content = verticalStackLayout;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue31145.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue31145.cs
new file mode 100644
index 000000000000..58d3f8d75071
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue31145.cs
@@ -0,0 +1,57 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 31145, "MaximumVisible Property Not Working with IndicatorTemplate in IndicatorView", PlatformAffected.All)]
+public class Issue31145 : ContentPage
+{
+ public Issue31145()
+ {
+ Label descriptionLabel = new Label
+ {
+ Text = "The test passes if the IndicatorView with an IndicatorTemplate respects the MaximumVisible property; otherwise, it fails.",
+ FontSize = 18,
+ HorizontalOptions = LayoutOptions.Center,
+ };
+
+ IndicatorView indicatorView = new IndicatorView
+ {
+ ItemsSource = new List { "Item1", "Item2", "Item3", "Item4", "Item5" },
+ IndicatorColor = Colors.Yellow,
+ SelectedIndicatorColor = Colors.Gray,
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ indicatorView.IndicatorTemplate = new DataTemplate(() =>
+ {
+ return new BoxView
+ {
+ WidthRequest = 10,
+ HeightRequest = 10,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+ });
+
+ Button updateMaximumVisibleBtn = new Button
+ {
+ AutomationId = "UpdateMaximumVisibleBtn",
+ Text = "Set MaximumVisible Property to 2"
+ };
+
+ updateMaximumVisibleBtn.Clicked += (s, e) =>
+ {
+ indicatorView.MaximumVisible = 2;
+ };
+
+ Content = new StackLayout
+ {
+ Padding = 20,
+ Spacing = 20,
+ Children =
+ {
+ descriptionLabel,
+ indicatorView,
+ updateMaximumVisibleBtn
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue31239.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue31239.cs
new file mode 100644
index 000000000000..c70057f5cddd
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue31239.cs
@@ -0,0 +1,120 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 31239, "[iOS, Mac, Windows] GraphicsView does not change the Background/BackgroundColor", PlatformAffected.iOS | PlatformAffected.macOS | PlatformAffected.UWP)]
+public class Issue31239 : TestContentPage
+{
+ GraphicsView _backgroundColorGraphicsView;
+ GraphicsView _backgroundGraphicsView;
+
+ public Issue31239()
+ {
+
+ }
+
+ protected override void Init()
+ {
+ _backgroundColorGraphicsView = new GraphicsView()
+ {
+ HeightRequest = 200,
+ WidthRequest = 200,
+ BackgroundColor = Colors.Red,
+ Drawable = new Issue31239_Drawable()
+ };
+
+ _backgroundGraphicsView = new GraphicsView()
+ {
+ HeightRequest = 200,
+ WidthRequest = 200,
+ Background = new LinearGradientBrush
+ {
+ StartPoint = new Point(0, 0),
+ EndPoint = new Point(1, 1),
+ GradientStops =
+ [
+ new GradientStop { Color = Colors.Blue, Offset = 0.0f },
+ new GradientStop { Color = Colors.Green, Offset = 1.0f }
+ ]
+ },
+ Drawable = new Issue31239_Drawable()
+ };
+
+ var backgroundColorLabel = new Label
+ {
+ Text = "BackgroundColor",
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ var backgroundBrushLabel = new Label
+ {
+ Text = "Background",
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ Grid.SetRow(backgroundColorLabel, 0);
+ Grid.SetRow(_backgroundColorGraphicsView, 1);
+ Grid.SetRow(backgroundBrushLabel, 2);
+ Grid.SetRow(_backgroundGraphicsView, 3);
+
+ var grid = new Grid
+ {
+ RowDefinitions =
+ {
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Auto }
+ },
+ Children = { backgroundColorLabel, backgroundBrushLabel, _backgroundColorGraphicsView, _backgroundGraphicsView }
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Padding = 20,
+ Children =
+ {
+ new Label
+ {
+ Text = "This test verifies that GraphicsView Background and BackgroundColor properties are properly applied and can be changed dynamically.",
+ FontSize = 14,
+ HorizontalOptions = LayoutOptions.Center,
+ HorizontalTextAlignment = TextAlignment.Center
+ },
+
+ grid,
+
+ new Button
+ {
+ Text = "Change Background Properties",
+ AutomationId = "changeBackgroundButton",
+ Command = new Command(ChangeBackgroundProperties)
+ }
+ }
+ };
+ }
+
+ void ChangeBackgroundProperties()
+ {
+ _backgroundColorGraphicsView.BackgroundColor = Colors.Purple;
+
+ _backgroundGraphicsView.Background = new LinearGradientBrush
+ {
+ StartPoint = new Point(0, 0),
+ EndPoint = new Point(0, 1),
+ GradientStops =
+ [
+ new GradientStop { Color = Colors.Orange, Offset = 0.0f },
+ new GradientStop { Color = Colors.Pink, Offset = 1.0f }
+ ]
+ };
+ }
+}
+
+class Issue31239_Drawable : IDrawable
+{
+ public void Draw(ICanvas canvas, RectF dirtyRect)
+ {
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue31551.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue31551.cs
new file mode 100644
index 000000000000..a22759572088
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue31551.cs
@@ -0,0 +1,109 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 31551, "ArgumentOutOfRangeException thrown by ScrollTo when group index is invalid", PlatformAffected.Android)]
+public class Issue31551 : ContentPage
+{
+ CollectionView _collectionView;
+ public List Animals { get; set; } = new List();
+ const int VALID_ITEM_INDEX = 1;
+ const int INVALID_GROUP_INDEX = 10;
+ public Issue31551()
+ {
+ CreateAnimalsCollection();
+
+ Button scrollButton = new Button
+ {
+ AutomationId = "Issue31551ScrollBtn",
+ Text = "Scroll to Invalid Group"
+ };
+ scrollButton.Clicked += ScrollButton_Clicked;
+
+ _collectionView = new CollectionView
+ {
+ AutomationId = "Issue31551CollectionView",
+ IsGrouped = true,
+ ItemsSource = Animals,
+ GroupHeaderTemplate = new DataTemplate(() =>
+ {
+ Label label = new Label
+ {
+ FontAttributes = FontAttributes.Bold,
+ BackgroundColor = Colors.LightGray,
+ };
+
+ label.SetBinding(Label.TextProperty, "Name");
+ return label;
+ }),
+ ItemTemplate = new DataTemplate(() =>
+ {
+ Label textLabel = new Label
+ {
+ FontSize = 20,
+ HeightRequest = 40,
+ FontAttributes = FontAttributes.Bold,
+ };
+
+ textLabel.SetBinding(Label.TextProperty, ".");
+ return textLabel;
+ })
+ };
+
+ Grid grid = new Grid
+ {
+ RowSpacing = 10,
+ Padding = 10,
+ RowDefinitions =
+ {
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Star },
+ }
+ };
+
+ grid.Add(scrollButton, 0, 0);
+ grid.Add(_collectionView, 0, 1);
+
+ Content = grid;
+ }
+
+ void CreateAnimalsCollection()
+ {
+ Animals.Add(new Issue31551AnimalGroup("Bears", new List
+ {
+ "American Black Bear",
+ "Asian Black Bear",
+ "Brown Bear",
+ "Polar Bear"
+ }));
+
+ Animals.Add(new Issue31551AnimalGroup("Cats", new List
+ {
+ "American Shorthair",
+ "Bengal",
+ "Sphynx",
+ "Persian"
+ }));
+
+ Animals.Add(new Issue31551AnimalGroup("Dogs", new List
+ {
+ "German Shepherd",
+ "Golden Retriever",
+ "Bulldog",
+ "Poodle"
+ }));
+ }
+
+ private void ScrollButton_Clicked(object sender, EventArgs e)
+ {
+ _collectionView.ScrollTo(VALID_ITEM_INDEX, INVALID_GROUP_INDEX, ScrollToPosition.Start, true);
+ }
+}
+
+public class Issue31551AnimalGroup : List
+{
+ public string Name { get; set; }
+
+ public Issue31551AnimalGroup(string name, IEnumerable items) : base(items)
+ {
+ Name = name;
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32016.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32016.cs
new file mode 100644
index 000000000000..c47d8556c6fc
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32016.cs
@@ -0,0 +1,15 @@
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 32016, "iOS 26 MaxLength not enforced on Entry", PlatformAffected.iOS)]
+ public class Issue32016 : ContentPage
+ {
+ public Issue32016()
+ {
+ Content = new Entry()
+ {
+ AutomationId = "TestEntry",
+ MaxLength = 10,
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32200.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue32200.xaml
new file mode 100644
index 000000000000..1d19d535b74a
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32200.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32200.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32200.xaml.cs
new file mode 100644
index 000000000000..3ca1badf773b
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32200.xaml.cs
@@ -0,0 +1,15 @@
+namespace Maui.Controls.Sample.Issues;
+
+[XamlCompilation(XamlCompilationOptions.Compile)]
+[Issue(IssueTracker.Github, 32200, "NavigationPage TitleView iOS 26", PlatformAffected.iOS)]
+public class Issue32200NavigationPage : NavigationPage
+{
+ public Issue32200NavigationPage() : base(new Issue32200()) { }
+}
+public partial class Issue32200 : ContentPage
+{
+ public Issue32200()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32243.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32243.cs
new file mode 100644
index 000000000000..be1abd98f289
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32243.cs
@@ -0,0 +1,315 @@
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 32243, "CollectionView does not disconnect handlers when DataTemplateSelector changes template", PlatformAffected.Android | PlatformAffected.UWP)]
+public class Issue32243 : NavigationPage
+{
+ public Issue32243() : base(new _Issue32243MainPage())
+ {
+ _Issue32243TrackingLabel.ResetAll();
+ }
+}
+
+class _Issue32243MainPage : ContentPage
+{
+ readonly Label _handlerCountLabel;
+ readonly Label _statusLabel;
+
+ public _Issue32243MainPage()
+ {
+ Title = "Issue 32243";
+
+ _statusLabel = new Label
+ {
+ Text = "Navigate to the CollectionView page, switch templates, go back, then check handlers.",
+ AutomationId = "StatusLabel"
+ };
+
+ _handlerCountLabel = new Label
+ {
+ Text = "Connected handlers will be shown here.",
+ AutomationId = "HandlerCountLabel"
+ };
+
+ var navigateButton = new Button
+ {
+ Text = "Navigate to template page",
+ AutomationId = "NavigateButton"
+ };
+
+ navigateButton.Clicked += async (sender, args) =>
+ {
+ await Navigation.PushAsync(new _Issue32243CollectionPage());
+ };
+
+ var checkHandlersButton = new Button
+ {
+ Text = "Show connected handlers",
+ AutomationId = "CheckHandlersButton"
+ };
+
+ checkHandlersButton.Clicked += (sender, args) =>
+ {
+ var labelsWithHandlers = _Issue32243TrackingLabel.GetLabelsWithConnectedHandlers();
+
+ if (labelsWithHandlers.Count == 0)
+ {
+ _handlerCountLabel.Text = "✓ No labels have connected handlers (all cleaned up!)";
+ _handlerCountLabel.TextColor = Colors.Green;
+ _statusLabel.Text = "Status: No connected handlers found.";
+ }
+ else
+ {
+ var details = string.Join("\n", labelsWithHandlers.Select(label =>
+ $"Label #{label.InstanceId} - Text: '{label.Text}'"));
+
+ _handlerCountLabel.Text = $"⚠️ {labelsWithHandlers.Count} labels still have connected handlers:\n{details}";
+ _handlerCountLabel.TextColor = Colors.Brown;
+ _statusLabel.Text = "Status: Connected handlers are still present.";
+ }
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 10,
+ Children =
+ {
+ new Label
+ {
+ Text = "This test mirrors the sandbox flow: navigate, switch templates, navigate back, then verify disconnected handlers."
+ },
+ navigateButton,
+ checkHandlersButton,
+ _statusLabel,
+ _handlerCountLabel
+ }
+ };
+ }
+}
+
+class _Issue32243CollectionPage : ContentPage
+{
+ readonly List<_Issue32243Item> _items;
+ readonly CollectionView _collectionView;
+ readonly Label _statusLabel;
+
+ public _Issue32243CollectionPage()
+ {
+ Title = "Template Page";
+
+ _items = Enumerable.Range(1, 50).Select(i => new _Issue32243Item
+ {
+ Name = $"Item {i}",
+ UseTemplateA = i % 2 == 1
+ }).ToList();
+
+ var templateA = new DataTemplate(() =>
+ {
+ var label = new _Issue32243TrackingLabel();
+ label.SetBinding(Label.TextProperty, nameof(_Issue32243Item.Name));
+ return new VerticalStackLayout
+ {
+ BackgroundColor = Colors.LightBlue,
+ Padding = new Thickness(10),
+ Children =
+ {
+ label,
+ new Label { Text = "Template A", TextColor = Colors.Blue }
+ }
+ };
+ });
+
+ var templateB = new DataTemplate(() =>
+ {
+ var label = new Label();
+ label.SetBinding(Label.TextProperty, nameof(_Issue32243Item.Name));
+ return new VerticalStackLayout
+ {
+ BackgroundColor = Colors.LightGray,
+ Padding = new Thickness(10),
+ Children =
+ {
+ label,
+ new Label { Text = "Template B", TextColor = Colors.Gray }
+ }
+ };
+ });
+
+ _statusLabel = new Label
+ {
+ Text = "Status: Mixed templates active",
+ AutomationId = "TemplatePageStatusLabel"
+ };
+
+ var switchTemplateButton = new Button
+ {
+ Text = "Switch to all Template B",
+ AutomationId = "SwitchTemplateButton"
+ };
+
+ switchTemplateButton.Clicked += (sender, args) =>
+ {
+ foreach (var item in _items)
+ {
+ item.UseTemplateA = false;
+ }
+
+ _collectionView.ItemsSource = _items.ToList();
+ _statusLabel.Text = "Status: All items using Template B";
+ };
+
+ var navigateBackButton = new Button
+ {
+ Text = "Navigate back",
+ AutomationId = "NavigateBackButton"
+ };
+
+ navigateBackButton.Clicked += async (sender, args) =>
+ {
+ await Navigation.PopAsync();
+ };
+
+ _collectionView = new CollectionView
+ {
+ AutomationId = "ItemsCollectionView",
+ SelectionMode = SelectionMode.None,
+ ItemTemplate = new _Issue32243TemplateSelector
+ {
+ TemplateA = templateA,
+ TemplateB = templateB
+ },
+ ItemsSource = _items
+ };
+
+ Content = new Grid
+ {
+ RowDefinitions =
+ {
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Star }
+ },
+ Children =
+ {
+ new VerticalStackLayout
+ {
+ Padding = new Thickness(20, 20, 20, 10),
+ Spacing = 8,
+ Children =
+ {
+ new Label { Text = "50 items — odd=Template A (blue), even=Template B (gray)" },
+ switchTemplateButton,
+ navigateBackButton,
+ _statusLabel
+ }
+ },
+ _collectionView
+ }
+ };
+
+ Grid.SetRow(_collectionView, 1);
+ }
+}
+
+class _Issue32243TrackingLabel : Label
+{
+ static int _instanceCounter;
+ static readonly List> _allInstances = [];
+ readonly int _instanceId;
+
+ public _Issue32243TrackingLabel()
+ {
+ _instanceId = ++_instanceCounter;
+ _allInstances.Add(new WeakReference<_Issue32243TrackingLabel>(this));
+ DeviceDisplay.MainDisplayInfoChanged += OnMainDisplayInfoChanged;
+ }
+
+ protected override void OnHandlerChanged()
+ {
+ base.OnHandlerChanged();
+
+ if (Handler is null)
+ {
+ DeviceDisplay.MainDisplayInfoChanged -= OnMainDisplayInfoChanged;
+ }
+ }
+
+ public int InstanceId => _instanceId;
+
+ public static List<_Issue32243TrackingLabel> GetLabelsWithConnectedHandlers()
+ {
+ var result = new List<_Issue32243TrackingLabel>();
+
+ _allInstances.RemoveAll(wr =>
+ {
+ if (wr.TryGetTarget(out var label))
+ {
+ if (label.Handler != null)
+ {
+ result.Add(label);
+ }
+
+ return false;
+ }
+
+ return true;
+ });
+
+ return result;
+ }
+
+ void OnMainDisplayInfoChanged(object sender, DisplayInfoChangedEventArgs e)
+ {
+ _ = _instanceId;
+ }
+
+ public static void ResetAll()
+ {
+ _allInstances.Clear();
+ _instanceCounter = 0;
+ }
+}
+
+class _Issue32243TemplateSelector : DataTemplateSelector
+{
+ public DataTemplate TemplateA { get; set; }
+ public DataTemplate TemplateB { get; set; }
+
+ protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
+ {
+ return item is _Issue32243Item { UseTemplateA: true } ? TemplateA : TemplateB;
+ }
+}
+
+class _Issue32243Item : INotifyPropertyChanged
+{
+ string _name = string.Empty;
+ bool _useTemplateA;
+
+ public string Name
+ {
+ get => _name;
+ set
+ {
+ _name = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public bool UseTemplateA
+ {
+ get => _useTemplateA;
+ set
+ {
+ _useTemplateA = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected void OnPropertyChanged([CallerMemberName] string name = null)
+ => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32266.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32266.cs
new file mode 100644
index 000000000000..feb6c3c864a1
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32266.cs
@@ -0,0 +1,52 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 32266, "TimePicker on Windows Defaults to Midnight When Time Value Is Null", PlatformAffected.UWP)]
+public class Issue32266 : ContentPage
+{
+ TimePicker timePicker;
+ Button clearTimeButton;
+ Button setTimeButton;
+
+ public Issue32266()
+ {
+ timePicker = new TimePicker
+ {
+ AutomationId = "Issue32266TimePicker",
+ Time = null,
+ Format = "hh:mm"
+ };
+
+ setTimeButton = new Button
+ {
+ AutomationId = "SetTimeButton",
+ Text = "Set Time to 6:00 AM"
+ };
+ setTimeButton.Clicked += SetTime_Clicked;
+
+ clearTimeButton = new Button
+ {
+ AutomationId = "ClearTimeButton",
+ Text = "Clear Time (Set to Null)"
+ };
+ clearTimeButton.Clicked += ClearTime_Clicked;
+
+ VerticalStackLayout stackLayout = new VerticalStackLayout
+ {
+ Padding = 25,
+ Spacing = 10,
+ Children = { timePicker, setTimeButton, clearTimeButton }
+ };
+
+ Content = stackLayout;
+ }
+
+ void SetTime_Clicked(object sender, EventArgs e)
+ {
+ timePicker.Time = new TimeSpan(6, 0, 0);
+ }
+
+ void ClearTime_Clicked(object sender, EventArgs e)
+ {
+ timePicker.Time = null;
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32316.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32316.cs
new file mode 100644
index 000000000000..af473e79626b
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32316.cs
@@ -0,0 +1,101 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 32316, "RTL mode: Padding for the label is not mirroring properly", PlatformAffected.iOS | PlatformAffected.Android)]
+public class Issue32316 : ContentPage
+{
+ private readonly HorizontalStackLayout _row1;
+ private readonly HorizontalStackLayout _row2;
+
+ public Issue32316()
+ {
+ var toggleButton = new Button
+ {
+ Text = "Toggle FlowDirection",
+ AutomationId = "ToggleFlowDirectionButton",
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ toggleButton.Clicked += OnToggleClicked;
+
+ _row1 = BuildRow();
+ _row2 = BuildRow();
+
+ Content = new VerticalStackLayout
+ {
+ Spacing = 12,
+ Padding = 12,
+ Children =
+ {
+ toggleButton,
+ _row1,
+ _row2,
+ }
+ };
+ }
+
+ private void OnToggleClicked(object sender, EventArgs e)
+ {
+ _row2.FlowDirection = FlowDirection.RightToLeft;
+ }
+
+ private static HorizontalStackLayout BuildRow()
+ {
+ var stackLayout = new HorizontalStackLayout
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ };
+
+ stackLayout.Children.Add(CreateIconBorder("✂"));
+ stackLayout.Children.Add(CreateIconBorder("✖"));
+
+ return stackLayout;
+ }
+
+ private static View CreateIconBorder(string icon)
+ {
+ return new Border
+ {
+ BackgroundColor = Colors.LightGray,
+ Stroke = Colors.Gray,
+ StrokeThickness = 1,
+ WidthRequest = 60,
+ HeightRequest = 40,
+ InputTransparent = true,
+ Content = CreateIconView(icon)
+ };
+ }
+
+ private static View CreateIconView(string icon)
+ {
+ var iconLabel = new Label
+ {
+ Text = icon,
+ FontSize = 16,
+ FontAutoScalingEnabled = false,
+ Padding = new Thickness(10, 0, 0, 0),
+ HorizontalOptions = LayoutOptions.Start,
+ VerticalOptions = LayoutOptions.Center,
+ VerticalTextAlignment = TextAlignment.Center,
+ BackgroundColor = Colors.Transparent,
+ HeightRequest = 40
+ };
+
+ var dropDownLabel = new Label
+ {
+ Text = "V",
+ FontSize = 16,
+ Margin = new Thickness(0, 0, 10, 0),
+ FontFamily = "MauiMaterialAssets",
+ VerticalTextAlignment = TextAlignment.Center,
+ VerticalOptions = LayoutOptions.Center,
+ BackgroundColor = Colors.Transparent
+ };
+
+ return new HorizontalStackLayout
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ HeightRequest = 40,
+ Children = { iconLabel, dropDownLabel }
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32406.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32406.cs
new file mode 100644
index 000000000000..371a974f3482
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32406.cs
@@ -0,0 +1,64 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 32406, "LayoutCycleException caused by nested Borders in ControlTemplates", PlatformAffected.UWP)]
+public class Issue32406 : ContentPage
+{
+ public Issue32406()
+ {
+ var resultLabel = new Label
+ {
+ Text = "Waiting",
+ AutomationId = "ResultLabel"
+ };
+
+ var container = new VerticalStackLayout();
+
+ // Create enough nested Border elements to trigger the layout cycle.
+ // The original report shows crashes at ~300 elements with 3+ nesting levels.
+ // Each element creates nestingDepth Borders (350 * 3 = 1050 total Borders).
+ const int elementCount = 350;
+ const int nestingDepth = 3;
+
+ for (int i = 0; i < elementCount; i++)
+ {
+ View innermost = new Label { Text = $"Item {i}" };
+
+ for (int j = 0; j < nestingDepth; j++)
+ {
+ innermost = new Border
+ {
+ StrokeThickness = 1,
+ Stroke = Colors.Gray,
+ Content = new HorizontalStackLayout
+ {
+ Children = { innermost }
+ }
+ };
+ }
+
+ container.Children.Add(innermost);
+ }
+
+ var scrollView = new ScrollView { Content = container };
+
+ Content = new Grid
+ {
+ RowDefinitions =
+ {
+ new RowDefinition(GridLength.Auto),
+ new RowDefinition(GridLength.Star)
+ },
+ Children =
+ {
+ resultLabel,
+ scrollView
+ }
+ };
+
+ Grid.SetRow(scrollView, 1);
+
+ // If we reach this point without a LayoutCycleException, the test passes.
+ // Use Loaded to confirm the page rendered successfully.
+ Loaded += (s, e) => resultLabel.Text = "Success";
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32416.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32416.cs
new file mode 100644
index 000000000000..47b6a21a33fe
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32416.cs
@@ -0,0 +1,51 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 32416, "Shell.FlyoutVerticalScrollMode Disabled does not disable scrolling", PlatformAffected.UWP)]
+public class Issue32416 : TestShell
+{
+ protected override void Init()
+ {
+ FlyoutVerticalScrollMode = ScrollMode.Disabled;
+ FlyoutBehavior = FlyoutBehavior.Locked;
+ var flyoutItem = new FlyoutItem
+ {
+ Title = "Menu"
+ };
+
+ // Add a ShellContent
+ flyoutItem.Items.Add(new ShellContent
+ {
+ Title = "Home",
+ ContentTemplate = new DataTemplate(typeof(Issue32416_ContentPage))
+ });
+
+ // Add FlyoutItem to the Shell
+ Items.Add(flyoutItem);
+
+ // Add MenuItems (static links in flyout)
+ for (int i = 1; i <= 20; i++)
+ {
+ Items.Add(new MenuItem
+ {
+ Text = $"Item {i}"
+ });
+ }
+ }
+}
+
+public class Issue32416_ContentPage : ContentPage
+{
+ public Issue32416_ContentPage()
+ {
+ Content = new StackLayout
+ {
+ Children =
+ {
+ new Label
+ {
+ Text = "This is the Sample page."
+ }
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32791.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32791.cs
new file mode 100644
index 000000000000..c59a794b721c
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32791.cs
@@ -0,0 +1,66 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 32791, "Setting the IsEnabled property of the CarouselView to false does not prevent swiping through items", PlatformAffected.iOS)]
+public class Issue32791 : ContentPage
+{
+ CarouselView _carouselView;
+ Label _statusLabel;
+
+ public Issue32791()
+ {
+ _carouselView = new CarouselView
+ {
+ AutomationId = "DisabledCarouselView",
+ HeightRequest = 200,
+ IsEnabled = false,
+ Loop = false,
+ ItemsSource = new string[] { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" },
+ ItemTemplate = new DataTemplate(() =>
+ {
+ Label label = new Label
+ {
+ FontSize = 32,
+ BackgroundColor = Colors.LightGray,
+ Padding = 20,
+ HorizontalOptions = LayoutOptions.Fill,
+ VerticalOptions = LayoutOptions.Fill
+ };
+ label.SetBinding(Label.TextProperty, ".");
+
+ return label;
+ })
+ };
+
+ _carouselView.CurrentItemChanged += OnCarouselViewCurrentItemChanged;
+
+ _statusLabel = new Label
+ {
+ AutomationId = "Issue32791StatusLabel",
+ Text = "Success",
+ };
+
+ Grid grid = new Grid
+ {
+ Padding = 20,
+ RowSpacing = 20,
+ RowDefinitions =
+ {
+ new RowDefinition { Height = new GridLength(200) },
+ new RowDefinition { Height = new GridLength(60) },
+ }
+ };
+
+ grid.Add(_carouselView, 0, 0);
+ grid.Add(_statusLabel, 0, 1);
+
+ Content = grid;
+ }
+
+ void OnCarouselViewCurrentItemChanged(object sender, CurrentItemChangedEventArgs e)
+ {
+ if (e.CurrentItem?.ToString() != "Item 1")
+ {
+ _statusLabel.Text = "Failure";
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32983.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32983.cs
new file mode 100644
index 000000000000..852684bd116d
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32983.cs
@@ -0,0 +1,141 @@
+using System.Collections.ObjectModel;
+#if IOS || MACCATALYST
+using Microsoft.Maui.Platform;
+using UIKit;
+#endif
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 32983, "CollectionView messes up Measure operation on Views", PlatformAffected.iOS)]
+public class Issue32983 : ContentPage
+{
+ readonly Label _measuredHeightLabel;
+ readonly Label _statusLabel;
+ readonly VerticalStackLayout _rootLayout;
+
+ public Issue32983()
+ {
+ _measuredHeightLabel = new Label
+ {
+ AutomationId = "MeasuredHeight",
+ Text = "Pending"
+ };
+
+ _statusLabel = new Label
+ {
+ AutomationId = "StatusLabel",
+ Text = "Tap the button to reproduce the issue"
+ };
+
+ var showButton = new Button
+ {
+ Text = "ShowBottomSheet",
+ AutomationId = "ShowBottomSheetButton",
+ HorizontalOptions = LayoutOptions.Fill
+ };
+ showButton.Clicked += OnShowBottomSheetClicked;
+
+ _rootLayout = new VerticalStackLayout
+ {
+ Padding = 10,
+ Spacing = 8,
+ Children =
+ {
+ showButton,
+ _statusLabel,
+ new Label { Text = "Measured height (ContentView unmounted):" },
+ _measuredHeightLabel
+ }
+ };
+
+ Content = _rootLayout;
+ }
+
+ void OnShowBottomSheetClicked(object sender, EventArgs e)
+ {
+#if IOS || MACCATALYST
+ var bottomSheetContent = new Issue32983BottomSheetContentView();
+ var vm = new Issue32983ViewModel();
+ for (int i = 0; i < 10; i++)
+ {
+ vm.ListOfStuff.Add(new Issue32983Item("Item #" + (i + 1)));
+ }
+ bottomSheetContent.BindingContext = vm;
+
+ var mauiContext = this.Handler?.MauiContext ?? throw new Exception("MauiContext is null");
+ var parent = this.ToUIViewController(mauiContext);
+ var viewControllerToPresent = bottomSheetContent.ToUIViewController(mauiContext);
+
+ var display = DeviceDisplay.MainDisplayInfo;
+ double widthDip = display.Width / display.Density;
+ double heightDip = display.Height / display.Density;
+
+ var minimumSize = bottomSheetContent.Measure(widthDip, heightDip);
+ var detentSize = minimumSize.ToCGSize();
+
+ if (OperatingSystem.IsIOSVersionAtLeast(16) || OperatingSystem.IsMacCatalystVersionAtLeast(16))
+ {
+ var detent = UISheetPresentationControllerDetent.Create(
+ "ContentHeight",
+ (ctx) => (nfloat)detentSize.Height);
+
+ var sheet = viewControllerToPresent.SheetPresentationController;
+ if (sheet is not null)
+ {
+ sheet.Detents = new[] { detent };
+ sheet.LargestUndimmedDetentIdentifier = UISheetPresentationControllerDetentIdentifier.Unknown;
+ sheet.PrefersScrollingExpandsWhenScrolledToEdge = false;
+ sheet.PrefersEdgeAttachedInCompactHeight = true;
+ sheet.WidthFollowsPreferredContentSizeWhenEdgeAttached = true;
+ }
+ }
+ parent.PresentViewController(viewControllerToPresent, animated: true, completionHandler: null);
+#endif
+ }
+}
+
+public class Issue32983BottomSheetContentView : Microsoft.Maui.Controls.ContentView
+{
+ public Issue32983BottomSheetContentView()
+ {
+ BackgroundColor = Colors.AliceBlue;
+
+ var collectionView = new CollectionView
+ {
+ SelectionMode = SelectionMode.Single,
+ VerticalOptions = LayoutOptions.Start,
+ Margin = new Thickness(10),
+ AutomationId = "BottomSheetCollectionView",
+
+ ItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Vertical),
+
+ ItemTemplate = new DataTemplate(() =>
+ {
+ var label = new Label
+ {
+ FontSize = 20,
+ FontAttributes = FontAttributes.Bold
+ };
+
+ label.SetBinding(Label.TextProperty, nameof(Issue32983Item.Name));
+ label.SetBinding(Label.AutomationIdProperty, nameof(Issue32983Item.Name));
+
+ return label;
+ })
+ };
+
+ collectionView.SetBinding(ItemsView.ItemsSourceProperty, nameof(Issue32983ViewModel.ListOfStuff));
+
+ Content = collectionView;
+ }
+}
+
+public class Issue32983Item(string name)
+{
+ public string Name => name;
+}
+
+public class Issue32983ViewModel
+{
+ public ObservableCollection ListOfStuff { get; } = new();
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32989.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32989.cs
new file mode 100644
index 000000000000..20cc4c2fbc12
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32989.cs
@@ -0,0 +1,50 @@
+using System;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Essentials;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 32989, "Exception thrown on .NET 10 Windows when calling Permissions.CheckStatusAsync()", PlatformAffected.UWP)]
+public class Issue32989 : ContentPage
+{
+ Label statusLabel;
+
+ public Issue32989()
+ {
+ var titleLabel = new Label
+ {
+ Text = "Test microphone permission in unpackaged apps",
+ FontSize = 18,
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ var checkPermissionButton = new Button
+ {
+ Text = "Check Microphone Permission",
+ AutomationId = "CheckPermissionButton"
+ };
+ checkPermissionButton.Clicked += OnCheckPermissionClicked;
+
+ statusLabel = new Label
+ {
+ Text = "Status: Not checked",
+ FontSize = 16,
+ HorizontalOptions = LayoutOptions.Center,
+ AutomationId = "StatusLabel"
+ };
+
+ Content = new StackLayout
+ {
+ Padding = 20,
+ Spacing = 20,
+ Children = { titleLabel, checkPermissionButton, statusLabel }
+ };
+ }
+
+ async void OnCheckPermissionClicked(object sender, EventArgs e)
+ {
+ statusLabel.Text = "Checking...";
+ var status = await Permissions.CheckStatusAsync();
+ statusLabel.Text = "Test Passed";
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32995.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32995.cs
new file mode 100644
index 000000000000..f323f2007445
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32995.cs
@@ -0,0 +1,74 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 32995, "TabBarDisabledColor not applied to disabled tabs on iOS", PlatformAffected.iOS)]
+public class Issue32995 : Shell
+{
+ public Issue32995()
+ {
+ var tab2 = new Tab
+ {
+ Title = "Tab2",
+ Icon = "coffee.png",
+ AutomationId = "Tab2",
+ IsEnabled = false,
+ Items =
+ {
+ new ShellContent
+ {
+ Content = new ContentPage
+ {
+ Content = new Label
+ {
+ Text = "Tab2 Content",
+ AutomationId = "Tab2Label",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ }
+ }
+ }
+ }
+ };
+
+ var tab1 = new Tab
+ {
+ Title = "Tab1",
+ Icon = "coffee.png",
+ Items =
+ {
+ new ShellContent
+ {
+ Content = new ContentPage
+ {
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 15,
+ Children =
+ {
+ new Label { Text = "Tab1", FontAttributes = FontAttributes.Bold },
+ new Button
+ {
+ Text = "Enable Tab2",
+ AutomationId = "EnableButton",
+ Command = new Command(() => tab2.IsEnabled = true)
+ },
+ new Button
+ {
+ Text = "Disable Tab2",
+ AutomationId = "DisableButton",
+ Command = new Command(() => tab2.IsEnabled = false)
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+
+ Items.Add(tab1);
+ Items.Add(tab2);
+
+ // Set TabBarDisabledColor using attached property
+ Shell.SetTabBarDisabledColor(this, Colors.Green);
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33037.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33037.cs
new file mode 100644
index 000000000000..69d4c30bdfd0
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33037.cs
@@ -0,0 +1,51 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 33037, "iOS Large Title display disappears when scrolling in Shell", PlatformAffected.iOS)]
+public class Issue33037 : TestShell
+{
+ protected override void Init()
+ {
+ var page = new ContentPage
+ {
+ Title = "Large Title Test"
+ };
+
+ // Explicitly set LargeTitleDisplay to Always — this should show a large title
+ Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.Page.SetLargeTitleDisplay(
+ page,
+ Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.LargeTitleDisplayMode.Always);
+
+ var scrollView = new ScrollView
+ {
+ AutomationId = "TestScrollView",
+ Content = new VerticalStackLayout
+ {
+ Children =
+ {
+ new Label
+ {
+ Text = "Large Title Test Page",
+ AutomationId = "PageTitle",
+ FontSize = 18,
+ Margin = new Thickness(20, 10)
+ }
+ }
+ }
+ };
+
+ // Add enough items to make the page scrollable
+ var layout = (VerticalStackLayout)scrollView.Content;
+ for (int i = 0; i < 30; i++)
+ {
+ layout.Children.Add(new Label
+ {
+ Text = $"Item {i}",
+ AutomationId = $"Item{i}",
+ Margin = new Thickness(20, 5)
+ });
+ }
+
+ page.Content = scrollView;
+ AddContentPage(page);
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33304.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33304.cs
new file mode 100644
index 000000000000..a7efd41ecb0b
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33304.cs
@@ -0,0 +1,97 @@
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 33304, "Shell TitleView disappears when switching tabs on Android", PlatformAffected.Android)]
+ public class Issue33304 : Shell
+ {
+ public Issue33304()
+ {
+ Shell.SetTitleView(this, new Label
+ {
+ Text = "TitleView",
+ AutomationId = "HomeTitleView"
+ });
+ var tabBar = new TabBar();
+ tabBar.Route = "homePage";
+ // Home Tab
+ var homeTab = new Tab { Title = "Home" };
+ var homeContent = new ShellContent();
+ var homePage = new ContentPage
+ {
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 20,
+ Children =
+ {
+ new Label
+ {
+ Text = "This is the Home tab",
+ AutomationId = "HomeTabLabel",
+ FontSize = 18,
+ HorizontalOptions = LayoutOptions.Center
+ },
+ new Label
+ {
+ Text = "Switch to other tabs and back to verify TitleView persists",
+ HorizontalOptions = LayoutOptions.Center
+ }
+ }
+ }
+ };
+ homeContent.Content = homePage;
+ homeTab.Items.Add(homeContent);
+ tabBar.Items.Add(homeTab);
+
+ // Search Tab
+ var searchTab = new Tab { Title = "Search" };
+ var searchContent = new ShellContent();
+ var searchPage = new ContentPage
+ {
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 20,
+ Children =
+ {
+ new Label
+ {
+ Text = "This is the Search tab",
+ AutomationId = "SearchTabLabel",
+ FontSize = 18,
+ HorizontalOptions = LayoutOptions.Center
+ }
+ }
+ }
+ };
+ searchContent.Content = searchPage;
+ searchTab.Items.Add(searchContent);
+ tabBar.Items.Add(searchTab);
+
+ // Settings Tab
+ var settingsTab = new Tab { Title = "Settings" };
+ var settingsContent = new ShellContent();
+ var settingsPage = new ContentPage
+ {
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 20,
+ Children =
+ {
+ new Label
+ {
+ Text = "This is the Settings tab",
+ FontSize = 18,
+ HorizontalOptions = LayoutOptions.Center
+ }
+ }
+ }
+ };
+ settingsContent.Content = settingsPage;
+ settingsTab.Items.Add(settingsContent);
+ tabBar.Items.Add(settingsTab);
+
+ Items.Add(tabBar);
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33351.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33351.cs
new file mode 100644
index 000000000000..41f32a354496
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33351.cs
@@ -0,0 +1,167 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 33351, "Changing Shell Tab Visibility when navigating back multiple pages ignores Shell Tab Visibility", PlatformAffected.All)]
+public class Issue33351 : Shell
+{
+ public Issue33351()
+ {
+ Routing.RegisterRoute("Issue33351Page1", typeof(Issue33351Page1));
+ Routing.RegisterRoute("Issue33351Page2", typeof(Issue33351Page2));
+
+ var tabBar = new TabBar
+ {
+ Items =
+ {
+ new MyTab
+ {
+ Title = "Tab 1",
+ ContentTemplate = new DataTemplate(() => new Issue33351MainPage())
+ },
+ new ShellContent
+ {
+ Title = "Tab 2",
+ ContentTemplate = new DataTemplate(() => new Issue33351SecondTabPage())
+ }
+ }
+ };
+
+ Items.Add(tabBar);
+ }
+
+ class MyTab : ShellContent
+ {
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+
+ if (this.Parent != null)
+ Shell.SetTabBarIsVisible(this.Parent, true);
+
+ Shell.Current.Navigating -= OnShellNavigating;
+ Shell.Current.Navigating += OnShellNavigating;
+ }
+
+ protected override void OnDisappearing()
+ {
+ base.OnDisappearing();
+
+ Shell.Current.Navigating -= OnShellNavigating;
+ }
+
+ void OnShellNavigating(object sender, ShellNavigatingEventArgs e)
+ {
+ if (this.Parent != null)
+ Shell.SetTabBarIsVisible(this.Parent, false);
+ }
+ }
+
+ class Issue33351MainPage : ContentPage
+ {
+ public Issue33351MainPage()
+ {
+ Title = "Main Page";
+ AutomationId = "RootPage";
+
+ var statusLabel = new Label
+ {
+ AutomationId = "TabBarVisibleLabel",
+ Text = "Tab Bar Visible"
+ };
+
+ var navigateButton = new Button
+ {
+ AutomationId = "PushPage1Button",
+ Text = "Go to Page 1",
+ HorizontalOptions = LayoutOptions.Fill
+ };
+
+ navigateButton.Clicked += async (s, e) =>
+ {
+ await Shell.Current.GoToAsync("Issue33351Page1");
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = new Thickness(20),
+ Spacing = 20,
+ Children = { statusLabel, navigateButton }
+ };
+ }
+ }
+
+ class Issue33351Page1 : ContentPage
+ {
+ public Issue33351Page1()
+ {
+ Title = "Page 1";
+
+ var statusLabel = new Label
+ {
+ AutomationId = "TabBarHiddenLabel",
+ Text = "Tab Bar Hidden"
+ };
+
+ var navigateButton = new Button
+ {
+ AutomationId = "PushPage2Button",
+ Text = "Go to Page 2",
+ HorizontalOptions = LayoutOptions.Fill
+ };
+
+ navigateButton.Clicked += async (s, e) =>
+ {
+ await Shell.Current.GoToAsync("Issue33351Page2");
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = new Thickness(20),
+ Spacing = 20,
+ Children = { statusLabel, navigateButton }
+ };
+ }
+ }
+
+ class Issue33351Page2 : ContentPage
+ {
+ public Issue33351Page2()
+ {
+ Title = "Page 2";
+ AutomationId = "Page2";
+
+ var statusLabel = new Label
+ {
+ AutomationId = "TabBarHiddenLabel2",
+ Text = "Tab Bar Hidden"
+ };
+
+ var popToRootButton = new Button
+ {
+ AutomationId = "PopToRootButton",
+ Text = "Go Back (../..)",
+ HorizontalOptions = LayoutOptions.Fill
+ };
+
+ popToRootButton.Clicked += async (s, e) =>
+ {
+ await Shell.Current.GoToAsync("../..");
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = new Thickness(20),
+ Spacing = 20,
+ Children = { statusLabel, popToRootButton }
+ };
+ }
+ }
+
+ class Issue33351SecondTabPage : ContentPage
+ {
+ public Issue33351SecondTabPage()
+ {
+ Title = "Tab 2";
+ Content = new Label { Text = "Tab 2" };
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33375.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33375.cs
new file mode 100644
index 000000000000..3b2c50c1b922
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33375.cs
@@ -0,0 +1,122 @@
+using System.Collections.ObjectModel;
+using System.Windows.Input;
+
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 33375, "SwipeGestureRecognizer triggers while scrolling CollectionView horizontally on iOS", PlatformAffected.iOS | PlatformAffected.macOS)]
+ public class Issue33375 : TestContentPage
+ {
+ private int swipeCount = 0;
+ private Label _statusLabel;
+
+ protected override void Init()
+ {
+ var items = new ObservableCollection
+ {
+ new Issue33375_ItemData { Name = "Item 1", Description = "Scroll right" },
+ new Issue33375_ItemData { Name = "Item 2", Description = "Keep scrolling" },
+ new Issue33375_ItemData { Name = "Item 3", Description = "More items" },
+ new Issue33375_ItemData { Name = "Item 4", Description = "Even more" },
+ new Issue33375_ItemData { Name = "Item 5", Description = "Last item" },
+ new Issue33375_ItemData { Name = "Item 6", Description = "Extra item" },
+ new Issue33375_ItemData { Name = "Item 7", Description = "Another one" },
+ new Issue33375_ItemData { Name = "Item 8", Description = "Keep going" }
+ };
+
+ var collectionView = new CollectionView
+ {
+ AutomationId = "TestCollectionView",
+ HeightRequest = 200,
+ ItemsSource = items,
+ ItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Horizontal)
+ {
+ ItemSpacing = 10
+ },
+ ItemTemplate = new DataTemplate(() =>
+ {
+ var border = new Border
+ {
+ Background = Colors.DodgerBlue,
+ Padding = 20,
+ WidthRequest = 150,
+ HeightRequest = 180,
+ StrokeThickness = 0
+ };
+
+ var stack = new VerticalStackLayout
+ {
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ var nameLabel = new Label
+ {
+ FontSize = 20,
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center
+ };
+ nameLabel.SetBinding(Label.TextProperty, nameof(Issue33375_ItemData.Name));
+
+ var descLabel = new Label
+ {
+ FontSize = 14,
+ HorizontalOptions = LayoutOptions.Center,
+ Margin = new Thickness(0, 10, 0, 0)
+ };
+ descLabel.SetBinding(Label.TextProperty, nameof(Issue33375_ItemData.Description));
+
+ stack.Children.Add(nameLabel);
+ stack.Children.Add(descLabel);
+ border.Content = stack;
+
+ return border;
+ })
+ };
+
+ _statusLabel = new Label
+ {
+ AutomationId = "StatusLabel",
+ Text = "No swipe detected",
+ FontSize = 18,
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center,
+ Margin = new Thickness(0, 20)
+ };
+
+ var border = new Border
+ {
+ AutomationId = "TestBorder",
+ Background = Colors.LightGray,
+ Padding = 20,
+ Content = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Children =
+ {
+ _statusLabel,
+ collectionView,
+ }
+ }
+ };
+
+ var swipeGesture = new SwipeGestureRecognizer
+ {
+ Direction = SwipeDirection.Right,
+ Threshold = 200,
+ Command = new Command(() =>
+ {
+ swipeCount++;
+ _statusLabel.Text = $"Swipe triggered! Count: {swipeCount}";
+ _statusLabel.TextColor = Colors.Red;
+ })
+ };
+ border.GestureRecognizers.Add(swipeGesture);
+ Content = border;
+ }
+
+ class Issue33375_ItemData
+ {
+ public string Name { get; set; } = string.Empty;
+ public string Description { get; set; } = string.Empty;
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33400.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33400.cs
new file mode 100644
index 000000000000..c2aedf64dc0c
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33400.cs
@@ -0,0 +1,59 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 33400, "Runtime Scrollbar visibility not updating correctly on Android platform", PlatformAffected.Android)]
+public class Issue33400 : ContentPage
+{
+ readonly ScrollView scrollView;
+
+ public Issue33400()
+ {
+ var grid = new Grid
+ {
+ RowDefinitions = {
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Auto }
+ },
+ RowSpacing = 10
+ };
+ scrollView = new ScrollView
+ {
+ Orientation = ScrollOrientation.Horizontal,
+ AutomationId = "TestScrollView"
+ };
+
+ var horizontalStack = new HorizontalStackLayout();
+
+ var neverButton = new Button { Text = "Never", AutomationId = "Issue33400HorizontalNeverButton" };
+ neverButton.Clicked += (s, e) => { scrollView.HorizontalScrollBarVisibility = ScrollBarVisibility.Never; };
+
+ var defaultButton = new Button { Text = "Default", AutomationId = "Issue33400HorizontalDefaultButton" };
+ defaultButton.Clicked += (s, e) =>
+ {
+ scrollView.HorizontalScrollBarVisibility = ScrollBarVisibility.Default;
+ scrollView.ScrollToAsync(100, 0, true); //Slight scroll to show the scrollbar
+ };
+
+ var alwaysButton = new Button { Text = "Always", AutomationId = "Issue33400HorizontalAlwaysButton" };
+ alwaysButton.Clicked += (s, e) => { scrollView.HorizontalScrollBarVisibility = ScrollBarVisibility.Always; };
+
+ horizontalStack.Add(neverButton);
+ horizontalStack.Add(defaultButton);
+ horizontalStack.Add(alwaysButton);
+
+ var label = new Label
+ {
+ Padding = new Thickness(10),
+ Text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.",
+ };
+
+ scrollView.Content = label;
+
+ grid.Add(horizontalStack);
+ Grid.SetRow(horizontalStack, 0);
+ grid.Add(scrollView);
+ Grid.SetRow(scrollView, 1);
+
+ Content = grid;
+
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33407.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33407.cs
new file mode 100644
index 000000000000..8566fc3d2a3d
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33407.cs
@@ -0,0 +1,151 @@
+namespace Maui.Controls.Sample.Issues;
+
+class Issue33407CategoryItem
+{
+ public string Id { get; set; } = string.Empty;
+ public string Title { get; set; } = string.Empty;
+ public string AutomationId { get; set; } = string.Empty;
+}
+
+class Issue33407TestItem
+{
+ public string Id { get; set; } = string.Empty;
+ public string Title { get; set; } = string.Empty;
+ public string AutomationId { get; set; } = string.Empty;
+}
+
+[Issue(IssueTracker.Github, 33407, "Focusing and entering texts on entry control causes a gap at the top after rotating simulator.", PlatformAffected.iOS)]
+public class Issue33407 : Shell
+{
+ public Issue33407()
+ {
+ FlyoutBehavior = FlyoutBehavior.Disabled;
+
+ // Match ManualTests/Sandbox Shell styling so iOS safe area is handled correctly
+ Shell.SetBackgroundColor(this, Color.FromArgb("#512BD4"));
+ Shell.SetForegroundColor(this, Colors.White);
+ Shell.SetTitleColor(this, Colors.White);
+ Shell.SetNavBarHasShadow(this, false);
+
+ Items.Add(new ShellContent
+ {
+ Title = "Home",
+ Content = new Issue33407CategoriesPage()
+ });
+ }
+}
+
+class Issue33407CategoriesPage : ContentPage
+{
+ public Issue33407CategoriesPage()
+ {
+ Title = "Categories";
+
+ var collection = new CollectionView { SelectionMode = SelectionMode.Single };
+ collection.ItemsSource = new[]
+ {
+ new Issue33407CategoryItem { Id = "E", Title = "Entry", AutomationId = "CategoryE" }
+ };
+ collection.ItemTemplate = new DataTemplate(() =>
+ {
+ var grid = new Grid { Padding = new Thickness(10) };
+ grid.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Auto));
+ grid.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Star));
+
+ var idLabel = new Label { FontAttributes = FontAttributes.Bold, FontSize = 32, VerticalOptions = LayoutOptions.Center };
+ idLabel.SetBinding(Label.TextProperty, nameof(Issue33407CategoryItem.Id));
+ idLabel.SetBinding(Label.AutomationIdProperty, nameof(Issue33407CategoryItem.AutomationId));
+
+ var titleLabel = new Label { Margin = new Thickness(20, 0, 0, 0), FontSize = 28, VerticalOptions = LayoutOptions.Center };
+ titleLabel.SetBinding(Label.TextProperty, nameof(Issue33407CategoryItem.Title));
+
+ grid.Add(idLabel, 0, 0);
+ grid.Add(titleLabel, 1, 0);
+ return grid;
+ });
+ collection.SelectionChanged += (s, e) =>
+ {
+ if (e.CurrentSelection.FirstOrDefault() is Issue33407CategoryItem item && item.Id == "E")
+ Navigation.PushAsync(new Issue33407EntryListPage());
+ ((CollectionView)s).SelectedItem = null;
+ };
+
+ Content = collection;
+ }
+}
+
+class Issue33407EntryListPage : ContentPage
+{
+ public Issue33407EntryListPage()
+ {
+ Title = "Entry";
+
+ var collection = new CollectionView { SelectionMode = SelectionMode.Single };
+ collection.ItemsSource = new[]
+ {
+ new Issue33407TestItem { Id = "E1", Title = "No gap at top after rotation", AutomationId = "TestE1" }
+ };
+ collection.ItemTemplate = new DataTemplate(() =>
+ {
+ var grid = new Grid { Padding = new Thickness(10) };
+ grid.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Auto));
+ grid.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Star));
+
+ var idLabel = new Label { FontAttributes = FontAttributes.Bold, FontSize = 32, VerticalOptions = LayoutOptions.Center };
+ idLabel.SetBinding(Label.TextProperty, nameof(Issue33407TestItem.Id));
+ idLabel.SetBinding(Label.AutomationIdProperty, nameof(Issue33407TestItem.AutomationId));
+
+ var titleLabel = new Label { Margin = new Thickness(20, 0, 0, 0), FontSize = 12, VerticalOptions = LayoutOptions.Center };
+ titleLabel.SetBinding(Label.TextProperty, nameof(Issue33407TestItem.Title));
+
+ grid.Add(idLabel, 0, 0);
+ grid.Add(titleLabel, 1, 0);
+ return grid;
+ });
+ collection.SelectionChanged += (s, e) =>
+ {
+ if (e.CurrentSelection.FirstOrDefault() is Issue33407TestItem item && item.Id == "E1")
+ Navigation.PushAsync(new Issue33407E1Page());
+ ((CollectionView)s).SelectedItem = null;
+ };
+
+ Content = collection;
+ }
+}
+
+class Issue33407E1Page : ContentPage
+{
+ public Issue33407E1Page()
+ {
+ Title = "E1";
+
+ Content = new VerticalStackLayout
+ {
+ Children =
+ {
+ new Label
+ {
+ Text = "1. Rotate the device between portrait and landscape with the keyboard hidden. The test passes if no extra gap appears at the top of the page above the entries."
+ },
+ new UITestEntry
+ {
+ IsPassword = false,
+ IsCursorVisible = false,
+ Placeholder = "Top gap check (normal entry)",
+ AutomationId = "Entry1"
+ },
+ new Label
+ {
+ Text = "2. Tap into each Entry, then rotate the device again. The test passes if no additional top gap appears and both entries remain aligned directly under this text."
+ },
+ new UITestEntry
+ {
+ IsPassword = true,
+ IsCursorVisible = false,
+ Placeholder = "Top gap check (password entry)",
+ AutomationId = "Entry2"
+ }
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33415.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33415.cs
new file mode 100644
index 000000000000..e714715ed485
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33415.cs
@@ -0,0 +1,167 @@
+using Microsoft.Maui.Controls;
+using System.Collections.Generic;
+
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 33415, "ApplyQueryAttributes gets called with empty Dictionary on back", PlatformAffected.All)]
+ public class Issue33415 : TestShell
+ {
+ protected override void Init()
+ {
+ // Start on a landing page, not the test page
+ var landingPage = new ContentPage
+ {
+ Content = new Button
+ {
+ AutomationId = "NavigateToMainPageButton",
+ Text = "Navigate to Main Page with Parameters",
+ Command = new Command(async () =>
+ {
+ await Shell.Current.GoToAsync("MainPage", new Dictionary
+ {
+ { "TestKey", "TestValue" }
+ });
+ })
+ }
+ };
+ Items.Add(new ShellContent { Title = "Landing", Content = landingPage });
+
+ // Register the main test page as a route (not starting page)
+ Routing.RegisterRoute("MainPage", typeof(Issue33415MainPage));
+ Routing.RegisterRoute("SecondPage", typeof(Issue33415SecondPage));
+ }
+ }
+
+ public class Issue33415MainPage : ContentPage, IQueryAttributable
+ {
+ private readonly Label _statusLabel;
+ private readonly Label _callCountLabel;
+ private readonly Button _navigateButton;
+ private int _applyQueryCallCount = 0;
+
+ public Issue33415MainPage()
+ {
+ _statusLabel = new Label
+ {
+ AutomationId = "StatusLabel",
+ Text = "Status: Not called",
+ Margin = new Thickness(10)
+ };
+
+ _callCountLabel = new Label
+ {
+ AutomationId = "CallCountLabel",
+ Text = "Call count: 0",
+ Margin = new Thickness(10)
+ };
+
+ _navigateButton = new Button
+ {
+ AutomationId = "NavigateButton",
+ Text = "Navigate to Second Page",
+ Margin = new Thickness(10)
+ };
+
+ _navigateButton.Clicked += OnNavigateClicked;
+
+ Content = new StackLayout
+ {
+ Padding = new Thickness(10),
+ Children =
+ {
+ new Label
+ {
+ Text = "Issue 33415 - ApplyQueryAttributes called with empty Dictionary on back",
+ FontSize = 16,
+ FontAttributes = FontAttributes.Bold,
+ Margin = new Thickness(0, 0, 0, 20)
+ },
+ _statusLabel,
+ _callCountLabel,
+ _navigateButton
+ }
+ };
+ }
+
+ public void ApplyQueryAttributes(IDictionary query)
+ {
+ _applyQueryCallCount++;
+ _callCountLabel.Text = $"Call count: {_applyQueryCallCount}";
+
+ if (query.Count == 0)
+ {
+ _statusLabel.Text = "Status: ApplyQueryAttributes called with EMPTY dictionary";
+ }
+ else
+ {
+ _statusLabel.Text = $"Status: ApplyQueryAttributes called with {query.Count} parameter(s)";
+
+ // According to documentation, calling Clear() should prevent
+ // ApplyQueryAttributes from being called again on back navigation
+ query.Clear();
+ }
+ }
+
+ private async void OnNavigateClicked(object sender, System.EventArgs e)
+ {
+ // Navigate to second page - pass through the same parameter for testing
+ await Shell.Current.GoToAsync("SecondPage", new Dictionary
+ {
+ { "TestKey", "TestValue" }
+ });
+ }
+ }
+
+ [QueryProperty(nameof(TestKey), "TestKey")]
+ public class Issue33415SecondPage : ContentPage
+ {
+ private readonly Label _receivedLabel;
+ private string _testKey;
+
+ public string TestKey
+ {
+ get => _testKey;
+ set
+ {
+ _testKey = value;
+ _receivedLabel.Text = $"Received: {value ?? "null"}";
+ }
+ }
+
+ public Issue33415SecondPage()
+ {
+ _receivedLabel = new Label
+ {
+ AutomationId = "ReceivedLabel",
+ Text = "Received: null",
+ Margin = new Thickness(10)
+ };
+
+ var backButton = new Button
+ {
+ AutomationId = "BackButton",
+ Text = "Go Back",
+ Margin = new Thickness(10)
+ };
+
+ backButton.Clicked += async (s, e) => await Shell.Current.GoToAsync("..");
+
+ Content = new StackLayout
+ {
+ Padding = new Thickness(10),
+ Children =
+ {
+ new Label
+ {
+ Text = "Second Page",
+ FontSize = 16,
+ FontAttributes = FontAttributes.Bold,
+ Margin = new Thickness(0, 0, 0, 20)
+ },
+ _receivedLabel,
+ backButton
+ }
+ };
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33420.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue33420.xaml
new file mode 100644
index 000000000000..7c77389eacfa
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33420.xaml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33420.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33420.xaml.cs
new file mode 100644
index 000000000000..b64cb5a9b8b4
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33420.xaml.cs
@@ -0,0 +1,95 @@
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 33420, "System.InvalidCastException when using QueryPropertyAttribute with nullable types", PlatformAffected.All)]
+ public partial class Issue33420 : Shell
+ {
+ public Issue33420()
+ {
+ InitializeComponent();
+
+ Routing.RegisterRoute(nameof(Issue33420DetailsPage), typeof(Issue33420DetailsPage));
+ }
+ }
+
+ public class Issue33420MainPage : ContentPage
+ {
+ public Issue33420MainPage()
+ {
+ var button = new Button
+ {
+ Text = "Navigate with Nullable",
+ AutomationId = "NavigateButton"
+ };
+
+ button.Clicked += async (s, e) =>
+ {
+ try
+ {
+ var param = new Dictionary
+ {
+ { nameof(Issue33420DetailsPage.ID), (long?)1 }
+ };
+ await Shell.Current.GoToAsync(nameof(Issue33420DetailsPage), param);
+ }
+ catch (Exception ex)
+ {
+ // If navigation fails, update button text to indicate error
+ button.Text = $"Error: {ex.GetType().Name}";
+ }
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Children =
+ {
+ new Label
+ {
+ Text = "Test nullable QueryProperty navigation",
+ AutomationId = "MainLabel"
+ },
+ button
+ },
+ Padding = 10,
+ Spacing = 10
+ };
+ }
+ }
+
+ [QueryProperty(nameof(ID), nameof(ID))]
+ public class Issue33420DetailsPage : ContentPage
+ {
+ private readonly Label _resultLabel;
+
+ public long? ID
+ {
+ get => (long?)GetValue(IDProperty);
+ set => SetValue(IDProperty, value);
+ }
+
+ public static readonly BindableProperty IDProperty =
+ BindableProperty.Create(nameof(ID), typeof(long?), typeof(Issue33420DetailsPage), null, propertyChanged: OnIDChanged);
+
+ private static void OnIDChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is Issue33420DetailsPage page)
+ {
+ page._resultLabel.Text = $"Success: ID={newValue}";
+ }
+ }
+
+ public Issue33420DetailsPage()
+ {
+ _resultLabel = new Label
+ {
+ Text = "Waiting for navigation...",
+ AutomationId = "ResultLabel"
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Children = { _resultLabel },
+ Padding = 10
+ };
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33523.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33523.cs
index 80ee01a99d1c..3d5f56aad316 100644
--- a/src/Controls/tests/TestCases.HostApp/Issues/Issue33523.cs
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33523.cs
@@ -2,7 +2,7 @@
namespace Maui.Controls.Sample.Issues
{
- [Issue(IssueTracker.Github, 33523, "OnBackButtonPressed not firing for Shell Navigation Bar button in .NET 10 SR2", PlatformAffected.Android)]
+ [Issue(IssueTracker.Github, 33523, "OnBackButtonPressed not firing for Shell Navigation Bar button in .NET 10 SR2", PlatformAffected.Android | PlatformAffected.iOS | PlatformAffected.macOS)]
public class Issue33523 : Shell
{
public Issue33523()
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33547.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33547.cs
new file mode 100644
index 000000000000..da6e4fb1d01f
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33547.cs
@@ -0,0 +1,74 @@
+using Microsoft.Maui.Controls;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 33547, "[iOS] Shell Page gets moved partially outside of viewport when focusing element on page load", PlatformAffected.iOS)]
+public class Issue33547 : TestShell
+{
+ protected override void Init()
+ {
+ Shell.SetForegroundColor(this, Colors.Black);
+
+ Routing.RegisterRoute("newpage", typeof(Issue33547NewPage));
+
+ var mainPage = new ContentPage
+ {
+ Title = "Issue 33547",
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 10,
+ Children =
+ {
+ new Label
+ {
+ Text = "Tap button to navigate. Next page will auto-focus Entry and show keyboard.",
+ AutomationId = "InstructionLabel"
+ },
+ new Button
+ {
+ Text = "Navigate to Page with Entry",
+ AutomationId = "NavigationPushButton",
+ Command = new Command(async () =>
+ {
+ await Shell.Current.Navigation.PushAsync(new Issue33547NewPage());
+ })
+ }
+ }
+ }
+ };
+
+ AddContentPage(mainPage, "Issue 33547");
+ }
+}
+public class Issue33547NewPage : ContentPage
+{
+ public Issue33547NewPage()
+ {
+ Title = "NewPage";
+
+ var entry = new Entry
+ {
+ AutomationId = "TestEntry",
+ Placeholder = "Entry auto-focused (keyboard shown)",
+ ReturnType = ReturnType.Done
+ };
+
+ var label = new Label
+ {
+ Text = "Under the Entry"
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 10,
+ Children = { entry, label }
+ };
+
+ Loaded += (sender, args) =>
+ {
+ entry.Focus();
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33604.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33604.cs
new file mode 100644
index 000000000000..c468f1f1d3af
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33604.cs
@@ -0,0 +1,144 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 33604, "CollectionView does not respect content SafeAreaEdges choices", PlatformAffected.iOS | PlatformAffected.Android)]
+public class Issue33604 : Shell
+{
+ public Issue33604()
+ {
+ Shell.SetNavBarIsVisible(this, false);
+ Items.Add(new ShellContent
+ {
+ Route = "MainPage",
+ ContentTemplate = new DataTemplate(typeof(Issue33604Page)),
+ });
+ }
+}
+
+public class Issue33604Page : ContentPage
+{
+ public Issue33604Page()
+ {
+ BackgroundColor = Colors.Blue;
+
+ var labelStyle = new Style(typeof(Label));
+ labelStyle.Setters.Add(new Setter { Property = Label.HorizontalOptionsProperty, Value = LayoutOptions.Fill });
+ labelStyle.Setters.Add(new Setter { Property = Label.BackgroundColorProperty, Value = Colors.Aquamarine });
+ labelStyle.Setters.Add(new Setter { Property = Label.PaddingProperty, Value = new Thickness(4, 0) });
+ labelStyle.Setters.Add(new Setter { Property = Label.FontSizeProperty, Value = 32.0 });
+ Resources = new ResourceDictionary { labelStyle };
+
+ var containerNone = new SafeAreaEdges(SafeAreaRegions.Container, SafeAreaRegions.None);
+
+ var topLabel = new Label
+ {
+ AutomationId = "TopLabel",
+ Text = "Hello, World! Left Side Test",
+ HorizontalTextAlignment = TextAlignment.Start,
+ };
+ var topView = new ContentView { BackgroundColor = Colors.Plum, Content = topLabel };
+ topView.SafeAreaEdges = new SafeAreaEdges(SafeAreaRegions.Container);
+
+ var collectionView = new CollectionView
+ {
+ AutomationId = "TestCollectionView",
+ BackgroundColor = Colors.Red,
+ IsGrouped = true,
+ GroupHeaderTemplate = new DataTemplate(() =>
+ {
+ var grid = new Grid
+ {
+ RowDefinitions = { new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto) },
+ BackgroundColor = Color.FromArgb("#2F4F4F"),
+ Margin = new Thickness(0, 2),
+ RowSpacing = 4,
+ SafeAreaEdges = containerNone,
+ };
+
+ var leftLabel = new Label { FontSize = 22.0, HorizontalTextAlignment = TextAlignment.Start };
+ leftLabel.SetBinding(Label.TextProperty, "LeftTest");
+
+ var rightLabel = new Label { FontSize = 22.0, HorizontalTextAlignment = TextAlignment.End };
+ rightLabel.SetBinding(Label.TextProperty, "RightTest");
+
+ grid.Add(leftLabel, 0, 0);
+ grid.Add(rightLabel, 0, 1);
+ return grid;
+ }),
+ ItemTemplate = new DataTemplate(() =>
+ {
+ var grid = new Grid
+ {
+ RowDefinitions = { new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Auto) },
+ BackgroundColor = Colors.LightGray,
+ Margin = new Thickness(0, 2),
+ RowSpacing = 4,
+ SafeAreaEdges = containerNone,
+ };
+
+ var leftLabel = new Label { FontSize = 12.0, HorizontalTextAlignment = TextAlignment.Start };
+ leftLabel.SetBinding(Label.TextProperty, "LeftTest");
+
+ var rightLabel = new Label { FontSize = 12.0, HorizontalTextAlignment = TextAlignment.End };
+ rightLabel.SetBinding(Label.TextProperty, "RightTest");
+
+ grid.Add(leftLabel, 0, 0);
+ grid.Add(rightLabel, 0, 1);
+ return grid;
+ }),
+ };
+
+ var bottomLabel = new Label
+ {
+ AutomationId = "BottomLabel",
+ Text = "Testing Right Side Here",
+ HorizontalTextAlignment = TextAlignment.End,
+ };
+ var bottomView = new ContentView { BackgroundColor = Colors.Plum, Content = bottomLabel };
+ bottomView.SafeAreaEdges = new SafeAreaEdges(SafeAreaRegions.Container);
+
+ var rootGrid = new Grid
+ {
+ RowDefinitions = { new RowDefinition(GridLength.Auto), new RowDefinition(GridLength.Star), new RowDefinition(GridLength.Auto) },
+ BackgroundColor = Colors.Yellow,
+ SafeAreaEdges = SafeAreaEdges.None,
+ };
+
+ rootGrid.Add(topView, 0, 0);
+ rootGrid.Add(collectionView, 0, 1);
+ rootGrid.Add(bottomView, 0, 2);
+
+ Content = rootGrid;
+
+ var groups = new List();
+ for (var i = 1; i <= 10; i++)
+ {
+ var group = new Issue33604ModelGroup
+ {
+ LeftTest = $"Title Left {i}",
+ RightTest = $"Title Right {i}",
+ };
+ for (var j = 1; j <= i; j++)
+ {
+ group.Add(new Issue33604Model
+ {
+ LeftTest = $"Content Test for Left Side {i}",
+ RightTest = $"Content Test for Right Side {i}",
+ });
+ }
+ groups.Add(group);
+ }
+ collectionView.ItemsSource = groups;
+ }
+}
+
+public class Issue33604ModelGroup : List
+{
+ public string LeftTest { get; set; }
+ public string RightTest { get; set; }
+}
+
+public class Issue33604Model
+{
+ public string LeftTest { get; set; }
+ public string RightTest { get; set; }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33614.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33614.cs
new file mode 100644
index 000000000000..cde932b66b16
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33614.cs
@@ -0,0 +1,76 @@
+using System.Collections.ObjectModel;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 33614, "CollectionView Scrolled event reports incorrect FirstVisibleItemIndex after programmatic ScrollTo", PlatformAffected.iOS | PlatformAffected.macOS)]
+public class Issue33614 : ContentPage
+{
+ public ObservableCollection Items { get; set; }
+ private Label _firstIndexLabel;
+ private CollectionView2 _collectionView;
+ public Issue33614()
+ {
+ Items = new ObservableCollection();
+ for (int i = 0; i <= 50; i++)
+ {
+ Items.Add($"Item_{i}");
+ }
+
+ _firstIndexLabel = new Label
+ {
+ AutomationId = "FirstIndexLabel",
+ Text = "FirstVisibleItemIndex: 0",
+ IsVisible = false,
+ };
+
+ var scrollToButton = new Button
+ {
+ AutomationId = "ScrollToButton",
+ Text = "ScrollTo Index 15",
+ WidthRequest = 150
+ };
+ scrollToButton.Clicked += OnScrollToButtonClicked;
+
+ _collectionView = new CollectionView2
+ {
+ AutomationId = "TestCollectionView",
+ ItemsSource = Items,
+ HeightRequest = 600,
+ ItemTemplate = new DataTemplate(() =>
+ {
+ var label = new Label();
+ label.SetBinding(Label.TextProperty, ".");
+ return new Border
+ {
+ Margin = new Thickness(5),
+ Padding = new Thickness(10),
+ Stroke = Colors.Gray,
+ Content = label
+ };
+ })
+ };
+
+ _collectionView.Scrolled += OnCollectionViewScrolled;
+
+ Content = new StackLayout
+ {
+ Children =
+ {
+ _firstIndexLabel,
+ scrollToButton,
+ _collectionView
+ }
+ };
+ }
+
+ private void OnCollectionViewScrolled(object sender, ItemsViewScrolledEventArgs e)
+ {
+ _firstIndexLabel.Text = $"FirstVisibleItemIndex: {e.FirstVisibleItemIndex}";
+ _firstIndexLabel.IsVisible = true;
+ }
+
+ private void OnScrollToButtonClicked(object sender, EventArgs e)
+ {
+ _collectionView.ScrollTo(15, position: ScrollToPosition.Start, animate: true);
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33706.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33706.cs
new file mode 100644
index 000000000000..e07386dc56cb
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33706.cs
@@ -0,0 +1,94 @@
+using Microsoft.Maui.Media;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 33706, "MediaPicker gets stuck if LaunchMode is SingleTask", PlatformAffected.Android)]
+public class Issue33706 : ContentPage
+{
+ private readonly ActivityIndicator _activityIndicator;
+ private readonly Label _statusLabel;
+ private readonly Label _resumedLabel;
+
+ public Issue33706()
+ {
+ var pickButton = new Button
+ {
+ Text = "Pick Media",
+ AutomationId = "PickMediaButton"
+ };
+ pickButton.Clicked += OnPickMediaClicked;
+
+ _activityIndicator = new ActivityIndicator
+ {
+ AutomationId = "MediaPickerIndicator",
+ IsVisible = false,
+ IsRunning = false
+ };
+
+ _statusLabel = new Label
+ {
+ Text = "Ready",
+ AutomationId = "StatusLabel",
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ _resumedLabel = new Label
+ {
+ Text = "",
+ AutomationId = "ResumedLabel"
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 20,
+ Children =
+ {
+ new Label
+ {
+ Text = "Test MediaPicker with LaunchMode.SingleTask",
+ FontSize = 18,
+ HorizontalOptions = LayoutOptions.Center
+ },
+ pickButton,
+ _activityIndicator,
+ _statusLabel,
+ _resumedLabel
+ }
+ };
+ this.Loaded += (s, e) =>
+ {
+ this.Window.Resumed += (s, e) => _resumedLabel.Text = "Resumed";
+ };
+ }
+
+ private async void OnPickMediaClicked(object sender, EventArgs e)
+ {
+ try
+ {
+ _activityIndicator.IsRunning = true;
+ _activityIndicator.IsVisible = true;
+ _statusLabel.Text = "Picking...";
+
+ var result = await MediaPicker.PickPhotoAsync();
+
+ _activityIndicator.IsRunning = false;
+ _activityIndicator.IsVisible = false;
+
+ if (result != null)
+ {
+ _statusLabel.Text = "Photo picked";
+ }
+ else
+ {
+ _statusLabel.Text = "Cancelled";
+ }
+ }
+ catch (Exception ex)
+ {
+ _activityIndicator.IsRunning = false;
+ _activityIndicator.IsVisible = false;
+ _statusLabel.Text = $"Error: {ex.Message}";
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33769.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33769.cs
new file mode 100644
index 000000000000..bec41adc8879
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33769.cs
@@ -0,0 +1,55 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 33769, "Stepper control fails to reach maximum value when increment exceeds remaining threshold", PlatformAffected.iOS)]
+public class Issue33769 : ContentPage
+{
+ Label descriptionLabel;
+ Label stepperStatusLabel;
+ Stepper stepper;
+ public Issue33769()
+ {
+ descriptionLabel = new Label
+ {
+ Text = "The test passes if the stepper is able to reach the maximum and minimum value when increment exceeds remaining threshold",
+ FontSize = 16,
+ HorizontalOptions = LayoutOptions.Center,
+ HorizontalTextAlignment = TextAlignment.Center
+ };
+
+ stepper = new Stepper
+ {
+ AutomationId = "Issue33769_Stepper",
+ Minimum = 0,
+ Increment = 3,
+ Maximum = 2,
+ Value = 1,
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ stepperStatusLabel = new Label
+ {
+ AutomationId = "Issue33769_StepperStatusLabel",
+ Text = "Failure",
+ FontSize = 16,
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ stepper.ValueChanged += (sender, e) =>
+ {
+ stepperStatusLabel.Text = (stepper.Value == stepper.Maximum || stepper.Value == stepper.Minimum)
+ ? "Success" : "Failure";
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 15,
+ Children =
+ {
+ descriptionLabel,
+ stepper,
+ stepperStatusLabel
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33772.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33772.cs
new file mode 100644
index 000000000000..bd2753eebaf1
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33772.cs
@@ -0,0 +1,83 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 33772, "Shell SearchHandler SearchBoxVisibility does not update when changed dynamically", PlatformAffected.Android)]
+public class Issue33772 : Shell
+{
+ readonly SearchHandler _searchHandler;
+ readonly Label _statusLabel;
+
+ public Issue33772()
+ {
+ _searchHandler = new SearchHandler
+ {
+ Placeholder = "Search here...",
+ AutomationId = "SearchHandler",
+ SearchBoxVisibility = SearchBoxVisibility.Collapsible
+ };
+
+ _statusLabel = new Label
+ {
+ Text = "Current: Collapsible",
+ FontSize = 18,
+ HorizontalOptions = LayoutOptions.Center,
+ AutomationId = "StatusLabel"
+ };
+
+ var expandButton = new Button
+ {
+ Text = "Change to Expanded",
+ AutomationId = "ExpandButton",
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ expandButton.Clicked += OnExpandedClicked;
+
+ var collapsibleButton = new Button
+ {
+ Text = "Change to Collapsible",
+ AutomationId = "CollapsibleButton",
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ collapsibleButton.Clicked += OnCollapsibleClicked;
+
+ var contentPage = new ContentPage
+ {
+ Title = "SearchHandler Visibility Test",
+ Content = new VerticalStackLayout
+ {
+ Padding = 10,
+ Spacing = 25,
+ Children =
+ {
+ new Label
+ {
+ Text = "SearchHandler SearchBoxVisibility Test",
+ FontSize = 18,
+ HorizontalOptions = LayoutOptions.Center,
+ AutomationId = "TitleLabel"
+ },
+ expandButton,
+ collapsibleButton,
+ _statusLabel
+ }
+ }
+ };
+
+ SetSearchHandler(contentPage, _searchHandler);
+
+ Items.Add(new ShellContent { Content = contentPage });
+ }
+
+ void OnExpandedClicked(object sender, EventArgs e)
+ {
+ _searchHandler.SearchBoxVisibility = SearchBoxVisibility.Expanded;
+ _statusLabel.Text = "Current: Expanded";
+ }
+
+ void OnCollapsibleClicked(object sender, EventArgs e)
+ {
+ _searchHandler.SearchBoxVisibility = SearchBoxVisibility.Collapsible;
+ _statusLabel.Text = "Current: Collapsible";
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33904.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33904.cs
new file mode 100644
index 000000000000..6894b3b904fe
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33904.cs
@@ -0,0 +1,61 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 33904, "CharacterSpacing applied to Label is not inherited by Span elements in FormattedString", PlatformAffected.All)]
+public class Issue33904 : ContentPage
+{
+ public Issue33904()
+ {
+ Label directLabel = new Label
+ {
+ Text = "DIRECT CHARACTER SPACING",
+ CharacterSpacing = 4,
+ FontAttributes = FontAttributes.Bold
+ };
+
+ Label overrideLabel = new Label
+ {
+ CharacterSpacing = 14,
+ FontSize = 16,
+ FormattedText = new FormattedString
+ {
+ Spans =
+ {
+ new Span { Text = "Individual ", CharacterSpacing = 0 },
+ new Span { Text = "span overrides", CharacterSpacing = 6 }
+ }
+ }
+ };
+
+ Label inheritedLabel = new Label
+ {
+ AutomationId = "InheritedCharacterSpacingLabel",
+ CharacterSpacing = 4,
+ TextColor = Colors.Purple,
+ FontSize = 16,
+ FormattedText = new FormattedString
+ {
+ Spans =
+ {
+ new Span { Text = "Inherited " },
+ new Span { Text = "character ", FontAttributes = FontAttributes.Italic },
+ new Span { Text = "spacing ", TextColor = Colors.Orange },
+ new Span { Text = "from " },
+ new Span { Text = "parent ", FontAttributes = FontAttributes.Bold },
+ new Span { Text = "label"}
+ }
+ }
+ };
+
+ VerticalStackLayout stackLayout = new VerticalStackLayout
+ {
+ Spacing = 16,
+ Padding = new Thickness(20)
+ };
+
+ stackLayout.Add(directLabel);
+ stackLayout.Add(overrideLabel);
+ stackLayout.Add(inheritedLabel);
+
+ Content = stackLayout;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33909.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33909.cs
new file mode 100644
index 000000000000..0ba3805299c2
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33909.cs
@@ -0,0 +1,101 @@
+namespace Controls.TestCases.HostApp.Issues;
+
+[Issue(IssueTracker.Github, 33909, "[iOS, Android, Catalyst] Shell.ForegroundColor does not reset correctly for the back button", PlatformAffected.iOS | PlatformAffected.Android | PlatformAffected.macOS)]
+public class Issue33909 : Shell
+{
+ public Issue33909()
+ {
+ // Disable flyout to hide hamburger menu
+ FlyoutBehavior = FlyoutBehavior.Disabled;
+
+ var applyColorButton = new Button
+ {
+ Text = "Apply Red ForegroundColor",
+ AutomationId = "ApplyColorButton"
+ };
+
+ var removeColorButton = new Button
+ {
+ Text = "Remove ForegroundColor (Reset)",
+ AutomationId = "RemoveColorButton"
+ };
+
+ var navigateButton = new Button
+ {
+ Text = "Navigate to Second Page",
+ AutomationId = "NavigateButton"
+ };
+
+ applyColorButton.Clicked += (sender, e) =>
+ {
+ // Apply red foreground color
+ Shell.SetForegroundColor(this, Colors.Red);
+ };
+
+ removeColorButton.Clicked += (sender, e) =>
+ {
+ // Reset to null (should reset to platform default)
+ Shell.SetForegroundColor(this, null);
+ };
+
+ navigateButton.Clicked += async (sender, e) =>
+ {
+ await Shell.Current.GoToAsync("secondpage");
+ };
+
+ var mainPage = new ContentPage
+ {
+ Title = "Shell ForegroundColor Test",
+ Content = new VerticalStackLayout
+ {
+ Margin = new Thickness(20),
+ Spacing = 20,
+ Children =
+ {
+ applyColorButton,
+ removeColorButton,
+ navigateButton
+ }
+ }
+ };
+
+ // Add the main page to the Shell structure directly
+ var shellItem = new ShellItem();
+ var shellSection = new ShellSection();
+ var shellContent = new ShellContent
+ {
+ Content = mainPage,
+ Title = "Main",
+ Route = "Main"
+ };
+
+ shellSection.Items.Add(shellContent);
+ shellItem.Items.Add(shellSection);
+ Items.Add(shellItem);
+ // Register route to second page
+ Routing.RegisterRoute("secondpage", typeof(Issue33909SecondPage));
+ }
+}
+
+// Separate page class for navigation
+public class Issue33909SecondPage : ContentPage
+{
+ public Issue33909SecondPage()
+ {
+ Content = new VerticalStackLayout
+ {
+ Margin = new Thickness(20),
+ Spacing = 20,
+ Children =
+ {
+ new Label
+ {
+ Text = "After Reset ForegroundColor, the back button should be default color",
+ FontSize = 16,
+ HorizontalOptions = LayoutOptions.Center,
+ AutomationId = "SecondPageLabel"
+ }
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34073.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34073.cs
new file mode 100644
index 000000000000..63ec63b9c66f
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34073.cs
@@ -0,0 +1,73 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 34073, "OnNavigatingFrom is reporting wrong DestinationPage", PlatformAffected.All)]
+public class Issue34073 : TestShell
+{
+ protected override void Init()
+ {
+ Routing.RegisterRoute(nameof(Issue34073PageB), typeof(Issue34073PageB));
+ AddContentPage(new Issue34073PageA());
+ }
+
+ public class Issue34073PageA : ContentPage
+ {
+ readonly Label _navigatingFromResult;
+
+ public Issue34073PageA()
+ {
+ _navigatingFromResult = new Label
+ {
+ Text = "Waiting...",
+ AutomationId = "NavigatingFromResult"
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Spacing = 10,
+ Padding = new Thickness(20),
+ Children =
+ {
+ new Label { Text = "Page A" },
+ new Button
+ {
+ Text = "Navigate to Page B",
+ AutomationId = "NavigateButton",
+ Command = new Command(async () =>
+ await Shell.Current.GoToAsync(nameof(Issue34073PageB)))
+ },
+ new Label { Text = "OnNavigatingFrom DestinationPage:" },
+ _navigatingFromResult
+ }
+ };
+ }
+
+ protected override void OnNavigatingFrom(NavigatingFromEventArgs args)
+ {
+ base.OnNavigatingFrom(args);
+ _navigatingFromResult.Text = args.DestinationPage?.GetType().Name ?? "null";
+ }
+ }
+
+ public class Issue34073PageB : ContentPage
+ {
+ public Issue34073PageB()
+ {
+ Content = new VerticalStackLayout
+ {
+ Spacing = 10,
+ Padding = new Thickness(20),
+ Children =
+ {
+ new Label { Text = "Page B" },
+ new Button
+ {
+ Text = "Go Back",
+ AutomationId = "GoBackButton",
+ Command = new Command(async () =>
+ await Shell.Current.GoToAsync(".."))
+ }
+ }
+ };
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34083.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34083.cs
new file mode 100644
index 000000000000..e3f8e3175699
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34083.cs
@@ -0,0 +1,32 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 34083, "Toolbar Items Do Not Reflect Shell ForegroundColor", PlatformAffected.iOS)]
+public class Issue34083 : Shell
+{
+ public Issue34083()
+ {
+ Items.Add(new Issue34083Page());
+ Shell.SetForegroundColor(this, Colors.Purple);
+ }
+
+ public class Issue34083Page : ContentPage
+ {
+ public Issue34083Page()
+ {
+ Title = "Home";
+ ToolbarItems.Add(new ToolbarItem
+ {
+ IconImageSource = "calculator.png",
+ Order = ToolbarItemOrder.Primary
+ });
+
+ Content = new Label
+ {
+ Text = "The test passes if the color is being applied to the toolbar.",
+ AutomationId = "Issue34083_DescriptionLabel",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34114.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34114.cs
new file mode 100644
index 000000000000..cf69d9acfa5e
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34114.cs
@@ -0,0 +1,50 @@
+using Microsoft.Maui.Controls.Shapes;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 34114, "Label with background clip is not working properly", PlatformAffected.iOS | PlatformAffected.macOS | PlatformAffected.UWP)]
+public class Issue34114 : ContentPage
+{
+ Label _clipTestLabel;
+ Button _changeClipButton;
+ public Issue34114()
+ {
+ _clipTestLabel = new Label
+ {
+ Text = "Clipped Label",
+ BackgroundColor = Colors.Red,
+ HorizontalTextAlignment = TextAlignment.Center,
+ VerticalTextAlignment = TextAlignment.Center,
+ WidthRequest = 300,
+ HeightRequest = 300,
+ AutomationId = "ClippedLabel",
+ Clip = new EllipseGeometry
+ {
+ Center = new Point(150, 150),
+ RadiusX = 150,
+ RadiusY = 150
+ }
+ };
+
+ _changeClipButton = new Button
+ {
+ Text = "Change Clip",
+ AutomationId = "ChangeClip"
+ };
+
+ _changeClipButton.Clicked += (s, e) =>
+ {
+ _clipTestLabel.Clip = new RoundRectangleGeometry(new CornerRadius(50), new Rect(75, 100, 150, 100));
+ };
+
+ Content = new VerticalStackLayout
+ {
+ VerticalOptions = LayoutOptions.Center,
+ Children =
+ {
+ _clipTestLabel,
+ _changeClipButton
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34122.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34122.cs
new file mode 100644
index 000000000000..ff178d08d629
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34122.cs
@@ -0,0 +1,129 @@
+using System.Collections.ObjectModel;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 34122,
+ "I5_EmptyView_Swap - Continuously turning the Toggle EmptyViews on and off would cause an item from the list to show up",
+ PlatformAffected.Android)]
+public class Issue34122 : ContentPage
+{
+ readonly ObservableCollection _items;
+ readonly List _allItems;
+ readonly View _basicEmptyView;
+ readonly View _advancedEmptyView;
+ bool _useBasicEmptyView;
+ CollectionView _collectionView;
+
+ public Issue34122()
+ {
+ _allItems = new List
+ {
+ "Baboon", "Capuchin Monkey", "Blue Monkey", "Squirrel Monkey",
+ "Golden Lion Tamarin", "Howler Monkey", "Japanese Macaque",
+ "Mandrill", "Proboscis Monkey", "Gelada"
+ };
+
+ _items = new ObservableCollection(_allItems);
+
+ // Pre-built EmptyViews reused on each toggle — same objects swapped in/out,
+ // matching the original ManualTests resource-dictionary approach.
+ _basicEmptyView = new StackLayout
+ {
+ AutomationId = "BasicEmptyView",
+ BackgroundColor = Colors.LightBlue,
+ Children =
+ {
+ new Button
+ {
+ Text = "No items to display.",
+ AutomationId = "BasicEmptyViewButton",
+ HorizontalOptions = LayoutOptions.Center,
+ FontAttributes = FontAttributes.Bold,
+ FontSize = 18
+ }
+ }
+ };
+
+ _advancedEmptyView = new StackLayout
+ {
+ AutomationId = "AdvancedEmptyView",
+ BackgroundColor = Colors.LightYellow,
+ Children =
+ {
+ new Button
+ {
+ Text = "No results matched your filter.",
+ AutomationId = "AdvancedEmptyViewButton",
+ HorizontalOptions = LayoutOptions.Center,
+ FontAttributes = FontAttributes.Bold,
+ FontSize = 18
+ },
+ new Label
+ {
+ Text = "Try a broader filter?",
+ HorizontalOptions = LayoutOptions.Center,
+ FontAttributes = FontAttributes.Italic
+ }
+ }
+ };
+
+ _collectionView = new CollectionView
+ {
+ AutomationId = "MonkeyCollectionView",
+ ItemsSource = _items,
+ EmptyView = _advancedEmptyView,
+ ItemTemplate = new DataTemplate(() =>
+ {
+ var label = new Label { FontSize = 16, Margin = new Thickness(10, 5) };
+ label.SetBinding(Label.TextProperty, ".");
+ label.SetBinding(Label.AutomationIdProperty, ".");
+ return label;
+ })
+ };
+
+ var filterButton = new Button
+ {
+ Text = "Apply Filter (Clear All)",
+ AutomationId = "FilterButton"
+ };
+ filterButton.Clicked += OnFilterButtonClicked;
+
+ var toggleButton = new Button
+ {
+ Text = "Toggle EmptyView",
+ AutomationId = "ToggleEmptyViewButton"
+ };
+ toggleButton.Clicked += OnToggleEmptyViewClicked;
+
+ Content = new Grid
+ {
+ RowDefinitions =
+ [
+ new RowDefinition(GridLength.Auto),
+ new RowDefinition(GridLength.Star),
+ ],
+ Children =
+ {
+ new VerticalStackLayout { Children = { filterButton, toggleButton } },
+ _collectionView
+ }
+ };
+
+ Grid.SetRow(_collectionView, 1);
+ }
+
+ void OnFilterButtonClicked(object sender, EventArgs e)
+ {
+ // Remove items one-by-one (matching the original ManualTests FilterCommand),
+ // which fires multiple CollectionChanged events and puts RecyclerView into
+ // the state that triggers the EmptyView-swap bug.
+ foreach (var name in _allItems)
+ _items.Remove(name);
+ }
+
+ void OnToggleEmptyViewClicked(object sender, EventArgs e)
+ {
+ _useBasicEmptyView = !_useBasicEmptyView;
+ _collectionView.EmptyView = _useBasicEmptyView ? _basicEmptyView : _advancedEmptyView;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34143.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34143.cs
new file mode 100644
index 000000000000..a7e05fa1c117
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34143.cs
@@ -0,0 +1,152 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 34143, "Tab bar ghosting issue after navigating from modal via GoToAsync", PlatformAffected.iOS)]
+public class Issue34143 : TestShell
+{
+ protected override void Init()
+ {
+ var homeShellContent = new ShellContent
+ {
+ Title = "Home",
+ Route = "Issue34143Home",
+ ContentTemplate = new DataTemplate(() => new Issue34143MainPage())
+ };
+
+ var tab1 = new Tab
+ {
+ Title = "Tab 1",
+ Route = "Issue34143Tab1",
+ AutomationId = "Issue34143Tab1",
+ Icon = "coffee.png",
+ Items =
+ {
+ new ShellContent
+ {
+ Route = "Issue34143Tab1Content",
+ ContentTemplate = new DataTemplate(() => new Issue34143TabPage("Tab 1 Content"))
+ }
+ }
+ };
+
+ var tab2 = new Tab
+ {
+ Title = "Tab 2",
+ Route = "Issue34143Tab2",
+ AutomationId = "Issue34143Tab2",
+ Icon = "coffee.png",
+ Items =
+ {
+ new ShellContent
+ {
+ Route = "Issue34143Tab2Content",
+ ContentTemplate = new DataTemplate(() => new Issue34143TabPage("Tab 2 Content"))
+ }
+ }
+ };
+
+ var tab3 = new Tab
+ {
+ Title = "Tab 3",
+ Route = "Issue34143Tab3",
+ AutomationId = "Issue34143Tab3",
+ Icon = "coffee.png",
+ Items =
+ {
+ new ShellContent
+ {
+ Route = "Issue34143Tab3Content",
+ ContentTemplate = new DataTemplate(() => new Issue34143TabPage("Tab 3 Content"))
+ }
+ }
+ };
+
+ var tabBar = new TabBar
+ {
+ Items = { tab1, tab2, tab3 }
+ };
+
+ Items.Add(homeShellContent);
+ Items.Add(tabBar);
+ }
+
+ public class Issue34143MainPage : ContentPage
+ {
+ public Issue34143MainPage()
+ {
+ BackgroundColor = Colors.White;
+ Content = new VerticalStackLayout
+ {
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ Children =
+ {
+ new Label
+ {
+ Text = "Home Page",
+ AutomationId = "Issue34143HomeLabel",
+ HorizontalOptions = LayoutOptions.Center
+ },
+ new Button
+ {
+ Text = "Push Modal",
+ AutomationId = "Issue34143PushModal",
+ Command = new Command(async () =>
+ await Shell.Current.Navigation.PushModalAsync(new Issue34143ModalPage()))
+ }
+ }
+ };
+ }
+ }
+
+ public class Issue34143ModalPage : ContentPage
+ {
+ public Issue34143ModalPage()
+ {
+ BackgroundColor = Colors.White;
+ Title = "Modal";
+ Content = new VerticalStackLayout
+ {
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ Children =
+ {
+ new Label
+ {
+ Text = "Modal Page",
+ AutomationId = "Issue34143ModalLabel",
+ HorizontalOptions = LayoutOptions.Center
+ },
+ new Button
+ {
+ Text = "Go to Tab Bar",
+ AutomationId = "Issue34143GoToTabBar",
+ Command = new Command(async () =>
+ await Shell.Current.GoToAsync("//Issue34143Tab1"))
+ }
+ }
+ };
+ }
+ }
+
+ public class Issue34143TabPage : ContentPage
+ {
+ public Issue34143TabPage(string contentText)
+ {
+ BackgroundColor = Colors.White;
+ Content = new VerticalStackLayout
+ {
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ Children =
+ {
+ new Label
+ {
+ Text = contentText,
+ AutomationId = "Issue34143TabContent",
+ HorizontalOptions = LayoutOptions.Center
+ }
+ }
+ };
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs
new file mode 100644
index 000000000000..4245292fdda1
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34165.cs
@@ -0,0 +1,39 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 34165, "CollectionView is scrolling left/right when the collection is empty and inside a RefreshView", PlatformAffected.iOS | PlatformAffected.macOS)]
+public class Issue34165 : ContentPage
+{
+ public const string CollectionViewId = "CollectionView";
+ public const string EmptyViewLabelId = "EmptyViewLabel";
+ public const string RefreshViewId = "RefreshView";
+
+ public Issue34165()
+ {
+ var emptyViewLabel = new Label
+ {
+ Text = "No items — swipe left/right here to test",
+ AutomationId = EmptyViewLabelId,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ };
+
+ var collectionView = new CollectionView
+ {
+ AutomationId = CollectionViewId,
+ EmptyView = emptyViewLabel,
+ };
+
+ var refreshView = new RefreshView
+ {
+ AutomationId = RefreshViewId,
+ Content = collectionView,
+ };
+
+ refreshView.Refreshing += (s, e) =>
+ {
+ ((RefreshView)s!).IsRefreshing = false;
+ };
+
+ Content = refreshView;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34210.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34210.cs
new file mode 100644
index 000000000000..08fc629b6986
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34210.cs
@@ -0,0 +1,59 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 34210, "SwipeItem ignores FontImageSource rendered size on Android", PlatformAffected.Android)]
+public class Issue34210 : ContentPage
+{
+ public Issue34210()
+ {
+ var swipeItem = new SwipeItem
+ {
+ Text = "Action",
+ BackgroundColor = Colors.Blue,
+ IconImageSource = new FontImageSource
+ {
+ FontFamily = "FA",
+ Glyph = "\uf0f3",
+ Size = 20,
+ }
+ };
+
+ var swipeView = new SwipeView
+ {
+ HeightRequest = 100,
+ LeftItems = new SwipeItems { swipeItem },
+ Content = new Grid
+ {
+ HeightRequest = 100,
+ BackgroundColor = Colors.LightGray,
+ Children =
+ {
+ new Label
+ {
+ Text = "Swipe right to reveal",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ AutomationId = "SwipeContent"
+ }
+ }
+ }
+ };
+
+ var openButton = new Button
+ {
+ Text = "Open SwipeView",
+ AutomationId = "OpenSwipeViewButton"
+ };
+
+ openButton.Clicked += (sender, e) =>
+ {
+ swipeView.Open(OpenSwipeItem.LeftItems);
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 16,
+ Spacing = 8,
+ Children = { swipeView, openButton }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34211.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34211.cs
new file mode 100644
index 000000000000..5985ef89b8e6
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34211.cs
@@ -0,0 +1,88 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 34211, "Android display-size change causes parent and drawable children mismatch in .NET MAUI", PlatformAffected.Android)]
+public class Issue34211 : ContentPage
+{
+ readonly Issue34211_Drawable _drawable = new();
+ GraphicsView _graphicsView;
+ Label _statusLabel;
+
+ public Issue34211()
+ {
+ _graphicsView = new GraphicsView
+ {
+ AutomationId = "Issue34211_GraphicsView",
+ BackgroundColor = Color.FromArgb("#F0F0F0"),
+ Drawable = _drawable,
+ };
+
+ _statusLabel = new Label
+ {
+ AutomationId = "Issue34211_StatusLabel",
+ Text = "Waiting for first draw...",
+ HorizontalOptions = LayoutOptions.Center,
+ Margin = new Thickness(0, 0, 0, 10),
+ };
+
+ var checkButton = new Button
+ {
+ AutomationId = "Issue34211_CheckButton",
+ Text = "Check Size Match",
+ };
+ checkButton.Clicked += (_, _) => _graphicsView.Invalidate();
+
+ Content = new Grid
+ {
+ Padding = 20,
+ RowDefinitions =
+ {
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Star },
+ },
+ Children =
+ {
+ _statusLabel,
+ checkButton,
+ _graphicsView,
+ }
+ };
+
+ Grid.SetRow(_statusLabel, 0);
+ Grid.SetRow(checkButton, 1);
+ Grid.SetRow(_graphicsView, 2);
+
+ _graphicsView.SizeChanged += (_, _) => _graphicsView.Invalidate();
+
+ _drawable.OnDrawn = rect =>
+ {
+ MainThread.BeginInvokeOnMainThread(() =>
+ {
+ double viewW = _graphicsView.Width;
+ double viewH = _graphicsView.Height;
+ bool widthMatch = Math.Abs(viewW - rect.Width) <= 1.0;
+ bool heightMatch = Math.Abs(viewH - rect.Height) <= 1.0;
+ _statusLabel.Text = widthMatch && heightMatch
+ ? "PASS: sizes match"
+ : $"FAIL: GraphicsView={viewW:F1}x{viewH:F1} Drawable={rect.Width:F1}x{rect.Height:F1}";
+ });
+ };
+ }
+}
+
+public class Issue34211_Drawable : IDrawable
+{
+ public Action OnDrawn { get; set; }
+
+ public void Draw(ICanvas canvas, RectF dirtyRect)
+ {
+ canvas.FillColor = Colors.CornflowerBlue;
+ canvas.FillRectangle(dirtyRect);
+
+ canvas.StrokeColor = Colors.DarkBlue;
+ canvas.StrokeSize = 3;
+ canvas.DrawRectangle(dirtyRect);
+
+ OnDrawn?.Invoke(dirtyRect);
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34247.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34247.cs
new file mode 100644
index 000000000000..184755af5a0a
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34247.cs
@@ -0,0 +1,69 @@
+using System.Collections.ObjectModel;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 34247, "CollectionView with HeaderTemplate and SelectionMode.Single crashes on selection", PlatformAffected.Android)]
+public class Issue34247 : ContentPage
+{
+ public Issue34247()
+ {
+ var layout = new StackLayout();
+
+ var resultLabel = new Label()
+ {
+ Text = "Select an item to test",
+ AutomationId = "ResultLabel"
+ };
+
+ var items = new ObservableCollection { "Item 1", "Item 2", "Item 3" };
+
+ var collectionView = new CollectionView()
+ {
+ SelectionMode = SelectionMode.Single,
+ AutomationId = "TestCollectionView",
+ HeightRequest = 300
+ };
+
+ collectionView.HeaderTemplate = new DataTemplate(() =>
+ {
+ return new Label
+ {
+ Text = "Header",
+ FontSize = 18,
+ FontAttributes = FontAttributes.Bold,
+ Margin = new Thickness(10)
+ };
+ });
+
+ collectionView.FooterTemplate = new DataTemplate(() =>
+ {
+ return new Label
+ {
+ Text = "Footer",
+ FontSize = 18,
+ FontAttributes = FontAttributes.Bold,
+ Margin = new Thickness(10)
+ };
+ });
+
+ collectionView.ItemTemplate = new DataTemplate(() =>
+ {
+ var label = new Label();
+ label.SetBinding(Label.TextProperty, ".");
+ label.Margin = new Thickness(10);
+ label.FontSize = 16;
+ return label;
+ });
+
+ collectionView.ItemsSource = items;
+ collectionView.SelectionChanged += (s, e) =>
+ {
+ resultLabel.Text = "Success";
+ };
+
+ layout.Children.Add(resultLabel);
+ layout.Children.Add(collectionView);
+
+ Content = layout;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34273.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34273.cs
new file mode 100644
index 000000000000..d9d3ca2177e1
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34273.cs
@@ -0,0 +1,64 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 34273, "Rotating simulator makes Stepper and Label overlap", PlatformAffected.iOS)]
+public class Issue34273 : NavigationPage
+{
+ public Issue34273() : base(new MainPage())
+ {
+ }
+
+ public class MainPage : ContentPage
+ {
+ public MainPage()
+ {
+ var stepper = new Stepper
+ {
+ AutomationId = "CursorStepper",
+ Minimum = 0
+ };
+
+ var stepperValueLabel = new Label
+ {
+ AutomationId = "CursorPositionLabel",
+ VerticalOptions = LayoutOptions.Center,
+ VerticalTextAlignment = TextAlignment.Center
+ };
+
+ var editor = new Editor
+ {
+ AutomationId = "TestEditor",
+ Text = "testing"
+ };
+
+ stepper.BindingContext = editor;
+ stepper.SetBinding(Stepper.MaximumProperty, new Binding("Text.Length"));
+
+ stepperValueLabel.BindingContext = stepper;
+ stepperValueLabel.SetBinding(Label.TextProperty, new Binding("Value"));
+
+ editor.BindingContext = stepper;
+ editor.SetBinding(Editor.CursorPositionProperty, new Binding("Value", BindingMode.TwoWay));
+
+ Content = new ScrollView
+ {
+ Content = new VerticalStackLayout
+ {
+ Children =
+ {
+ new Label { Text = "1. Click or tap within the editor below to place the cursor within the middle of the string 'testing'. (NOTE: On iOS, just tapping the entry will place the cursor at the beginning or end. You must touch and hold, and then move the cursor to position it if you want it in the middle of the string.)" },
+ new Label { Text = "2. The test fails if the label next to the -/+ controls does not update to the index of the cursor position." },
+ new Label { Text = "3. For example, if you place the cursor at the beginning of the editor, the label should display 0." },
+ new Label { Text = "4. Play with the - and + buttons and observe if the cursor position within the editor changes." },
+ new Label { Text = "5. If the focus has left the editor, press the Tab key to return focus to the editor." },
+ new Label { Text = "6. The test fails if the cursor position within the editor has not changed to reflect the index shown." },
+ new HorizontalStackLayout
+ {
+ Children = { stepper, stepperValueLabel }
+ },
+ editor
+ }
+ }
+ };
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34336.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34336.cs
new file mode 100644
index 000000000000..dced98fda404
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34336.cs
@@ -0,0 +1,104 @@
+using System.Collections.ObjectModel;
+using Microsoft.Maui.Controls.Shapes;
+
+namespace Maui.Controls.Sample.Issues;
+
+// Reproduces: [iOS] CollectionView has excessive height if ObservableCollection source delayed in loading
+// A horizontal CollectionView inside a Grid row set to Auto should resize to fit its content
+// even when the ItemsSource is populated after a delay (after the page has already appeared).
+[Issue(IssueTracker.Github, 34336,
+ "[iOS] CollectionView has excessive height if ObservableCollection source delayed in loading",
+ PlatformAffected.iOS)]
+public class Issue34336 : ContentPage
+{
+ readonly ObservableCollection _items = [];
+
+ public Issue34336()
+ {
+ // Inline DataTemplate mirroring the ItemCard ContentView from the repro repo
+ var itemTemplate = new DataTemplate(() =>
+ {
+ var nameLabel = new Label { FontSize = 14, TextTransform = TextTransform.Uppercase };
+ nameLabel.SetBinding(Label.TextProperty, "Name");
+ // AutomationId is bound to Name so the test can reliably wait for "ITEM 0" on all platforms
+ nameLabel.SetBinding(Label.AutomationIdProperty, "Name");
+
+ var descLabel = new Label { LineBreakMode = LineBreakMode.WordWrap };
+ descLabel.SetBinding(Label.TextProperty, "Description");
+
+ var textStack = new VerticalStackLayout { Spacing = 2 };
+ textStack.Children.Add(nameLabel);
+ textStack.Children.Add(descLabel);
+ Grid.SetColumn(textStack, 1);
+
+ var cardGrid = new Grid { VerticalOptions = LayoutOptions.Start };
+ cardGrid.ColumnDefinitions.Add(new ColumnDefinition(150));
+ cardGrid.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Star));
+ cardGrid.RowDefinitions.Add(new RowDefinition(GridLength.Auto));
+ cardGrid.Children.Add(new BoxView { Color = Colors.LightGreen, HeightRequest = 150, WidthRequest = 150 });
+ cardGrid.Children.Add(textStack);
+
+ return new Border
+ {
+ BackgroundColor = Colors.DarkGray,
+ StrokeShape = new RoundRectangle { CornerRadius = 5 },
+ MinimumHeightRequest = 250,
+ Content = cardGrid
+ };
+ });
+
+ // CollectionView inside a Grid row set to Auto — mirrors the repro project layout
+ var collectionView = new CollectionView
+ {
+ AutomationId = "ItemCollection",
+ MinimumHeightRequest = 200,
+ ItemsSource = _items,
+ ItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Horizontal) { ItemSpacing = 7.5 },
+ ItemTemplate = itemTemplate
+ };
+
+ var innerGrid = new Grid
+ {
+ BackgroundColor = Colors.Gray
+ };
+ innerGrid.RowDefinitions.Add(new RowDefinition(GridLength.Auto));
+ innerGrid.Children.Add(collectionView);
+
+ // Label positioned directly below the CollectionView's grid row.
+ // Used by the test to measure that the CollectionView did not take up excessive space.
+ var belowLabel = new Label
+ {
+ AutomationId = "BelowCollectionViewLabel",
+ Text = "Below CollectionView",
+ BackgroundColor = Colors.LightBlue,
+ HorizontalTextAlignment = TextAlignment.Center
+ };
+
+ var outerGrid = new Grid();
+ outerGrid.RowDefinitions.Add(new RowDefinition(GridLength.Auto));
+ outerGrid.RowDefinitions.Add(new RowDefinition(GridLength.Star));
+ outerGrid.Children.Add(innerGrid);
+ Grid.SetRow(belowLabel, 1);
+ outerGrid.Children.Add(belowLabel);
+
+ Content = outerGrid;
+
+ _ = LoadDataAsync();
+ }
+
+ async Task LoadDataAsync()
+ {
+ // 1-second delay to replicate the original DataService.LoadData() in the repro repo.
+ // This is the trigger for the bug: items arrive after the page has already appeared.
+ await Task.Delay(1000);
+
+ for (int i = 0; i < 10; i++)
+ {
+ _items.Add(new Issue34336Item(
+ $"ITEM {i}",
+ $"This is the description for item {i}."));
+ }
+ }
+}
+
+record Issue34336Item(string Name, string Description);
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34343.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34343.cs
new file mode 100644
index 000000000000..ae110ed397ac
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34343.cs
@@ -0,0 +1,146 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 34343, "TabBar displays wrong tabs after first tab becomes invisible", PlatformAffected.Android | PlatformAffected.iOS)]
+public class Issue34343 : Shell
+{
+ ContentPage _tab1Page;
+ public Issue34343()
+ {
+ Routing.RegisterRoute("Page51", typeof(Issue34343_Page51));
+
+ var tabBar = new TabBar();
+ var button = new Button
+ {
+ Text = "Hide Tab1 and Go to Tab5",
+ AutomationId = "HideAndNavigateButton",
+ };
+
+ _tab1Page = new ContentPage
+ {
+ Title = "Tab1",
+ Content = new VerticalStackLayout
+ {
+ Children = { button }
+ }
+ };
+
+ button.Clicked += async (s, e) =>
+ {
+ // Setting IsVisible = false on the page (as in the issue repro: "this.IsVisible = false")
+ _tab1Page.IsVisible = false;
+ await Shell.Current.GoToAsync("///Tab5");
+ };
+
+ var tab1Content = new ShellContent
+ {
+ Title = "Tab1",
+ Route = "Tab1",
+ Content = _tab1Page
+ };
+
+ var tab1 = new Tab { Title = "Tab1", AutomationId = "Tab1" };
+ tab1.Items.Add(tab1Content);
+ tabBar.Items.Add(tab1);
+
+ for (int i = 2; i <= 4; i++)
+ {
+ int tabNum = i;
+ var content = new ShellContent
+ {
+ Title = $"Tab{tabNum}",
+ Route = $"Tab{tabNum}",
+ ContentTemplate = new DataTemplate(() => new ContentPage
+ {
+ Title = $"Tab{tabNum}",
+ Content = new Label
+ {
+ Text = $"Tab{tabNum} Content",
+ AutomationId = $"Tab{tabNum}Content",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ }
+ })
+ };
+ var tab = new Tab { Title = $"Tab{tabNum}", AutomationId = $"Tab{tabNum}" };
+ tab.Items.Add(content);
+ tabBar.Items.Add(tab);
+ }
+
+ // Tab 5 - has a button to navigate to Page51 (iOS repro: relative sub-page navigation)
+ var navigateToPage51Button = new Button
+ {
+ Text = "Navigate to Page51",
+ AutomationId = "NavigateToPage51Button",
+ };
+ navigateToPage51Button.Clicked += async (s, e) =>
+ {
+ await Shell.Current.GoToAsync("Page51");
+ };
+
+ var tab5Page = new ContentPage
+ {
+ Title = "Tab5",
+ Content = new VerticalStackLayout
+ {
+ VerticalOptions = LayoutOptions.Center,
+ Children =
+ {
+ new Label
+ {
+ Text = "Tab5 Content",
+ AutomationId = "Tab5Content",
+ HorizontalOptions = LayoutOptions.Center
+ },
+ navigateToPage51Button
+ }
+ }
+ };
+
+ var tab5Content = new ShellContent { Title = "Tab5", Route = "Tab5", Content = tab5Page };
+ var tab5 = new Tab { Title = "Tab5", AutomationId = "Tab5" };
+ tab5.Items.Add(tab5Content);
+ tabBar.Items.Add(tab5);
+
+ for (int i = 6; i <= 7; i++)
+ {
+ int tabNum = i;
+ var content = new ShellContent
+ {
+ Title = $"Tab{tabNum}",
+ Route = $"Tab{tabNum}",
+ ContentTemplate = new DataTemplate(() => new ContentPage
+ {
+ Title = $"Tab{tabNum}",
+ Content = new Label
+ {
+ Text = $"Tab{tabNum} Content",
+ AutomationId = $"Tab{tabNum}Content",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ }
+ })
+ };
+ var tab = new Tab { Title = $"Tab{tabNum}", AutomationId = $"Tab{tabNum}" };
+ tab.Items.Add(content);
+ tabBar.Items.Add(tab);
+ }
+
+ Items.Add(tabBar);
+ }
+}
+
+// Sub-page navigated to from Tab5 (relative route "Page51")
+public class Issue34343_Page51 : ContentPage
+{
+ public Issue34343_Page51()
+ {
+ Title = "Page51";
+ Content = new Label
+ {
+ Text = "Page51 Content",
+ AutomationId = "Page51Content",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34464.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34464.cs
new file mode 100644
index 000000000000..685941b45cb8
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34464.cs
@@ -0,0 +1,82 @@
+using System.Collections.Generic;
+using Microsoft.Maui;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Graphics;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 34464, "FlexLayout with BindableLayout and Label text display", PlatformAffected.All)]
+public class Issue34464 : ContentPage
+{
+ public Issue34464()
+ {
+ var items = new List
+ {
+ "Some Medium Text",
+ "Shorter Text",
+ "Slightly More Text",
+ "One - Two",
+ "Two - Four",
+ "Two - Three",
+ "One - Eleven",
+ };
+
+ var flexLayout = new FlexLayout
+ {
+ Wrap = Microsoft.Maui.Layouts.FlexWrap.Wrap,
+ AutomationId = "TestFlexLayout"
+ };
+
+ BindableLayout.SetItemsSource(flexLayout, items);
+ BindableLayout.SetItemTemplate(flexLayout, new DataTemplate(() =>
+ {
+ var border = new Border
+ {
+ BackgroundColor = Color.FromArgb("#ffcccccc"),
+ Stroke = Color.FromArgb("#ffb8b8b8"),
+ Padding = new Thickness(12)
+ };
+
+ FlexLayout.SetGrow(border, 1);
+ FlexLayout.SetShrink(border, 0);
+
+ var backgroundBorder = new Border
+ {
+ BackgroundColor = Color.FromArgb("#44ff0000")
+ };
+
+ var label = new Label
+ {
+ LineBreakMode = LineBreakMode.NoWrap,
+ FontSize = 20,
+ HorizontalTextAlignment = TextAlignment.Center,
+ VerticalTextAlignment = TextAlignment.Center,
+ TextColor = Color.FromArgb("#ff000000")
+ };
+
+ label.SetBinding(Label.TextProperty, ".");
+
+ var grid = new Grid
+ {
+ Children = { backgroundBorder, label }
+ };
+
+ border.Content = grid;
+
+ return border;
+ }));
+
+ var headerLabel = new Label
+ {
+ Text = "FlexLayout with BindableLayout Items:",
+ FontSize = 16,
+ Margin = new Thickness(10),
+ AutomationId = "HeaderLabel"
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Children = { headerLabel, flexLayout }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue8486.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue8486.cs
new file mode 100644
index 000000000000..15d5948b91a8
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue8486.cs
@@ -0,0 +1,40 @@
+namespace Maui.Controls.Sample.Issues
+{
+
+ [Issue(IssueTracker.Github, 8486, "GraphicsView DrawString not rendering in iOS", PlatformAffected.iOS)]
+ public class Issue8486 : ContentPage
+ {
+ public Issue8486()
+ {
+ var graphicsView = new GraphicsView
+ {
+ AutomationId = "GraphicsView",
+ HeightRequest = 200,
+ BackgroundColor = Colors.White,
+ Drawable = new GraphicsDrawable()
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Children = { graphicsView }
+ };
+ }
+
+ public class GraphicsDrawable : IDrawable
+ {
+ public void Draw(ICanvas canvas, RectF dirtyRect)
+ {
+ float centerX = dirtyRect.Center.X;
+
+ canvas.FontColor = Colors.Black;
+ canvas.FontSize = 18;
+ canvas.Font = Microsoft.Maui.Graphics.Font.Default;
+
+ canvas.DrawString("LeftAligned", centerX, 30, HorizontalAlignment.Left);
+ canvas.DrawString("CenterAligned", centerX, 90, HorizontalAlignment.Center);
+ canvas.DrawString("RightAligned", centerX, 150, HorizontalAlignment.Right);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Resources/Images/img_0111.heic b/src/Controls/tests/TestCases.HostApp/Resources/Images/img_0111.heic
new file mode 100644
index 000000000000..e1319d32ad54
Binary files /dev/null and b/src/Controls/tests/TestCases.HostApp/Resources/Images/img_0111.heic differ
diff --git a/src/Controls/tests/TestCases.HostApp/Resources/Images/subfolder/dotnet_bot_red.png b/src/Controls/tests/TestCases.HostApp/Resources/Images/subfolder/dotnet_bot_red.png
new file mode 100644
index 000000000000..a6dd48ef09d5
Binary files /dev/null and b/src/Controls/tests/TestCases.HostApp/Resources/Images/subfolder/dotnet_bot_red.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ArabicStringShouldBeLeftToRight.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ArabicStringShouldBeLeftToRight.png
new file mode 100644
index 000000000000..53560206d32e
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ArabicStringShouldBeLeftToRight.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BackButtonBehavior_IconOverride_CustomIconShownOnBackButton.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BackButtonBehavior_IconOverride_CustomIconShownOnBackButton.png
new file mode 100644
index 000000000000..220fae4f6bc8
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BackButtonBehavior_IconOverride_CustomIconShownOnBackButton.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks.png
new file mode 100644
index 000000000000..482c151d4195
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipNull_NoCrash.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipNull_NoCrash.png
new file mode 100644
index 000000000000..39e4b99b0ebb
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipNull_NoCrash.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithNestedContent.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithNestedContent.png
new file mode 100644
index 000000000000..60942a97a427
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithNestedContent.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithRotation.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithRotation.png
new file mode 100644
index 000000000000..42eab6bff47b
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithRotationAndScale.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithRotationAndScale.png
new file mode 100644
index 000000000000..a5050ff6598d
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithRotationAndScale.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithShadow.png
new file mode 100644
index 000000000000..8cd6ed191f6e
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithStrokeColorBlue.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithStrokeColorBlue.png
new file mode 100644
index 000000000000..811e8d9341db
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithStrokeColorBlue.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithStrokeColorGreen.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithStrokeColorGreen.png
new file mode 100644
index 000000000000..7792fcc25e6b
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithStrokeColorGreen.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithStrokeShapeRoundRectangle.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithStrokeShapeRoundRectangle.png
new file mode 100644
index 000000000000..ff098bc0b46a
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithStrokeShapeRoundRectangle.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithStrokeThickness.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithStrokeThickness.png
new file mode 100644
index 000000000000..8dea32bfc8a1
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Border_ClipWithStrokeThickness.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BoxView_ClipWithColorGreen.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BoxView_ClipWithColorGreen.png
new file mode 100644
index 000000000000..0c4eacd5fa66
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BoxView_ClipWithColorGreen.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BoxView_ClipWithCornerRadius.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BoxView_ClipWithCornerRadius.png
new file mode 100644
index 000000000000..11f74a52e47f
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BoxView_ClipWithCornerRadius.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BoxView_ClipWithRotation.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BoxView_ClipWithRotation.png
new file mode 100644
index 000000000000..0b7cd2224f9a
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BoxView_ClipWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BoxView_ClipWithShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BoxView_ClipWithShadow.png
new file mode 100644
index 000000000000..0cf4f76b9db2
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/BoxView_ClipWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ButtonRTLTextAndImageShouldNotOverlap.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ButtonRTLTextAndImageShouldNotOverlap.png
new file mode 100644
index 000000000000..034307a40161
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ButtonRTLTextAndImageShouldNotOverlap.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Button_ClipNull_NoCrash.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Button_ClipNull_NoCrash.png
new file mode 100644
index 000000000000..6a07cd87dbc5
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Button_ClipNull_NoCrash.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Button_ClipWithImageSource.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Button_ClipWithImageSource.png
new file mode 100644
index 000000000000..211c7675eeec
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Button_ClipWithImageSource.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Button_ClipWithScale.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Button_ClipWithScale.png
new file mode 100644
index 000000000000..397e2a127f31
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Button_ClipWithScale.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Button_ClipWithShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Button_ClipWithShadow.png
new file mode 100644
index 000000000000..3dae753ba4c8
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Button_ClipWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Button_ClipWithText.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Button_ClipWithText.png
new file mode 100644
index 000000000000..42c055234850
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Button_ClipWithText.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CheckBox_ChangeColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CheckBox_ChangeColor_VerifyVisualState.png
index 3a06d3b9bf0e..1c1107ddce7f 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CheckBox_ChangeColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CheckBox_ChangeColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png
index 1a1b79161d65..0896111bb74a 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CheckBox_VerifyWithShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CheckBox_VerifyWithShadow.png
new file mode 100644
index 000000000000..189a82615021
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CheckBox_VerifyWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CollectionViewShouldChangeItemsLayout.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CollectionViewShouldChangeItemsLayout.png
new file mode 100644
index 000000000000..2c7a2e5e09cc
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CollectionViewShouldChangeItemsLayout.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CompareStateTrigger_Checked.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CompareStateTrigger_Checked.png
new file mode 100644
index 000000000000..06ce7f6e44db
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CompareStateTrigger_Checked.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CompareStateTrigger_Unchecked.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CompareStateTrigger_Unchecked.png
new file mode 100644
index 000000000000..2b67bcac5088
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/CompareStateTrigger_Unchecked.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipNull_NoCrash.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipNull_NoCrash.png
new file mode 100644
index 000000000000..7de722ba624a
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipNull_NoCrash.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipWithEllipseGeometry.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipWithEllipseGeometry.png
new file mode 100644
index 000000000000..ac8835688f52
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipWithEllipseGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipWithNestedClippedContent.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipWithNestedClippedContent.png
new file mode 100644
index 000000000000..23b1d921423d
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipWithNestedClippedContent.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipWithRectangleGeometry.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipWithRectangleGeometry.png
new file mode 100644
index 000000000000..441245f06b6e
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipWithRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipWithRoundRectangleGeometry.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipWithRoundRectangleGeometry.png
new file mode 100644
index 000000000000..bbdb0edf90c2
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipWithRoundRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipWithShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipWithShadow.png
new file mode 100644
index 000000000000..10f8df6a98b0
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ContentView_ClipWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/DataTrigger_ButtonDisabled.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/DataTrigger_ButtonDisabled.png
new file mode 100644
index 000000000000..1136fa6ef3f7
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/DataTrigger_ButtonDisabled.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/DataTrigger_ButtonEnabled.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/DataTrigger_ButtonEnabled.png
new file mode 100644
index 000000000000..47ac2424ded8
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/DataTrigger_ButtonEnabled.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/DrawStringShouldDrawText.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/DrawStringShouldDrawText.png
new file mode 100644
index 000000000000..ee9d45c09f3d
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/DrawStringShouldDrawText.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/DrawTextWithinBounds.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/DrawTextWithinBounds.png
new file mode 100644
index 000000000000..16bf91d858e4
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/DrawTextWithinBounds.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/EnsureSearchBarExplicitSize.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/EnsureSearchBarExplicitSize.png
new file mode 100644
index 000000000000..778084669101
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/EnsureSearchBarExplicitSize.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/EventTrigger_InvalidText.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/EventTrigger_InvalidText.png
new file mode 100644
index 000000000000..a2f9f3ff30c2
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/EventTrigger_InvalidText.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/EventTrigger_ValidNumeric.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/EventTrigger_ValidNumeric.png
new file mode 100644
index 000000000000..410da10b7f11
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/EventTrigger_ValidNumeric.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlexLayoutWithBindableLayoutDisplaysLabels.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlexLayoutWithBindableLayoutDisplaysLabels.png
new file mode 100644
index 000000000000..a71c9b410b12
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlexLayoutWithBindableLayoutDisplaysLabels.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutIconRemainsVisible.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutIconRemainsVisible.png
new file mode 100644
index 000000000000..2d742f677df8
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutIconRemainsVisible.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutIconUpdatedAfterInsertPageBefore.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutIconUpdatedAfterInsertPageBefore.png
new file mode 100644
index 000000000000..f78fcba49019
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutIconUpdatedAfterInsertPageBefore.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/GraphicsViewBackgroundShouldBeApplied.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/GraphicsViewBackgroundShouldBeApplied.png
new file mode 100644
index 000000000000..8346ef752d6a
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/GraphicsViewBackgroundShouldBeApplied.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/GraphicsViewBackgroundShouldBeChanged.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/GraphicsViewBackgroundShouldBeChanged.png
new file mode 100644
index 000000000000..a168ba50a6e0
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/GraphicsViewBackgroundShouldBeChanged.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/GroupedCollectionViewItems.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/GroupedCollectionViewItems.png
new file mode 100644
index 000000000000..8913e14007ec
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/GroupedCollectionViewItems.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/HEICImageShouldNotRenderUpsideDown.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/HEICImageShouldNotRenderUpsideDown.png
new file mode 100644
index 000000000000..049f7f874500
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/HEICImageShouldNotRenderUpsideDown.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageButton_ClipWithEllipseGeometry.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageButton_ClipWithEllipseGeometry.png
new file mode 100644
index 000000000000..d259a107cdbc
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageButton_ClipWithEllipseGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageButton_ClipWithRectangleGeometry.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageButton_ClipWithRectangleGeometry.png
new file mode 100644
index 000000000000..89b07e826e1b
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageButton_ClipWithRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageButton_ClipWithRoundRectangleGeometry.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageButton_ClipWithRoundRectangleGeometry.png
new file mode 100644
index 000000000000..415b13f76431
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageButton_ClipWithRoundRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageButton_ClipWithScale.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageButton_ClipWithScale.png
new file mode 100644
index 000000000000..3cc3d6627994
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageButton_ClipWithScale.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageButton_ClipWithShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageButton_ClipWithShadow.png
new file mode 100644
index 000000000000..b15f316c37de
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageButton_ClipWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageShouldLoadFromSubfolder.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageShouldLoadFromSubfolder.png
new file mode 100644
index 000000000000..4d713f4fc373
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ImageShouldLoadFromSubfolder.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipNull_NoCrash.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipNull_NoCrash.png
new file mode 100644
index 000000000000..c2797ab8f711
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipNull_NoCrash.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithArcSegmentPath.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithArcSegmentPath.png
new file mode 100644
index 000000000000..2a7bde60440a
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithArcSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithBezierSegmentPath.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithBezierSegmentPath.png
new file mode 100644
index 000000000000..de434554b190
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithBezierSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithComplexPolyBezierAndRotation.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithComplexPolyBezierAndRotation.png
new file mode 100644
index 000000000000..2e7242bac2fa
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithComplexPolyBezierAndRotation.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithComplexPolyLineGeometry.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithComplexPolyLineGeometry.png
new file mode 100644
index 000000000000..b290219b69d0
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithComplexPolyLineGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithEllipseGeometry.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithEllipseGeometry.png
new file mode 100644
index 000000000000..1306a03026a5
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithEllipseGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithGeometryGroup.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithGeometryGroup.png
new file mode 100644
index 000000000000..f575d04e62a7
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithGeometryGroup.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithLineSegmentPath.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithLineSegmentPath.png
new file mode 100644
index 000000000000..e50e8631fdd6
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithLineSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithPolyBezierSegmentPath.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithPolyBezierSegmentPath.png
new file mode 100644
index 000000000000..a9b9dcf6bae8
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithPolyBezierSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithPolyLineSegmentPath.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithPolyLineSegmentPath.png
new file mode 100644
index 000000000000..4e94b3fb9bfb
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithPolyLineSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithPolyQuadraticBezierSegmentPath.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithPolyQuadraticBezierSegmentPath.png
new file mode 100644
index 000000000000..cf1e594f609b
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithPolyQuadraticBezierSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithQuadraticBezierSegmentPath.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithQuadraticBezierSegmentPath.png
new file mode 100644
index 000000000000..d7a2fe0bd7a7
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithQuadraticBezierSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithRectangleGeometry.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithRectangleGeometry.png
new file mode 100644
index 000000000000..cd9901ff939f
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithRotation.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithRotation.png
new file mode 100644
index 000000000000..65b306230e5b
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithRoundRectangleGeometry.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithRoundRectangleGeometry.png
new file mode 100644
index 000000000000..2a1df3a0b27e
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithRoundRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithScale.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithScale.png
new file mode 100644
index 000000000000..0c9538a20a7f
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Image_ClipWithScale.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Issue23377ItemSpacing.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Issue23377ItemSpacing.png
new file mode 100644
index 000000000000..3cdb92a0b61e
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Issue23377ItemSpacing.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Issue33037_AfterScroll.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Issue33037_AfterScroll.png
new file mode 100644
index 000000000000..55b69b7bad3f
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Issue33037_AfterScroll.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Issue33037_BeforeScroll.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Issue33037_BeforeScroll.png
new file mode 100644
index 000000000000..0150d15f4ad4
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Issue33037_BeforeScroll.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Issue33909ForegroundColorReset.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Issue33909ForegroundColorReset.png
new file mode 100644
index 000000000000..ec5ea79e7848
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Issue33909ForegroundColorReset.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/LabelShadowRemainsAfterOpacityChange.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/LabelShadowRemainsAfterOpacityChange.png
new file mode 100644
index 000000000000..279198874762
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/LabelShadowRemainsAfterOpacityChange.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Map_IsVisible.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Map_IsVisible.png
new file mode 100644
index 000000000000..7ff231ad3fd3
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Map_IsVisible.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/MultiTrigger_Both_Filled.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/MultiTrigger_Both_Filled.png
new file mode 100644
index 000000000000..605317f60e95
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/MultiTrigger_Both_Filled.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/MultiTrigger_EmailOnly_Filled.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/MultiTrigger_EmailOnly_Filled.png
new file mode 100644
index 000000000000..d3c7ea80a2d2
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/MultiTrigger_EmailOnly_Filled.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/MultiTrigger_PhoneOnly_Filled.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/MultiTrigger_PhoneOnly_Filled.png
new file mode 100644
index 000000000000..9331a67b8eda
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/MultiTrigger_PhoneOnly_Filled.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/NavigatingBetweenPagesWithSetTitleViewShouldNotCrash.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/NavigatingBetweenPagesWithSetTitleViewShouldNotCrash.png
new file mode 100644
index 000000000000..9c4b0b46a28d
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/NavigatingBetweenPagesWithSetTitleViewShouldNotCrash.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/NavigationPageTitleViewShouldRespectMargins.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/NavigationPageTitleViewShouldRespectMargins.png
new file mode 100644
index 000000000000..61930b320b73
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/NavigationPageTitleViewShouldRespectMargins.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/PointerOverWithSelectedStateShouldWork.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/PointerOverWithSelectedStateShouldWork.png
new file mode 100644
index 000000000000..3478e7dbdd7e
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/PointerOverWithSelectedStateShouldWork.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/PropertyTrigger_Focused.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/PropertyTrigger_Focused.png
new file mode 100644
index 000000000000..4fea8b5b5afe
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/PropertyTrigger_Focused.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/PropertyTrigger_UnFocused.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/PropertyTrigger_UnFocused.png
new file mode 100644
index 000000000000..c04d6855310b
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/PropertyTrigger_UnFocused.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RTLModePaddingShouldWork.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RTLModePaddingShouldWork.png
new file mode 100644
index 000000000000..42959a38a772
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RTLModePaddingShouldWork.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RadioButtonTextColorShouldNotBeOverriddenByGlobalLabelStyle.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RadioButtonTextColorShouldNotBeOverriddenByGlobalLabelStyle.png
new file mode 100644
index 000000000000..71d46eaa6254
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RadioButtonTextColorShouldNotBeOverriddenByGlobalLabelStyle.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RadioButtonWithValueChangeSelected.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RadioButtonWithValueChangeSelected.png
index b38b2efcd601..0311245d4018 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RadioButtonWithValueChangeSelected.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RadioButtonWithValueChangeSelected.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RefreshView_SetShadowWithCollectionView_VerifyShadowApplied.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RefreshView_SetShadowWithCollectionView_VerifyShadowApplied.png
index d24fb1ce6bc2..48360fd55bab 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RefreshView_SetShadowWithCollectionView_VerifyShadowApplied.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RefreshView_SetShadowWithCollectionView_VerifyShadowApplied.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RefreshView_SetShadow_VerifyShadowApplied.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RefreshView_SetShadow_VerifyShadowApplied.png
index eb91b9376ed0..b1aa4a7cd664 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RefreshView_SetShadow_VerifyShadowApplied.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RefreshView_SetShadow_VerifyShadowApplied.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShadowShouldUpdateOnCornerRadiusChange.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShadowShouldUpdateOnCornerRadiusChange.png
new file mode 100644
index 000000000000..29d8cc67a611
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShadowShouldUpdateOnCornerRadiusChange.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellContentTitleNotRendering.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellContentTitleNotRendering.png
new file mode 100644
index 000000000000..6bf6990ed382
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellContentTitleNotRendering.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_IsVisibleFalse.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_IsVisibleFalse.png
new file mode 100644
index 000000000000..e287f759d71e
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_IsVisibleFalse.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_IsVisibleTrue.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_IsVisibleTrue.png
new file mode 100644
index 000000000000..06d51c8069bd
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_IsVisibleTrue.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_PresentationModeAnimated.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_PresentationModeAnimated.png
new file mode 100644
index 000000000000..63e75da153bb
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_PresentationModeAnimated.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_PresentationModeModal.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_PresentationModeModal.png
new file mode 100644
index 000000000000..cedca623260e
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_PresentationModeModal.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_PresentationModeModalAnimated.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_PresentationModeModalAnimated.png
new file mode 100644
index 000000000000..cf3a7f315ce6
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_PresentationModeModalAnimated.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_PresentationModeModalNotAnimated.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_PresentationModeModalNotAnimated.png
new file mode 100644
index 000000000000..75c7954a3aec
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_PresentationModeModalNotAnimated.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_PresentationModeNotAnimated.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_PresentationModeNotAnimated.png
new file mode 100644
index 000000000000..85a1c4fca1e3
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_PresentationModeNotAnimated.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_ShowTitleView.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_ShowTitleView.png
new file mode 100644
index 000000000000..134635b588ec
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_ShowTitleView.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_ShowTitleViewHidden.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_ShowTitleViewHidden.png
new file mode 100644
index 000000000000..d6a32a5a4791
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellPages_ShowTitleViewHidden.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellTabBarBackgroundColor.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellTabBarBackgroundColor.png
new file mode 100644
index 000000000000..932f00beaac6
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShellTabBarBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShouldAppearFlyoutIconAndContentPageTitle.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShouldAppearFlyoutIconAndContentPageTitle.png
new file mode 100644
index 000000000000..85ecd47d2efa
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ShouldAppearFlyoutIconAndContentPageTitle.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Slider_SetThumbImageSourceAndReset_VerifyVisualState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Slider_SetThumbImageSourceAndReset_VerifyVisualState.png
new file mode 100644
index 000000000000..e0938b10c61b
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/Slider_SetThumbImageSourceAndReset_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/StateTrigger_SwitchOff.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/StateTrigger_SwitchOff.png
new file mode 100644
index 000000000000..7b6b47436f70
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/StateTrigger_SwitchOff.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/StateTrigger_SwitchOn.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/StateTrigger_SwitchOn.png
new file mode 100644
index 000000000000..224d8be30514
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/StateTrigger_SwitchOn.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SubPageNavigationShouldWorkAfterFirstTabBecomesInvisible.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SubPageNavigationShouldWorkAfterFirstTabBecomesInvisible.png
new file mode 100644
index 000000000000..fbbaef18a543
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SubPageNavigationShouldWorkAfterFirstTabBecomesInvisible.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SwipeItemFontImageSourceSizeIsRespected.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SwipeItemFontImageSourceSizeIsRespected.png
new file mode 100644
index 000000000000..222597a47086
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SwipeItemFontImageSourceSizeIsRespected.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SwitchThumbShouldBeVisibleWithShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SwitchThumbShouldBeVisibleWithShadow.png
new file mode 100644
index 000000000000..e2081f0edfdc
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SwitchThumbShouldBeVisibleWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabBarShouldBeVisibleAfterNavigatingFromModalViaGoToAsync.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabBarShouldBeVisibleAfterNavigatingFromModalViaGoToAsync.png
new file mode 100644
index 000000000000..4a38fa9890ef
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabBarShouldBeVisibleAfterNavigatingFromModalViaGoToAsync.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabBarVisibilityAfterMultiLevelPopToRoot.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabBarVisibilityAfterMultiLevelPopToRoot.png
new file mode 100644
index 000000000000..cc5e93dbd5a4
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabBarVisibilityAfterMultiLevelPopToRoot.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabbedPageFlowDirection_AfterChangingBackToLeftToRight.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabbedPageFlowDirection_AfterChangingBackToLeftToRight.png
new file mode 100644
index 000000000000..2ebb4051f18c
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabbedPageFlowDirection_AfterChangingBackToLeftToRight.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabbedPageFlowDirection_AfterChangingToLeftToRight.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabbedPageFlowDirection_AfterChangingToLeftToRight.png
new file mode 100644
index 000000000000..be97acb1e833
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabbedPageFlowDirection_AfterChangingToLeftToRight.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabbedPageFlowDirection_DefaultRightToLeftLayout.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabbedPageFlowDirection_DefaultRightToLeftLayout.png
new file mode 100644
index 000000000000..fadf29f947ce
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabbedPageFlowDirection_DefaultRightToLeftLayout.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TestIssue18668.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TestIssue18668.png
new file mode 100644
index 000000000000..127f4197ea45
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TestIssue18668.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TransparentShapeShouldNotDisplayShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TransparentShapeShouldNotDisplayShadow.png
new file mode 100644
index 000000000000..2aa08b7f9085
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TransparentShapeShouldNotDisplayShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyAllIndicatorDotsShowShadowsWhenIndicatorSize.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyAllIndicatorDotsShowShadowsWhenIndicatorSize.png
new file mode 100644
index 000000000000..779cc24260a3
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyAllIndicatorDotsShowShadowsWhenIndicatorSize.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyBackgroundColorCleared.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyBackgroundColorCleared.png
new file mode 100644
index 000000000000..91afb8d8caa4
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyBackgroundColorCleared.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyBackgroundColorSet.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyBackgroundColorSet.png
new file mode 100644
index 000000000000..e3708307c5b7
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyBackgroundColorSet.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyClearedTimeDoesNotShowMidnight.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyClearedTimeDoesNotShowMidnight.png
new file mode 100644
index 000000000000..cf349d349371
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyClearedTimeDoesNotShowMidnight.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png
new file mode 100644
index 000000000000..370699f69a9a
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png
new file mode 100644
index 000000000000..70cfb6083c20
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png
new file mode 100644
index 000000000000..4946e5ab99c0
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png
new file mode 100644
index 000000000000..466ed1ff0fa8
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png
new file mode 100644
index 000000000000..f9a80f9844b5
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png
new file mode 100644
index 000000000000..3548c64f14f2
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyDefaultScrollToRequested.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyDefaultScrollToRequested.png
new file mode 100644
index 000000000000..da2a1e29fd37
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyDefaultScrollToRequested.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyDynamicFlyoutIconBackgroundColor.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyDynamicFlyoutIconBackgroundColor.png
new file mode 100644
index 000000000000..9acaf8bd00a9
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyDynamicFlyoutIconBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png
new file mode 100644
index 000000000000..b2b861347eae
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png
new file mode 100644
index 000000000000..aea0c90d004a
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedList.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedList.png
new file mode 100644
index 000000000000..30353267f37c
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedList.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollection.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollection.png
new file mode 100644
index 000000000000..4e65efed14c2
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollection.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedList.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedList.png
new file mode 100644
index 000000000000..65401cd133b9
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedList.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollection.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollection.png
new file mode 100644
index 000000000000..761e30753d35
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollection.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutIconBackgroundColor.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutIconBackgroundColor.png
new file mode 100644
index 000000000000..a2291512a061
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutIconBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPageToolbarItemsRender.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPageToolbarItemsRender.png
new file mode 100644
index 000000000000..d1c7a89311a6
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPageToolbarItemsRender.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_BackgroundColor.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_BackgroundColor.png
index 5f5a2672df99..ba8a81a418db 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_BackgroundColor.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_BackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_DetailPageIconImageSource.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_DetailPageIconImageSource.png
index fd2e2a7988c0..3cc9f5c1c182 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_DetailPageIconImageSource.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_DetailPageIconImageSource.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_DetailPageIconImageSource_FlyoutLayoutBehavior.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_DetailPageIconImageSource_FlyoutLayoutBehavior.png
index 149372a7a4d8..85a085fb19dc 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_DetailPageIconImageSource_FlyoutLayoutBehavior.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_DetailPageIconImageSource_FlyoutLayoutBehavior.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_FlyoutLayoutBehaviorPopover.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_FlyoutLayoutBehaviorPopover.png
index 545befba6617..5e51e13ceff1 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_FlyoutLayoutBehaviorPopover.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_FlyoutLayoutBehaviorPopover.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_Title.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_Title.png
index e1f37bdcd724..d18a981a60f8 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_Title.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutPage_Title.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutVerticalScrollModeDisabled.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutVerticalScrollModeDisabled.png
new file mode 100644
index 000000000000..fe3fbe9da894
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFlyoutVerticalScrollModeDisabled.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFontImageAreCenterAlign.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFontImageAreCenterAlign.png
new file mode 100644
index 000000000000..f4c0c3d24a08
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyFontImageAreCenterAlign.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyGraphicsViewWithoutGrayLine.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyGraphicsViewWithoutGrayLine.png
deleted file mode 100644
index ba286ea4eaf0..000000000000
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyGraphicsViewWithoutGrayLine.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyIndicatorColorWithFlowDirection.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyIndicatorColorWithFlowDirection.png
index 4baa73bc1cdf..80654236ee98 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyIndicatorColorWithFlowDirection.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyIndicatorColorWithFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyIndicatorHideSingleIsFalse.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyIndicatorHideSingleIsFalse.png
index 6384eb02f1ef..444ba139379d 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyIndicatorHideSingleIsFalse.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyIndicatorHideSingleIsFalse.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyIndicatorViewMaximumVisibleWithTemplate.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyIndicatorViewMaximumVisibleWithTemplate.png
new file mode 100644
index 000000000000..25bf4d7c5e6e
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyIndicatorViewMaximumVisibleWithTemplate.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyLabelBackgroundIsClippedWithRectangleGeometry.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyLabelBackgroundIsClippedWithRectangleGeometry.png
new file mode 100644
index 000000000000..6e47faffaefa
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyLabelBackgroundIsClippedWithRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyMeasureAllItemsWithGroupedList.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyMeasureAllItemsWithGroupedList.png
index eeefdb54f464..61b3391545c0 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyMeasureAllItemsWithGroupedList.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyMeasureAllItemsWithGroupedList.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyMeasureAllItemsWithObservableCollection.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyMeasureAllItemsWithObservableCollection.png
index 324c1dca5438..9d5400471357 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyMeasureAllItemsWithObservableCollection.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyMeasureAllItemsWithObservableCollection.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyNavBarStatusAtInitialLoading.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyNavBarStatusAtInitialLoading.png
new file mode 100644
index 000000000000..7c7d00184e54
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyNavBarStatusAtInitialLoading.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyNavBarStatusAtRuntime.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyNavBarStatusAtRuntime.png
new file mode 100644
index 000000000000..0c9c970e0ef8
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyNavBarStatusAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyRadioButtonTextWithLowerTransform.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyRadioButtonTextWithLowerTransform.png
new file mode 100644
index 000000000000..9750bf6f4168
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyRadioButtonTextWithLowerTransform.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyRadioButtonTextWithUpperTransform.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyRadioButtonTextWithUpperTransform.png
new file mode 100644
index 000000000000..6ea9530bf112
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyRadioButtonTextWithUpperTransform.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByIndexWithCenterPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByIndexWithCenterPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..8245531cd3e2
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByIndexWithCenterPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByIndexWithEndPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByIndexWithEndPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..80f3387b23b6
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByIndexWithEndPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..c6f9a41d7e98
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByItemWithCenterPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByItemWithCenterPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..d4cf11b9ac11
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByItemWithCenterPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByItemWithEndPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByItemWithEndPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..25248175846d
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByItemWithEndPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByItemWithMakeVisiblePositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByItemWithMakeVisiblePositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..cd22b61f0c42
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollToByItemWithMakeVisiblePositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySearchBarBackground.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySearchBarBackground.png
new file mode 100644
index 000000000000..5d18ea9a4b15
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySearchBarBackground.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySelectedIndicatorColorWithIndicatorColor.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySelectedIndicatorColorWithIndicatorColor.png
index 08711821f15a..139c3c101dd7 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySelectedIndicatorColorWithIndicatorColor.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySelectedIndicatorColorWithIndicatorColor.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyShellForegroundColorIsAppliedToToolbarItems.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyShellForegroundColorIsAppliedToToolbarItems.png
new file mode 100644
index 000000000000..18d3263d2f28
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyShellForegroundColorIsAppliedToToolbarItems.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySliderColors.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySliderColors.png
new file mode 100644
index 000000000000..decea37521e9
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySliderColors.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwitchControlSize.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwitchControlSize.png
new file mode 100644
index 000000000000..e05cd2a2d1a0
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwitchControlSize.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyTimePickerFormat.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyTimePickerFormat.png
new file mode 100644
index 000000000000..56026a581544
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyTimePickerFormat.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyTimePickerIsNullOnInitialLoad.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyTimePickerIsNullOnInitialLoad.png
new file mode 100644
index 000000000000..c0737633cfb2
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyTimePickerIsNullOnInitialLoad.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Button_Disable.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Button_Disable.png
new file mode 100644
index 000000000000..f0aa42a30391
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Button_Disable.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Button_InitialState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Button_InitialState.png
new file mode 100644
index 000000000000..f2e3e2bf2ecb
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Button_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Button_PressedAndReleased.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Button_PressedAndReleased.png
new file mode 100644
index 000000000000..d539c1418196
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Button_PressedAndReleased.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Button_Reset.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Button_Reset.png
new file mode 100644
index 000000000000..9ce4ea1c0aea
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Button_Reset.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CheckBox_Checked.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CheckBox_Checked.png
new file mode 100644
index 000000000000..b8ca50cac135
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CheckBox_Checked.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CheckBox_Disable.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CheckBox_Disable.png
new file mode 100644
index 000000000000..23698717da52
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CheckBox_Disable.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CheckBox_DisableWhileChecked.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CheckBox_DisableWhileChecked.png
new file mode 100644
index 000000000000..dc54327f76b5
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CheckBox_DisableWhileChecked.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CheckBox_InitialState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CheckBox_InitialState.png
new file mode 100644
index 000000000000..19b5f9e7f116
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CheckBox_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CheckBox_Reset.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CheckBox_Reset.png
new file mode 100644
index 000000000000..9838418772e5
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CheckBox_Reset.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CollectionView_Disabled.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CollectionView_Disabled.png
new file mode 100644
index 000000000000..7a3a04212e6d
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CollectionView_Disabled.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CollectionView_InitialState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CollectionView_InitialState.png
new file mode 100644
index 000000000000..e7cc6b5db631
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CollectionView_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CollectionView_Normal.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CollectionView_Normal.png
new file mode 100644
index 000000000000..f95822290081
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CollectionView_Normal.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CollectionView_Reset.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CollectionView_Reset.png
new file mode 100644
index 000000000000..94736c0a0c7f
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CollectionView_Reset.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CollectionView_Selected.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CollectionView_Selected.png
new file mode 100644
index 000000000000..9041c46956ce
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_CollectionView_Selected.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Completed.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Completed.png
new file mode 100644
index 000000000000..7d3cb6b86ae6
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Completed.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Disable.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Disable.png
new file mode 100644
index 000000000000..a09c1dc9ddb6
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Disable.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_DisableAndEnableWhileFocused.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_DisableAndEnableWhileFocused.png
new file mode 100644
index 000000000000..21cc330d0111
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_DisableAndEnableWhileFocused.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_DisableAndEnableWhileUnFocused.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_DisableAndEnableWhileUnFocused.png
new file mode 100644
index 000000000000..6be451c33540
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_DisableAndEnableWhileUnFocused.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_DisableWhileFocused.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_DisableWhileFocused.png
new file mode 100644
index 000000000000..444ac3253eeb
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_DisableWhileFocused.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_DisableWhileReset.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_DisableWhileReset.png
new file mode 100644
index 000000000000..099678d9aad5
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_DisableWhileReset.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_DisableWhileUnFocused.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_DisableWhileUnFocused.png
new file mode 100644
index 000000000000..93a9ffeb7035
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_DisableWhileUnFocused.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Focus.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Focus.png
new file mode 100644
index 000000000000..6bad603790b8
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Focus.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_InitialState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_InitialState.png
new file mode 100644
index 000000000000..439a1ec602bb
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Invalid.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Invalid.png
new file mode 100644
index 000000000000..472f216fc538
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Invalid.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Reset.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Reset.png
new file mode 100644
index 000000000000..6fe9fd2361bc
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Reset.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Unfocus.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Unfocus.png
new file mode 100644
index 000000000000..1ab8c08588a5
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Unfocus.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Validate.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Validate.png
new file mode 100644
index 000000000000..cf14b00292b6
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Entry_Validate.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_DisableWhileNormal.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_DisableWhileNormal.png
new file mode 100644
index 000000000000..873c1c5011da
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_DisableWhileNormal.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_DisableWhileSelected.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_DisableWhileSelected.png
new file mode 100644
index 000000000000..62ecf0fee6c0
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_DisableWhileSelected.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_Disabled.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_Disabled.png
new file mode 100644
index 000000000000..ae9bff848dc9
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_Disabled.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_InitialState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_InitialState.png
new file mode 100644
index 000000000000..1d57454f9871
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_Reset.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_Reset.png
new file mode 100644
index 000000000000..a671534531c5
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_Reset.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_Selected.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_Selected.png
new file mode 100644
index 000000000000..d1a400582743
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Label_Selected.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Slider_DisabledState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Slider_DisabledState.png
new file mode 100644
index 000000000000..82420161f259
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Slider_DisabledState.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Slider_FocusedState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Slider_FocusedState.png
new file mode 100644
index 000000000000..25bb08d6500a
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Slider_FocusedState.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Slider_NormalOrUnfocusedState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Slider_NormalOrUnfocusedState.png
new file mode 100644
index 000000000000..1f4e8e178129
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Slider_NormalOrUnfocusedState.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Slider_NormalState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Slider_NormalState.png
new file mode 100644
index 000000000000..3731231ab21e
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Slider_NormalState.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Slider_ResetState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Slider_ResetState.png
new file mode 100644
index 000000000000..7ac2f4e6639f
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Slider_ResetState.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_DisableWhileOff.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_DisableWhileOff.png
new file mode 100644
index 000000000000..30de9ab7e799
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_DisableWhileOff.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_DisableWhileOn.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_DisableWhileOn.png
new file mode 100644
index 000000000000..aa7066e36ee8
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_DisableWhileOn.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_InitialState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_InitialState.png
new file mode 100644
index 000000000000..ba11dfa02c9a
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_Off.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_Off.png
new file mode 100644
index 000000000000..e88a0d5c9a1a
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_Off.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_On.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_On.png
new file mode 100644
index 000000000000..8c07a0b8678c
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_On.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_Reset.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_Reset.png
new file mode 100644
index 000000000000..ed01a630a747
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyVSM_Switch_Reset.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_AnchorXWithAnchorY.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_AnchorXWithAnchorY.png
new file mode 100644
index 000000000000..861baaaaeebb
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_AnchorXWithAnchorY.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_AnchorX_ScaleYWithRotation.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_AnchorX_ScaleYWithRotation.png
new file mode 100644
index 000000000000..a2b813a9797f
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_AnchorX_ScaleYWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_AnchorY_ScaleWithRotationY.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_AnchorY_ScaleWithRotationY.png
new file mode 100644
index 000000000000..244faaa41101
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_AnchorY_ScaleWithRotationY.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_AnchorY_ScaleXWithRotationX.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_AnchorY_ScaleXWithRotationX.png
new file mode 100644
index 000000000000..b37842b4d44b
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_AnchorY_ScaleXWithRotationX.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_IsVisible.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_IsVisible.png
new file mode 100644
index 000000000000..232eff3182e2
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_IsVisible.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_Rotation.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_Rotation.png
new file mode 100644
index 000000000000..8d9c13a26d37
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_Rotation.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationWithRotationX.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationWithRotationX.png
new file mode 100644
index 000000000000..087fa754767f
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationWithRotationX.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationWithScale.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationWithScale.png
new file mode 100644
index 000000000000..32fd6501ecf0
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationWithScale.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationX.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationX.png
new file mode 100644
index 000000000000..17d53c7dc880
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationX.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationXWithRotationY.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationXWithRotationY.png
new file mode 100644
index 000000000000..ee9f39c563df
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationXWithRotationY.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationXWithScaleX.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationXWithScaleX.png
new file mode 100644
index 000000000000..7005b64de2a2
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationXWithScaleX.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationY.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationY.png
new file mode 100644
index 000000000000..6e814a04db66
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationY.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationYWithScaleY.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationYWithScaleY.png
new file mode 100644
index 000000000000..c607285ddbb2
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_RotationYWithScaleY.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_Scale.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_Scale.png
new file mode 100644
index 000000000000..7a00f6bb8ed5
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_Scale.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_ScaleWithAnchorXAndRotationY.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_ScaleWithAnchorXAndRotationY.png
new file mode 100644
index 000000000000..15e519f527b9
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_ScaleWithAnchorXAndRotationY.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_ScaleX.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_ScaleX.png
new file mode 100644
index 000000000000..7ff4d28f24fc
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_ScaleX.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_ScaleXWithAnchorYAndRotationY.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_ScaleXWithAnchorYAndRotationY.png
new file mode 100644
index 000000000000..4d11083460d1
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_ScaleXWithAnchorYAndRotationY.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_ScaleY.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_ScaleY.png
new file mode 100644
index 000000000000..662bfaa9262b
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_ScaleY.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_ScaleYWithAnchorXAndRotationY.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_ScaleYWithAnchorXAndRotationY.png
new file mode 100644
index 000000000000..d786cb91c446
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VisualTransform_ScaleYWithAnchorXAndRotationY.png differ
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/BorderFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/BorderFeatureTests.cs
index 7fe9bb907229..7a79738f4c65 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/BorderFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/BorderFeatureTests.cs
@@ -4,6 +4,7 @@
namespace Microsoft.Maui.TestCases.Tests;
+[Category(UITestCategories.Border)]
public class BorderFeatureTests : _GalleryUITest
{
public const string BorderFeatureMatrix = "Border Feature Matrix";
@@ -15,12 +16,40 @@ public BorderFeatureTests(TestDevice device)
{
}
- [Test]
- [Category(UITestCategories.Border)]
- public void Border_PaddingWithContent_Label()
+ // Navigates to the Options page; also resets the ViewModel to clean defaults on each call.
+ private void NavigateToOptions()
{
App.WaitForElement("Options");
App.Tap("Options");
+ }
+
+ // On Windows, crop the top portion to exclude platform chrome before comparing screenshots.
+ private void VerifyBorderScreenshot()
+ {
+#if WINDOWS
+ VerifyScreenshot(cropTop: 100);
+#else
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+#endif
+ }
+
+ // ── Non-DashArray tests (Order 1–9) ──
+
+ [Test]
+ [Order(1)]
+ public void Border_DefaultValues()
+ {
+ // Verify the initial Border appearance without applying any options.
+ // Defaults: Stroke=Red, StrokeThickness=1, StrokeShape=Rectangle, Padding=5, Content=Label.
+ App.WaitForElement("Options");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Order(2)]
+ public void Border_PaddingWithContent_Label()
+ {
+ NavigateToOptions();
App.WaitForElement("PaddingEntry");
App.EnterText("PaddingEntry", "10,20,60,10");
@@ -30,18 +59,15 @@ public void Border_PaddingWithContent_Label()
App.WaitForElement("Apply");
App.Tap("Apply");
+
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
[Test]
- [Category(UITestCategories.Border)]
- public void Border_StrokeColorWithPaddingAndContent_Image()
+ [Order(3)]
+ public void Border_PaddingWithContent_Image()
{
- App.WaitForElement("Options");
- App.Tap("Options");
-
- App.WaitForElement("RedColorButton");
- App.Tap("RedColorButton");
+ NavigateToOptions();
App.WaitForElement("PaddingEntry");
App.EnterText("PaddingEntry", "10,20,60,10");
@@ -51,15 +77,33 @@ public void Border_StrokeColorWithPaddingAndContent_Image()
App.WaitForElement("Apply");
App.Tap("Apply");
+
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Order(4)]
+ public void Border_StrokeColorWithContent_Button()
+ {
+ NavigateToOptions();
+
+ App.WaitForElement("BlueColorButton");
+ App.Tap("BlueColorButton");
+
+ App.WaitForElement("ButtonRadioButton");
+ App.Tap("ButtonRadioButton");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
[Test]
- [Category(UITestCategories.Border)]
+ [Order(5)]
public void Border_StrokeColorWithStrokeShape_RoundRectangle()
{
- App.WaitForElement("Options");
- App.Tap("Options");
+ NavigateToOptions();
App.WaitForElement("BlueColorButton");
App.Tap("BlueColorButton");
@@ -73,11 +117,10 @@ public void Border_StrokeColorWithStrokeShape_RoundRectangle()
}
[Test]
- [Category(UITestCategories.Border)]
+ [Order(6)]
public void Border_StrokeColorWithStrokeThickness()
{
- App.WaitForElement("Options");
- App.Tap("Options");
+ NavigateToOptions();
App.WaitForElement("GreenColorButton");
App.Tap("GreenColorButton");
@@ -91,11 +134,10 @@ public void Border_StrokeColorWithStrokeThickness()
}
[Test]
- [Category(UITestCategories.Border)]
+ [Order(7)]
public void Border_StrokeShapeWithStrokeThickness_Ellipse()
{
- App.WaitForElement("Options");
- App.Tap("Options");
+ NavigateToOptions();
App.WaitForElement("EllipseShapeRadio");
App.Tap("EllipseShapeRadio");
@@ -108,15 +150,129 @@ public void Border_StrokeShapeWithStrokeThickness_Ellipse()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
+ [Test]
+ [Order(8)]
+ public void Border_StrokeShape_Path()
+ {
+ // Path shape without a DashArray so this test runs on all platforms.
+ // (The DashArray+Path combination is covered separately but is iOS/Catalyst-guarded.)
+ NavigateToOptions();
+
+ App.WaitForElement("PathShapeRadio");
+ App.Tap("PathShapeRadio");
+
+ App.WaitForElement("StrokeThicknessEntry");
+ App.EnterText("StrokeThicknessEntry", "10");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Order(9)]
+ public void Border_Shadow()
+ {
+ NavigateToOptions();
+
+ App.WaitForElement("OffsetXEntry");
+ App.EnterText("OffsetXEntry", "10");
+ App.WaitForElement("OffsetYEntry");
+ App.EnterText("OffsetYEntry", "10");
+ App.WaitForElement("OpacityEntry");
+ App.EnterText("OpacityEntry", "0.8");
+ App.WaitForElement("RadiusEntry");
+ App.EnterText("RadiusEntry", "10");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyBorderScreenshot();
+ }
+
+ // ── Tests that fail on iOS/Catalyst — excluded via preprocessor (Order 10–14) ──
+
+#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST // For more information, see : https://github.com/dotnet/maui/issues/29743
+
+ [Test]
+ [Order(10)]
+ public void Border_StrokeMiterLimitWithStrokeLineJoin_Miter()
+ {
+ NavigateToOptions();
-#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST //For more information, see : https://github.com/dotnet/maui/issues/29661 , https://github.com/dotnet/maui/issues/29743
+ App.WaitForElement("StrokeThicknessEntry");
+ App.EnterText("StrokeThicknessEntry", "20");
+
+ App.WaitForElement("MiterLimitEntry");
+ App.EnterText("MiterLimitEntry", "1");
+
+ App.WaitForElement("MiterLineJoinRadio");
+ App.Tap("MiterLineJoinRadio");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
[Test]
- [Category(UITestCategories.Border)]
+ [Order(11)]
public void Border_StrokeShapeWithStrokeLineJoin_Bevel()
{
- App.WaitForElement("Options");
- App.Tap("Options");
+ NavigateToOptions();
+
+ App.WaitForElement("PolygonShapeRadio");
+ App.Tap("PolygonShapeRadio");
+
+ App.WaitForElement("BevelLineJoinRadio");
+ App.Tap("BevelLineJoinRadio");
+
+ App.WaitForElement("StrokeThicknessEntry");
+ App.EnterText("StrokeThicknessEntry", "10");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Order(12)]
+ public void Border_StrokeColorWithStrokeLineJoin_Round()
+ {
+ NavigateToOptions();
+
+ App.WaitForElement("RoundLineJoinRadio");
+ App.Tap("RoundLineJoinRadio");
+
+ App.WaitForElement("StrokeThicknessEntry");
+ App.EnterText("StrokeThicknessEntry", "10");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Order(13)]
+ public void Border_StrokeThicknessWithStrokeLineJoin_Bevel()
+ {
+ NavigateToOptions();
+
+ App.WaitForElement("BevelLineJoinRadio");
+ App.Tap("BevelLineJoinRadio");
+
+ App.WaitForElement("StrokeThicknessEntry");
+ App.EnterText("StrokeThicknessEntry", "15");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Order(14)]
+ public void Border_PolygonShapeWithStrokeLineJoin_Bevel()
+ {
+ NavigateToOptions();
App.WaitForElement("PolygonShapeRadio");
App.Tap("PolygonShapeRadio");
@@ -124,6 +280,42 @@ public void Border_StrokeShapeWithStrokeLineJoin_Bevel()
App.WaitForElement("BevelLineJoinRadio");
App.Tap("BevelLineJoinRadio");
+ App.WaitForElement("StrokeThicknessEntry");
+ App.EnterText("StrokeThicknessEntry", "15");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+#endif
+
+ [Test]
+ [Order(15)]
+ public void Border_StrokeShapeWithPolygon()
+ {
+ NavigateToOptions();
+
+ App.WaitForElement("PolygonShapeRadio");
+ App.Tap("PolygonShapeRadio");
+
+ App.WaitForElement("StrokeThicknessEntry");
+ App.EnterText("StrokeThicknessEntry", "10");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Order(16)]
+ public void Border_StrokeColorWithRed()
+ {
+ NavigateToOptions();
+
+ App.WaitForElement("RedColorButton");
+ App.Tap("RedColorButton");
+
App.WaitForElement("StrokeThicknessEntry");
App.EnterText("StrokeThicknessEntry", "10");
@@ -132,13 +324,105 @@ public void Border_StrokeShapeWithStrokeLineJoin_Bevel()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
+ // Explicitly switch to another shape, then back to Rectangle to verify reset behavior.
+ [Test]
+ [Order(17)]
+ public void Border_StrokeShapeRectangle_AfterChange()
+ {
+ NavigateToOptions();
+
+ // First switch to Ellipse
+ App.WaitForElement("EllipseShapeRadio");
+ App.Tap("EllipseShapeRadio");
+
+ // Then switch back to Rectangle
+ App.WaitForElement("RectangleShapeRadio");
+ App.Tap("RectangleShapeRadio");
+
+ App.WaitForElement("StrokeThicknessEntry");
+ App.EnterText("StrokeThicknessEntry", "10");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
[Test]
- [Category(UITestCategories.Border)]
+ [Order(18)]
+ public void Border_ShadowWithColor()
+ {
+ NavigateToOptions();
+
+ App.WaitForElement("ShadowRedColorButton");
+ App.Tap("ShadowRedColorButton");
+
+ App.WaitForElement("OffsetXEntry");
+ App.EnterText("OffsetXEntry", "10");
+ App.WaitForElement("OffsetYEntry");
+ App.EnterText("OffsetYEntry", "10");
+ App.WaitForElement("OpacityEntry");
+ App.EnterText("OpacityEntry", "0.8");
+ App.WaitForElement("RadiusEntry");
+ App.EnterText("RadiusEntry", "10");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyBorderScreenshot();
+ }
+
+ [Test]
+ [Order(19)]
+ public void Border_BackgroundColor()
+ {
+ NavigateToOptions();
+
+ App.WaitForElement("BackgroundYellowButton");
+ App.Tap("BackgroundYellowButton");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Order(20)]
+ public void Border_StrokeGradientBrush()
+ {
+ NavigateToOptions();
+
+ App.WaitForElement("GradientStrokeButton");
+ App.Tap("GradientStrokeButton");
+
+ App.WaitForElement("StrokeThicknessEntry");
+ App.EnterText("StrokeThicknessEntry", "10");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Order(21)]
+ public void Border_ZeroPadding()
+ {
+ NavigateToOptions();
+
+ App.WaitForElement("PaddingEntry");
+ App.EnterText("PaddingEntry", "0,0,0,0");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ // ── DashArray tests (Order 15–18) ──
+
+ [Test]
+ [Order(22)]
public void Border_StrokeShapeWithDashArray_Path()
{
- App.WaitForElement("Options");
- App.Tap("Options");
+ NavigateToOptions();
App.WaitForElement("StrokeDashArrayEntry");
App.EnterText("StrokeDashArrayEntry", "5,3");
@@ -155,11 +439,10 @@ public void Border_StrokeShapeWithDashArray_Path()
}
[Test]
- [Category(UITestCategories.Border)]
+ [Order(23)]
public void Border_StrokeThicknessWithDashArray()
{
- App.WaitForElement("Options");
- App.Tap("Options");
+ NavigateToOptions();
App.WaitForElement("StrokeThicknessEntry");
App.EnterText("StrokeThicknessEntry", "10");
@@ -170,19 +453,14 @@ public void Border_StrokeThicknessWithDashArray()
App.WaitForElement("Apply");
App.Tap("Apply");
-#if WINDOWS
- VerifyScreenshot(cropTop: 100);
-#else
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
-#endif
+ VerifyBorderScreenshot();
}
[Test]
- [Category(UITestCategories.Border)]
+ [Order(24)]
public void Border_StrokeDashArrayWithDashOffset()
{
- App.WaitForElement("Options");
- App.Tap("Options");
+ NavigateToOptions();
App.WaitForElement("StrokeDashArrayEntry");
App.EnterText("StrokeDashArrayEntry", "5,3");
@@ -196,40 +474,57 @@ public void Border_StrokeDashArrayWithDashOffset()
App.WaitForElement("Apply");
App.Tap("Apply");
-#if WINDOWS
- VerifyScreenshot(cropTop: 100);
-#else
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
-#endif
+ VerifyBorderScreenshot();
}
[Test]
- [Category(UITestCategories.Border)]
- public void Border_StrokeColorWithStrokeLineJoin_Round()
+ [Order(25)]
+ public void Border_StrokeDashArrayWithStrokeColor()
{
- App.WaitForElement("Options");
- App.Tap("Options");
+ NavigateToOptions();
- App.WaitForElement("RectangleShapeRadio");
- App.Tap("RectangleShapeRadio");
-
- App.WaitForElement("RoundLineJoinRadio");
- App.Tap("RoundLineJoinRadio");
+ App.WaitForElement("StrokeDashArrayEntry");
+ App.EnterText("StrokeDashArrayEntry", "5,3");
App.WaitForElement("StrokeThicknessEntry");
App.EnterText("StrokeThicknessEntry", "10");
+ App.WaitForElement("BlackColorButton");
+ App.Tap("BlackColorButton");
+
App.WaitForElement("Apply");
App.Tap("Apply");
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
+
+ // ── DashArray + StrokeLineCap tests disabled for Windows (Order 19–24) ──
+
#if TEST_FAILS_ON_WINDOWS // For more information, see : https://github.com/dotnet/maui/issues/29741
[Test]
- [Category(UITestCategories.Border)]
+ [Order(26)]
+ public void Border_StrokeDashArrayWithStrokeLineCap_Flat()
+ {
+ NavigateToOptions();
+
+ App.WaitForElement("StrokeDashArrayEntry");
+ App.EnterText("StrokeDashArrayEntry", "5,3");
+
+ App.WaitForElement("StrokeThicknessEntry");
+ App.EnterText("StrokeThicknessEntry", "10");
+
+ App.WaitForElement("FlatLineCapRadio");
+ App.Tap("FlatLineCapRadio");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Order(27)]
public void Border_StrokeDashArrayWithStrokeLineCap_Round()
{
- App.WaitForElement("Options");
- App.Tap("Options");
+ NavigateToOptions();
App.WaitForElement("StrokeDashArrayEntry");
App.EnterText("StrokeDashArrayEntry", "5,3");
@@ -246,15 +541,17 @@ public void Border_StrokeDashArrayWithStrokeLineCap_Round()
}
[Test]
- [Category(UITestCategories.Border)]
+ [Order(28)]
public void Border_StrokeDashArrayWithDashOffsetAndStrokeLineCapRound()
{
- App.WaitForElement("Options");
- App.Tap("Options");
+ NavigateToOptions();
App.WaitForElement("StrokeDashArrayEntry");
App.EnterText("StrokeDashArrayEntry", "5,3");
+ App.WaitForElement("DashOffsetEntry");
+ App.EnterText("DashOffsetEntry", "2");
+
App.WaitForElement("StrokeThicknessEntry");
App.EnterText("StrokeThicknessEntry", "10");
@@ -267,11 +564,10 @@ public void Border_StrokeDashArrayWithDashOffsetAndStrokeLineCapRound()
}
[Test]
- [Category(UITestCategories.Border)]
+ [Order(29)]
public void Border_StrokeDashArrayWithStrokeLineCap_Square()
{
- App.WaitForElement("Options");
- App.Tap("Options");
+ NavigateToOptions();
App.WaitForElement("StrokeDashArrayEntry");
App.EnterText("StrokeDashArrayEntry", "5,3");
@@ -288,11 +584,10 @@ public void Border_StrokeDashArrayWithStrokeLineCap_Square()
}
[Test]
- [Category(UITestCategories.Border)]
+ [Order(30)]
public void Border_StrokeDashArrayWithEllipseShapeAndStrokeLineCap_Square()
{
- App.WaitForElement("Options");
- App.Tap("Options");
+ NavigateToOptions();
App.WaitForElement("StrokeDashArrayEntry");
App.EnterText("StrokeDashArrayEntry", "5,3");
@@ -312,11 +607,10 @@ public void Border_StrokeDashArrayWithEllipseShapeAndStrokeLineCap_Square()
}
[Test]
- [Category(UITestCategories.Border)]
+ [Order(31)]
public void Border_PolygonShapeWithStrokeLineCap_Round()
{
- App.WaitForElement("Options");
- App.Tap("Options");
+ NavigateToOptions();
App.WaitForElement("StrokeDashArrayEntry");
App.EnterText("StrokeDashArrayEntry", "5,3");
@@ -334,120 +628,59 @@ public void Border_PolygonShapeWithStrokeLineCap_Round()
App.Tap("Apply");
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
-
#endif
+ // ── DashArray+Offset (Order 25) — runs on all platforms; issue #29661 (DashArray on iOS/Catalyst) is closed/fixed ──
+
[Test]
- [Category(UITestCategories.Border)]
- public void Border_StrokeDashArrayWithStrokeColor()
+ [Order(32)]
+ public void Border_StrokeColorWithDashArrayAndOffset()
{
- App.WaitForElement("Options");
- App.Tap("Options");
+ NavigateToOptions();
App.WaitForElement("StrokeDashArrayEntry");
App.EnterText("StrokeDashArrayEntry", "5,3");
- App.WaitForElement("StrokeThicknessEntry");
- App.EnterText("StrokeThicknessEntry", "10");
-
- App.WaitForElement("RedColorButton");
- App.Tap("RedColorButton");
-
- App.WaitForElement("Apply");
- App.Tap("Apply");
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
- }
-
- [Test]
- [Category(UITestCategories.Border)]
- public void Border_StrokeThicknessWithStrokeLineJoin_Bevel()
- {
- App.WaitForElement("Options");
- App.Tap("Options");
-
- App.WaitForElement("BevelLineJoinRadio");
- App.Tap("BevelLineJoinRadio");
+ App.WaitForElement("DashOffsetEntry");
+ App.EnterText("DashOffsetEntry", "2");
App.WaitForElement("StrokeThicknessEntry");
App.EnterText("StrokeThicknessEntry", "15");
+ App.WaitForElement("BlackColorButton");
+ App.Tap("BlackColorButton");
+
App.WaitForElement("Apply");
App.Tap("Apply");
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
+#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS // For more information, see: https://github.com/dotnet/maui/issues/29898
[Test]
- [Category(UITestCategories.Border)]
- public void Border_PolygonShapeWithStrokeLineJoin_Bevel()
+ [Order(33)]
+ public void Border_StrokeDashArray_Reset()
{
- App.WaitForElement("Options");
- App.Tap("Options");
-
- App.WaitForElement("RectangleShapeRadio");
- App.Tap("RectangleShapeRadio");
+ NavigateToOptions();
- App.WaitForElement("BevelLineJoinRadio");
- App.Tap("BevelLineJoinRadio");
+ App.WaitForElement("StrokeDashArrayEntry");
+ App.EnterText("StrokeDashArrayEntry", "5,3");
App.WaitForElement("StrokeThicknessEntry");
- App.EnterText("StrokeThicknessEntry", "15");
+ App.EnterText("StrokeThicknessEntry", "15"); // Increased thickness to make dashes more visible.
App.WaitForElement("Apply");
App.Tap("Apply");
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
- }
-#endif
-
- [Test]
- [Category(UITestCategories.Border)]
- public void Border_Shadow()
- {
- App.WaitForElement("Options");
- App.Tap("Options");
- App.WaitForElement("OffsetXEntry");
- App.EnterText("OffsetXEntry", "10");
- App.WaitForElement("OffsetYEntry");
- App.EnterText("OffsetYEntry", "10");
- App.WaitForElement("OpacityEntry");
- App.EnterText("OpacityEntry", "0.8");
- App.WaitForElement("RadiusEntry");
- App.EnterText("RadiusEntry", "10");
-
- App.WaitForElement("Apply");
- App.Tap("Apply");
-
-#if WINDOWS
- VerifyScreenshot(cropTop: 100);
-#else
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
-#endif
- }
-
-#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST // For more information, see : https://github.com/dotnet/maui/issues/29898
- [Test]
- [Category(UITestCategories.Border)]
- public void Border_StrokeColorWithDashArrayAndOffset()
- {
- App.WaitForElement("Options");
- App.Tap("Options");
-
- App.WaitForElement("StrokeDashArrayEntry");
- App.EnterText("StrokeDashArrayEntry", "5,3");
-
- App.WaitForElement("DashOffsetEntry");
- App.EnterText("DashOffsetEntry", "2");
+ NavigateToOptions();
App.WaitForElement("StrokeThicknessEntry");
- App.EnterText("StrokeThicknessEntry", "15");
-
- App.WaitForElement("BlackColorButton");
- App.Tap("BlackColorButton");
+ App.EnterText("StrokeThicknessEntry", "5"); // Change thickness to ensure the reset takes effect.
App.WaitForElement("Apply");
App.Tap("Apply");
+
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
#endif
-}
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/BoxViewFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/BoxViewFeatureTests.cs
index 7924be4cd6b4..6f86d5b0f199 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/BoxViewFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/BoxViewFeatureTests.cs
@@ -5,6 +5,7 @@
namespace Microsoft.Maui.TestCases.Tests
{
+ [Category(UITestCategories.BoxView)]
public class BoxViewFeatureTests : _GalleryUITest
{
public const string BoxViewFeatureMatrix = "BoxView Feature Matrix";
@@ -16,152 +17,194 @@ public BoxViewFeatureTests(TestDevice device)
{
}
-
- [Test]
- [Category(UITestCategories.BoxView)]
- public void BoxView_IsVisible()
+ void ResetBoxView()
{
App.WaitForElement("ResetButton");
App.Tap("ResetButton");
+ }
- App.WaitForElement("VisibilityCheckBox");
- App.Tap("VisibilityCheckBox");
+ // ── Color tests (Order 1–3) ──
- // Use retryTimeout to allow UI to settle after visibility change
+ [Test, Order(1)]
+ public void BoxView_Color()
+ {
+ ResetBoxView();
+ App.WaitForElement("RedRadioButton");
+ App.Tap("RedRadioButton");
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.BoxView)]
- public void BoxView_CornerRadiusWithColor()
+ [Test, Order(2)]
+ public void BoxView_GreenColor()
{
- App.WaitForElement("ResetButton");
- App.Tap("ResetButton");
+ ResetBoxView();
+ App.WaitForElement("GreenRadioButton");
+ App.Tap("GreenRadioButton");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+ [Test, Order(3)]
+ public void BoxView_BlueColor()
+ {
+ ResetBoxView();
+ // Switch away from the default Blue then back to explicitly verify the Blue radio button
+ App.WaitForElement("RedRadioButton");
+ App.Tap("RedRadioButton");
+ App.WaitForElement("BlueRadioButton");
+ App.Tap("BlueRadioButton");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ // ── CornerRadius tests (Order 4) ──
+
+ [Test, Order(4)]
+ public void BoxView_UniformCornerRadius()
+ {
+ ResetBoxView();
App.WaitForElement("CornerRadiusEntry");
- App.EnterText("CornerRadiusEntry", "60,10,20,40");
+ App.ClearText("CornerRadiusEntry");
+ App.EnterText("CornerRadiusEntry", "30");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
- App.WaitForElement("CornerRadiusLabel");
- App.Tap("CornerRadiusLabel");
+ // ── Dimension tests (Order 5) ──
- App.WaitForElement("RedRadioButton");
- App.Tap("RedRadioButton");
+ [Test, Order(5)]
+ public void BoxView_WidthAndHeight()
+ {
+ ResetBoxView();
+ App.WaitForElement("WidthEntry");
+ App.ClearText("WidthEntry");
+ App.EnterText("WidthEntry", "300");
+ App.WaitForElement("HeightEntry");
+ App.ClearText("HeightEntry");
+ App.EnterText("HeightEntry", "150");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
- // Use retryTimeout to allow UI to settle after color change
+ // ── Opacity tests (Order 6) ──
+
+ [Test, Order(6)]
+ public void BoxView_OpacityZero()
+ {
+ ResetBoxView();
+ App.WaitForElement("OpacityEntry");
+ App.ClearText("OpacityEntry");
+ App.EnterText("OpacityEntry", "0");
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.BoxView)]
- public void BoxView_ColorWithOpacity()
+ // ── Visibility tests (Order 7) ──
+
+ [Test, Order(7)]
+ public void BoxView_IsVisible()
{
- App.WaitForElement("ResetButton");
- App.Tap("ResetButton");
+ ResetBoxView();
+ App.WaitForElement("VisibilityCheckBox");
+ App.Tap("VisibilityCheckBox");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+ // ── Combined property tests (Order 8–11) ──
+
+ [Test, Order(8)]
+ public void BoxView_CornerRadiusWithColor()
+ {
+ ResetBoxView();
+ App.WaitForElement("CornerRadiusEntry");
+ App.ClearText("CornerRadiusEntry");
+ App.EnterText("CornerRadiusEntry", "60,10,20,40");
App.WaitForElement("RedRadioButton");
App.Tap("RedRadioButton");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+ [Test, Order(9)]
+ public void BoxView_ColorWithOpacity()
+ {
+ ResetBoxView();
+ App.WaitForElement("RedRadioButton");
+ App.Tap("RedRadioButton");
App.WaitForElement("OpacityEntry");
+ App.ClearText("OpacityEntry");
App.EnterText("OpacityEntry", "0.5");
-
- App.WaitForElement("OpacityLabel");
- App.Tap("OpacityLabel");
-
- // Use retryTimeout to allow UI to settle after opacity change
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.BoxView)]
+ [Test, Order(10)]
+ [Ignore("Fails on all platforms, related issue link: https://github.com/dotnet/maui/issues/34402")]
public void BoxView_CornerRadiusWithFlowDirection()
{
- App.WaitForElement("ResetButton");
- App.Tap("ResetButton");
-
+ ResetBoxView();
App.WaitForElement("CornerRadiusEntry");
+ App.ClearText("CornerRadiusEntry");
App.EnterText("CornerRadiusEntry", "60,10,20,40");
-
- App.WaitForElement("CornerRadiusLabel");
- App.Tap("CornerRadiusLabel");
-
App.WaitForElement("FlowDirectionRTLCheckBox");
App.Tap("FlowDirectionRTLCheckBox");
-
- // Use retryTimeout to allow UI to settle after flow direction change
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
-#if TEST_FAILS_ON_WINDOWS // For more information see: https://github.com/dotnet/maui/issues/27732
- [Test]
- [Category(UITestCategories.BoxView)]
- public void BoxView_CornerRadiusWithOpacityAndShadow()
+
+ [Test, Order(11)]
+ public void BoxView_Reset()
{
+ ResetBoxView();
+ App.WaitForElement("RedRadioButton");
+ App.Tap("RedRadioButton");
+ App.WaitForElement("CornerRadiusEntry");
+ App.ClearText("CornerRadiusEntry");
+ App.EnterText("CornerRadiusEntry", "30,30,30,30");
+
+ // Reset back to default state and verify
App.WaitForElement("ResetButton");
App.Tap("ResetButton");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#if TEST_FAILS_ON_WINDOWS // For more information see: https://github.com/dotnet/maui/issues/27732
+ // ── Shadow tests - disabled on Windows (Order 12–14) ──
+
+ [Test, Order(12)]
+ public void BoxView_CornerRadiusWithOpacityAndShadow()
+ {
+ ResetBoxView();
App.WaitForElement("CornerRadiusEntry");
+ App.ClearText("CornerRadiusEntry");
App.EnterText("CornerRadiusEntry", "60,10,20,40");
-
- App.WaitForElement("CornerRadiusLabel");
- App.Tap("CornerRadiusLabel");
-
App.WaitForElement("OpacityEntry");
+ App.ClearText("OpacityEntry");
App.EnterText("OpacityEntry", "0.5");
-
- App.WaitForElement("OpacityLabel");
- App.Tap("OpacityLabel");
-
App.WaitForElement("ShadowCheckBox");
App.Tap("ShadowCheckBox");
-
- // Use retryTimeout to allow UI to settle after shadow change
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.BoxView)]
+ [Test, Order(13)]
public void BoxView_CornerRadiusWithColorAndShadow()
{
- App.WaitForElement("ResetButton");
- App.Tap("ResetButton");
-
+ ResetBoxView();
App.WaitForElement("CornerRadiusEntry");
+ App.ClearText("CornerRadiusEntry");
App.EnterText("CornerRadiusEntry", "60,10,20,40");
-
- App.WaitForElement("CornerRadiusLabel");
- App.Tap("CornerRadiusLabel");
-
App.WaitForElement("RedRadioButton");
App.Tap("RedRadioButton");
-
App.WaitForElement("ShadowCheckBox");
App.Tap("ShadowCheckBox");
-
- // Use retryTimeout to allow UI to settle after shadow change
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.BoxView)]
+ [Test, Order(14)]
public void BoxView_ColorWithOpacityAndShadow()
{
- App.WaitForElement("ResetButton");
- App.Tap("ResetButton");
-
+ ResetBoxView();
App.WaitForElement("RedRadioButton");
App.Tap("RedRadioButton");
-
App.WaitForElement("OpacityEntry");
+ App.ClearText("OpacityEntry");
App.EnterText("OpacityEntry", "0.5");
-
- App.WaitForElement("OpacityLabel");
- App.Tap("OpacityLabel");
-
App.WaitForElement("ShadowCheckBox");
App.Tap("ShadowCheckBox");
-
- // Use retryTimeout to allow UI to settle after shadow change
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
#endif
}
-}
\ No newline at end of file
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CheckBoxFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CheckBoxFeatureTests.cs
index b5fd1e75b075..63c04cd33d6a 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CheckBoxFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CheckBoxFeatureTests.cs
@@ -4,6 +4,7 @@
namespace Microsoft.Maui.TestCases.Tests;
+[Category(UITestCategories.CheckBox)]
public class CheckBoxFeatureTests : _GalleryUITest
{
const string IsCheckedLabel = "IsCheckedLabel";
@@ -12,9 +13,13 @@ public class CheckBoxFeatureTests : _GalleryUITest
const string IsCheckedSwitch = "IsCheckedSwitch";
const string IsEnabledSwitch = "IsEnabledSwitch";
const string IsVisibleSwitch = "IsVisibleSwitch";
- const string BlueColorButton = "BlueColorButton";
const string CommandStatusLabel = "CommandStatusLabel";
const string CheckedChangedStatusLabel = "CheckedChangedStatusLabel";
+ const string HasShadowCheckBox = "HasShadowCheckBox";
+ const string GreenColorButton = "GreenColorButton";
+ const string BlueColorButton = "BlueColorButton";
+ const string DefaultColorButton = "DefaultColorButton";
+ const string CommandParameterEntry = "CommandParameterEntry";
public const string CheckBoxFeatureMatrix = "CheckBox Feature Matrix";
@@ -26,175 +31,235 @@ public CheckBoxFeatureTests(TestDevice device)
}
[Test, Order(1)]
- [Category(UITestCategories.CheckBox)]
public void CheckBox_ValidateDefaultValues_VerifyLabels()
{
+ App.WaitForElement(IsCheckedLabel);
Assert.That(App.FindElement(IsCheckedLabel).GetText(), Is.EqualTo("True"));
App.WaitForElement(CheckBoxControl);
}
- [Test]
- [Category(UITestCategories.CheckBox)]
+ [Test, Order(2)]
public void CheckBox_SetIsCheckedState()
{
App.WaitForElement(ResetButton);
App.Tap(ResetButton);
App.WaitForElement(IsCheckedSwitch);
App.Tap(IsCheckedSwitch);
-
Assert.That(App.FindElement(IsCheckedLabel).GetText(), Is.EqualTo("False"));
-
App.WaitForElement(IsCheckedSwitch);
App.Tap(IsCheckedSwitch);
-
Assert.That(App.FindElement(IsCheckedLabel).GetText(), Is.EqualTo("True"));
}
- [Test]
- [Category(UITestCategories.CheckBox)]
+ [Test, Order(3)]
public void CheckBox_VerifyCheckedChangedEvent()
{
App.WaitForElement(ResetButton);
App.Tap(ResetButton);
-
App.WaitForElement(IsEnabledSwitch);
App.Tap(IsEnabledSwitch);
-
+ App.WaitForElement(CheckBoxControl);
App.Tap(CheckBoxControl);
-
App.WaitForNoElement("CheckedChanged Triggered");
-
App.WaitForElement(IsEnabledSwitch);
App.Tap(IsEnabledSwitch);
-
+ App.WaitForElement(CheckBoxControl);
App.Tap(CheckBoxControl);
-
Assert.That(App.FindElement(CheckedChangedStatusLabel).GetText(), Is.EqualTo("CheckedChanged Triggered"));
}
- [Test]
- [Category(UITestCategories.CheckBox)]
+ [Test, Order(4)]
public void CheckBox_SetVisibilityToFalse_VerifyVisualState()
{
App.WaitForElement(ResetButton);
App.Tap(ResetButton);
-
App.WaitForElement(IsVisibleSwitch);
App.Tap(IsVisibleSwitch);
-
Assert.That(App.FindElements(CheckBoxControl), Is.Empty);
-
App.WaitForElement(IsVisibleSwitch);
App.Tap(IsVisibleSwitch);
-
Assert.That(App.FindElements(CheckBoxControl), Is.Not.Empty);
}
- [Test]
- [Category(UITestCategories.CheckBox)]
- public void CheckBox_ChangeColor_VerifyVisualState()
+ [Test, Order(5)]
+ public void CheckBox_VerifyWithShadow()
{
App.WaitForElement(ResetButton);
App.Tap(ResetButton);
-
- App.WaitForElement("GreenColorButton");
- App.Tap("GreenColorButton");
-
+ App.WaitForElement(HasShadowCheckBox);
+ App.Tap(HasShadowCheckBox);
+ App.WaitForElement(CheckBoxControl);
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.CheckBox)]
- public void CheckBox_SetIsCheckedAndColor_VerifyVisualState()
+ [Test, Order(6)]
+ public void CheckBox_VerifyCommandExecution()
{
App.WaitForElement(ResetButton);
App.Tap(ResetButton);
+ App.WaitForElement(CheckBoxControl);
+ App.Tap(CheckBoxControl);
+ Assert.That(App.FindElement(CommandStatusLabel).GetText(), Is.EqualTo("Command Executed"));
+ }
+
+ [Test, Order(7)]
+ public void CheckBox_VerifyCommandWithParameterWhileChecked()
+ {
+ App.WaitForElement(ResetButton);
+ App.Tap(ResetButton);
+ App.WaitForElement(CommandParameterEntry);
+ App.EnterText(CommandParameterEntry, "TestParameter");
+ App.DismissKeyboard();
+ App.WaitForElement(CheckBoxControl);
+ App.Tap(CheckBoxControl);
+ Assert.That(App.FindElement(CommandStatusLabel).GetText(), Is.EqualTo("Command Executed: TestParameter"));
+ }
+ [Test, Order(8)]
+ public void CheckBox_VerifyCommandWithParameterWhileUnChecked()
+ {
+ App.WaitForElement(ResetButton);
+ App.Tap(ResetButton);
+ App.WaitForElement(CommandParameterEntry);
+ App.EnterText(CommandParameterEntry, "TestParameter");
+ App.DismissKeyboard();
App.WaitForElement(IsCheckedSwitch);
App.Tap(IsCheckedSwitch);
-
- App.WaitForElement(BlueColorButton);
- App.Tap(BlueColorButton);
-
- Assert.That(App.FindElement(IsCheckedLabel).GetText(), Is.EqualTo("False"));
-
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ Assert.That(App.FindElement(CommandStatusLabel).GetText(), Is.EqualTo("Command Executed: TestParameter"));
+ App.WaitForElement(CheckBoxControl);
+ App.Tap(CheckBoxControl);
+ Assert.That(App.FindElement(CommandStatusLabel).GetText(), Is.EqualTo("Command Executed: TestParameter"));
}
- [Test]
- [Category(UITestCategories.CheckBox)]
- public void CheckBox_SetAllProperties_VerifyVisualState()
+ [Test, Order(9)]
+ public void CheckBox_VerifyCommandNotExecutedWhenDisabled()
{
App.WaitForElement(ResetButton);
App.Tap(ResetButton);
-
- App.WaitForElement(BlueColorButton);
- App.Tap(BlueColorButton);
-
App.WaitForElement(IsEnabledSwitch);
App.Tap(IsEnabledSwitch);
-
+ App.WaitForElement(CheckBoxControl);
App.Tap(CheckBoxControl);
-
- Assert.That(App.FindElement(IsCheckedLabel).GetText(), Is.EqualTo("True"));
+ App.WaitForNoElement("Command Executed");
}
- [Test]
- [Category(UITestCategories.CheckBox)]
- public void CheckBox_VerifyCommandExecution()
+ [Test, Order(10)]
+ public void CheckBox_VerifyBothEventAndCommandExecuted()
{
App.WaitForElement(ResetButton);
App.Tap(ResetButton);
-
App.WaitForElement(CheckBoxControl);
App.Tap(CheckBoxControl);
-
+ Assert.That(App.FindElement(CheckedChangedStatusLabel).GetText(), Is.EqualTo("CheckedChanged Triggered"));
Assert.That(App.FindElement(CommandStatusLabel).GetText(), Is.EqualTo("Command Executed"));
}
- [Test]
- [Category(UITestCategories.CheckBox)]
- public void CheckBox_VerifyCommandWithParameter()
+ [Test, Order(11)]
+ public void CheckBox_DirectTap_TogglesIsChecked()
{
App.WaitForElement(ResetButton);
App.Tap(ResetButton);
-
- App.WaitForElement("CommandParameterEntry");
- App.EnterText("CommandParameterEntry", "TestParameter");
-
+ Assert.That(App.FindElement(IsCheckedLabel).GetText(), Is.EqualTo("True"));
App.WaitForElement(CheckBoxControl);
App.Tap(CheckBoxControl);
+ Assert.That(App.FindElement(IsCheckedLabel).GetText(), Is.EqualTo("False"));
+ App.WaitForElement(CheckBoxControl);
+ App.Tap(CheckBoxControl);
+ Assert.That(App.FindElement(IsCheckedLabel).GetText(), Is.EqualTo("True"));
+ }
- Assert.That(App.FindElement(CommandStatusLabel).GetText(), Is.EqualTo("Command Executed: TestParameter"));
+ [Test, Order(12)]
+ public void CheckBox_VerifyIsCheckedAfterReset()
+ {
+ App.WaitForElement(ResetButton);
+ App.Tap(ResetButton);
+ App.WaitForElement(IsCheckedSwitch);
+ App.Tap(IsCheckedSwitch);
+ Assert.That(App.FindElement(IsCheckedLabel).GetText(), Is.EqualTo("False"));
+ App.WaitForElement(ResetButton);
+ App.Tap(ResetButton);
+ Assert.That(App.FindElement(IsCheckedLabel).GetText(), Is.EqualTo("True"));
}
- [Test]
- [Category(UITestCategories.CheckBox)]
- public void CheckBox_VerifyCommandNotExecutedWhenDisabled()
+ [Test, Order(13)]
+ public void CheckBox_VerifyCheckedWhileIsEnableSetFalseUsingTap()
{
App.WaitForElement(ResetButton);
App.Tap(ResetButton);
+ App.WaitForElement(IsCheckedSwitch);
+ App.Tap(IsCheckedSwitch);
+ Assert.That(App.FindElement(IsCheckedLabel).GetText(), Is.EqualTo("False"));
+ App.WaitForElement(IsEnabledSwitch);
+ App.Tap(IsEnabledSwitch);
+ App.WaitForElement(CheckBoxControl);
+ App.Tap(CheckBoxControl);
+ Assert.That(App.FindElement(IsCheckedLabel).GetText(), Is.EqualTo("False"));
+ }
+ [Test, Order(14)]
+ public void CheckBox_VerifyUnCheckedWhileIsEnableSetFalseUsingTap()
+ {
+ App.WaitForElement(ResetButton);
+ App.Tap(ResetButton);
+ Assert.That(App.FindElement(IsCheckedLabel).GetText(), Is.EqualTo("True"));
App.WaitForElement(IsEnabledSwitch);
App.Tap(IsEnabledSwitch);
+ App.WaitForElement(CheckBoxControl);
+ App.Tap(CheckBoxControl);
+ Assert.That(App.FindElement(IsCheckedLabel).GetText(), Is.EqualTo("True"));
+ }
+ [Test, Order(15)]
+ public void CheckBox_SetAllProperties_VerifyVisualState()
+ {
+ App.WaitForElement(ResetButton);
+ App.Tap(ResetButton);
+ App.WaitForElement(GreenColorButton);
+ App.Tap(GreenColorButton);
+ App.WaitForElement(IsEnabledSwitch);
+ App.Tap(IsEnabledSwitch);
App.WaitForElement(CheckBoxControl);
App.Tap(CheckBoxControl);
+ Assert.That(App.FindElement(IsCheckedLabel).GetText(), Is.EqualTo("True"));
+ }
- App.WaitForNoElement("Command Executed");
+ [Test, Order(16)]
+ public void CheckBox_ChangeColor_VerifyVisualState()
+ {
+ App.WaitForElement(ResetButton);
+ App.Tap(ResetButton);
+ App.WaitForElement(GreenColorButton);
+ App.Tap(GreenColorButton);
+ App.WaitForElement(CheckBoxControl);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test, Order(11)]
- [Category(UITestCategories.CheckBox)]
- public void CheckBox_VerifyBothEventAndCommandExecuted()
+ [Test, Order(17)]
+ public void CheckBox_SetIsCheckedAndColor_VerifyVisualState()
{
App.WaitForElement(ResetButton);
App.Tap(ResetButton);
+ App.WaitForElement(IsCheckedSwitch);
+ App.Tap(IsCheckedSwitch);
+ App.WaitForElement(BlueColorButton);
+ App.Tap(BlueColorButton);
+ Assert.That(App.FindElement(IsCheckedLabel).GetText(), Is.EqualTo("False"));
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST // Related Issue: https://github.com/dotnet/maui/issues/34278
+ [Test, Order(18)]
+ public void CheckBox_VerifyColorAfterReset()
+ {
+ App.WaitForElement(ResetButton);
+ App.Tap(ResetButton);
+ App.WaitForElement(GreenColorButton);
+ App.Tap(GreenColorButton);
+ App.WaitForElement(ResetButton);
+ App.Tap(ResetButton);
App.WaitForElement(CheckBoxControl);
- App.Tap(CheckBoxControl);
-
- Assert.That(App.FindElement(CheckedChangedStatusLabel).GetText(), Is.EqualTo("CheckedChanged Triggered"));
- Assert.That(App.FindElement(CommandStatusLabel).GetText(), Is.EqualTo("Command Executed"));
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
+#endif
}
+
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs
new file mode 100644
index 000000000000..9c2dbe320ba3
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ClipFeatureTests.cs
@@ -0,0 +1,1122 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+public class ClipFeatureTests : _GalleryUITest
+{
+ public const string ClipFeatureMatrix = "Clip Feature Matrix";
+ public const string Options = "Options";
+ public const string Apply = "Apply";
+ public const string RectangleGeometry = "RectangleGeometry";
+ public const string EllipseGeometry = "EllipseGeometry";
+ public const string RoundRectangleGeometry = "RoundRectangleGeometry";
+ public const string GeometryGroup = "GeometryGroup";
+ public const string LineSegment = "LineSegment";
+ public const string ArcSegment = "ArcSegment";
+ public const string BezierSegment = "BezierSegment";
+ public const string PolyLineSegment = "PolyLineSegment";
+ public const string PolyBezierSegment = "PolyBezierSegment";
+ public const string QuadraticBezierSegment = "QuadraticBezierSegment";
+ public const string PolyQuadraticBezierSegment = "PolyQuadraticBezierSegment";
+
+ public override string GalleryPageName => ClipFeatureMatrix;
+
+ public ClipFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+ // ==================== Border Tests ====================
+
+ [Test, Order(1)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Border_ClipWithStrokeThickness()
+ {
+ App.WaitForElement("BorderButton");
+ App.Tap("BorderButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RectangleGeometry);
+ App.Tap(RectangleGeometry);
+
+ App.WaitForElement("StrokeThicknessEntry");
+ App.ClearText("StrokeThicknessEntry");
+ App.EnterText("StrokeThicknessEntry", "10");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(2)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Border_ClipWithStrokeColorBlue()
+ {
+ TapButtonIfOnClipControlPage("BorderButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(EllipseGeometry);
+ App.Tap(EllipseGeometry);
+
+ App.WaitForElement("BlueColorButton");
+ App.Tap("BlueColorButton");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(3)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Border_ClipWithStrokeColorGreen()
+ {
+ TapButtonIfOnClipControlPage("BorderButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RoundRectangleGeometry);
+ App.Tap(RoundRectangleGeometry);
+
+ App.WaitForElement("BorderGreenColorButton");
+ App.Tap("BorderGreenColorButton");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(4)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Border_ClipWithStrokeShapeRoundRectangle()
+ {
+ TapButtonIfOnClipControlPage("BorderButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(GeometryGroup);
+ App.Tap(GeometryGroup);
+
+ App.WaitForElement("RoundRectangleShapeRadio");
+ App.Tap("RoundRectangleShapeRadio");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+#if TEST_FAILS_ON_WINDOWS // Issue: https://github.com/dotnet/maui/issues/30778
+
+ [Test, Order(5)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Border_ClipWithShadow()
+ {
+ TapButtonIfOnClipControlPage("BorderButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(ArcSegment);
+ App.Tap(ArcSegment);
+
+ App.WaitForElement("ShadowTrueRadioButton");
+ App.Tap("ShadowTrueRadioButton");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ // ==================== BoxView Tests ====================
+
+ [Test, Order(6)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void BoxView_ClipWithColorGreen()
+ {
+ TapButtonIfOnClipControlPage("BoxViewButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("BoxViewButton");
+ App.Tap("BoxViewButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RectangleGeometry);
+ App.Tap(RectangleGeometry);
+
+ App.WaitForElement("BoxViewGreenColorButton");
+ App.Tap("BoxViewGreenColorButton");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(7)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void BoxView_ClipWithCornerRadius()
+ {
+ TapButtonIfOnClipControlPage("BoxViewButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(EllipseGeometry);
+ App.Tap(EllipseGeometry);
+
+ App.WaitForElement("CornerRadiusEntry");
+ App.ClearText("CornerRadiusEntry");
+ App.EnterText("CornerRadiusEntry", "30");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+#if TEST_FAILS_ON_WINDOWS // Issue: https://github.com/dotnet/maui/issues/30778
+
+ [Test, Order(8)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void BoxView_ClipWithShadow()
+ {
+ TapButtonIfOnClipControlPage("BoxViewButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RoundRectangleGeometry);
+ App.Tap(RoundRectangleGeometry);
+
+ App.WaitForElement("ShadowTrueRadioButton");
+ App.Tap("ShadowTrueRadioButton");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ // ==================== Button Tests ====================
+
+ [Test, Order(9)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Button_ClipWithImageSource()
+ {
+ TapButtonIfOnClipControlPage("ButtonId");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("ButtonId");
+ App.Tap("ButtonId");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RectangleGeometry);
+ App.Tap(RectangleGeometry);
+
+ App.WaitForElement("ImageSource");
+ App.Tap("ImageSource");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(10)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Button_ClipWithText()
+ {
+ TapButtonIfOnClipControlPage("ButtonId");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(EllipseGeometry);
+ App.Tap(EllipseGeometry);
+
+ App.WaitForElement("TextEntry");
+ App.ClearText("TextEntry");
+ App.EnterText("TextEntry", "Clipped Button");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+#if TEST_FAILS_ON_WINDOWS // Issue: https://github.com/dotnet/maui/issues/30778
+
+ [Test, Order(11)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Button_ClipWithShadow()
+ {
+ TapButtonIfOnClipControlPage("ButtonId");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RoundRectangleGeometry);
+ App.Tap(RoundRectangleGeometry);
+
+ App.WaitForElement("ShadowTrueRadioButton");
+ App.Tap("ShadowTrueRadioButton");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ // ==================== Image Tests (Geometry Variations) ====================
+
+ [Test, Order(12)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Image_ClipWithRectangleGeometry()
+ {
+ TapButtonIfOnClipControlPage("ImageButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("ImageButton");
+ App.Tap("ImageButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RectangleGeometry);
+ App.Tap(RectangleGeometry);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(13)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Image_ClipWithEllipseGeometry()
+ {
+ TapButtonIfOnClipControlPage("ImageButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(EllipseGeometry);
+ App.Tap(EllipseGeometry);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(14)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Image_ClipWithRoundRectangleGeometry()
+ {
+ TapButtonIfOnClipControlPage("ImageButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RoundRectangleGeometry);
+ App.Tap(RoundRectangleGeometry);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(15)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Image_ClipWithGeometryGroup()
+ {
+ TapButtonIfOnClipControlPage("ImageButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(GeometryGroup);
+ App.Tap(GeometryGroup);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(16)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Image_ClipWithLineSegmentPath()
+ {
+ TapButtonIfOnClipControlPage("ImageButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(LineSegment);
+ App.Tap(LineSegment);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(17)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Image_ClipWithArcSegmentPath()
+ {
+ TapButtonIfOnClipControlPage("ImageButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(ArcSegment);
+ App.Tap(ArcSegment);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(18)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Image_ClipWithBezierSegmentPath()
+ {
+ TapButtonIfOnClipControlPage("ImageButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(BezierSegment);
+ App.Tap(BezierSegment);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(19)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Image_ClipWithPolyLineSegmentPath()
+ {
+ TapButtonIfOnClipControlPage("ImageButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(PolyLineSegment);
+ App.Tap(PolyLineSegment);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(20)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Image_ClipWithPolyBezierSegmentPath()
+ {
+ TapButtonIfOnClipControlPage("ImageButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(PolyBezierSegment);
+ App.Tap(PolyBezierSegment);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(21)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Image_ClipWithQuadraticBezierSegmentPath()
+ {
+ TapButtonIfOnClipControlPage("ImageButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(QuadraticBezierSegment);
+ App.Tap(QuadraticBezierSegment);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(22)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Image_ClipWithPolyQuadraticBezierSegmentPath()
+ {
+ TapButtonIfOnClipControlPage("ImageButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(PolyQuadraticBezierSegment);
+ App.Tap(PolyQuadraticBezierSegment);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ // ==================== Label Tests ====================
+
+#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS && TEST_FAILS_ON_WINDOWS // Issue: https://github.com/dotnet/maui/issues/34114
+
+ [Test, Order(23)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Label_ClipWithLongText()
+ {
+ TapButtonIfOnClipControlPage("LabelButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("LabelButton");
+ App.Tap("LabelButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RectangleGeometry);
+ App.Tap(RectangleGeometry);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(24)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Label_ClipWithDifferentFontSize()
+ {
+ TapButtonIfOnClipControlPage("LabelButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RoundRectangleGeometry);
+ App.Tap(RoundRectangleGeometry);
+
+ App.WaitForElement("FontSizeEntry");
+ App.ClearText("FontSizeEntry");
+ App.EnterText("FontSizeEntry", "36");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(25)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Label_ClipWithFormattedText()
+ {
+ TapButtonIfOnClipControlPage("LabelButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(EllipseGeometry);
+ App.Tap(EllipseGeometry);
+
+ App.WaitForElement("FormattedText");
+ App.Tap("FormattedText");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ // ==================== ContentView Tests ====================
+
+ [Test, Order(26)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void ContentView_ClipWithRectangleGeometry()
+ {
+ TapButtonIfOnClipControlPage("ContentViewButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("ContentViewButton");
+ App.Tap("ContentViewButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RectangleGeometry);
+ App.Tap(RectangleGeometry);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(27)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void ContentView_ClipWithEllipseGeometry()
+ {
+ TapButtonIfOnClipControlPage("ContentViewButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(EllipseGeometry);
+ App.Tap(EllipseGeometry);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(28)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void ContentView_ClipWithRoundRectangleGeometry()
+ {
+ TapButtonIfOnClipControlPage("ContentViewButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RoundRectangleGeometry);
+ App.Tap(RoundRectangleGeometry);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+#if TEST_FAILS_ON_WINDOWS // Issue: https://github.com/dotnet/maui/issues/30778
+
+ [Test, Order(29)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void ContentView_ClipWithShadow()
+ {
+ TapButtonIfOnClipControlPage("ContentViewButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(GeometryGroup);
+ App.Tap(GeometryGroup);
+
+ App.WaitForElement("ShadowTrueRadioButton");
+ App.Tap("ShadowTrueRadioButton");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ // ==================== ImageButton Tests ====================
+
+ [Test, Order(30)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void ImageButton_ClipWithRectangleGeometry()
+ {
+ TapButtonIfOnClipControlPage("ImageButtonButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("ImageButtonButton");
+ App.Tap("ImageButtonButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RectangleGeometry);
+ App.Tap(RectangleGeometry);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(31)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void ImageButton_ClipWithEllipseGeometry()
+ {
+ TapButtonIfOnClipControlPage("ImageButtonButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(EllipseGeometry);
+ App.Tap(EllipseGeometry);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(32)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void ImageButton_ClipWithRoundRectangleGeometry()
+ {
+ TapButtonIfOnClipControlPage("ImageButtonButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RoundRectangleGeometry);
+ App.Tap(RoundRectangleGeometry);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+#if TEST_FAILS_ON_WINDOWS // Issue: https://github.com/dotnet/maui/issues/30778
+
+ [Test, Order(33)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void ImageButton_ClipWithShadow()
+ {
+ TapButtonIfOnClipControlPage("ImageButtonButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(GeometryGroup);
+ App.Tap(GeometryGroup);
+
+ App.WaitForElement("ShadowTrueRadioButton");
+ App.Tap("ShadowTrueRadioButton");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ // ==================== Negative Tests ====================
+
+ [Test, Order(34)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Border_ClipNull_NoCrash()
+ {
+ TapButtonIfOnClipControlPage("BorderButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("BorderButton");
+ App.Tap("BorderButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RectangleGeometry);
+ App.Tap(RectangleGeometry);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+
+ // Now clear the clip by setting it to null
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement("ClearClipButton");
+ App.Tap("ClearClipButton");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(35)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Image_ClipNull_NoCrash()
+ {
+ TapButtonIfOnClipControlPage("ImageButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("ImageButton");
+ App.Tap("ImageButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(EllipseGeometry);
+ App.Tap(EllipseGeometry);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+
+ // Now clear the clip by setting it to null
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement("ClearClipButton");
+ App.Tap("ClearClipButton");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(36)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Button_ClipNull_NoCrash()
+ {
+ TapButtonIfOnClipControlPage("ButtonId");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("ButtonId");
+ App.Tap("ButtonId");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RoundRectangleGeometry);
+ App.Tap(RoundRectangleGeometry);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+
+ // Now clear the clip by setting it to null
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement("ClearClipButton");
+ App.Tap("ClearClipButton");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(37)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void ContentView_ClipNull_NoCrash()
+ {
+ TapButtonIfOnClipControlPage("ContentViewButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("ContentViewButton");
+ App.Tap("ContentViewButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(GeometryGroup);
+ App.Tap(GeometryGroup);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+
+ // Now clear the clip by setting it to null
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement("ClearClipButton");
+ App.Tap("ClearClipButton");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ // ==================== Clip with Rotation Tests ====================
+
+ [Test, Order(38)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Image_ClipWithRotation()
+ {
+ TapButtonIfOnClipControlPage("ImageButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("ImageButton");
+ App.Tap("ImageButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(EllipseGeometry);
+ App.Tap(EllipseGeometry);
+
+ App.WaitForElement("RotationEntry");
+ App.ClearText("RotationEntry");
+ App.EnterText("RotationEntry", "45");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(39)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Border_ClipWithRotation()
+ {
+ TapButtonIfOnClipControlPage("BorderButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("BorderButton");
+ App.Tap("BorderButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RectangleGeometry);
+ App.Tap(RectangleGeometry);
+
+ App.WaitForElement("RotationEntry");
+ App.ClearText("RotationEntry");
+ App.EnterText("RotationEntry", "30");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS && TEST_FAILS_ON_WINDOWS // Issue: https://github.com/dotnet/maui/issues/34114
+
+ [Test, Order(40)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Label_ClipWithRotation()
+ {
+ TapButtonIfOnClipControlPage("LabelButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("LabelButton");
+ App.Tap("LabelButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RoundRectangleGeometry);
+ App.Tap(RoundRectangleGeometry);
+
+ App.WaitForElement("RotationEntry");
+ App.ClearText("RotationEntry");
+ App.EnterText("RotationEntry", "90");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ // ==================== Clip with Scale Tests ====================
+
+ [Test, Order(41)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Button_ClipWithScale()
+ {
+ TapButtonIfOnClipControlPage("ButtonId");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("ButtonId");
+ App.Tap("ButtonId");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RectangleGeometry);
+ App.Tap(RectangleGeometry);
+
+ App.WaitForElement("ScaleXEntry");
+ App.ClearText("ScaleXEntry");
+ App.EnterText("ScaleXEntry", "0.5");
+
+ App.WaitForElement("ScaleYEntry");
+ App.ClearText("ScaleYEntry");
+ App.EnterText("ScaleYEntry", "0.5");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(42)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Image_ClipWithScale()
+ {
+ TapButtonIfOnClipControlPage("ImageButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("ImageButton");
+ App.Tap("ImageButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RoundRectangleGeometry);
+ App.Tap(RoundRectangleGeometry);
+
+ App.WaitForElement("ScaleXEntry");
+ App.ClearText("ScaleXEntry");
+ App.EnterText("ScaleXEntry", "1.5");
+
+ App.WaitForElement("ScaleYEntry");
+ App.ClearText("ScaleYEntry");
+ App.EnterText("ScaleYEntry", "0.75");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(43)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void ImageButton_ClipWithScale()
+ {
+ TapButtonIfOnClipControlPage("ImageButtonButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("ImageButtonButton");
+ App.Tap("ImageButtonButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(EllipseGeometry);
+ App.Tap(EllipseGeometry);
+
+ App.WaitForElement("ScaleXEntry");
+ App.ClearText("ScaleXEntry");
+ App.EnterText("ScaleXEntry", "0.8");
+
+ App.WaitForElement("ScaleYEntry");
+ App.ClearText("ScaleYEntry");
+ App.EnterText("ScaleYEntry", "0.8");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ // ==================== Nested Clip Tests ====================
+
+ [Test, Order(44)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void ContentView_ClipWithNestedClippedContent()
+ {
+ TapButtonIfOnClipControlPage("ContentViewButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("ContentViewButton");
+ App.Tap("ContentViewButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ // Apply an ellipse clip to the ContentView (which contains child elements)
+ App.WaitForElement(EllipseGeometry);
+ App.Tap(EllipseGeometry);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+#if TEST_FAILS_ON_WINDOWS // Issue: https://github.com/dotnet/maui/issues/30778
+
+ [Test, Order(45)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Border_ClipWithNestedContent()
+ {
+ TapButtonIfOnClipControlPage("BorderButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("BorderButton");
+ App.Tap("BorderButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ // Apply a geometry group clip to Border (which has child Label)
+ App.WaitForElement(GeometryGroup);
+ App.Tap(GeometryGroup);
+
+ App.WaitForElement("ShadowTrueRadioButton");
+ App.Tap("ShadowTrueRadioButton");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ // ==================== Complex Geometry Tests ====================
+
+#if TEST_FAILS_ON_WINDOWS // Issue: https://github.com/dotnet/maui/issues/30778
+
+ [Test, Order(46)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Image_ClipWithComplexPolyLineGeometry()
+ {
+ TapButtonIfOnClipControlPage("ImageButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("ImageButton");
+ App.Tap("ImageButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ // PolyLine creates a complex star shape - tests performance with many points
+ App.WaitForElement(PolyLineSegment);
+ App.Tap(PolyLineSegment);
+
+ App.WaitForElement("ShadowTrueRadioButton");
+ App.Tap("ShadowTrueRadioButton");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ [Test, Order(47)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Image_ClipWithComplexPolyBezierAndRotation()
+ {
+ TapButtonIfOnClipControlPage("ImageButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("ImageButton");
+ App.Tap("ImageButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ // Combine complex geometry with transform for stress test
+ App.WaitForElement(PolyBezierSegment);
+ App.Tap(PolyBezierSegment);
+
+ App.WaitForElement("RotationEntry");
+ App.ClearText("RotationEntry");
+ App.EnterText("RotationEntry", "15");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ // ==================== Clip with Combined Transforms ====================
+
+ [Test, Order(48)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void Border_ClipWithRotationAndScale()
+ {
+ TapButtonIfOnClipControlPage("BorderButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("BorderButton");
+ App.Tap("BorderButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(EllipseGeometry);
+ App.Tap(EllipseGeometry);
+
+ App.WaitForElement("RotationEntry");
+ App.ClearText("RotationEntry");
+ App.EnterText("RotationEntry", "25");
+
+ App.WaitForElement("ScaleXEntry");
+ App.ClearText("ScaleXEntry");
+ App.EnterText("ScaleXEntry", "0.7");
+
+ App.WaitForElement("ScaleYEntry");
+ App.ClearText("ScaleYEntry");
+ App.EnterText("ScaleYEntry", "0.7");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(49)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void BoxView_ClipWithRotation()
+ {
+ TapButtonIfOnClipControlPage("BoxViewButton");
+ App.TapBackArrow(Device == TestDevice.iOS || Device == TestDevice.Mac ? "ClipControlPage" : "");
+ App.WaitForElement("BoxViewButton");
+ App.Tap("BoxViewButton");
+
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(RectangleGeometry);
+ App.Tap(RectangleGeometry);
+
+ App.WaitForElement("RotationEntry");
+ App.ClearText("RotationEntry");
+ App.EnterText("RotationEntry", "60");
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ public void TapButtonIfOnClipControlPage(string buttonAutomationId)
+ {
+ var button = App.FindElement(buttonAutomationId);
+ if (button != null)
+ {
+ App.WaitForElement(buttonAutomationId);
+ App.Tap(buttonAutomationId);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_DynamicChangesFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_DynamicChangesFeatureTests.cs
index cc862bf758e9..521847366634 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_DynamicChangesFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_DynamicChangesFeatureTests.cs
@@ -40,7 +40,7 @@ public void ValidateDynamicItemTemplateDisplayed()
}
#if TEST_FAILS_ON_ANDROID
-//Dynamic Updates to CollectionView Header/Footer and Templates Are Not Displayed Issue Link: https://github.com/dotnet/maui/issues/28676
+ //Dynamic Updates to CollectionView Header/Footer and Templates Are Not Displayed Issue Link: https://github.com/dotnet/maui/issues/28676
[Test]
[Category(UITestCategories.CollectionView)]
public void ValidateDynamicHeaderStringDisplayed()
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_EmptyViewFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_EmptyViewFeatureTests.cs
index afbb28ff09e9..f9a01d5925b5 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_EmptyViewFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_EmptyViewFeatureTests.cs
@@ -183,6 +183,105 @@ public void ValidateEmptyViewStringDisplayedSetFirst_AndGroupedList()
App.WaitForNoElement("No Items Available(String)");
}
+#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS && TEST_FAILS_ON_WINDOWS // Issue Link - https://github.com/dotnet/maui/issues/32404
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyEmptyViewStringDisplaysCorrectly_WithLeftToRightFlowDirection()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("EmptyViewString");
+ App.Tap("EmptyViewString");
+ App.WaitForElement("FlowDirectionLeftToRight");
+ App.Tap("FlowDirectionLeftToRight");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("No Items Available(String)");
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyEmptyViewStringDisplaysCorrectly_WithRightToLeftFlowDirection()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("EmptyViewString");
+ App.Tap("EmptyViewString");
+ App.WaitForElement("FlowDirectionRightToLeft");
+ App.Tap("FlowDirectionRightToLeft");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("No Items Available(String)");
+ VerifyScreenshot();
+ }
+#endif
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyCustomEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("EmptyViewGrid");
+ App.Tap("EmptyViewGrid");
+ App.WaitForElement("FlowDirectionLeftToRight");
+ App.Tap("FlowDirectionLeftToRight");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("No Items Available(Grid View)");
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyCustomEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("EmptyViewGrid");
+ App.Tap("EmptyViewGrid");
+ App.WaitForElement("FlowDirectionRightToLeft");
+ App.Tap("FlowDirectionRightToLeft");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("No Items Available(Grid View)");
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyCustomSizedEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("EmptyViewCustomSize");
+ App.Tap("EmptyViewCustomSize");
+ App.WaitForElement("FlowDirectionLeftToRight");
+ App.Tap("FlowDirectionLeftToRight");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("Custom Empty View (Sized)");
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyCustomSizedEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("EmptyViewCustomSize");
+ App.Tap("EmptyViewCustomSize");
+ App.WaitForElement("FlowDirectionRightToLeft");
+ App.Tap("FlowDirectionRightToLeft");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("Custom Empty View (Sized)");
+ VerifyScreenshot();
+ }
+
[Test]
[Category(UITestCategories.CollectionView)]
public void ValidateCustomEmptyViewDisplayed_AndEmptyObservableCollectionSetFirst()
@@ -603,6 +702,70 @@ public void ValidateCustomSizeEmptyViewTemplateDisplayed()
App.WaitForElement("Custom EmptyViewTemplate (Sized)");
}
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("EmptyViewTemplateGrid");
+ App.Tap("EmptyViewTemplateGrid");
+ App.WaitForElement("FlowDirectionLeftToRight");
+ App.Tap("FlowDirectionLeftToRight");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("No Template Items Available(Grid View)");
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("EmptyViewTemplateGrid");
+ App.Tap("EmptyViewTemplateGrid");
+ App.WaitForElement("FlowDirectionRightToLeft");
+ App.Tap("FlowDirectionRightToLeft");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("No Template Items Available(Grid View)");
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("EmptyViewTemplateCustomSize");
+ App.Tap("EmptyViewTemplateCustomSize");
+ App.WaitForElement("FlowDirectionLeftToRight");
+ App.Tap("FlowDirectionLeftToRight");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("Custom EmptyViewTemplate (Sized)");
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("EmptyViewTemplateCustomSize");
+ App.Tap("EmptyViewTemplateCustomSize");
+ App.WaitForElement("FlowDirectionRightToLeft");
+ App.Tap("FlowDirectionRightToLeft");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("Custom EmptyViewTemplate (Sized)");
+ VerifyScreenshot();
+ }
+
[Test]
[Category(UITestCategories.CollectionView)]
public void ValidateEmptyViewTemplateDisplayed_EmptyObservableCollectionSetFirst()
@@ -1003,7 +1166,7 @@ public void ValidateEmptyViewSize()
int expectedHeight = 525;
#elif MACCATALYST
int expectedWidth = 235;
- int expectedHeight = 150;
+ int expectedHeight = 150;
#else
int expectedWidth = 300;
int expectedHeight = 200;
@@ -1041,9 +1204,9 @@ public void ValidateEmptyViewTemplateSize()
#endif
#if TEST_FAILS_ON_ANDROID
-// CollectionView Footer Becomes Scrollable When EmptyView is Active on Android. Issue Link: https://github.com/dotnet/maui/issues/28350
-// HeaderTemplate and FooterTemplate are not displayed when ItemsSource is initially set to null on Android. Issue Link: https://github.com/dotnet/maui/issues/28337
-// Header and footer are not displayed when emptyview selected first Issue Link: https://github.com/dotnet/maui/issues/28351
+ // CollectionView Footer Becomes Scrollable When EmptyView is Active on Android. Issue Link: https://github.com/dotnet/maui/issues/28350
+ // HeaderTemplate and FooterTemplate are not displayed when ItemsSource is initially set to null on Android. Issue Link: https://github.com/dotnet/maui/issues/28337
+ // Header and footer are not displayed when emptyview selected first Issue Link: https://github.com/dotnet/maui/issues/28351
[Test]
[Category(UITestCategories.CollectionView)]
public void ValidateEmptyViewStringDisplayed_AndHeaderString()
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_GroupingFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_GroupingFeatureTests.cs
index e7b3f4241e5f..970ecd86c86a 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_GroupingFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_GroupingFeatureTests.cs
@@ -18,7 +18,8 @@ public class CollectionView_GroupingFeatureTests : _GalleryUITest
public const string ItemsLayoutHorizontalList = "ItemsLayoutHorizontalList";
public const string ItemsLayoutHorizontalGrid = "ItemsLayoutHorizontalGrid";
public const string ItemsLayoutVerticalGrid = "ItemsLayoutVerticalGrid";
-
+ public const string FlowDirectionLTR = "FlowDirectionLeftToRight";
+ public const string FlowDirectionRTL = "FlowDirectionRightToLeft";
public override string GalleryPageName => GroupingFeatureMatrix;
public CollectionView_GroupingFeatureTests(TestDevice device)
@@ -466,6 +467,29 @@ public void VerifyIsGrouped_WithHorizontalListAndGroupedList()
App.WaitForElement("Vegetables");
}
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLIsGrouped_WithHorizontalListAndGroupedList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(ItemsSourceGroupedList);
+ App.Tap(ItemsSourceGroupedList);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Fruits");
+ App.WaitForElement("Apple");
+ App.ScrollLeft("CollectionViewControl");
+ App.WaitForElement("Carrot");
+ App.WaitForElement("Vegetables");
+ }
+
[Test]
[Category(UITestCategories.CollectionView)]
public void VerifyIsGrouped_WithHorizontalGridAndGroupedList()
@@ -487,6 +511,26 @@ public void VerifyIsGrouped_WithHorizontalGridAndGroupedList()
App.WaitForElement("Vegetables");
}
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLIsGrouped_WithHorizontalGridAndGroupedList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(ItemsSourceGroupedList);
+ App.Tap(ItemsSourceGroupedList);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Fruits");
+ VerifyScreenshot();
+ }
+
[Test]
[Category(UITestCategories.CollectionView)]
public void VerifyGroupHeaderAndFooterTemplate_WithHorizontalListAndGroupedList()
@@ -506,7 +550,7 @@ public void VerifyGroupHeaderAndFooterTemplate_WithHorizontalListAndGroupedList(
App.WaitForElement(Apply);
App.Tap(Apply);
App.WaitForElement("GroupHeaderTemplate");
- App.WaitForElement("Apple");
+ App.WaitForElement("Banana"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
App.WaitForElement("Carrot");
@@ -515,6 +559,36 @@ public void VerifyGroupHeaderAndFooterTemplate_WithHorizontalListAndGroupedList(
App.WaitForElement("GroupFooterTemplate");
}
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLGroupHeaderAndFooterTemplate_WithHorizontalListAndGroupedList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(GroupHeaderTemplateGrid);
+ App.Tap(GroupHeaderTemplateGrid);
+ App.WaitForElement(GroupFooterTemplateGrid);
+ App.Tap(GroupFooterTemplateGrid);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(ItemsSourceGroupedList);
+ App.Tap(ItemsSourceGroupedList);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("GroupHeaderTemplate");
+ App.WaitForElement("Banana"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
+ App.ScrollLeft("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.ScrollLeft("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement("Carrot");
+ App.ScrollLeft("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.ScrollLeft("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement("GroupFooterTemplate");
+ }
+
[Test]
[Category(UITestCategories.CollectionView)]
public void VerifyGroupHeaderAndFooterTemplate_WithHorizontalGridAndGroupedList()
@@ -534,13 +608,41 @@ public void VerifyGroupHeaderAndFooterTemplate_WithHorizontalGridAndGroupedList(
App.WaitForElement(Apply);
App.Tap(Apply);
App.WaitForElement("GroupHeaderTemplate");
- App.WaitForElement("Apple");
+ App.WaitForElement("Banana"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
App.WaitForElement("Carrot");
App.WaitForElement("GroupFooterTemplate");
}
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLGroupHeaderAndFooterTemplate_WithHorizontalGridAndGroupedList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(GroupHeaderTemplateGrid);
+ App.Tap(GroupHeaderTemplateGrid);
+ App.WaitForElement(GroupFooterTemplateGrid);
+ App.Tap(GroupFooterTemplateGrid);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(ItemsSourceGroupedList);
+ App.Tap(ItemsSourceGroupedList);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("GroupHeaderTemplate");
+ App.WaitForElement("Banana"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
+ App.ScrollLeft("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.ScrollLeft("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement("Carrot");
+ App.WaitForElement("GroupFooterTemplate");
+ }
+
[Test]
[Category(UITestCategories.CollectionView)]
public void VerifyGroupHeaderAndFooterTemplate_WithVerticalGridAndGroupedList()
@@ -560,11 +662,35 @@ public void VerifyGroupHeaderAndFooterTemplate_WithVerticalGridAndGroupedList()
App.WaitForElement(Apply);
App.Tap(Apply);
App.WaitForElement("GroupHeaderTemplate");
- App.WaitForElement("Apple");
+ App.WaitForElement("Banana"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
App.WaitForElement("Carrot");
App.WaitForElement("GroupFooterTemplate");
}
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLGroupHeaderAndFooterTemplate_WithVerticalGridAndGroupedList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(GroupHeaderTemplateGrid);
+ App.Tap(GroupHeaderTemplateGrid);
+ App.WaitForElement(GroupFooterTemplateGrid);
+ App.Tap(GroupFooterTemplateGrid);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(ItemsSourceGroupedList);
+ App.Tap(ItemsSourceGroupedList);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("GroupHeaderTemplate");
+ VerifyScreenshot();
+ }
+
[Test]
[Category(UITestCategories.CollectionView)]
public void VerifyIsGrouped_WithVerticalGridAndGroupedList()
@@ -580,10 +706,30 @@ public void VerifyIsGrouped_WithVerticalGridAndGroupedList()
App.WaitForElement(Apply);
App.Tap(Apply);
App.WaitForElement("Fruits");
- App.WaitForElement("Apple");
+ App.WaitForElement("Banana"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
App.WaitForElement("Carrot");
App.WaitForElement("Vegetables");
}
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLIsGrouped_WithVerticalGridAndGroupedList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(ItemsSourceGroupedList);
+ App.Tap(ItemsSourceGroupedList);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Fruits");
+ VerifyScreenshot();
+ }
#endif
#endif
@@ -651,7 +797,7 @@ public void VerifyIsGrouped_WithHorizontalListAndObservableCollection()
App.WaitForElement(Apply);
App.Tap(Apply);
App.WaitForNoElement("Fruits");
- App.WaitForElement("Apple");
+ App.WaitForElement("Banana"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
App.ScrollRight("CollectionViewControl");
App.WaitForElement("Carrot");
App.WaitForNoElement("Vegetables");
@@ -670,7 +816,7 @@ public void VerifyIsGrouped_WithHorizontalGridAndObservableCollection()
App.WaitForElement(Apply);
App.Tap(Apply);
App.WaitForNoElement("Fruits");
- App.WaitForElement("Apple");
+ App.WaitForElement("Banana"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
App.ScrollRight("CollectionViewControl");
App.WaitForElement("Carrot");
App.WaitForNoElement("Vegetables");
@@ -689,7 +835,7 @@ public void VerifyIsGrouped_WithVerticalGridAndObservableCollection()
App.WaitForElement(Apply);
App.Tap(Apply);
App.WaitForNoElement("Fruits");
- App.WaitForElement("Apple");
+ App.WaitForElement("Banana"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
App.WaitForElement("Carrot");
App.WaitForNoElement("Vegetables");
}
@@ -705,7 +851,7 @@ public void VerifyIsGrouped_WithVerticalListAndObservableCollection()
App.WaitForElement(Apply);
App.Tap(Apply);
App.WaitForNoElement("Fruits");
- App.WaitForElement("Apple");
+ App.WaitForElement("Banana"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
App.WaitForElement("Carrot");
App.WaitForNoElement("Vegetables");
}
@@ -727,15 +873,13 @@ public void VerifyIsGrouped_WithGroupHeaderAndFooterTemplateAndObservableCollect
App.Tap(Apply);
App.WaitForNoElement("GroupHeaderTemplate");
App.WaitForNoElement("GroupFooterTemplate");
- App.WaitForElement("Apple");
+ App.WaitForElement("Banana"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
App.WaitForElement("Carrot");
}
#endif
-#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_IOS
- //CanMixGroups Set to False Still Allows Reordering Between Groups in CollectionView on Catalyst Issue Link : https://github.com/dotnet/maui/issues/28530
- //Test fails on CV2 . GroupHeader and GroupFooter template is not visible Issue Link: https://github.com/dotnet/maui/issues/28509
- //.NET MAUI CollectionView does not reorder when grouped on windows Issue Link: https://github.com/dotnet/maui/issues/13027
+#if TEST_FAILS_ON_WINDOWS //.NET MAUI CollectionView does not reorder when grouped on windows Issue Link: https://github.com/dotnet/maui/issues/13027
+
[Test]
[Category(UITestCategories.CollectionView)]
public void VerifyCanMixGroupsFalseWithCanReorderItems()
@@ -750,10 +894,32 @@ public void VerifyCanMixGroupsFalseWithCanReorderItems()
App.Tap(IsGroupedTrue);
App.WaitForElement(Apply);
App.Tap(Apply);
- var initialY = App.WaitForElement("Apple").GetRect().Y;
- App.DragAndDrop("Apple", "Potato");
+ var initialY = App.WaitForElement("Banana").GetRect().Y;
+ App.DragAndDrop("Banana", "Potato"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
+ var newY = App.WaitForElement("Carrot").GetRect().Y;
+ Assert.That(newY, Is.GreaterThan(initialY), "The Y position of 'Carrot' should be greater than Banana after the drag-and-drop operation.");
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLCanMixGroupsFalseWithCanReorderItems()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList);
+ App.Tap(ItemsSourceGroupedList);
+ App.WaitForElement("CanReorderItemsTrue");
+ App.Tap("CanReorderItemsTrue");
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ var initialY = App.WaitForElement("Banana").GetRect().Y;
+ App.DragAndDrop("Banana", "Potato"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
var newY = App.WaitForElement("Carrot").GetRect().Y;
- Assert.That(newY, Is.GreaterThan(initialY), "The Y position of 'Carrot' should be greater than Apple after the drag-and-drop operation.");
+ Assert.That(newY, Is.GreaterThan(initialY), "The Y position of 'Carrot' should be greater than Banana after the drag-and-drop operation.");
}
[Test]
@@ -772,10 +938,34 @@ public void VerifyCanMixGroupsTrueWithCanReorderItems()
App.Tap(IsGroupedTrue);
App.WaitForElement(Apply);
App.Tap(Apply);
- var initialY = App.WaitForElement("Apple").GetRect().Y;
- App.DragAndDrop("Apple", "Potato");
- var newY = App.WaitForElement("Apple").GetRect().Y;
- Assert.That(newY, Is.GreaterThan(initialY), "The Y position of 'Apple' should be greater after the drag-and-drop operation.");
+ var initialY = App.WaitForElement("Banana").GetRect().Y;
+ App.DragAndDrop("Banana", "Potato"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
+ var newY = App.WaitForElement("Banana").GetRect().Y;
+ Assert.That(newY, Is.GreaterThan(initialY), "The Y position of 'Banana' should be greater after the drag-and-drop operation.");
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLCanMixGroupsTrueWithCanReorderItems()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("CanMixGroupsTrue");
+ App.Tap("CanMixGroupsTrue");
+ App.WaitForElement(ItemsSourceGroupedList);
+ App.Tap(ItemsSourceGroupedList);
+ App.WaitForElement("CanReorderItemsTrue");
+ App.Tap("CanReorderItemsTrue");
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ var initialY = App.WaitForElement("Banana").GetRect().Y;
+ App.DragAndDrop("Banana", "Potato"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
+ var newY = App.WaitForElement("Banana").GetRect().Y;
+ Assert.That(newY, Is.GreaterThan(initialY), "The Y position of 'Banana' should be greater after the drag-and-drop operation.");
}
[Test]
@@ -794,10 +984,34 @@ public void VerifyCanReorderItemsTrueWithCanMixGroups()
App.Tap(IsGroupedTrue);
App.WaitForElement(Apply);
App.Tap(Apply);
- var initialY = App.WaitForElement("Apple").GetRect().Y;
- App.DragAndDrop("Apple", "Potato");
- var newY = App.WaitForElement("Apple").GetRect().Y;
- Assert.That(newY, Is.GreaterThan(initialY), "The Y position of 'Apple' should be greater after the drag-and-drop operation.");
+ var initialY = App.WaitForElement("Banana").GetRect().Y;
+ App.DragAndDrop("Banana", "Potato"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
+ var newY = App.WaitForElement("Banana").GetRect().Y;
+ Assert.That(newY, Is.GreaterThan(initialY), "The Y position of 'Banana' should be greater after the drag-and-drop operation.");
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLCanReorderItemsTrueWithCanMixGroups()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("CanReorderItemsTrue");
+ App.Tap("CanReorderItemsTrue");
+ App.WaitForElement(ItemsSourceGroupedList);
+ App.Tap(ItemsSourceGroupedList);
+ App.WaitForElement("CanMixGroupsTrue");
+ App.Tap("CanMixGroupsTrue");
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ var initialY = App.WaitForElement("Banana").GetRect().Y;
+ App.DragAndDrop("Banana", "Potato"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
+ var newY = App.WaitForElement("Banana").GetRect().Y;
+ Assert.That(newY, Is.GreaterThan(initialY), "The Y position of 'Banana' should be greater after the drag-and-drop operation.");
}
[Test]
@@ -814,10 +1028,32 @@ public void VerifyCanReorderItemsFalseWithCanMixGroups()
App.Tap(IsGroupedTrue);
App.WaitForElement(Apply);
App.Tap(Apply);
- var initialY = App.WaitForElement("Apple").GetRect().Y;
- App.DragAndDrop("Apple", "Potato");
- var newY = App.WaitForElement("Apple").GetRect().Y;
- Assert.That(newY, Is.EqualTo(initialY), "The Y position of 'Apple' should be Same Value after the drag-and-drop operation.");
+ var initialY = App.WaitForElement("Banana").GetRect().Y;
+ App.DragAndDrop("Banana", "Potato"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
+ var newY = App.WaitForElement("Banana").GetRect().Y;
+ Assert.That(newY, Is.EqualTo(initialY), "The Y position of 'Banana' should be Same Value after the drag-and-drop operation.");
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLCanReorderItemsFalseWithCanMixGroups()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList);
+ App.Tap(ItemsSourceGroupedList);
+ App.WaitForElement("CanMixGroupsTrue");
+ App.Tap("CanMixGroupsTrue");
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ var initialY = App.WaitForElement("Banana").GetRect().Y;
+ App.DragAndDrop("Banana", "Potato"); // Changed from Apple to Banana because Appium taps, scrolls, or drags the Apple icon instead of the CollectionView Apple item.
+ var newY = App.WaitForElement("Banana").GetRect().Y;
+ Assert.That(newY, Is.EqualTo(initialY), "The Y position of 'Banana' should be Same Value after the drag-and-drop operation.");
}
#endif
}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_ScrollingFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_ScrollingFeatureTests.cs
index be979e21d6d1..f169caaf79a1 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_ScrollingFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_ScrollingFeatureTests.cs
@@ -4,6 +4,7 @@
namespace Microsoft.Maui.TestCases.Tests;
+
public class CollectionView_ScrollingFeatureTests : _GalleryUITest
{
public const string ScrollingFeatureMatrix = "CollectionView Feature Matrix";
@@ -23,7 +24,10 @@ public class CollectionView_ScrollingFeatureTests : _GalleryUITest
public const string ItemsLayoutHorizontalGrid = "ItemsLayoutHorizontalGrid";
public const string ItemsLayoutVerticalList = "ItemsLayoutVerticalList";
public const string ItemsLayoutHorizontalList = "ItemsLayoutHorizontalList";
+ public const string ScrollToIndexEntry = "ScrollToIndexEntry";
public const string AddButton = "AddButton";
+ public const string FlowDirectionLTR = "FlowDirectionLeftToRight";
+ public const string FlowDirectionRTL = "FlowDirectionRightToLeft";
public override string GalleryPageName => ScrollingFeatureMatrix;
@@ -49,6 +53,40 @@ public void VerifyMeasureAllItemsWithObservableCollection()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollection()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceObservableCollection2);
+ App.Tap(ItemsSourceObservableCollection2);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollection()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceObservableCollection2);
+ App.Tap(ItemsSourceObservableCollection2);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
#if TEST_FAILS_ON_WINDOWS // [Windows] NullReferenceException thrown When Toggling IsGrouped to True in ObservableCollection Binding Issue Link: https://github.com/dotnet/maui/issues/28824
[Test]
[Category(UITestCategories.CollectionView)]
@@ -66,6 +104,44 @@ public void VerifyMeasureAllItemsWithGroupedList()
App.Tap(Apply);
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsSourceGroupedList2);
+ App.Tap(ItemsSourceGroupedList2);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsSourceGroupedList2);
+ App.Tap(ItemsSourceGroupedList2);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
#endif
#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST
@@ -90,7 +166,7 @@ public void VerifyMeasureFirstItemsWithObservableCollection()
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyMeasureFirstItemsWithObservableCollectionWhenVerticalGrid()
+ public void VerifyFlowDirectionLTRAndMeasureFirstItemsWithObservableCollection()
{
App.WaitForElement(Options);
App.Tap(Options);
@@ -98,42 +174,42 @@ public void VerifyMeasureFirstItemsWithObservableCollectionWhenVerticalGrid()
App.Tap(ItemsSourceObservableCollection2);
App.WaitForElement(ItemSizingMeasureFirstItem);
App.Tap(ItemSizingMeasureFirstItem);
- App.WaitForElement(ItemsLayoutVerticalGrid);
- App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
App.WaitForElement(Apply);
App.Tap(Apply);
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyMeasureFirstItemsWithGroupedList()
+ public void VerifyFlowDirectionRTLAndMeasureFirstItemsWithObservableCollection()
{
App.WaitForElement(Options);
App.Tap(Options);
+ App.WaitForElement(ItemsSourceObservableCollection2);
+ App.Tap(ItemsSourceObservableCollection2);
App.WaitForElement(ItemSizingMeasureFirstItem);
App.Tap(ItemSizingMeasureFirstItem);
- App.WaitForElement(IsGroupedTrue);
- App.Tap(IsGroupedTrue);
- App.WaitForElement(ItemsSourceGroupedList2);
- App.Tap(ItemsSourceGroupedList2);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
App.WaitForElement(Apply);
App.Tap(Apply);
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyMeasureAllItemsWithObservableCollectionWhenHorizontalGrid()
+ public void VerifyMeasureFirstItemsWithObservableCollectionWhenVerticalGrid()
{
App.WaitForElement(Options);
App.Tap(Options);
App.WaitForElement(ItemsSourceObservableCollection2);
App.Tap(ItemsSourceObservableCollection2);
- App.WaitForElement(ItemSizingMeasureAllItems);
- App.Tap(ItemSizingMeasureAllItems);
- App.WaitForElement(ItemsLayoutHorizontalGrid);
- App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(ItemSizingMeasureFirstItem);
+ App.Tap(ItemSizingMeasureFirstItem);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
App.WaitForElement(Apply);
App.Tap(Apply);
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
@@ -141,108 +217,107 @@ public void VerifyMeasureAllItemsWithObservableCollectionWhenHorizontalGrid()
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyMeasureAllItemsWithObservableCollectionWhenHorizontalList()
+ public void VerifyFlowDirectionLTRAndMeasureFirstItemsWithObservableCollectionWhenVerticalGrid()
{
App.WaitForElement(Options);
App.Tap(Options);
App.WaitForElement(ItemsSourceObservableCollection2);
App.Tap(ItemsSourceObservableCollection2);
- App.WaitForElement(ItemSizingMeasureAllItems);
- App.Tap(ItemSizingMeasureAllItems);
- App.WaitForElement(ItemsLayoutHorizontalList);
- App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(ItemSizingMeasureFirstItem);
+ App.Tap(ItemSizingMeasureFirstItem);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
App.WaitForElement(Apply);
App.Tap(Apply);
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyMeasureAllItemsWithObservableCollectionWhenVerticalGrid()
+ public void VerifyFlowDirectionRTLAndMeasureFirstItemsWithObservableCollectionWhenVerticalGrid()
{
App.WaitForElement(Options);
App.Tap(Options);
App.WaitForElement(ItemsSourceObservableCollection2);
App.Tap(ItemsSourceObservableCollection2);
- App.WaitForElement(ItemSizingMeasureAllItems);
- App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(ItemSizingMeasureFirstItem);
+ App.Tap(ItemSizingMeasureFirstItem);
App.WaitForElement(ItemsLayoutVerticalGrid);
App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
App.WaitForElement(Apply);
App.Tap(Apply);
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyMeasureAllItemsWithGroupedListWhenVerticalGrid()
+ public void VerifyMeasureFirstItemsWithGroupedList()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemSizingMeasureAllItems);
- App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(ItemSizingMeasureFirstItem);
+ App.Tap(ItemSizingMeasureFirstItem);
App.WaitForElement(IsGroupedTrue);
App.Tap(IsGroupedTrue);
App.WaitForElement(ItemsSourceGroupedList2);
App.Tap(ItemsSourceGroupedList2);
- App.WaitForElement(ItemsLayoutVerticalGrid);
- App.Tap(ItemsLayoutVerticalGrid);
App.WaitForElement(Apply);
App.Tap(Apply);
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
-
+
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyMeasureAllItemsWithGroupedListWhenHorizontalGrid()
+ public void VerifyFlowDirectionLTRAndMeasureFirstItemsWithGroupedList()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemSizingMeasureAllItems);
- App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(ItemSizingMeasureFirstItem);
+ App.Tap(ItemSizingMeasureFirstItem);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
App.WaitForElement(IsGroupedTrue);
App.Tap(IsGroupedTrue);
App.WaitForElement(ItemsSourceGroupedList2);
App.Tap(ItemsSourceGroupedList2);
- App.WaitForElement(ItemsLayoutHorizontalGrid);
- App.Tap(ItemsLayoutHorizontalGrid);
App.WaitForElement(Apply);
App.Tap(Apply);
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyMeasureAllItemsWithGroupedListWhenHorizontalList()
+ public void VerifyFlowDirectionRTLAndMeasureFirstItemsWithGroupedList()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemSizingMeasureAllItems);
- App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(ItemSizingMeasureFirstItem);
+ App.Tap(ItemSizingMeasureFirstItem);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
App.WaitForElement(IsGroupedTrue);
App.Tap(IsGroupedTrue);
App.WaitForElement(ItemsSourceGroupedList2);
App.Tap(ItemsSourceGroupedList2);
- App.WaitForElement(ItemsLayoutHorizontalList);
- App.Tap(ItemsLayoutHorizontalList);
App.WaitForElement(Apply);
App.Tap(Apply);
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ VerifyScreenshot();
}
-#if TEST_FAILS_ON_ANDROID
-//[Android] CollectionView with ItemSizingStrategy="MeasureFirstItem" Does Not Work as Expected for HorizontalList and HorizontalGrid Layouts Issue Link: https://github.com/dotnet/maui/issues/29192
-//[Android] ItemSizingStrategy="MeasureFirstItem" does not work correctly with VerticalGrid and grouped ItemsSource Issue Link: https://github.com/dotnet/maui/issues/29191
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyMeasureFirstItemsWithObservableCollectionWhenHorizontalGrid()
+ public void VerifyMeasureAllItemsWithObservableCollectionWhenHorizontalGrid()
{
App.WaitForElement(Options);
App.Tap(Options);
App.WaitForElement(ItemsSourceObservableCollection2);
App.Tap(ItemsSourceObservableCollection2);
- App.WaitForElement(ItemSizingMeasureFirstItem);
- App.Tap(ItemSizingMeasureFirstItem);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
App.WaitForElement(ItemsLayoutHorizontalGrid);
App.Tap(ItemsLayoutHorizontalGrid);
App.WaitForElement(Apply);
@@ -252,52 +327,52 @@ public void VerifyMeasureFirstItemsWithObservableCollectionWhenHorizontalGrid()
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyMeasureFirstItemsWithObservableCollectionWhenHorizontalList()
+ public void VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollectionWhenHorizontalGrid()
{
App.WaitForElement(Options);
App.Tap(Options);
App.WaitForElement(ItemsSourceObservableCollection2);
App.Tap(ItemsSourceObservableCollection2);
- App.WaitForElement(ItemSizingMeasureFirstItem);
- App.Tap(ItemSizingMeasureFirstItem);
- App.WaitForElement(ItemsLayoutHorizontalList);
- App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
App.WaitForElement(Apply);
App.Tap(Apply);
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyMeasureFirstItemsWithGroupedListWhenVerticalGrid()
+ public void VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollectionWhenHorizontalGrid()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemSizingMeasureFirstItem);
- App.Tap(ItemSizingMeasureFirstItem);
- App.WaitForElement(IsGroupedTrue);
- App.Tap(IsGroupedTrue);
- App.WaitForElement(ItemsSourceGroupedList2);
- App.Tap(ItemsSourceGroupedList2);
- App.WaitForElement(ItemsLayoutVerticalGrid);
- App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(ItemsSourceObservableCollection2);
+ App.Tap(ItemsSourceObservableCollection2);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
App.WaitForElement(Apply);
App.Tap(Apply);
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyMeasureFirstItemsWithGroupedListWhenHorizontalList()
+ public void VerifyMeasureAllItemsWithObservableCollectionWhenHorizontalList()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemSizingMeasureFirstItem);
- App.Tap(ItemSizingMeasureFirstItem);
- App.WaitForElement(IsGroupedTrue);
- App.Tap(IsGroupedTrue);
- App.WaitForElement(ItemsSourceGroupedList2);
- App.Tap(ItemsSourceGroupedList2);
+ App.WaitForElement(ItemsSourceObservableCollection2);
+ App.Tap(ItemsSourceObservableCollection2);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
App.WaitForElement(ItemsLayoutHorizontalList);
App.Tap(ItemsLayoutHorizontalList);
App.WaitForElement(Apply);
@@ -307,133 +382,2850 @@ public void VerifyMeasureFirstItemsWithGroupedListWhenHorizontalList()
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyMeasureFirstItemsWithGroupedListWhenHorizontalGrid()
+ public void VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollectionWhenHorizontalList()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemSizingMeasureFirstItem);
- App.Tap(ItemSizingMeasureFirstItem);
- App.WaitForElement(IsGroupedTrue);
- App.Tap(IsGroupedTrue);
- App.WaitForElement(ItemsSourceGroupedList2);
- App.Tap(ItemsSourceGroupedList2);
- App.WaitForElement(ItemsLayoutHorizontalGrid);
- App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(ItemsSourceObservableCollection2);
+ App.Tap(ItemsSourceObservableCollection2);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
App.WaitForElement(Apply);
App.Tap(Apply);
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ VerifyScreenshot();
}
-#endif
-#endif
-#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST
-//[Android] KeepItemsInView and KeepScrollOffset doesn't not works as expected when new items are added in CollectionView Issue Link: https://github.com/dotnet/maui/issues/29131
-//[iOS] KeepItemsInView Does Not Show Newly Added Items After Scrolling Down in CollectionView Issue Link: https://github.com/dotnet/maui/issues/29145
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepItemsInViewWithObservableList()
+ public void VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollectionWhenHorizontalList()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemsUpdatingKeepItemsInView);
- App.Tap(ItemsUpdatingKeepItemsInView);
+ App.WaitForElement(ItemsSourceObservableCollection2);
+ App.Tap(ItemsSourceObservableCollection2);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
App.WaitForElement(Apply);
App.Tap(Apply);
- App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForElement("Dragonfruit");
- App.Tap(AddButton);
- App.WaitForElement("Passionfruit");
+ VerifyScreenshot();
}
-
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepItemsInViewWithGroupedList()
+ public void VerifyMeasureAllItemsWithObservableCollectionWhenVerticalGrid()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemsSourceGroupedList3);
- App.Tap(ItemsSourceGroupedList3);
- App.WaitForElement(IsGroupedTrue);
- App.Tap(IsGroupedTrue);
- App.WaitForElement(ItemsUpdatingKeepItemsInView);
- App.Tap(ItemsUpdatingKeepItemsInView);
+ App.WaitForElement(ItemsSourceObservableCollection2);
+ App.Tap(ItemsSourceObservableCollection2);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
App.WaitForElement(Apply);
App.Tap(Apply);
- App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForElement("Dragonfruit");
- App.Tap(AddButton);
- App.WaitForElement("Passionfruit");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
-#if TEST_FAILS_ON_WINDOWS // //CollectionView ItemsLayout does not update Issue Link: https://github.com/dotnet/maui/issues/27946
- [Test]
+ [Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepItemsInViewWithObservableListWhenVerticalGrid()
+ public void VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollectionWhenVerticalGrid()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemsUpdatingKeepItemsInView);
- App.Tap(ItemsUpdatingKeepItemsInView);
+ App.WaitForElement(ItemsSourceObservableCollection2);
+ App.Tap(ItemsSourceObservableCollection2);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
App.WaitForElement(ItemsLayoutVerticalGrid);
App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
App.WaitForElement(Apply);
App.Tap(Apply);
- App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForElement("Dragonfruit");
- App.Tap(AddButton);
- App.WaitForElement("Passionfruit");
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepItemsInViewWithObservableListWhenHorizontalGrid()
+ public void VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollectionWhenVerticalGrid()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemsUpdatingKeepItemsInView);
- App.Tap(ItemsUpdatingKeepItemsInView);
- App.WaitForElement(ItemsLayoutHorizontalGrid);
- App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(ItemsSourceObservableCollection2);
+ App.Tap(ItemsSourceObservableCollection2);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
App.WaitForElement(Apply);
App.Tap(Apply);
- App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForElement("Dragonfruit");
- App.Tap(AddButton);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyMeasureAllItemsWithGroupedListWhenVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsSourceGroupedList2);
+ App.Tap(ItemsSourceGroupedList2);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedListWhenVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsSourceGroupedList2);
+ App.Tap(ItemsSourceGroupedList2);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedListWhenVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsSourceGroupedList2);
+ App.Tap(ItemsSourceGroupedList2);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyMeasureAllItemsWithGroupedListWhenHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsSourceGroupedList2);
+ App.Tap(ItemsSourceGroupedList2);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedListWhenHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsSourceGroupedList2);
+ App.Tap(ItemsSourceGroupedList2);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedListWhenHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsSourceGroupedList2);
+ App.Tap(ItemsSourceGroupedList2);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyMeasureAllItemsWithGroupedListWhenHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsSourceGroupedList2);
+ App.Tap(ItemsSourceGroupedList2);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedListWhenHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsSourceGroupedList2);
+ App.Tap(ItemsSourceGroupedList2);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedListWhenHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemSizingMeasureAllItems);
+ App.Tap(ItemSizingMeasureAllItems);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsSourceGroupedList2);
+ App.Tap(ItemsSourceGroupedList2);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+#if TEST_FAILS_ON_ANDROID
+//[Android] CollectionView with ItemSizingStrategy="MeasureFirstItem" Does Not Work as Expected for HorizontalList and HorizontalGrid Layouts Issue Link: https://github.com/dotnet/maui/issues/29192
+//[Android] ItemSizingStrategy="MeasureFirstItem" does not work correctly with VerticalGrid and grouped ItemsSource Issue Link: https://github.com/dotnet/maui/issues/29191
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyMeasureFirstItemsWithObservableCollectionWhenHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceObservableCollection2);
+ App.Tap(ItemsSourceObservableCollection2);
+ App.WaitForElement(ItemSizingMeasureFirstItem);
+ App.Tap(ItemSizingMeasureFirstItem);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyMeasureFirstItemsWithObservableCollectionWhenHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceObservableCollection2);
+ App.Tap(ItemsSourceObservableCollection2);
+ App.WaitForElement(ItemSizingMeasureFirstItem);
+ App.Tap(ItemSizingMeasureFirstItem);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyMeasureFirstItemsWithGroupedListWhenVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemSizingMeasureFirstItem);
+ App.Tap(ItemSizingMeasureFirstItem);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsSourceGroupedList2);
+ App.Tap(ItemsSourceGroupedList2);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyMeasureFirstItemsWithGroupedListWhenHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemSizingMeasureFirstItem);
+ App.Tap(ItemSizingMeasureFirstItem);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsSourceGroupedList2);
+ App.Tap(ItemsSourceGroupedList2);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyMeasureFirstItemsWithGroupedListWhenHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemSizingMeasureFirstItem);
+ App.Tap(ItemSizingMeasureFirstItem);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsSourceGroupedList2);
+ App.Tap(ItemsSourceGroupedList2);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+#endif
+
+#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST
+ //[Android] KeepItemsInView and KeepScrollOffset doesn't not works as expected when new items are added in CollectionView Issue Link: https://github.com/dotnet/maui/issues/29131
+ //[iOS] KeepItemsInView Does Not Show Newly Added Items After Scrolling Down in CollectionView Issue Link: https://github.com/dotnet/maui/issues/29145
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepItemsInViewWithObservableList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepItemsInView);
+ App.Tap(ItemsUpdatingKeepItemsInView);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Dragonfruit");
+ App.Tap(AddButton);
+ App.WaitForElement("Passionfruit");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepItemsInViewWithGroupedList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsUpdatingKeepItemsInView);
+ App.Tap(ItemsUpdatingKeepItemsInView);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Dragonfruit");
+ App.Tap(AddButton);
+ App.WaitForElement("Passionfruit");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+#if TEST_FAILS_ON_WINDOWS // //CollectionView ItemsLayout does not update Issue Link: https://github.com/dotnet/maui/issues/27946
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepItemsInViewWithObservableListWhenVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepItemsInView);
+ App.Tap(ItemsUpdatingKeepItemsInView);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Dragonfruit");
+ App.Tap(AddButton);
App.WaitForElement("Passionfruit");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepItemsInViewWithObservableListWhenHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepItemsInView);
+ App.Tap(ItemsUpdatingKeepItemsInView);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Dragonfruit");
+ App.Tap(AddButton);
+ App.WaitForElement("Passionfruit");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepItemsInViewWithObservableListWhenHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepItemsInView);
+ App.Tap(ItemsUpdatingKeepItemsInView);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Dragonfruit");
+ App.Tap(AddButton);
+ App.WaitForElement("Passionfruit");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepItemsInViewWithGroupedListWhenVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsUpdatingKeepItemsInView);
+ App.Tap(ItemsUpdatingKeepItemsInView);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Dragonfruit");
+ App.Tap(AddButton);
+ App.WaitForElement("Passionfruit");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepItemsInViewWithGroupedListWhenHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsUpdatingKeepItemsInView);
+ App.Tap(ItemsUpdatingKeepItemsInView);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Dragonfruit");
+ App.Tap(AddButton);
+ App.WaitForElement("Passionfruit");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepItemsInViewWithGroupedListWhenHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsUpdatingKeepItemsInView);
+ App.Tap(ItemsUpdatingKeepItemsInView);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Dragonfruit");
+ App.Tap(AddButton);
+ App.WaitForElement("Passionfruit");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+#endif
+#endif
+
+#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS
+ //Scroll mode "KeepLastItemInView" does not keep the last item at the end of the displayed list when adding new items Issue Link: https://github.com/dotnet/maui/issues/28716
+ //KeepLastItemInView Does Not Scroll to Last Item When Adding Items at Top, Instead Scrolls to SecondLast Item : https://github.com/dotnet/maui/issues/29207
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepLastItemInViewWithObservableList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepLastItemInView);
+ App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Cabbage");
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepLastItemInViewWithObservableListWhenVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepLastItemInView);
+ App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Cabbage");
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionLTRAndKeepLastItemInViewWithObservableListWhenVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepLastItemInView);
+ App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Cabbage");
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLAndKeepLastItemInViewWithObservableListWhenVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepLastItemInView);
+ App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Cabbage");
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepLastItemInViewWithObservableListWhenHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepLastItemInView);
+ App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Cabbage");
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionLTRAndKeepLastItemInViewWithObservableListWhenHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepLastItemInView);
+ App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Cabbage");
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLAndKeepLastItemInViewWithObservableListWhenHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepLastItemInView);
+ App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Cabbage");
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepLastItemInViewWithObservableListWhenHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepLastItemInView);
+ App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Cabbage");
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionLTRAndKeepLastItemInViewWithObservableListWhenHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepLastItemInView);
+ App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Cabbage");
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLAndKeepLastItemInViewWithObservableListWhenHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepLastItemInView);
+ App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Cabbage");
+ VerifyScreenshot();
+ }
+
+#if TEST_FAILS_ON_ANDROID
+//[Android] ArgumentOutOfRangeException Occurs with KeepLastItemInView for Grouped List Issue Link: https://github.com/dotnet/maui/issues/29153
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepLastItemInViewWithGroupedList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsUpdatingKeepLastItemInView);
+ App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Pumpkin");
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepLastItemInViewWithGroupedListWhenVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsUpdatingKeepLastItemInView);
+ App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Pumpkin");
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepLastItemInViewWithGroupedListWhenHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsUpdatingKeepLastItemInView);
+ App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Pumpkin");
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepLastItemInViewWithGroupedListWhenHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsUpdatingKeepLastItemInView);
+ App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForElement("Pumpkin");
+ }
+#endif
+#endif
+
+#if TEST_FAILS_ON_ANDROID
+ //[Android] KeepScrollOffset doesn't not works as expected when new items are added in CollectionView Issue Link: https://github.com/dotnet/maui/issues/29131
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepScrollOffsetWithObservableList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepScrollOffset);
+ App.Tap(ItemsUpdatingKeepScrollOffset);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("Dragonfruit");
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("Passionfruit");
+ App.WaitForElement("Cabbage");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepScrollOffsetWithGroupedList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsUpdatingKeepScrollOffset);
+ App.Tap(ItemsUpdatingKeepScrollOffset);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("WaterMelon");
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("Mango");
+ App.WaitForElement("Pumpkin");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLAndKeepScrollOffsetWithGroupedList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsUpdatingKeepScrollOffset);
+ App.Tap(ItemsUpdatingKeepScrollOffset);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("WaterMelon");
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("Mango");
+ App.WaitForElement("Pumpkin");
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLAndKeepScrollOffsetWithGroupedList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsUpdatingKeepScrollOffset);
+ App.Tap(ItemsUpdatingKeepScrollOffset);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("WaterMelon");
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("Mango");
+ App.WaitForElement("Pumpkin");
+ }
+
+#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_IOS
+//CollectionView Fails to Preserve Scroll Offset with GridItemsLayout Using KeepScrollOffset Issue Link: https://github.com/dotnet/maui/issues/29202
+//CollectionView ItemsLayout does not update while switch from LinearItemsLayout to GridItemsLayout Issue Link: https://github.com/dotnet/maui/issues/27946
+//CollectionView CollectionView2 doesnot change ItemsLayout Issue Link: https://github.com/dotnet/maui/issues/28656
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepScrollOffsetWithObservableListWhenVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepScrollOffset);
+ App.Tap(ItemsUpdatingKeepScrollOffset);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("Dragonfruit");
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("Passionfruit");
+ App.WaitForElement("Cabbage");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepScrollOffsetWithObservableListWhenHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepScrollOffset);
+ App.Tap(ItemsUpdatingKeepScrollOffset);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("Dragonfruit");
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("Passionfruit");
+ App.WaitForElement("Cabbage");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepScrollOffsetWithObservableListWhenHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsUpdatingKeepScrollOffset);
+ App.Tap(ItemsUpdatingKeepScrollOffset);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("Dragonfruit");
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("Passionfruit");
+ App.WaitForElement("Cabbage");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepScrollOffsetWithGroupedListWhenVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsUpdatingKeepScrollOffset);
+ App.Tap(ItemsUpdatingKeepScrollOffset);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("WaterMelon");
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("Mango");
+ App.WaitForElement("Pumpkin");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepScrollOffsetWithGroupedListWhenHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsUpdatingKeepScrollOffset);
+ App.Tap(ItemsUpdatingKeepScrollOffset);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("WaterMelon");
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("Mango");
+ App.WaitForElement("Pumpkin");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyKeepScrollOffsetWithGroupedListWhenHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement(ItemsUpdatingKeepScrollOffset);
+ App.Tap(ItemsUpdatingKeepScrollOffset);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("WaterMelon");
+ App.WaitForElement(AddButton);
+ App.Tap(AddButton);
+ App.WaitForNoElement("Mango");
+ App.WaitForElement("Pumpkin");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+#endif
+#endif
+
+ //Scrolled Event Tests
+#if TEST_FAILS_ON_ANDROID // Issue Link: https://github.com/dotnet/maui/issues/33333
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrolledEventWithVerticalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+#endif
+
+#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_IOS
+ //Issue Link: https://github.com/dotnet/maui/issues/33333
+ //CollectionView Fails to Preserve Scroll Offset with GridItemsLayout Using KeepScrollOffset Issue Link: https://github.com/dotnet/maui/issues/29202
+ //CollectionView ItemsLayout does not update while switch from LinearItemsLayout to GridItemsLayout Issue Link: https://github.com/dotnet/maui/issues/27946
+ //CollectionView CollectionView2 doesnot change ItemsLayout Issue Link: https://github.com/dotnet/maui/issues/28656
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrolledEventWithVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrolledEventWithHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrolledEventWithHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ Assert.That(App.WaitForElement("ScrolledEventLabel").GetText(), Is.EqualTo("Fired"));
+ }
+#endif
+
+ //ScrollToRequested Event Tests
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyDefaultScrollToRequested()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ VerifyScreenshot();
+ }
+
+ // ScrollTo By Index Tests
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalList_Carrot()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "15");
+ App.WaitForElement("ScrollToPositionMakeVisible");
+ App.Tap("ScrollToPositionMakeVisible");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Carrot");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("15"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("15"));
+ VerifyScreenshot();
+ }
+
+#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS // Issue Link: https://github.com/dotnet/maui/issues/33614
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByIndexWithStartPositionAndVerticalList_Carrot()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "15");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Carrot");
+ Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("15"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("15"));
+ VerifyScreenshot();
+ }
+#endif
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByIndexWithCenterPositionAndVerticalList_Carrot()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionCenter");
+ App.Tap("ScrollToPositionCenter");
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "15");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Carrot");
+ Assert.That(App.WaitForElement("CenterIndexLabel").GetText(), Is.EqualTo("15"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("15"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByIndexWithEndPositionAndVerticalList_Carrot()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionEnd");
+ App.Tap("ScrollToPositionEnd");
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "15");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Carrot");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("15"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("15"));
+ VerifyScreenshot();
+ }
+
+ // ScrollTo By Item Tests
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByItemWithMakeVisiblePositionAndVerticalList_Carrot()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Carrot");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Carrot");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("15"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Carrot"));
+ VerifyScreenshot();
+ }
+
+#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS // Issue Link: https://github.com/dotnet/maui/issues/33614
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByItemWithStartPositionAndVerticalList_Carrot()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Carrot");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Carrot");
+ Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("15"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Carrot"));
+ VerifyScreenshot();
+ }
+#endif
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByItemWithCenterPositionAndVerticalList_Carrot()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToPositionCenter");
+ App.Tap("ScrollToPositionCenter");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Carrot");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Carrot");
+ Assert.That(App.WaitForElement("CenterIndexLabel").GetText(), Is.EqualTo("15"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Carrot"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByItemWithEndPositionAndVerticalList_Carrot()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToPositionEnd");
+ App.Tap("ScrollToPositionEnd");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Carrot");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Carrot");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("15"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Carrot"));
+ VerifyScreenshot();
+ }
+
+ // Grouped ScrollTo By Index Tests
+#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_IOS // Issue - https://github.com/dotnet/maui/issues/17664
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupIndexScrollToByIndexWithMakeVisiblePositionAndVerticalList_Apricot()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("FruitGroup");
+ App.Tap("FruitGroup");
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "23");
+ App.WaitForElement("GroupIndexEntry");
+ App.ClearText("GroupIndexEntry");
+ App.EnterText("GroupIndexEntry", "0");
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionMakeVisible");
+ App.Tap("ScrollToPositionMakeVisible");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Apricot");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("24"));
+ Assert.That(App.WaitForElement("GroupIndexLabel").GetText(), Is.EqualTo("0"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("23"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupIndexScrollToByIndexWithStartPositionAndVerticalList_Carrot()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "0");
+ App.WaitForElement("GroupIndexEntry");
+ App.ClearText("GroupIndexEntry");
+ App.EnterText("GroupIndexEntry", "1");
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Carrot");
+ Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("27"));
+ Assert.That(App.WaitForElement("GroupIndexLabel").GetText(), Is.EqualTo("1"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("0"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupIndexScrollToByIndexWithCenterPositionAndVerticalList_Potato()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "3");
+ App.WaitForElement("GroupIndexEntry");
+ App.ClearText("GroupIndexEntry");
+ App.EnterText("GroupIndexEntry", "1");
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionCenter");
+ App.Tap("ScrollToPositionCenter");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Potato");
+ Assert.That(App.WaitForElement("GroupIndexLabel").GetText(), Is.EqualTo("1"));
+ Assert.That(App.WaitForElement("CenterIndexLabel").GetText(), Is.EqualTo("30"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("3"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupIndexScrollToByIndexWithEndPositionAndVerticalList_Papaya()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "11");
+ App.WaitForElement("GroupIndexEntry");
+ App.ClearText("GroupIndexEntry");
+ App.EnterText("GroupIndexEntry", "0");
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionEnd");
+ App.Tap("ScrollToPositionEnd");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Papaya");
+ Assert.That(App.WaitForElement("GroupIndexLabel").GetText(), Is.EqualTo("0"));
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("12"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("11"));
+ VerifyScreenshot();
+ }
+
+ //Grouped ScrollTo By Item Tests
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupItemScrollToByItemWithMakeVisiblePositionAndVerticalList_Apricot()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Apricot");
+ App.WaitForElement("ScrollToPositionMakeVisible");
+ App.Tap("ScrollToPositionMakeVisible");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Apricot");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("24"));
+ Assert.That(App.WaitForElement("GroupLabel").GetText(), Is.EqualTo("Fruits"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Apricot"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupItemScrollToByItemWithStartPositionAndVerticalList_Carrot()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("VegetableGroup");
+ App.Tap("VegetableGroup");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Carrot");
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Carrot");
+ Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("27"));
+ Assert.That(App.WaitForElement("GroupLabel").GetText(), Is.EqualTo("Vegetables"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Carrot"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupItemScrollToByItemWithCenterPositionAndVerticalList_Potato()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("VegetableGroup");
+ App.Tap("VegetableGroup");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Potato");
+ App.WaitForElement("ScrollToPositionCenter");
+ App.Tap("ScrollToPositionCenter");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Potato");
+ Assert.That(App.WaitForElement("GroupLabel").GetText(), Is.EqualTo("Vegetables"));
+ Assert.That(App.WaitForElement("CenterIndexLabel").GetText(), Is.EqualTo("30"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Potato"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupItemScrollToByItemWithEndPositionAndVerticalList_Papaya()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Papaya");
+ App.WaitForElement("ScrollToPositionEnd");
+ App.Tap("ScrollToPositionEnd");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Papaya");
+ Assert.That(App.WaitForElement("GroupLabel").GetText(), Is.EqualTo("Fruits"));
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("12"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Papaya"));
+ VerifyScreenshot();
+ }
+#endif
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyRemainingItemsThresholdReachedWithVerticalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("RemainingItemsThresholdLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement("RemainingItemsThresholdLabel");
+ Assert.That(App.WaitForElement("RemainingItemsThresholdLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyReorderCompletedWithVerticalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("CanReorderItemsTrue");
+ App.Tap("CanReorderItemsTrue");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ Assert.That(App.WaitForElement("ReorderCompletedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.DragAndDrop("Banana", "Mango");
+ App.WaitForElement("ReorderCompletedLabel");
+ Assert.That(App.WaitForElement("ReorderCompletedLabel").GetText(), Is.EqualTo("Fired"));
+ }
+
+#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_IOS
+ //CollectionView Fails to Preserve Scroll Offset with GridItemsLayout Using KeepScrollOffset Issue Link: https://github.com/dotnet/maui/issues/29202
+ //CollectionView ItemsLayout does not update while switch from LinearItemsLayout to GridItemsLayout Issue Link: https://github.com/dotnet/maui/issues/27946
+ //CollectionView CollectionView2 doesnot change ItemsLayout Issue Link: https://github.com/dotnet/maui/issues/28656
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalGrid_Radish()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "27");
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Radish");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("27"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("27"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByIndexWithMakeVisiblePositionAndHorizontalList_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "12");
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("12"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("12"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByIndexWithMakeVisiblePositionAndHorizontalGrid_Pear()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "13");
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Pear");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("13"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("13"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByIndexWithStartPositionAndVerticalGrid_Mango()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "4");
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Mango");
+ Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("4"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("4"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByIndexWithStartPositionAndHorizontalList_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "12");
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("12"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("12"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByIndexWithStartPositionAndHorizontalGrid_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "12");
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("12"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("12"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByIndexWithCenterPositionAndVerticalGrid_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToPositionCenter");
+ App.Tap("ScrollToPositionCenter");
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "12");
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("CenterIndexLabel").GetText(), Is.EqualTo("12"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("12"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByIndexWithCenterPositionAndHorizontalList_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToPositionCenter");
+ App.Tap("ScrollToPositionCenter");
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "12");
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("CenterIndexLabel").GetText(), Is.EqualTo("12"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("12"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByIndexWithCenterPositionAndHorizontalGrid_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToPositionCenter");
+ App.Tap("ScrollToPositionCenter");
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "12");
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("CenterIndexLabel").GetText(), Is.EqualTo("12"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("12"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByIndexWithEndPositionAndVerticalGrid_Radish()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToPositionEnd");
+ App.Tap("ScrollToPositionEnd");
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "27");
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Radish");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("27"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("27"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByIndexWithEndPositionAndHorizontalList_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToPositionEnd");
+ App.Tap("ScrollToPositionEnd");
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "12");
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("12"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("12"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByIndexWithEndPositionAndHorizontalGrid_Pear()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToPositionEnd");
+ App.Tap("ScrollToPositionEnd");
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "13");
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Pear");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("13"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("13"));
+ VerifyScreenshot();
+ }
+
+ // ScrollTo By Item Tests
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByItemWithMakeVisiblePositionAndVerticalGrid_Radish()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Radish");
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Radish");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("27"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Radish"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByItemWithMakeVisiblePositionAndHorizontalList_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Kiwi");
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("12"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Kiwi"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByItemWithMakeVisiblePositionAndHorizontalGrid_Pear()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Pear");
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Pear");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("13"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Pear"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByItemWithStartPositionAndVerticalGrid_Mango()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Mango");
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Mango");
+ Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("4"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Mango"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByItemWithStartPositionAndHorizontalList_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Kiwi");
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("12"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Kiwi"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByItemWithStartPositionAndHorizontalGrid_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Kiwi");
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("12"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Kiwi"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByItemWithCenterPositionAndVerticalGrid_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToPositionCenter");
+ App.Tap("ScrollToPositionCenter");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Kiwi");
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("CenterIndexLabel").GetText(), Is.EqualTo("12"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Kiwi"));
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepItemsInViewWithObservableListWhenHorizontalList()
+ public void VerifyScrollToByItemWithCenterPositionAndHorizontalList_Kiwi()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemsUpdatingKeepItemsInView);
- App.Tap(ItemsUpdatingKeepItemsInView);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToPositionCenter");
+ App.Tap("ScrollToPositionCenter");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Kiwi");
App.WaitForElement(ItemsLayoutHorizontalList);
App.Tap(ItemsLayoutHorizontalList);
App.WaitForElement(Apply);
App.Tap(Apply);
- App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForElement("Dragonfruit");
- App.Tap(AddButton);
- App.WaitForElement("Passionfruit");
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("CenterIndexLabel").GetText(), Is.EqualTo("12"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Kiwi"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByItemWithCenterPositionAndHorizontalGrid_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToPositionCenter");
+ App.Tap("ScrollToPositionCenter");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Kiwi");
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("CenterIndexLabel").GetText(), Is.EqualTo("12"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Kiwi"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByItemWithEndPositionAndVerticalGrid_Radish()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToPositionEnd");
+ App.Tap("ScrollToPositionEnd");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Radish");
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Radish");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("27"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Radish"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByItemWithEndPositionAndHorizontalList_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToPositionEnd");
+ App.Tap("ScrollToPositionEnd");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Kiwi");
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("12"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Kiwi"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyScrollToByItemWithEndPositionAndHorizontalGrid_Pear()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToPositionEnd");
+ App.Tap("ScrollToPositionEnd");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Pear");
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Pear");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("13"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Pear"));
+ VerifyScreenshot();
+ }
+
+ // Group ScrollTo test by index
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupIndexScrollToByIndexWithMakeVisiblePositionAndVerticalGrid_Apricot()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "23");
+ App.WaitForElement("GroupIndexEntry");
+ App.ClearText("GroupIndexEntry");
+ App.EnterText("GroupIndexEntry", "0");
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionMakeVisible");
+ App.Tap("ScrollToPositionMakeVisible");
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Apricot");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("24"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("23"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupIndexScrollToByIndexWithMakeVisiblePositionAndHorizontalList_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "12");
+ App.WaitForElement("GroupIndexEntry");
+ App.ClearText("GroupIndexEntry");
+ App.EnterText("GroupIndexEntry", "0");
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionMakeVisible");
+ App.Tap("ScrollToPositionMakeVisible");
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("13"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("12"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupIndexScrollToByIndexWithMakeVisiblePositionAndHorizontalGrid_Pear()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "13");
+ App.WaitForElement("GroupIndexEntry");
+ App.ClearText("GroupIndexEntry");
+ App.EnterText("GroupIndexEntry", "0");
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionMakeVisible");
+ App.Tap("ScrollToPositionMakeVisible");
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Pear");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("14"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("13"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupIndexScrollToByIndexWithStartPositionAndVerticalGrid_Carrot()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "0");
+ App.WaitForElement("GroupIndexEntry");
+ App.ClearText("GroupIndexEntry");
+ App.EnterText("GroupIndexEntry", "1");
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Carrot");
+ Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("27"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("0"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupIndexScrollToByIndexWithStartPositionAndHorizontalList_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "12");
+ App.WaitForElement("GroupIndexEntry");
+ App.ClearText("GroupIndexEntry");
+ App.EnterText("GroupIndexEntry", "0");
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("13"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("12"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupIndexScrollToByIndexWithStartPositionAndHorizontalGrid_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "12");
+ App.WaitForElement("GroupIndexEntry");
+ App.ClearText("GroupIndexEntry");
+ App.EnterText("GroupIndexEntry", "0");
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("13"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("12"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupIndexScrollToByIndexWithCenterPositionAndVerticalGrid_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "12");
+ App.WaitForElement("GroupIndexEntry");
+ App.ClearText("GroupIndexEntry");
+ App.EnterText("GroupIndexEntry", "0");
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionCenter");
+ App.Tap("ScrollToPositionCenter");
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("CenterIndexLabel").GetText(), Is.EqualTo("13"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("12"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupIndexScrollToByIndexWithCenterPositionAndHorizontalList_Tomato()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "4");
+ App.WaitForElement("GroupIndexEntry");
+ App.ClearText("GroupIndexEntry");
+ App.EnterText("GroupIndexEntry", "1");
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionCenter");
+ App.Tap("ScrollToPositionCenter");
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Tomato");
+ Assert.That(App.WaitForElement("CenterIndexLabel").GetText(), Is.EqualTo("31"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("4"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupIndexScrollToByIndexWithCenterPositionAndHorizontalGrid_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "12");
+ App.WaitForElement("GroupIndexEntry");
+ App.ClearText("GroupIndexEntry");
+ App.EnterText("GroupIndexEntry", "0");
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionCenter");
+ App.Tap("ScrollToPositionCenter");
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("CenterIndexLabel").GetText(), Is.EqualTo("13"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("12"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupIndexScrollToByIndexWithEndPositionAndVerticalGrid_Apricot()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "23");
+ App.WaitForElement("GroupIndexEntry");
+ App.ClearText("GroupIndexEntry");
+ App.EnterText("GroupIndexEntry", "0");
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionEnd");
+ App.Tap("ScrollToPositionEnd");
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Apricot");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("24"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("23"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupIndexScrollToByIndexWithEndPositionAndHorizontalList_Kiwi()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "12");
+ App.WaitForElement("GroupIndexEntry");
+ App.ClearText("GroupIndexEntry");
+ App.EnterText("GroupIndexEntry", "0");
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionEnd");
+ App.Tap("ScrollToPositionEnd");
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("13"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("12"));
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupIndexScrollToByIndexWithEndPositionAndHorizontalGrid_Potato()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToIndexEntry");
+ App.ClearText("ScrollToIndexEntry");
+ App.EnterText("ScrollToIndexEntry", "3");
+ App.WaitForElement("GroupIndexEntry");
+ App.ClearText("GroupIndexEntry");
+ App.EnterText("GroupIndexEntry", "1");
+ App.WaitForElement("ScrollToByIndex");
+ App.Tap("ScrollToByIndex");
+ App.WaitForElement("ScrollToPositionEnd");
+ App.Tap("ScrollToPositionEnd");
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Potato");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("30"));
+ Assert.That(App.WaitForElement("IndexLabel").GetText(), Is.EqualTo("3"));
+ VerifyScreenshot();
}
+ // Group name ScrollTo test by item
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepItemsInViewWithGroupedListWhenVerticalGrid()
+ public void VerifyGroupItemScrollToByIndexWithMakeVisiblePositionAndVerticalGrid_Apricot()
{
App.WaitForElement(Options);
App.Tap(Options);
@@ -441,23 +3233,33 @@ public void VerifyKeepItemsInViewWithGroupedListWhenVerticalGrid()
App.Tap(ItemsSourceGroupedList3);
App.WaitForElement(IsGroupedTrue);
App.Tap(IsGroupedTrue);
- App.WaitForElement(ItemsUpdatingKeepItemsInView);
- App.Tap(ItemsUpdatingKeepItemsInView);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Apricot");
+ App.WaitForElement("ScrollToPositionMakeVisible");
+ App.Tap("ScrollToPositionMakeVisible");
App.WaitForElement(ItemsLayoutVerticalGrid);
App.Tap(ItemsLayoutVerticalGrid);
App.WaitForElement(Apply);
App.Tap(Apply);
- App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForElement("Dragonfruit");
- App.Tap(AddButton);
- App.WaitForElement("Passionfruit");
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Apricot");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("24"));
+ Assert.That(App.WaitForElement("GroupLabel").GetText(), Is.EqualTo("Fruits"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Apricot"));
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepItemsInViewWithGroupedListWhenHorizontalList()
+ public void VerifyGroupItemScrollToByIndexWithMakeVisiblePositionAndHorizontalList_Kiwi()
{
App.WaitForElement(Options);
App.Tap(Options);
@@ -465,23 +3267,33 @@ public void VerifyKeepItemsInViewWithGroupedListWhenHorizontalList()
App.Tap(ItemsSourceGroupedList3);
App.WaitForElement(IsGroupedTrue);
App.Tap(IsGroupedTrue);
- App.WaitForElement(ItemsUpdatingKeepItemsInView);
- App.Tap(ItemsUpdatingKeepItemsInView);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Kiwi");
+ App.WaitForElement("ScrollToPositionMakeVisible");
+ App.Tap("ScrollToPositionMakeVisible");
App.WaitForElement(ItemsLayoutHorizontalList);
App.Tap(ItemsLayoutHorizontalList);
App.WaitForElement(Apply);
App.Tap(Apply);
- App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForElement("Dragonfruit");
- App.Tap(AddButton);
- App.WaitForElement("Passionfruit");
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("13"));
+ Assert.That(App.WaitForElement("GroupLabel").GetText(), Is.EqualTo("Fruits"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Kiwi"));
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepItemsInViewWithGroupedListWhenHorizontalGrid()
+ public void VerifyGroupItemScrollToByIndexWithMakeVisiblePositionAndHorizontalGrid_Pear()
{
App.WaitForElement(Options);
App.Tap(Options);
@@ -489,97 +3301,141 @@ public void VerifyKeepItemsInViewWithGroupedListWhenHorizontalGrid()
App.Tap(ItemsSourceGroupedList3);
App.WaitForElement(IsGroupedTrue);
App.Tap(IsGroupedTrue);
- App.WaitForElement(ItemsUpdatingKeepItemsInView);
- App.Tap(ItemsUpdatingKeepItemsInView);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Pear");
+ App.WaitForElement("ScrollToPositionMakeVisible");
+ App.Tap("ScrollToPositionMakeVisible");
App.WaitForElement(ItemsLayoutHorizontalGrid);
App.Tap(ItemsLayoutHorizontalGrid);
App.WaitForElement(Apply);
App.Tap(Apply);
- App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForElement("Dragonfruit");
- App.Tap(AddButton);
- App.WaitForElement("Passionfruit");
- }
-#endif
-#endif
-
-#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS
- //Scroll mode "KeepLastItemInView" does not keep the last item at the end of the displayed list when adding new items Issue Link: https://github.com/dotnet/maui/issues/28716
- //KeepLastItemInView Does Not Scroll to Last Item When Adding Items at Top, Instead Scrolls to SecondLast Item : https://github.com/dotnet/maui/issues/29207
-
- [Test]
- [Category(UITestCategories.CollectionView)]
- public void VerifyKeepLastItemInViewWithObservableList()
- {
- App.WaitForElement(Options);
- App.Tap(Options);
- App.WaitForElement(ItemsUpdatingKeepLastItemInView);
- App.Tap(ItemsUpdatingKeepLastItemInView);
- App.WaitForElement(Apply);
- App.Tap(Apply);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForElement("Cabbage");
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Pear");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("14"));
+ Assert.That(App.WaitForElement("GroupLabel").GetText(), Is.EqualTo("Fruits"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Pear"));
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepLastItemInViewWithObservableListWhenVerticalGrid()
+ public void VerifyGroupItemScrollToByIndexWithStartPositionAndVerticalGrid_Carrot()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemsUpdatingKeepLastItemInView);
- App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("VegetableGroup");
+ App.Tap("VegetableGroup");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Carrot");
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
App.WaitForElement(ItemsLayoutVerticalGrid);
App.Tap(ItemsLayoutVerticalGrid);
App.WaitForElement(Apply);
App.Tap(Apply);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForElement("Cabbage");
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Carrot");
+ Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("27"));
+ Assert.That(App.WaitForElement("GroupLabel").GetText(), Is.EqualTo("Vegetables"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Carrot"));
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepLastItemInViewWithObservableListWhenHorizontalList()
+ public void VerifyGroupItemScrollToByIndexWithStartPositionAndHorizontalList_Kiwi()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemsUpdatingKeepLastItemInView);
- App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("FruitGroup");
+ App.Tap("FruitGroup");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Kiwi");
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
App.WaitForElement(ItemsLayoutHorizontalList);
App.Tap(ItemsLayoutHorizontalList);
App.WaitForElement(Apply);
App.Tap(Apply);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForElement("Cabbage");
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("13"));
+ Assert.That(App.WaitForElement("GroupLabel").GetText(), Is.EqualTo("Fruits"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Kiwi"));
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepLastItemInViewWithObservableListWhenHorizontalGrid()
+ public void VerifyGroupItemScrollToByIndexWithStartPositionAndHorizontalGrid_Kiwi()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemsUpdatingKeepLastItemInView);
- App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("FruitGroup");
+ App.Tap("FruitGroup");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Kiwi");
+ App.WaitForElement("ScrollToPositionStart");
+ App.Tap("ScrollToPositionStart");
App.WaitForElement(ItemsLayoutHorizontalGrid);
App.Tap(ItemsLayoutHorizontalGrid);
App.WaitForElement(Apply);
App.Tap(Apply);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForElement("Cabbage");
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("13"));
+ Assert.That(App.WaitForElement("GroupLabel").GetText(), Is.EqualTo("Fruits"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Kiwi"));
+ VerifyScreenshot();
}
-#if TEST_FAILS_ON_ANDROID
-//[Android] ArgumentOutOfRangeException Occurs with KeepLastItemInView for Grouped List Issue Link: https://github.com/dotnet/maui/issues/29153
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepLastItemInViewWithGroupedList()
+ public void VerifyGroupItemScrollToByIndexWithCenterPositionAndVerticalGrid_Kiwi()
{
App.WaitForElement(Options);
App.Tap(Options);
@@ -587,18 +3443,35 @@ public void VerifyKeepLastItemInViewWithGroupedList()
App.Tap(ItemsSourceGroupedList3);
App.WaitForElement(IsGroupedTrue);
App.Tap(IsGroupedTrue);
- App.WaitForElement(ItemsUpdatingKeepLastItemInView);
- App.Tap(ItemsUpdatingKeepLastItemInView);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("FruitGroup");
+ App.Tap("FruitGroup");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Kiwi");
+ App.WaitForElement("ScrollToPositionCenter");
+ App.Tap("ScrollToPositionCenter");
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
App.WaitForElement(Apply);
App.Tap(Apply);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForElement("Pumpkin");
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("CenterIndexLabel").GetText(), Is.EqualTo("13"));
+ Assert.That(App.WaitForElement("GroupLabel").GetText(), Is.EqualTo("Fruits"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Kiwi"));
+ VerifyScreenshot();
}
-
+
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepLastItemInViewWithGroupedListWhenVerticalGrid()
+ public void VerifyGroupItemScrollToByIndexWithCenterPositionAndHorizontalList_Tomato()
{
App.WaitForElement(Options);
App.Tap(Options);
@@ -606,41 +3479,71 @@ public void VerifyKeepLastItemInViewWithGroupedListWhenVerticalGrid()
App.Tap(ItemsSourceGroupedList3);
App.WaitForElement(IsGroupedTrue);
App.Tap(IsGroupedTrue);
- App.WaitForElement(ItemsUpdatingKeepLastItemInView);
- App.Tap(ItemsUpdatingKeepLastItemInView);
- App.WaitForElement(ItemsLayoutVerticalGrid);
- App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("VegetableGroup");
+ App.Tap("VegetableGroup");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Tomato");
+ App.WaitForElement("ScrollToPositionCenter");
+ App.Tap("ScrollToPositionCenter");
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
App.WaitForElement(Apply);
App.Tap(Apply);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForElement("Pumpkin");
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Tomato");
+ Assert.That(App.WaitForElement("CenterIndexLabel").GetText(), Is.EqualTo("31"));
+ Assert.That(App.WaitForElement("GroupLabel").GetText(), Is.EqualTo("Vegetables"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Tomato"));
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepLastItemInViewWithGroupedListWhenHorizontalList()
+ public void VerifyGroupItemScrollToByIndexWithCenterPositionAndHorizontalGrid_Kiwi()
{
App.WaitForElement(Options);
App.Tap(Options);
App.WaitForElement(ItemsSourceGroupedList3);
App.Tap(ItemsSourceGroupedList3);
- App.WaitForElement(IsGroupedTrue);
+ App.WaitForElement(IsGroupedTrue);
App.Tap(IsGroupedTrue);
- App.WaitForElement(ItemsUpdatingKeepLastItemInView);
- App.Tap(ItemsUpdatingKeepLastItemInView);
- App.WaitForElement(ItemsLayoutHorizontalList);
- App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("FruitGroup");
+ App.Tap("FruitGroup");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Kiwi");
+ App.WaitForElement("ScrollToPositionCenter");
+ App.Tap("ScrollToPositionCenter");
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
App.WaitForElement(Apply);
App.Tap(Apply);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForElement("Pumpkin");
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("CenterIndexLabel").GetText(), Is.EqualTo("13"));
+ Assert.That(App.WaitForElement("GroupLabel").GetText(), Is.EqualTo("Fruits"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Kiwi"));
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepLastItemInViewWithGroupedListWhenHorizontalGrid()
+ public void VerifyGroupItemScrollToByIndexWithEndPositionAndVerticalGrid_Apricot()
{
App.WaitForElement(Options);
App.Tap(Options);
@@ -648,46 +3551,71 @@ public void VerifyKeepLastItemInViewWithGroupedListWhenHorizontalGrid()
App.Tap(ItemsSourceGroupedList3);
App.WaitForElement(IsGroupedTrue);
App.Tap(IsGroupedTrue);
- App.WaitForElement(ItemsUpdatingKeepLastItemInView);
- App.Tap(ItemsUpdatingKeepLastItemInView);
- App.WaitForElement(ItemsLayoutHorizontalGrid);
- App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("FruitGroup");
+ App.Tap("FruitGroup");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Apricot");
+ App.WaitForElement("ScrollToPositionEnd");
+ App.Tap("ScrollToPositionEnd");
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
App.WaitForElement(Apply);
App.Tap(Apply);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForElement("Pumpkin");
+ App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Apricot");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("24"));
+ Assert.That(App.WaitForElement("GroupLabel").GetText(), Is.EqualTo("Fruits"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Apricot"));
+ VerifyScreenshot();
}
-#endif
-#endif
-#if TEST_FAILS_ON_ANDROID
-//[Android] KeepScrollOffset doesn't not works as expected when new items are added in CollectionView Issue Link: https://github.com/dotnet/maui/issues/29131
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepScrollOffsetWithObservableList()
+ public void VerifyGroupItemScrollToByIndexWithEndPositionAndHorizontalList_Kiwi()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemsUpdatingKeepScrollOffset);
- App.Tap(ItemsUpdatingKeepScrollOffset);
+ App.WaitForElement(ItemsSourceGroupedList3);
+ App.Tap(ItemsSourceGroupedList3);
+ App.WaitForElement(IsGroupedTrue);
+ App.Tap(IsGroupedTrue);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("FruitGroup");
+ App.Tap("FruitGroup");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Kiwi");
+ App.WaitForElement("ScrollToPositionEnd");
+ App.Tap("ScrollToPositionEnd");
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
App.WaitForElement(Apply);
App.Tap(Apply);
App.WaitForElement("CollectionViewControl");
- App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForNoElement("Dragonfruit");
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForNoElement("Passionfruit");
- App.WaitForElement("Cabbage");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Kiwi");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("13"));
+ Assert.That(App.WaitForElement("GroupLabel").GetText(), Is.EqualTo("Fruits"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Kiwi"));
+ VerifyScreenshot();
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepScrollOffsetWithGroupedList()
+ public void VerifyGroupItemScrollToByIndexWithEndPositionAndHorizontalGrid_Potato()
{
App.WaitForElement(Options);
App.Tap(Options);
@@ -695,182 +3623,144 @@ public void VerifyKeepScrollOffsetWithGroupedList()
App.Tap(ItemsSourceGroupedList3);
App.WaitForElement(IsGroupedTrue);
App.Tap(IsGroupedTrue);
- App.WaitForElement(ItemsUpdatingKeepScrollOffset);
- App.Tap(ItemsUpdatingKeepScrollOffset);
+ App.WaitForElement("ScrollToByItem");
+ App.Tap("ScrollToByItem");
+ App.WaitForElement("VegetableGroup");
+ App.Tap("VegetableGroup");
+ App.WaitForElement("ScrollToItemEntry");
+ App.ClearText("ScrollToItemEntry");
+ App.EnterText("ScrollToItemEntry", "Potato");
+ App.WaitForElement("ScrollToPositionEnd");
+ App.Tap("ScrollToPositionEnd");
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
App.WaitForElement(Apply);
App.Tap(Apply);
App.WaitForElement("CollectionViewControl");
- App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForNoElement("WaterMelon");
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForNoElement("Mango");
- App.WaitForElement("Pumpkin");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.WaitForElement("ScrollTo");
+ App.Tap("ScrollTo");
+ App.WaitForElement("ScrollToRequestedLabel");
+ Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
+ App.WaitForElement("Potato");
+ Assert.That(App.WaitForElement("LastIndexLabel").GetText(), Is.EqualTo("30"));
+ Assert.That(App.WaitForElement("GroupLabel").GetText(), Is.EqualTo("Vegetables"));
+ Assert.That(App.WaitForElement("ItemLabel").GetText(), Is.EqualTo("Potato"));
+ VerifyScreenshot();
}
-#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_IOS
-//CollectionView Fails to Preserve Scroll Offset with GridItemsLayout Using KeepScrollOffset Issue Link: https://github.com/dotnet/maui/issues/29202
-//CollectionView ItemsLayout does not update while switch from LinearItemsLayout to GridItemsLayout Issue Link: https://github.com/dotnet/maui/issues/27946
-//CollectionView CollectionView2 doesnot change ItemsLayout Issue Link: https://github.com/dotnet/maui/issues/28656
-
+ // RemainingItemsThresholdReached Event Tests
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepScrollOffsetWithObservableListWhenVerticalGrid()
+ public void VerifyRemainingItemsThresholdReachedWithVerticalGrid()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemsUpdatingKeepScrollOffset);
- App.Tap(ItemsUpdatingKeepScrollOffset);
App.WaitForElement(ItemsLayoutVerticalGrid);
App.Tap(ItemsLayoutVerticalGrid);
App.WaitForElement(Apply);
App.Tap(Apply);
App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("RemainingItemsThresholdLabel").GetText(), Is.EqualTo("Not Fired"));
App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForNoElement("Dragonfruit");
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForNoElement("Passionfruit");
- App.WaitForElement("Cabbage");
+ App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
+ App.WaitForElement("RemainingItemsThresholdLabel");
+ Assert.That(App.WaitForElement("RemainingItemsThresholdLabel").GetText(), Is.EqualTo("Fired"));
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepScrollOffsetWithObservableListWhenHorizontalList()
+ public void VerifyRemainingItemsThresholdReachedWithHorizontalList()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemsUpdatingKeepScrollOffset);
- App.Tap(ItemsUpdatingKeepScrollOffset);
App.WaitForElement(ItemsLayoutHorizontalList);
App.Tap(ItemsLayoutHorizontalList);
App.WaitForElement(Apply);
App.Tap(Apply);
App.WaitForElement("CollectionViewControl");
- App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForNoElement("Dragonfruit");
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForNoElement("Passionfruit");
- App.WaitForElement("Cabbage");
+ Assert.That(App.WaitForElement("RemainingItemsThresholdLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 100);
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 100);
+ App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 100);
+ App.WaitForElement("RemainingItemsThresholdLabel");
+ Assert.That(App.WaitForElement("RemainingItemsThresholdLabel").GetText(), Is.EqualTo("Fired"));
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepScrollOffsetWithObservableListWhenHorizontalGrid()
+ public void VerifyRemainingItemsThresholdReachedWithHorizontalGrid()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemsUpdatingKeepScrollOffset);
- App.Tap(ItemsUpdatingKeepScrollOffset);
App.WaitForElement(ItemsLayoutHorizontalGrid);
App.Tap(ItemsLayoutHorizontalGrid);
App.WaitForElement(Apply);
App.Tap(Apply);
App.WaitForElement("CollectionViewControl");
+ Assert.That(App.WaitForElement("RemainingItemsThresholdLabel").GetText(), Is.EqualTo("Not Fired"));
App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForNoElement("Dragonfruit");
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForNoElement("Passionfruit");
- App.WaitForElement("Cabbage");
+ App.WaitForElement("RemainingItemsThresholdLabel");
+ Assert.That(App.WaitForElement("RemainingItemsThresholdLabel").GetText(), Is.EqualTo("Fired"));
}
+ // ReorderCompleted Event Tests
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepScrollOffsetWithGroupedListWhenVerticalGrid()
+ public void VerifyReorderCompletedWithVerticalGrid()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemsSourceGroupedList3);
- App.Tap(ItemsSourceGroupedList3);
- App.WaitForElement(IsGroupedTrue);
- App.Tap(IsGroupedTrue);
- App.WaitForElement(ItemsUpdatingKeepScrollOffset);
- App.Tap(ItemsUpdatingKeepScrollOffset);
+ App.WaitForElement("CanReorderItemsTrue");
+ App.Tap("CanReorderItemsTrue");
App.WaitForElement(ItemsLayoutVerticalGrid);
App.Tap(ItemsLayoutVerticalGrid);
App.WaitForElement(Apply);
App.Tap(Apply);
App.WaitForElement("CollectionViewControl");
- App.ScrollDown("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForNoElement("WaterMelon");
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForNoElement("Mango");
- App.WaitForElement("Pumpkin");
+ Assert.That(App.WaitForElement("ReorderCompletedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.DragAndDrop("Banana", "Mango");
+ App.WaitForElement("ReorderCompletedLabel");
+ Assert.That(App.WaitForElement("ReorderCompletedLabel").GetText(), Is.EqualTo("Fired"));
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepScrollOffsetWithGroupedListWhenHorizontalList()
+ public void VerifyReorderCompletedWithHorizontalList()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemsSourceGroupedList3);
- App.Tap(ItemsSourceGroupedList3);
- App.WaitForElement(IsGroupedTrue);
- App.Tap(IsGroupedTrue);
- App.WaitForElement(ItemsUpdatingKeepScrollOffset);
- App.Tap(ItemsUpdatingKeepScrollOffset);
+ App.WaitForElement("CanReorderItemsTrue");
+ App.Tap("CanReorderItemsTrue");
App.WaitForElement(ItemsLayoutHorizontalList);
App.Tap(ItemsLayoutHorizontalList);
App.WaitForElement(Apply);
App.Tap(Apply);
App.WaitForElement("CollectionViewControl");
- App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForNoElement("WaterMelon");
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForNoElement("Mango");
- App.WaitForElement("Pumpkin");
+ Assert.That(App.WaitForElement("ReorderCompletedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.DragAndDrop("Banana", "Mango");
+ App.WaitForElement("ReorderCompletedLabel");
+ Assert.That(App.WaitForElement("ReorderCompletedLabel").GetText(), Is.EqualTo("Fired"));
}
[Test]
[Category(UITestCategories.CollectionView)]
- public void VerifyKeepScrollOffsetWithGroupedListWhenHorizontalGrid()
+ public void VerifyReorderCompletedWithHorizontalGrid()
{
App.WaitForElement(Options);
App.Tap(Options);
- App.WaitForElement(ItemsSourceGroupedList3);
- App.Tap(ItemsSourceGroupedList3);
- App.WaitForElement(IsGroupedTrue);
- App.Tap(IsGroupedTrue);
- App.WaitForElement(ItemsUpdatingKeepScrollOffset);
- App.Tap(ItemsUpdatingKeepScrollOffset);
+ App.WaitForElement("CanReorderItemsTrue");
+ App.Tap("CanReorderItemsTrue");
App.WaitForElement(ItemsLayoutHorizontalGrid);
App.Tap(ItemsLayoutHorizontalGrid);
App.WaitForElement(Apply);
App.Tap(Apply);
App.WaitForElement("CollectionViewControl");
- App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.ScrollRight("CollectionViewControl", ScrollStrategy.Gesture, 0.9, 500);
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForNoElement("WaterMelon");
- App.WaitForElement(AddButton);
- App.Tap(AddButton);
- App.WaitForNoElement("Mango");
- App.WaitForElement("Pumpkin");
+ Assert.That(App.WaitForElement("ReorderCompletedLabel").GetText(), Is.EqualTo("Not Fired"));
+ App.DragAndDrop("Banana", "Mango");
+ App.WaitForElement("ReorderCompletedLabel");
+ Assert.That(App.WaitForElement("ReorderCompletedLabel").GetText(), Is.EqualTo("Fired"));
}
#endif
-#endif
}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_SelectionFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_SelectionFeatureTests.cs
index c78f09f96b11..804cc81f149b 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_SelectionFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_SelectionFeatureTests.cs
@@ -27,9 +27,15 @@ public class CollectionView_SelectionFeatureTests : _GalleryUITest
public const string CurrentSelectionTextLabel = "CurrentSelectionTextLabel";
public const string PreviousSelectionTextLabel = "PreviousSelectionTextLabel";
public const string SelectionChangedEventCountLabel = "SelectionChangedEventCountLabel";
-
+ public const string FlowDirectionLTR = "FlowDirectionLeftToRight";
+ public const string FlowDirectionRTL = "FlowDirectionRightToLeft";
+ public const string HeaderString = "HeaderString";
+ public const string HeaderGrid = "HeaderGrid";
+ public const string FooterString = "FooterString";
+ public const string FooterGrid = "FooterGrid";
+ public const string HeaderTemplateGrid = "HeaderTemplateGrid";
+ public const string FooterTemplateGrid = "FooterTemplateGrid";
public override string GalleryPageName => SelectionFeatureMatrix;
-
public CollectionView_SelectionFeatureTests(TestDevice device)
: base(device)
{
@@ -756,4 +762,1044 @@ public void VerifySelectionModeSingleSelectionChangedEventCount()
Assert.That(App.WaitForElement(SelectionChangedEventCountLabel).GetText(), Is.EqualTo("2 times"));
Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Banana"));
}
+
+#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS && TEST_FAILS_ON_WINDOWS //In iOS and Mac, related issue: https://github.com/dotnet/maui/issues/32225 and In windows, related issue: https://github.com/dotnet/maui/issues/27946
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionLTRWithVerticalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
+ App.WaitForElement(ItemsLayoutVerticalList);
+ App.Tap(ItemsLayoutVerticalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLWithVerticalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(ItemsLayoutVerticalList);
+ App.Tap(ItemsLayoutVerticalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionLTRWithHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLWithHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionLTRWithVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLWithVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionLTRWithHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLWithHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLWithHeaderStringAndFooterStringAndVerticalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(HeaderString);
+ App.Tap(HeaderString);
+ App.WaitForElement(FooterString);
+ App.Tap(FooterString);
+ App.WaitForElement(ItemsLayoutVerticalList);
+ App.Tap(ItemsLayoutVerticalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLWithHeaderViewAndFooterViewAndVerticalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(ItemsLayoutVerticalList);
+ App.Tap(ItemsLayoutVerticalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLWithHeaderStringAndFooterStringAndHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(HeaderString);
+ App.Tap(HeaderString);
+ App.WaitForElement(FooterString);
+ App.Tap(FooterString);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLWithHeaderViewAndFooterViewAndHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLWithHeaderStringAndFooterStringAndVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(HeaderString);
+ App.Tap(HeaderString);
+ App.WaitForElement(FooterString);
+ App.Tap(FooterString);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLWithHeaderViewAndFooterViewAndVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLWithHeaderStringAndFooterStringAndHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(HeaderString);
+ App.Tap(HeaderString);
+ App.WaitForElement(FooterString);
+ App.Tap(FooterString);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLWithHeaderViewAndFooterViewAndHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLWithHeaderTemplateAndFooterTemplateAndVerticalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(HeaderTemplateGrid);
+ App.Tap(HeaderTemplateGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(FooterTemplateGrid);
+ App.Tap(FooterTemplateGrid);
+ App.WaitForElement(ItemsLayoutVerticalList);
+ App.Tap(ItemsLayoutVerticalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionLTRWithHeaderTemplateAndFooterTemplateAndVerticalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(HeaderTemplateGrid);
+ App.Tap(HeaderTemplateGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(FooterTemplateGrid);
+ App.Tap(FooterTemplateGrid);
+ App.WaitForElement(ItemsLayoutVerticalList);
+ App.Tap(ItemsLayoutVerticalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLWithHeaderTemplateAndFooterTemplateAndHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(HeaderTemplateGrid);
+ App.Tap(HeaderTemplateGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(FooterTemplateGrid);
+ App.Tap(FooterTemplateGrid);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionLTRWithHeaderTemplateAndFooterTemplateAndHorizontalList()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(HeaderTemplateGrid);
+ App.Tap(HeaderTemplateGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(FooterTemplateGrid);
+ App.Tap(FooterTemplateGrid);
+ App.WaitForElement(ItemsLayoutHorizontalList);
+ App.Tap(ItemsLayoutHorizontalList);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLWithHeaderTemplateAndFooterTemplateAndVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(HeaderTemplateGrid);
+ App.Tap(HeaderTemplateGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(FooterTemplateGrid);
+ App.Tap(FooterTemplateGrid);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionLTRWithHeaderTemplateAndFooterTemplateAndVerticalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(HeaderTemplateGrid);
+ App.Tap(HeaderTemplateGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(FooterTemplateGrid);
+ App.Tap(FooterTemplateGrid);
+ App.WaitForElement(ItemsLayoutVerticalGrid);
+ App.Tap(ItemsLayoutVerticalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionRTLWithHeaderTemplateAndFooterTemplateAndHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(HeaderTemplateGrid);
+ App.Tap(HeaderTemplateGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(FooterTemplateGrid);
+ App.Tap(FooterTemplateGrid);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyFlowDirectionLTRWithHeaderTemplateAndFooterTemplateAndHorizontalGrid()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionLTR);
+ App.Tap(FlowDirectionLTR);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(HeaderTemplateGrid);
+ App.Tap(HeaderTemplateGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(FooterTemplateGrid);
+ App.Tap(FooterTemplateGrid);
+ App.WaitForElement(ItemsLayoutHorizontalGrid);
+ App.Tap(ItemsLayoutHorizontalGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+#endif
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeNoneWithHeaderString()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeNone);
+ App.Tap(SelectionModeNone);
+ App.WaitForElement(HeaderString);
+ App.Tap(HeaderString);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("No items selected"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("0"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeSingleWithHeaderString()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeSingle);
+ App.Tap(SelectionModeSingle);
+ App.WaitForElement(HeaderString);
+ App.Tap(HeaderString);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("1"));
+ }
+
+#if TEST_FAILS_ON_CATALYST //related issue link: https://github.com/dotnet/maui/issues/18028
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeMultipleWithHeaderString()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeMultiple);
+ App.Tap(SelectionModeMultiple);
+ App.WaitForElement(HeaderString);
+ App.Tap(HeaderString);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Orange, Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("2"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeMultipleWithHeaderView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeMultiple);
+ App.Tap(SelectionModeMultiple);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Orange, Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("2"));
+ }
+#endif
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeNoneWithHeaderView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeNone);
+ App.Tap(SelectionModeNone);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("No items selected"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("0"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeSingleWithHeaderView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeSingle);
+ App.Tap(SelectionModeSingle);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("1"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeNoneWithFooterString()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeNone);
+ App.Tap(SelectionModeNone);
+ App.WaitForElement(FooterString);
+ App.Tap(FooterString);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("No items selected"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("0"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeSingleWithFooterString()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeSingle);
+ App.Tap(SelectionModeSingle);
+ App.WaitForElement(FooterString);
+ App.Tap(FooterString);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("1"));
+ }
+
+#if TEST_FAILS_ON_CATALYST //related issue link: https://github.com/dotnet/maui/issues/18028
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeMultipleWithFooterString()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeMultiple);
+ App.Tap(SelectionModeMultiple);
+ App.WaitForElement(FooterString);
+ App.Tap(FooterString);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Orange, Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("2"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeMultipleWithFooterView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeMultiple);
+ App.Tap(SelectionModeMultiple);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Orange, Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("2"));
+ }
+#endif
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeNoneWithFooterView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeNone);
+ App.Tap(SelectionModeNone);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("No items selected"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("0"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeSingleWithFooterView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeSingle);
+ App.Tap(SelectionModeSingle);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("1"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeNoneWithHeaderStringAndFooterString()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeNone);
+ App.Tap(SelectionModeNone);
+ App.WaitForElement(HeaderString);
+ App.Tap(HeaderString);
+ App.WaitForElement(FooterString);
+ App.Tap(FooterString);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("No items selected"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("0"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeSingleWithHeaderStringAndFooterString()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeSingle);
+ App.Tap(SelectionModeSingle);
+ App.WaitForElement(HeaderString);
+ App.Tap(HeaderString);
+ App.WaitForElement(FooterString);
+ App.Tap(FooterString);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("1"));
+ }
+
+#if TEST_FAILS_ON_CATALYST //related issue link: https://github.com/dotnet/maui/issues/18028
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeMultipleWithHeaderStringAndFooterString()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeMultiple);
+ App.Tap(SelectionModeMultiple);
+ App.WaitForElement(HeaderString);
+ App.Tap(HeaderString);
+ App.WaitForElement(FooterString);
+ App.Tap(FooterString);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Orange, Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("2"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeMultipleWithHeaderViewAndFooterView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeMultiple);
+ App.Tap(SelectionModeMultiple);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Orange, Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("2"));
+ }
+#endif
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeNoneWithHeaderViewAndFooterView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeNone);
+ App.Tap(SelectionModeNone);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("No items selected"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("0"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeSingleWithHeaderViewAndFooterView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeSingle);
+ App.Tap(SelectionModeSingle);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("1"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeNoneWithHeaderTemplateView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeNone);
+ App.Tap(SelectionModeNone);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(HeaderTemplateGrid);
+ App.Tap(HeaderTemplateGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("No items selected"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("0"));
+ }
+
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeNoneWithFooterTemplateView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeNone);
+ App.Tap(SelectionModeNone);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(FooterTemplateGrid);
+ App.Tap(FooterTemplateGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("No items selected"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("0"));
+ }
+
+#if TEST_FAILS_ON_ANDROID // Issue Link - https://github.com/dotnet/maui/issues/32212
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeSingleWithHeaderTemplateView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeSingle);
+ App.Tap(SelectionModeSingle);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(HeaderTemplateGrid);
+ App.Tap(HeaderTemplateGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("1"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeSingleWithFooterTemplateView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeSingle);
+ App.Tap(SelectionModeSingle);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(FooterTemplateGrid);
+ App.Tap(FooterTemplateGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("1"));
+ }
+
+#if TEST_FAILS_ON_CATALYST //related issue link: https://github.com/dotnet/maui/issues/18028
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeMultipleWithHeaderTemplateView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeMultiple);
+ App.Tap(SelectionModeMultiple);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(HeaderTemplateGrid);
+ App.Tap(HeaderTemplateGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Orange, Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("2"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeMultipleWithFooterTemplateView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeMultiple);
+ App.Tap(SelectionModeMultiple);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(FooterTemplateGrid);
+ App.Tap(FooterTemplateGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Orange, Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("2"));
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeMultipleWithHeaderTemplateViewAndFooterTemplateView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeMultiple);
+ App.Tap(SelectionModeMultiple);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(HeaderTemplateGrid);
+ App.Tap(HeaderTemplateGrid);
+ App.WaitForElement(FooterTemplateGrid);
+ App.Tap(FooterTemplateGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Orange, Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("2"));
+ }
+#endif
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeSingleWithHeaderTemplateViewAndFooterTemplateView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeSingle);
+ App.Tap(SelectionModeSingle);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(HeaderTemplateGrid);
+ App.Tap(HeaderTemplateGrid);
+ App.WaitForElement(FooterTemplateGrid);
+ App.Tap(FooterTemplateGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("Banana"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("1"));
+ }
+#endif
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifySelectionModeNoneWithHeaderTemplateViewAndFooterTemplateView()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SelectionModeNone);
+ App.Tap(SelectionModeNone);
+ App.WaitForElement(HeaderGrid);
+ App.Tap(HeaderGrid);
+ App.WaitForElement(FooterGrid);
+ App.Tap(FooterGrid);
+ App.WaitForElement(HeaderTemplateGrid);
+ App.Tap(HeaderTemplateGrid);
+ App.WaitForElement(FooterTemplateGrid);
+ App.Tap(FooterTemplateGrid);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Orange");
+ App.Tap("Orange");
+ Assert.That(App.WaitForElement(SelectedSingle).GetText(), Is.EqualTo("No items selected"));
+ Assert.That(App.WaitForElement(SelectedMultiple).GetText(), Is.EqualTo("0"));
+ }
}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/DatePickerMaterial3FeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/DatePickerMaterial3FeatureTests.cs
new file mode 100644
index 000000000000..c3c604262fac
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/DatePickerMaterial3FeatureTests.cs
@@ -0,0 +1,430 @@
+#if ANDROID
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+public class DatePickerMaterial3FeatureTests : _GalleryUITest
+{
+ public override string GalleryPageName => "Date Picker Feature Matrix";
+
+ public DatePickerMaterial3FeatureTests(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ [Test, Order(1)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_InitialState_VerifyVisualState()
+ {
+ App.WaitForElement("DatePickerControl");
+ App.Tap("DatePickerControl");
+ App.WaitForElement("OK");
+ App.Tap("OK");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(2)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_ModifyOldDateAndNewDate_VerifyVisualState()
+ {
+ App.WaitForElement("DatePickerControl");
+ App.Tap("DatePickerControl");
+ App.Tap("26");
+ App.WaitForElement("OK");
+ App.Tap("OK");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(3)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_OldDateAndNewDate_VerifyVisualState()
+ {
+ App.WaitForElement("DatePickerControl");
+ App.Tap("DatePickerControl");
+ App.WaitForElement("26");
+ App.Tap("26");
+ App.WaitForElement("OK");
+ App.Tap("OK");
+ App.WaitForElement("DatePickerControl");
+ App.Tap("DatePickerControl");
+ App.WaitForElement("28");
+ App.Tap("28");
+ App.WaitForElement("Cancel");
+ App.Tap("Cancel");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(4)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetCharacterSpacingAndDate_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("CharacterSpacingEntry");
+ App.ClearText("CharacterSpacingEntry");
+ App.EnterText("CharacterSpacingEntry", "5");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(5)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_DateSelectedEvent_FiresOnDateChange()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("DateEntry");
+ App.ClearText("DateEntry");
+ App.EnterText("DateEntry", "12/15/2026");
+ App.WaitForElement("SetDateButton");
+ App.Tap("SetDateButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(6)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetDateAndTextColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TextColorGreenButton");
+ App.Tap("TextColorGreenButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(7)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetDateAndFlowDirection_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FlowDirectionRTL");
+ App.Tap("FlowDirectionRTL");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(8)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetFontAttributesAndFontFamily_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontAttributesItalicButton");
+ App.Tap("FontAttributesItalicButton");
+ App.WaitForElement("FontFamilyDokdoButton");
+ App.Tap("FontFamilyDokdoButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(9)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetFontAttributesAndFontSize_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontAttributesItalicButton");
+ App.Tap("FontAttributesItalicButton");
+ App.WaitForElement("FontSizeEntry");
+ App.ClearText("FontSizeEntry");
+ App.EnterText("FontSizeEntry", "20");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(10)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetFontFamilyAndFontSize_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontFamilyDokdoButton");
+ App.Tap("FontFamilyDokdoButton");
+ App.WaitForElement("FontSizeEntry");
+ App.ClearText("FontSizeEntry");
+ App.EnterText("FontSizeEntry", "20");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(11)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetFontAttributesAndFormat_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontAttributesItalicButton");
+ App.Tap("FontAttributesItalicButton");
+ App.WaitForElement("FormatEntry");
+ App.ClearText("FormatEntry");
+ App.EnterText("FormatEntry", "D");
+ App.WaitForElement("SetFormatButton");
+ App.Tap("SetFormatButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(12)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetFontFamilyAndFormat_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontFamilyDokdoButton");
+ App.Tap("FontFamilyDokdoButton");
+ App.WaitForElement("FormatEntry");
+ App.ClearText("FormatEntry");
+ App.EnterText("FormatEntry", "D");
+ App.WaitForElement("SetFormatButton");
+ App.Tap("SetFormatButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(13)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetFontSizeAndFormat_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontSizeEntry");
+ App.ClearText("FontSizeEntry");
+ App.EnterText("FontSizeEntry", "30");
+ App.WaitForElement("FormatEntry");
+ App.ClearText("FormatEntry");
+ App.EnterText("FormatEntry", "D");
+ App.WaitForElement("SetFormatButton");
+ App.Tap("SetFormatButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ App.WaitForElement("CultureFormatLabel");
+ App.Tap("CultureFormatLabel");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(14)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetDateAndIsEnabled_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsEnabledFalseRadioButton");
+ App.Tap("IsEnabledFalseRadioButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ App.WaitForElement("DatePickerControl");
+ App.Tap("DatePickerControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(15)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetDateAndIsVisible_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsVisibleFalseRadioButton");
+ App.Tap("IsVisibleFalseRadioButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForNoElement("DatePickerControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(16)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetDateAndShadowOpacity_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ShadowTrueRadioButton");
+ App.Tap("ShadowTrueRadioButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(17)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetMinimumDateAndDate_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("MinimumDateEntry");
+ App.ClearText("MinimumDateEntry");
+ App.EnterText("MinimumDateEntry", "12/24/2024");
+ App.WaitForElement("SetMinimumDateButton");
+ App.Tap("SetMinimumDateButton");
+ App.WaitForElement("DateEntry");
+ App.ClearText("DateEntry");
+ App.EnterText("DateEntry", "12/24/2023");
+ App.WaitForElement("SetDateButton");
+ App.Tap("SetDateButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ App.WaitForElement("CultureFormatLabel");
+ App.Tap("CultureFormatLabel");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(18)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetMaximumDateAndDate_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("MaximumDateEntry");
+ App.ClearText("MaximumDateEntry");
+ App.EnterText("MaximumDateEntry", "12/24/2028");
+ App.WaitForElement("SetMaximumDateButton");
+ App.Tap("SetMaximumDateButton");
+ App.WaitForElement("DateEntry");
+ App.ClearText("DateEntry");
+ App.EnterText("DateEntry", "12/24/2031");
+ App.WaitForElement("SetDateButton");
+ App.Tap("SetDateButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ App.WaitForElement("CultureFormatLabel");
+ App.Tap("CultureFormatLabel");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(19)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_Format_D_LongDatePattern()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FormatEntry");
+ App.ClearText("FormatEntry");
+ App.EnterText("FormatEntry", "D");
+ App.WaitForElement("SetFormatButton");
+ App.Tap("SetFormatButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(20)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_Format_f_FullDateShortTime()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FormatEntry");
+ App.ClearText("FormatEntry");
+ App.EnterText("FormatEntry", "f");
+ App.WaitForElement("SetFormatButton");
+ App.Tap("SetFormatButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(21)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_Format_F_FullDateLongTime()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FormatEntry");
+ App.ClearText("FormatEntry");
+ App.EnterText("FormatEntry", "F");
+ App.WaitForElement("SetFormatButton");
+ App.Tap("SetFormatButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(22)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetFontAttributesAndFormat_f_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontAttributesItalicButton");
+ App.Tap("FontAttributesItalicButton");
+ App.WaitForElement("FormatEntry");
+ App.ClearText("FormatEntry");
+ App.EnterText("FormatEntry", "f");
+ App.WaitForElement("SetFormatButton");
+ App.Tap("SetFormatButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(23)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetFontFamilyAndFormat_f_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontFamilyDokdoButton");
+ App.Tap("FontFamilyDokdoButton");
+ App.WaitForElement("FormatEntry");
+ App.ClearText("FormatEntry");
+ App.EnterText("FormatEntry", "f");
+ App.WaitForElement("SetFormatButton");
+ App.Tap("SetFormatButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(24)]
+ [Category(UITestCategories.Material3)]
+ public void Material3DatePicker_SetFontSizeAndFormat_f_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontSizeEntry");
+ App.ClearText("FontSizeEntry");
+ App.EnterText("FontSizeEntry", "30");
+ App.WaitForElement("FormatEntry");
+ App.ClearText("FormatEntry");
+ App.EnterText("FormatEntry", "f");
+ App.WaitForElement("SetFormatButton");
+ App.Tap("SetFormatButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("DatePickerControl");
+ App.WaitForElement("CultureFormatLabel");
+ App.Tap("CultureFormatLabel");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/FlyoutPageFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/FlyoutPageFeatureTests.cs
index 330b2be4dbbe..4855b7263f36 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/FlyoutPageFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/FlyoutPageFeatureTests.cs
@@ -384,8 +384,194 @@ public void VerifyFlyoutPage_BackgroundColor()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
+#if TEST_FAILS_ON_WINDOWS
+ // NOTE:
+ // On Windows platform, FlyoutPage behaves as Split layout by default.
+ // Due to this, IsPresentedChanged event is triggered only during initial load (app deployment)
+ // and is not raised when IsPresented is changed programmatically or via UI interaction.
+ //
+ // As per official Microsoft.MAUI documentation, FlyoutLayoutBehavior is not fully supported
+ // on desktop platforms (Windows/macOS). Hence, this behavior is expected.
+ //
+ // Therefore, IsPresentedChanged event validation is not reliable on Windows
+ // and this test case should be ignored/skipped for Windows platform.
+ //Documentation: https://learn.microsoft.com/en-us/dotnet/maui/user-interface/pages/flyoutpage?view=net-maui-10.0#layout-behavior
+
[Test, Order(21)]
[Category(UITestCategories.FlyoutPage)]
+ public void Flyout_IsPresentedChanged_Raised_When_Flyout_Opened()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("OpenFlyoutButton");
+ App.Tap("OpenFlyoutButton");
+
+ App.WaitForElement("CloseFlyoutButton");
+ App.Tap("CloseFlyoutButton");
+
+ App.WaitForElement("IsPresentedChangedLabel");
+
+ var result = App.FindElement("IsPresentedChangedLabel").GetText();
+
+ Assert.That(result, Is.EqualTo("Raised"));
+ }
+
+ [Test, Order(26)]
+ [Category(UITestCategories.FlyoutPage)]
+ public void Flyout_IsPresented_True_Raises_IsPresentedChanged()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ // Toggle IsPresented switch
+ App.WaitForElement("IsPresentedTrue");
+ App.Tap("IsPresentedTrue");
+
+ App.Tap("Apply");
+ App.WaitForElement("CloseFlyoutButton");
+ App.Tap("CloseFlyoutButton");
+
+ App.WaitForElement("IsPresentedChangedLabel");
+
+ var result = App.FindElement("IsPresentedChangedLabel").GetText();
+
+ Assert.That(result, Is.EqualTo("Raised"));
+ }
+
+#endif
+
+#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST //Issue Link: https://github.com/dotnet/maui/issues/8296
+ [Test, Order(23)]
+ [Category(UITestCategories.FlyoutPage)]
+ public void Flyout_BackButtonPressed_Event_Raised()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("SetDetail1Button");
+ App.Tap("SetDetail1Button");
+
+ App.TapBackArrow();
+
+ // Validate event label updated
+ App.WaitForElement("BackButtonPressedLabel");
+
+ var result = App.FindElement("BackButtonPressedLabel").GetText();
+
+ Assert.That(result, Is.EqualTo("Raised"));
+ }
+
+ [Test, Order(24)]
+ [Category(UITestCategories.FlyoutPage)]
+ public void Flyout_BackButtonPressed_Handled_False_Should_Allow_Navigation()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("HandleBackButtonSwitch"); // Turn OFF
+
+ App.WaitForElement("SetDetail2Button");
+ App.Tap("SetDetail2Button");
+
+ App.TapBackArrow();
+
+ // If handled → STILL on new page
+ App.WaitForElement("BackButtonHandledLabel");
+ Assert.That(App.FindElement("BackButtonHandledLabel").GetText(), Is.EqualTo("False"));
+
+ }
+
+ [Test, Order(25)]
+ [Category(UITestCategories.FlyoutPage)]
+ public void Flyout_BackButtonPressed_Handled_True_Should_Stop_Navigation()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("HandleBackButtonSwitch");
+ App.Tap("HandleBackButtonSwitch"); // Turn ON
+
+ App.WaitForElement("SetDetail2Button");
+ App.Tap("SetDetail2Button");
+
+ App.TapBackArrow();
+
+ // If handled → STILL on new page
+ App.WaitForElement("BackToOriginalDetailButton2");
+ App.Tap("BackToOriginalDetailButton2");
+
+ Assert.That(App.FindElement("BackButtonHandledLabel").GetText(), Is.EqualTo("True"));
+
+ }
+
+#endif
+
+
+ [Test, Order(27)]
+ [Category(UITestCategories.FlyoutPage)]
+ public void Flyout1_Navigation_Events_Raised()
+ {
+ App.WaitForElement("SetFlyout1Button");
+ App.Tap("SetFlyout1Button");
+
+ var navigatedTo = App.FindElement("NavigatedToLabel").GetText();
+ var navigatingFrom = App.FindElement("NavigatingFromLabel").GetText();
+ var navigatedFrom = App.FindElement("NavigatedFromLabel").GetText();
+
+ Assert.That(navigatedTo, Does.Contain("Flyout 1"));
+ Assert.That(navigatingFrom, Does.Contain("The Flyout"));
+ Assert.That(navigatedFrom, Does.Contain("The Flyout"));
+ }
+
+ [Test, Order(28)]
+ [Category(UITestCategories.FlyoutPage)]
+ public void Flyout2_Navigation_Events_Raised()
+ {
+ App.WaitForElement("SetFlyout2Button");
+ App.Tap("SetFlyout2Button");
+
+ var navigatedTo = App.FindElement("NavigatedToLabel").GetText();
+ var navigatingFrom = App.FindElement("NavigatingFromLabel").GetText();
+ var navigatedFrom = App.FindElement("NavigatedFromLabel").GetText();
+
+ Assert.That(navigatedTo, Does.Contain("Flyout 2"));
+ Assert.That(navigatingFrom, Does.Contain("Flyout 1"));
+ Assert.That(navigatedFrom, Does.Contain("Flyout 1"));
+ }
+
+ [Test, Order(29)]
+ [Category(UITestCategories.FlyoutPage)]
+ public void Flyout_Switch_From_Flyout1_To_Flyout2_NavigationEvents_Verified()
+ {
+ // Set Flyout 1
+ App.WaitForElement("SetFlyout1Button");
+ App.Tap("SetFlyout1Button");
+
+ // Set Flyout 2
+ App.WaitForElement("SetFlyout2Button");
+ App.Tap("SetFlyout2Button");
+
+ var navigatedTo = App.FindElement("NavigatedToLabel").GetText();
+ var navigatingFrom = App.FindElement("NavigatingFromLabel").GetText();
+ var navigatedFrom = App.FindElement("NavigatedFromLabel").GetText();
+
+ // Assertions
+ Assert.That(navigatingFrom, Does.Contain("Flyout 1"));
+ Assert.That(navigatedFrom, Does.Contain("Flyout 1"));
+ Assert.That(navigatedTo, Does.Contain("Flyout 2"));
+ }
+
+ [Test, Order(30)]
+ [Category(UITestCategories.FlyoutPage)]
public void VerifyFlyoutPage_IsVisible()
{
App.WaitForElement(Options);
@@ -396,4 +582,4 @@ public void VerifyFlyoutPage_IsVisible()
App.Tap(Apply);
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
-}
\ No newline at end of file
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ImageButtonMaterial3FeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ImageButtonMaterial3FeatureTests.cs
new file mode 100644
index 000000000000..d1ba96003071
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ImageButtonMaterial3FeatureTests.cs
@@ -0,0 +1,414 @@
+#if ANDROID
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+public class ImageButtonMaterial3FeatureTests : _GalleryUITest
+{
+ public const string ImageButtonFeatureMatrix = "ImageButton Feature Matrix";
+ public const string Options = "Options";
+ public const string Apply = "Apply";
+ public const string ImageAspectFit = "ImageAspectFit";
+ public const string ImageAspectFill = "ImageAspectFill";
+ public const string ImageFill = "ImageFill";
+ public const string ImageCenter = "ImageCenter";
+ public const string SourceTypeFile = "SourceTypeFile";
+ public const string SourceTypeFontImage = "SourceTypeFontImage";
+ public const string SourceTypeStream = "SourceTypeStream";
+ public const string SourceTypeUri = "SourceTypeUri";
+ public const string FlowDirectionRTL = "FlowDirectionRTL";
+ public const string ShadowTrue = "ShadowTrue";
+ public const string BorderGreen = "BorderGreen";
+ public const string BorderWidthEntry = "BorderWidthEntry";
+ public const string CornerRadiusEntry = "CornerRadiusEntry";
+ public const string IsVisibleFalse = "IsVisibleFalse";
+ public const string IsEnabledFalse = "IsEnabledFalse";
+ public const string PaddingEntry = "PaddingEntry";
+
+ public override string GalleryPageName => ImageButtonFeatureMatrix;
+
+ public ImageButtonMaterial3FeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+ [Test, Order(1)]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonAspect_AspectFitWithImageSourceFromFile()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageAspectFit);
+ App.Tap(ImageAspectFit);
+ App.WaitForElement(SourceTypeFile);
+ App.Tap(SourceTypeFile);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonAspect_AspectFitWithImageSourceFromUri()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageAspectFit);
+ App.Tap(ImageAspectFit);
+ App.WaitForElement(SourceTypeUri);
+ App.Tap(SourceTypeUri);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl", timeout: TimeSpan.FromSeconds(3));
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#if TEST_FAILS_ON_ANDROID // Issue Link: https://github.com/dotnet/maui/issues/30576
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonAspect_AspectFitWithImageSourceFromStream()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageAspectFit);
+ App.Tap(ImageAspectFit);
+ App.WaitForElement(SourceTypeStream);
+ App.Tap(SourceTypeStream);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonAspect_FillWithImageSourceFromStream()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageFill);
+ App.Tap(ImageFill);
+ App.WaitForElement(SourceTypeStream);
+ App.Tap(SourceTypeStream);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonAspect_AspectFitWithImageSourceFromFontImage()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageAspectFit);
+ App.Tap(ImageAspectFit);
+ App.WaitForElement(SourceTypeFontImage);
+ App.Tap(SourceTypeFontImage);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+#if TEST_FAILS_ON_ANDROID // Issue Link: https://github.com/dotnet/maui/issues/29956
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonAspect_AspectFillWithImageSourceFromUri()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageAspectFill);
+ App.Tap(ImageAspectFill);
+ App.WaitForElement(SourceTypeUri);
+ App.Tap(SourceTypeUri);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl", timeout: TimeSpan.FromSeconds(3));
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonAspect_FillWithImageSourceFromFile()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageFill);
+ App.Tap(ImageFill);
+ App.WaitForElement(SourceTypeFile);
+ App.Tap(SourceTypeFile);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonAspect_FillWithImageSourceFromUri()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageFill);
+ App.Tap(ImageFill);
+ App.WaitForElement(SourceTypeUri);
+ App.Tap(SourceTypeUri);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl", timeout: TimeSpan.FromSeconds(3));
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonAspect_CenterWithImageSourceFromFile()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageCenter);
+ App.Tap(ImageCenter);
+ App.WaitForElement(SourceTypeFile);
+ App.Tap(SourceTypeFile);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+
+#if TEST_FAILS_ON_WINDOWS // Issue Link: https://github.com/dotnet/maui/issues/29959
+
+#if TEST_FAILS_ON_ANDROID // Issue Link: https://github.com/dotnet/maui/issues/30576
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonAspect_AspectFillWithImageSourceFromStream()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageAspectFill);
+ App.Tap(ImageAspectFill);
+ App.WaitForElement(SourceTypeStream);
+ App.Tap(SourceTypeStream);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonAspect_CenterWithImageSourceFromStream()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageCenter);
+ App.Tap(ImageCenter);
+ App.WaitForElement(SourceTypeStream);
+ App.Tap(SourceTypeStream);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonAspect_AspectFillWithImageSourceFromFontImage()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageAspectFill);
+ App.Tap(ImageAspectFill);
+ App.WaitForElement(SourceTypeFontImage);
+ App.Tap(SourceTypeFontImage);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonAspect_FillWithImageSourceFromFontImage()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageFill);
+ App.Tap(ImageFill);
+ App.WaitForElement(SourceTypeFontImage);
+ App.Tap(SourceTypeFontImage);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonAspect_CenterWithImageSourceFromFontImage()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageCenter);
+ App.Tap(ImageCenter);
+ App.WaitForElement(SourceTypeFontImage);
+ App.Tap(SourceTypeFontImage);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+#if TEST_FAILS_ON_ANDROID // Issue Link: https://github.com/dotnet/maui/issues/29956
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonAspect_AspectFillWithImageSourceFromFile()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageAspectFill);
+ App.Tap(ImageAspectFill);
+ App.WaitForElement(SourceTypeFile);
+ App.Tap(SourceTypeFile);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonAspect_CenterWithImageSourceFromUri()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageCenter);
+ App.Tap(ImageCenter);
+ App.WaitForElement(SourceTypeUri);
+ App.Tap(SourceTypeUri);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl", timeout: TimeSpan.FromSeconds(3));
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+#endif
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonFlowDirectionRTL()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonWithPadding()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(PaddingEntry);
+ App.ClearText(PaddingEntry);
+ App.EnterText(PaddingEntry, "40,40,40,40");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonWithCornerRadius()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(CornerRadiusEntry);
+ App.ClearText(CornerRadiusEntry);
+ App.EnterText(CornerRadiusEntry, "50,50,50,50");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonWithBorderColor()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(BorderGreen);
+ App.Tap(BorderGreen);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonWithBorderWidth()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(BorderWidthEntry);
+ App.ClearText(BorderWidthEntry);
+ App.EnterText(BorderWidthEntry, "10");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonWithBorderColorAndWidth()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(BorderGreen);
+ App.Tap(BorderGreen);
+ App.WaitForElement(BorderWidthEntry);
+ App.ClearText(BorderWidthEntry);
+ App.EnterText(BorderWidthEntry, "15");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageButtonWithBorderWidthAndCornerRadius()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(BorderWidthEntry);
+ App.ClearText(BorderWidthEntry);
+ App.EnterText(BorderWidthEntry, "10");
+ App.WaitForElement(CornerRadiusEntry);
+ App.ClearText(CornerRadiusEntry);
+ App.EnterText(CornerRadiusEntry, "30,30,30,30");
+ App.WaitForElement(SourceTypeFile);
+ App.Tap(SourceTypeFile);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageButtonControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ImageFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ImageFeatureTests.cs
index d93b20a1cbf8..1858c7e5ca37 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ImageFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ImageFeatureTests.cs
@@ -111,7 +111,6 @@ public void VerifyImageAspect_AspectFitWithImageSourceFromFontImage()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
-#if TEST_FAILS_ON_WINDOWS // Issue Link: https://github.com/dotnet/maui/issues/29812
[Test]
[Category(UITestCategories.Image)]
public void VerifyImageAspect_AspectFillWithImageSourceFromFile()
@@ -176,7 +175,6 @@ public void VerifyImageAspect_AspectFillWithImageSourceFromFontImage()
App.WaitForElement("ImageControl");
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
-#endif
[Test]
[Category(UITestCategories.Image)]
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/MapFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/MapFeatureTests.cs
new file mode 100644
index 000000000000..834728bb3dc7
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/MapFeatureTests.cs
@@ -0,0 +1,361 @@
+#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_WINDOWS // This test will fail on Windows due to lack of Map control support, and on Android unless a valid API key is configured for Maps.
+
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+[Category(UITestCategories.Maps)]
+public class MapFeatureTests : _GalleryUITest
+{
+ public const string MapFeatureMatrix = "Map Feature Matrix";
+ public override string GalleryPageName => MapFeatureMatrix;
+
+ public MapFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+ [Test]
+ public void Map_IsVisible()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ResetToInitialButton");
+ App.Tap("ResetToInitialButton");
+
+ App.WaitForElement("IsVisibleCheckBox");
+ App.Tap("IsVisibleCheckBox");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void Map_IsEnabledScroll_WithZoomOut()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ResetToInitialButton");
+ App.Tap("ResetToInitialButton");
+
+ App.WaitForElement("IsScrollEnabledCheckBox");
+ App.Tap("IsScrollEnabledCheckBox");
+
+ App.WaitForElement("IsZoomEnabledCheckBox");
+ App.Tap("IsZoomEnabledCheckBox");
+
+ App.WaitForElement("ZoomOutButton");
+ App.Tap("ZoomOutButton");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ var value = App.WaitForElement("VisibleRegionLatitudeDegrees").GetText();
+ var rect = App.WaitForElement("MapView").GetRect();
+ App.DragCoordinates(rect.CenterX(), rect.CenterY(), rect.CenterX(), rect.CenterY() - 100);
+
+ Thread.Sleep(2000);
+ Assert.That(App.WaitForElement("VisibleRegionLatitudeDegrees").GetText(), Is.Not.EqualTo(value));
+ }
+
+ [Test]
+ public void Map_IsEnabledScroll_Hybrid_AndPins()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ResetToInitialButton");
+ App.Tap("ResetToInitialButton");
+
+ App.WaitForElement("IsScrollEnabledCheckBox");
+ App.Tap("IsScrollEnabledCheckBox");
+
+ App.WaitForElement("HybridRadioButton");
+ App.Tap("HybridRadioButton");
+
+ for (int i = 0; i < 7; i++)
+ {
+ App.WaitForElement("AddPinButton");
+ App.Tap("AddPinButton");
+ }
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ var value = App.WaitForElement("VisibleRegionLatitudeDegrees").GetText();
+ var rect = App.WaitForElement("MapView").GetRect();
+ App.DragCoordinates(rect.CenterX() - 100, rect.CenterY(), rect.CenterX(), rect.CenterY() - 100);
+
+ Thread.Sleep(2000);
+ Assert.That(App.WaitForElement("VisibleRegionLatitudeDegrees").GetText(), Is.Not.EqualTo(value));
+ }
+
+ [Test]
+ public void Map_IsEnabledScroll_Street_AndElements_PolyLine()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ResetToInitialButton");
+ App.Tap("ResetToInitialButton");
+
+ App.WaitForElement("IsScrollEnabledCheckBox");
+ App.Tap("IsScrollEnabledCheckBox");
+
+ for (int i = 0; i < 7; i++)
+ {
+ App.WaitForElement("AddElementButton");
+ App.Tap("AddElementButton");
+ }
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ var value = App.WaitForElement("VisibleRegionLatitudeDegrees").GetText();
+ var rect = App.WaitForElement("MapView").GetRect();
+ App.DragCoordinates(rect.CenterX(), rect.CenterY(), rect.CenterX(), rect.CenterY() - 100);
+
+ Thread.Sleep(2000);
+ Assert.That(App.WaitForElement("VisibleRegionLatitudeDegrees").GetText(), Is.Not.EqualTo(value));
+ }
+
+ [Test]
+ public void Map_IsEnabledScroll_Street_AndElements_Circle()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ResetToInitialButton");
+ App.Tap("ResetToInitialButton");
+
+ App.WaitForElement("IsScrollEnabledCheckBox");
+ App.Tap("IsScrollEnabledCheckBox");
+
+ App.WaitForElement("CircleRadioButton");
+ App.Tap("CircleRadioButton");
+
+ for (int i = 0; i < 7; i++)
+ {
+ App.WaitForElement("AddElementButton");
+ App.Tap("AddElementButton");
+ }
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ var value = App.WaitForElement("VisibleRegionLatitudeDegrees").GetText();
+ var rect = App.WaitForElement("MapView").GetRect();
+ App.DragCoordinates(rect.CenterX(), rect.CenterY(), rect.CenterX(), rect.CenterY() - 100);
+
+ Thread.Sleep(2000);
+ Assert.That(App.WaitForElement("VisibleRegionLatitudeDegrees").GetText(), Is.Not.EqualTo(value));
+ }
+
+ [Test]
+ public void Map_IsEnabledScroll_Street_AndElements_Polygon()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ResetToInitialButton");
+ App.Tap("ResetToInitialButton");
+
+ App.WaitForElement("IsScrollEnabledCheckBox");
+ App.Tap("IsScrollEnabledCheckBox");
+
+ App.WaitForElement("PolygonRadioButton");
+ App.Tap("PolygonRadioButton");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ var value = App.WaitForElement("VisibleRegionLatitudeDegrees").GetText();
+ var rect = App.WaitForElement("MapView").GetRect();
+ App.DragCoordinates(rect.CenterX(), rect.CenterY(), rect.CenterX(), rect.CenterY() - 100);
+
+ Thread.Sleep(2000);
+ Assert.That(App.WaitForElement("VisibleRegionLatitudeDegrees").GetText(), Is.Not.EqualTo(value));
+ }
+
+ [Test]
+ public void Map_MapClicked()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ResetToInitialButton");
+ App.Tap("ResetToInitialButton");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.WaitForElement("MapView");
+ App.TapCoordinates(300, 300);
+
+ Assert.That(App.WaitForElement("MapClickedLabel").GetText(), Is.Not.EqualTo("Not Clicked"));
+ }
+
+ [Test]
+ public void Map_IsEnabledScroll_Hybrid_WithItemTemplate()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ResetToInitialButton");
+ App.Tap("ResetToInitialButton");
+
+ App.WaitForElement("IsScrollEnabledCheckBox");
+ App.Tap("IsScrollEnabledCheckBox");
+
+ App.WaitForElement("HybridRadioButton");
+ App.Tap("HybridRadioButton");
+
+ for (int i = 0; i < 7; i++)
+ {
+ App.WaitForElement("AddPinButton");
+ App.Tap("AddPinButton");
+ }
+
+ App.WaitForElement("SetItemsSourceButton");
+ App.Tap("SetItemsSourceButton");
+
+ App.WaitForElement("SetItemTemplateButton");
+ App.Tap("SetItemTemplateButton");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ var value = App.WaitForElement("VisibleRegionLatitudeDegrees").GetText();
+ var rect = App.WaitForElement("MapView").GetRect();
+ App.DragCoordinates(rect.CenterX() - 100, rect.CenterY(), rect.CenterX(), rect.CenterY() - 100);
+
+ Thread.Sleep(2000);
+ Assert.That(App.WaitForElement("VisibleRegionLatitudeDegrees").GetText(), Is.Not.EqualTo(value));
+ }
+
+ [Test]
+ public void Map_IsZoomDisabled_PreventZoom()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ResetToInitialButton");
+ App.Tap("ResetToInitialButton");
+
+ // Ensure zoom is disabled (default state)
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ var initialValue = App.WaitForElement("VisibleRegionLatitudeDegrees").GetText();
+
+ // Try to zoom (should not work when disabled)
+ var rect = App.WaitForElement("MapView").GetRect();
+ App.PinchToZoomIn("MapView");
+
+ Thread.Sleep(1000);
+
+ // Verify zoom level hasn't changed significantly
+ var finalValue = App.WaitForElement("VisibleRegionLatitudeDegrees").GetText();
+ Assert.That(finalValue, Is.EqualTo(initialValue));
+ }
+
+ [Test]
+ public void Map_IsScrollDisabled_PreventScroll()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ResetToInitialButton");
+ App.Tap("ResetToInitialButton");
+
+ // Leave scroll disabled (default state)
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ var initialValue = App.WaitForElement("VisibleRegionLatitudeDegrees").GetText();
+ var rect = App.WaitForElement("MapView").GetRect();
+
+ // Try to scroll (should not work when disabled)
+ App.DragCoordinates(rect.CenterX(), rect.CenterY(), rect.CenterX(), rect.CenterY() - 100);
+
+ Thread.Sleep(2000);
+
+ // Verify region hasn't changed
+ Assert.That(App.WaitForElement("VisibleRegionLatitudeDegrees").GetText(), Is.EqualTo(initialValue));
+ }
+
+ [Test]
+ public void Map_ZoomIn_ChangesVisibleRegion()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ResetToInitialButton");
+ App.Tap("ResetToInitialButton");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ var initialValue = App.WaitForElement("VisibleRegionLatitudeDegrees").GetText();
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("IsZoomEnabledCheckBox");
+ App.Tap("IsZoomEnabledCheckBox");
+
+ // Zoom in
+ App.WaitForElement("ZoomInButton");
+ App.Tap("ZoomInButton");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ Thread.Sleep(1000);
+
+ // Verify region changed after zoom in (latitude degrees should be smaller)
+ var finalValue = App.WaitForElement("VisibleRegionLatitudeDegrees").GetText();
+ Assert.That(finalValue, Is.Not.EqualTo(initialValue));
+ }
+
+ [Test]
+ public void Map_ZoomOut_ChangesVisibleRegion()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ResetToInitialButton");
+ App.Tap("ResetToInitialButton");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ var initialValue = App.WaitForElement("VisibleRegionLatitudeDegrees").GetText();
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("IsZoomEnabledCheckBox");
+ App.Tap("IsZoomEnabledCheckBox");
+
+ // Zoom out
+ App.WaitForElement("ZoomOutButton");
+ App.Tap("ZoomOutButton");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ Thread.Sleep(1000);
+
+ // Verify region changed after zoom out (latitude degrees should be larger)
+ var finalValue = App.WaitForElement("VisibleRegionLatitudeDegrees").GetText();
+ Assert.That(finalValue, Is.Not.EqualTo(initialValue));
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3EntryFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3EntryFeatureTests.cs
new file mode 100644
index 000000000000..6445d5cc50ef
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3EntryFeatureTests.cs
@@ -0,0 +1,561 @@
+// Material3 Entry uses TextInputLayout + TextInputEditText instead of standard EditText.
+// These tests run only on Android where Material3 EntryHandler2 is used.
+#if ANDROID
+using System;
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+public class Material3EntryFeatureTests : _GalleryUITest
+{
+ public override string GalleryPageName => "Entry Material3 Feature Matrix";
+
+ private const int CropBottomValue = 1500;
+
+ public Material3EntryFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+ [Test, Order(1)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_InitialState_VerifyVisualState()
+ {
+ App.WaitForElement("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(2)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_TextWithHorizontalAlignment_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("HCenter");
+ App.Tap("HCenter");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(3)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_TextWithVerticalAlignment_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("VEnd");
+ App.Tap("VEnd");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(4)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_HorizontalAndVerticalAlignment_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("VEnd");
+ App.Tap("VEnd");
+ App.WaitForElement("HEnd");
+ App.Tap("HEnd");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(5)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_TextColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TextColorRed");
+ App.Tap("TextColorRed");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(6)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_FontSize_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontSizeEntry");
+ App.ClearText("FontSizeEntry");
+ App.EnterText("FontSizeEntry", "20");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(7)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_FontFamily_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontFamily");
+ App.EnterText("FontFamily", "MontserratBold");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(8)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_FontAttributes_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontAttributesBold");
+ App.Tap("FontAttributesBold");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(9)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_CharacterSpacing_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("CharacterSpacing");
+ App.ClearText("CharacterSpacing");
+ App.EnterText("CharacterSpacing", "5");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(10)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_HorizontalAlignmentWithCharacterSpacing_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("CharacterSpacing");
+ App.ClearText("CharacterSpacing");
+ App.EnterText("CharacterSpacing", "5");
+ App.WaitForElement("HCenter");
+ App.Tap("HCenter");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(11)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_VerticalAlignmentWithCharacterSpacing_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("CharacterSpacing");
+ App.ClearText("CharacterSpacing");
+ App.EnterText("CharacterSpacing", "5");
+ App.WaitForElement("VEnd");
+ App.Tap("VEnd");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(12)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_CharacterSpacingWithFontFamily_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("CharacterSpacing");
+ App.ClearText("CharacterSpacing");
+ App.EnterText("CharacterSpacing", "5");
+ App.WaitForElement("FontFamily");
+ App.EnterText("FontFamily", "MontserratBold");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(13)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_CharacterSpacingWithMaxLength_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("CharacterSpacing");
+ App.ClearText("CharacterSpacing");
+ App.EnterText("CharacterSpacing", "5");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.EnterText("TextEntryChanged", "Test Entered Set MaxLength");
+ App.WaitForElement("MaxLength");
+ App.ClearText("MaxLength");
+ App.EnterText("MaxLength", "6");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(14)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_IsPassword_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("PasswordTrue");
+ App.Tap("PasswordTrue");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(15)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_IsPasswordWithCharacterSpacing_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("CharacterSpacing");
+ App.ClearText("CharacterSpacing");
+ App.EnterText("CharacterSpacing", "5");
+ App.WaitForElement("PasswordTrue");
+ App.Tap("PasswordTrue");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(16)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_IsPasswordWithFontSize_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("PasswordTrue");
+ App.Tap("PasswordTrue");
+ App.ClearText("FontSizeEntry");
+ App.EnterText("FontSizeEntry", "20");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(17)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_FlowDirection_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FlowDirectionRightToLeft");
+ App.Tap("FlowDirectionRightToLeft");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(18)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_PlaceholderText_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("PlaceholderText");
+ App.ClearText("PlaceholderText");
+ App.EnterText("PlaceholderText", "Enter your name");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ App.ClearText("TestEntry");
+ App.DismissKeyboard();
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(19)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_PlaceholderColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("PlaceholderColorRed");
+ App.Tap("PlaceholderColorRed");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+#if TEST_FAILS_ON_ANDROID //FlowDirection does not apply to the placeholder in Material3 Entry.
+ [Test, Order(20)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_PlaceholderWithFlowDirection_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FlowDirectionRightToLeft");
+ App.Tap("FlowDirectionRightToLeft");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+#if TEST_FAILS_ON_ANDROID //HorizontalTextAlignment does not apply to the placeholder in Material3 Entry. TextInputLayout does not expose a public API for hint text gravity.
+ [Test, Order(21)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_PlaceholderWithHorizontalAlignment_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("HCenter");
+ App.Tap("HCenter");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ [Test, Order(22)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_PlaceholderWithVerticalAlignment_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("VStart");
+ App.Tap("VStart");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(23)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_PlaceholderWithFontFamily_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontFamily");
+ App.EnterText("FontFamily", "MontserratBold");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(24)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_PlaceholderWithFontSize_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontSizeEntry");
+ App.ClearText("FontSizeEntry");
+ App.EnterText("FontSizeEntry", "20");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(25)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_PlaceholderWithFontAttributes_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontAttributesItalic");
+ App.Tap("FontAttributesItalic");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+#if TEST_FAILS_ON_ANDROID //CharacterSpacing does not apply to the placeholder in Material3 Entry. TextInputLayout does not expose a public API for hint letter spacing.
+ [Test, Order(26)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_PlaceholderWithCharacterSpacing_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("CharacterSpacing");
+ App.ClearText("CharacterSpacing");
+ App.EnterText("CharacterSpacing", "5");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ [Test, Order(27)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_PlaceholderWithPasswordTrue_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("PlaceholderText");
+ App.ClearText("PlaceholderText");
+ App.EnterText("PlaceholderText", "Enter your password");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.WaitForElement("PasswordTrue");
+ App.Tap("PasswordTrue");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(28)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_Shadow_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ShadowCheckBox");
+ App.Tap("ShadowCheckBox");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(29)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_PlaceholderWithShadow_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ShadowCheckBox");
+ App.Tap("ShadowCheckBox");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(30)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_IsEnabled_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("EnabledFalse");
+ App.Tap("EnabledFalse");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(31)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_IsPasswordWithMaxLength_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.EnterText("TextEntryChanged", "Test Entered Set MaxLength");
+ App.WaitForElement("MaxLength");
+ App.ClearText("MaxLength");
+ App.EnterText("MaxLength", "6");
+ App.WaitForElement("PasswordTrue");
+ App.Tap("PasswordTrue");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(32)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_IsPasswordWithVerticalAlignment_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("PasswordTrue");
+ App.Tap("PasswordTrue");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(33)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_IsPasswordWithHorizontalAlignment_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("HEnd");
+ App.Tap("HEnd");
+ App.WaitForElement("PasswordTrue");
+ App.Tap("PasswordTrue");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, cropBottom: CropBottomValue, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(34)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Entry_PlaceholderColorAndTextColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TextColorRed");
+ App.Tap("TextColorRed");
+ App.WaitForElement("PlaceholderColorBlue");
+ App.Tap("PlaceholderColorBlue");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("TestEntry");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3ImageFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3ImageFeatureTests.cs
new file mode 100644
index 000000000000..08bf2ca0374c
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3ImageFeatureTests.cs
@@ -0,0 +1,356 @@
+#if ANDROID
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+public class Material3ImageFeatureTests : _GalleryUITest
+{
+ public const string ImageFeatureMatrix = "Image Feature Matrix";
+ public const string Options = "Options";
+ public const string Apply = "Apply";
+ public const string ImageAspectFit = "ImageAspectFit";
+ public const string ImageAspectFill = "ImageAspectFill";
+ public const string ImageFill = "ImageFill";
+ public const string ImageCenter = "ImageCenter";
+ public const string SourceTypeFile = "SourceTypeFile";
+ public const string SourceTypeFontImage = "SourceTypeFontImage";
+ public const string SourceTypeStream = "SourceTypeStream";
+ public const string SourceTypeUri = "SourceTypeUri";
+ public const string IsVisibleFalseRadio = "IsVisibleFalseRadio";
+ public const string FlowDirectionRTL = "FlowDirectionRTL";
+ public const string ShadowCheckBox = "ShadowCheckBox";
+ public const string IsAnimationTrue = "IsAnimationTrue";
+ public const string IsAnimationFalse = "IsAnimationFalse";
+
+ public override string GalleryPageName => ImageFeatureMatrix;
+
+ public Material3ImageFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageAspect_AspectFitWithFileSource()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageAspectFit);
+ App.Tap(ImageAspectFit);
+ App.WaitForElement(SourceTypeFile);
+ App.Tap(SourceTypeFile);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageAspect_AspectFitWithUriSource()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageAspectFit);
+ App.Tap(ImageAspectFit);
+ App.WaitForElement(SourceTypeUri);
+ App.Tap(SourceTypeUri);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl", timeout: TimeSpan.FromSeconds(3));
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageAspect_AspectFitWithStreamSource()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageAspectFit);
+ App.Tap(ImageAspectFit);
+ App.WaitForElement(SourceTypeStream);
+ App.Tap(SourceTypeStream);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageAspect_AspectFitWithFontImageSource()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageAspectFit);
+ App.Tap(ImageAspectFit);
+ App.WaitForElement(SourceTypeFontImage);
+ App.Tap(SourceTypeFontImage);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageAspect_AspectFillWithFileSource()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageAspectFill);
+ App.Tap(ImageAspectFill);
+ App.WaitForElement(SourceTypeFile);
+ App.Tap(SourceTypeFile);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageAspect_AspectFillWithUriSource()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageAspectFill);
+ App.Tap(ImageAspectFill);
+ App.WaitForElement(SourceTypeUri);
+ App.Tap(SourceTypeUri);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl", timeout: TimeSpan.FromSeconds(3));
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageAspect_AspectFillWithStreamSource()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageAspectFill);
+ App.Tap(ImageAspectFill);
+ App.WaitForElement(SourceTypeStream);
+ App.Tap(SourceTypeStream);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageAspect_AspectFillWithFontImageSource()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageAspectFill);
+ App.Tap(ImageAspectFill);
+ App.WaitForElement(SourceTypeFontImage);
+ App.Tap(SourceTypeFontImage);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageAspect_FillWithFileSource()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageFill);
+ App.Tap(ImageFill);
+ App.WaitForElement(SourceTypeFile);
+ App.Tap(SourceTypeFile);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageAspect_FillWithUriSource()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageFill);
+ App.Tap(ImageFill);
+ App.WaitForElement(SourceTypeUri);
+ App.Tap(SourceTypeUri);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl", timeout: TimeSpan.FromSeconds(3));
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageAspect_FillWithStreamSource()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageFill);
+ App.Tap(ImageFill);
+ App.WaitForElement(SourceTypeStream);
+ App.Tap(SourceTypeStream);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageAspect_FillWithFontImageSource()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageFill);
+ App.Tap(ImageFill);
+ App.WaitForElement(SourceTypeFontImage);
+ App.Tap(SourceTypeFontImage);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageAspect_CenterWithFileSource()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageCenter);
+ App.Tap(ImageCenter);
+ App.WaitForElement(SourceTypeFile);
+ App.Tap(SourceTypeFile);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageAspect_CenterWithUriSource()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageCenter);
+ App.Tap(ImageCenter);
+ App.WaitForElement(SourceTypeUri);
+ App.Tap(SourceTypeUri);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl", timeout: TimeSpan.FromSeconds(3));
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageAspect_CenterWithStreamSource()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageCenter);
+ App.Tap(ImageCenter);
+ App.WaitForElement(SourceTypeStream);
+ App.Tap(SourceTypeStream);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageAspect_CenterWithFontImageSource()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(ImageCenter);
+ App.Tap(ImageCenter);
+ App.WaitForElement(SourceTypeFontImage);
+ App.Tap(SourceTypeFontImage);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3FontImageWithFontColorGreen()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SourceTypeFontImage);
+ App.Tap(SourceTypeFontImage);
+ App.WaitForElement("FontColorGreen");
+ App.Tap("FontColorGreen");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3FontImageWithFontSize()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SourceTypeFontImage);
+ App.Tap(SourceTypeFontImage);
+ App.WaitForElement("EntryFontSize");
+ App.ClearText("EntryFontSize");
+ App.Tap("EntryFontSize");
+ App.EnterText("EntryFontSize", "100");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageWithShadow()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SourceTypeFile);
+ App.Tap(SourceTypeFile);
+ App.WaitForElement(ShadowCheckBox);
+ App.Tap(ShadowCheckBox);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void VerifyMaterial3ImageFlowDirectionRTL()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement(SourceTypeFile);
+ App.Tap(SourceTypeFile);
+ App.WaitForElement(FlowDirectionRTL);
+ App.Tap(FlowDirectionRTL);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ImageControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3ProgressBarFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3ProgressBarFeatureTests.cs
new file mode 100644
index 000000000000..70b972c40f5d
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3ProgressBarFeatureTests.cs
@@ -0,0 +1,107 @@
+// Material3 ProgressBar tests reuse the existing ProgressBar Feature Matrix HostApp page.
+// The native Android view differs (LinearProgressIndicator vs ProgressBar), so these tests
+// produce separate screenshot baselines under the Material3 category.
+#if ANDROID
+using System;
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+public class Material3ProgressBarFeatureTests : _GalleryUITest
+{
+ public override string GalleryPageName => "ProgressBar Feature Matrix";
+
+ public Material3ProgressBarFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+ [Test, Order(1)]
+ [Category(UITestCategories.Material3)]
+ public void Material3ProgressBar_ProgressToMethod_VerifyVisualState()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+ App.WaitForElement("ProgressToEntry");
+ App.ClearText("ProgressToEntry");
+ App.EnterText("ProgressToEntry", "0.90");
+ App.PressEnter();
+ App.WaitForElement("ProgressToButton");
+ App.Tap("ProgressToButton");
+ Task.Delay(1000).Wait();
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3ProgressBar_SetProgressOutOfRange()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+ App.WaitForElement("ProgressEntry");
+ App.ClearText("ProgressEntry");
+ App.EnterText("ProgressEntry", "1.44");
+ App.PressEnter();
+ App.WaitForElement("ProgressBarControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3ProgressBar_SetProgressNegativeValue()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+ App.WaitForElement("ProgressEntry");
+ App.ClearText("ProgressEntry");
+ App.EnterText("ProgressEntry", "-0.44");
+ App.PressEnter();
+ App.WaitForElement("ProgressBarControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3ProgressBar_SetProgressColorAndBackgroundColor_VerifyVisualState()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+ App.WaitForElement("ProgressEntry");
+ App.ClearText("ProgressEntry");
+ App.EnterText("ProgressEntry", "0.60");
+ App.PressEnter();
+ App.WaitForElement("ProgressColorRedButton");
+ App.Tap("ProgressColorRedButton");
+ App.WaitForElement("BackgroundColorLightBlueButton");
+ App.Tap("BackgroundColorLightBlueButton");
+ App.WaitForElement("ProgressBarControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3ProgressBar_ChangeFlowDirection_RTL_VerifyLabel()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+ App.WaitForElement("FlowDirectionRTL");
+ App.Tap("FlowDirectionRTL");
+ App.WaitForElement("ProgressBarControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3ProgressBar_ToggleShadow_VerifyVisualState()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+ App.WaitForElement("ShadowTrueRadio");
+ App.Tap("ShadowTrueRadio");
+ App.WaitForElement("ProgressBarControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3SliderFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3SliderFeatureTests.cs
new file mode 100644
index 000000000000..cb236a84c624
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3SliderFeatureTests.cs
@@ -0,0 +1,520 @@
+// Material3 Slider tests reuse the existing Slider Feature Matrix HostApp page.
+// The native Android Slider uses Material3 styling (Google Material3 Slider) when Material3 is enabled,
+// so these tests produce separate screenshot baselines under the Material3 category.
+#if ANDROID
+using System;
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+public class Material3SliderFeatureTests : _GalleryUITest
+{
+ public override string GalleryPageName => "Slider Feature Matrix";
+
+ public Material3SliderFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+ // ========== Single Property Tests ==========
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetEnabledStateToFalse_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsEnabledFalseRadio");
+ App.Tap("IsEnabledFalseRadio");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_ChangeFlowDirection_RTL_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FlowDirectionRTL");
+ App.Tap("FlowDirectionRTL");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetVisibilityToFalse_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsVisibleFalseRadio");
+ App.Tap("IsVisibleFalseRadio");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("Options");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_ChangeThumbColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ThumbColorGreenButton");
+ App.Tap("ThumbColorGreenButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_ChangeMinTrackColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("MinTrackColorYellowButton");
+ App.Tap("MinTrackColorYellowButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_ChangeMaxTrackColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("MaxTrackColorRedButton");
+ App.Tap("MaxTrackColorRedButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_ChangeBackgroundColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("BackgroundColorLightBlueButton");
+ App.Tap("BackgroundColorLightBlueButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_ChangeThumbImageSource_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ThumbImageSourceButton");
+ App.Tap("ThumbImageSourceButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ // ========== Two Property Combo Tests ==========
+
+ // --- Value + other property ---
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetValueAndMinTrackColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ValueEntry");
+ App.EnterText("ValueEntry", "1");
+ App.PressEnter();
+ App.Tap("MinTrackColorYellowButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetValueAndMaxTrackColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ValueEntry");
+ App.EnterText("ValueEntry", "0");
+ App.PressEnter();
+ App.Tap("MaxTrackColorRedButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetValueAndThumbImageSource_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ValueEntry");
+ App.EnterText("ValueEntry", "0");
+ App.PressEnter();
+ App.Tap("ThumbImageSourceButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetValueAndFlowDirection_RTL_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ValueEntry");
+ App.EnterText("ValueEntry", "0");
+ App.PressEnter();
+ App.Tap("FlowDirectionRTL");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ // --- ThumbColor + other property ---
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetThumbAndMaxTrackColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ThumbColorGreenButton");
+ App.Tap("ThumbColorGreenButton");
+ App.Tap("MaxTrackColorRedButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetThumbAndMinTrackColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ThumbColorGreenButton");
+ App.Tap("ThumbColorGreenButton");
+ App.Tap("MinTrackColorYellowButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetThumbAndBackgroundColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ThumbColorGreenButton");
+ App.Tap("ThumbColorGreenButton");
+ App.Tap("BackgroundColorLightBlueButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetThumbColorAndThumbImageSource_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ThumbColorGreenButton");
+ App.Tap("ThumbColorGreenButton");
+ App.Tap("ThumbImageSourceButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ // --- MinTrackColor + other property ---
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetMinTrackAndMaxTrackColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("MinTrackColorYellowButton");
+ App.Tap("MinTrackColorYellowButton");
+ App.Tap("MaxTrackColorRedButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetMinTrackAndBackgroundColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("MinTrackColorYellowButton");
+ App.Tap("MinTrackColorYellowButton");
+ App.Tap("BackgroundColorLightBlueButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetMinTrackColorAndFlowDirection_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("MinTrackColorYellowButton");
+ App.Tap("MinTrackColorYellowButton");
+ App.Tap("FlowDirectionRTL");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ // --- MaxTrackColor + other property ---
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetMaxTrackAndBackgroundColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("MaxTrackColorRedButton");
+ App.Tap("MaxTrackColorRedButton");
+ App.Tap("BackgroundColorLightBlueButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetMaxTrackColorAndFlowDirection_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("MaxTrackColorRedButton");
+ App.Tap("MaxTrackColorRedButton");
+ App.Tap("FlowDirectionRTL");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ // --- IsEnabled + other property ---
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetIsEnableAndThumbColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsEnabledTrueRadio");
+ App.Tap("IsEnabledTrueRadio");
+ App.Tap("ThumbColorGreenButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetIsEnableAndMinTrackColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsEnabledTrueRadio");
+ App.Tap("IsEnabledTrueRadio");
+ App.Tap("MinTrackColorYellowButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetIsEnableAndMaxTrackColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsEnabledTrueRadio");
+ App.Tap("IsEnabledTrueRadio");
+ App.Tap("MaxTrackColorRedButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetIsEnableAndBackgroundColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsEnabledTrueRadio");
+ App.Tap("IsEnabledTrueRadio");
+ App.Tap("BackgroundColorLightBlueButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ // --- IsVisible + other property ---
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetIsVisibleAndThumbColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsVisibleTrueRadio");
+ App.Tap("IsVisibleTrueRadio");
+ App.Tap("ThumbColorGreenButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetIsVisibleAndMinTrackColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsVisibleTrueRadio");
+ App.Tap("IsVisibleTrueRadio");
+ App.Tap("MinTrackColorYellowButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetIsVisibleAndMaxTrackColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsVisibleTrueRadio");
+ App.Tap("IsVisibleTrueRadio");
+ App.Tap("MaxTrackColorRedButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetIsVisibleAndBackgroundColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsVisibleTrueRadio");
+ App.Tap("BackgroundColorLightBlueButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ // --- Minimum/Maximum + FlowDirection ---
+
+#if TEST_FAILS_ON_ANDROID // Resetting ThumbImageSource to null does not restore the default thumb on Material3 Slider
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetThumbImageSourceAndReset_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ThumbImageSourceButton");
+ App.Tap("ThumbImageSourceButton");
+ App.Tap("ThumbImageResetButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+#if TEST_FAILS_ON_ANDROID // Setting Minimum=10 with default Maximum=1 causes IllegalStateException in Material3 Slider (valueFrom must be < valueTo)
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetMinimumAndChangeFlowDirection_RTL()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("MinimumEntry");
+ App.EnterText("MinimumEntry", "10");
+ App.PressEnter();
+ App.Tap("FlowDirectionRTL");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Slider_SetMaximumAndChangeFlowDirection_RTL()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("MaximumEntry");
+ App.EnterText("MaximumEntry", "50");
+ App.PressEnter();
+ App.Tap("FlowDirectionRTL");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3SwitchFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3SwitchFeatureTests.cs
new file mode 100644
index 000000000000..c05a066f775f
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3SwitchFeatureTests.cs
@@ -0,0 +1,103 @@
+// Material3 Switch tests reuse the existing Switch Feature Matrix HostApp page.
+// The native Android view differs (MaterialSwitch vs SwitchCompat), so these tests
+// produce separate screenshot baselines under the Material3 category.
+#if ANDROID
+using System;
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+public class Material3SwitchFeatureTests : _GalleryUITest
+{
+ public override string GalleryPageName => "Switch Feature Matrix";
+
+ public Material3SwitchFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+ [Test, Order(1)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Switch_InitialState_VerifyVisualState()
+ {
+ App.WaitForElement("SwitchControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(2)]
+ [Category(UITestCategories.Material3)]
+ public void Material3Switch_Click_VerifyVisualState()
+ {
+ App.WaitForElement("SwitchControl");
+ App.Tap("SwitchControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Switch_SetFlowDirectionAndToggled_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FlowDirectionRightToLeftCheckBox");
+ App.Tap("FlowDirectionRightToLeftCheckBox");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("SwitchControl");
+ App.Tap("SwitchControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Switch_SetToggledAndOnColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsToggledTrueCheckBox");
+ App.Tap("IsToggledTrueCheckBox");
+ App.WaitForElement("OnColorRedCheckBox");
+ App.Tap("OnColorRedCheckBox");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SwitchControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Switch_SetOnColorAndThumbColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("OnColorRedCheckBox");
+ App.Tap("OnColorRedCheckBox");
+ App.WaitForElement("ThumbColorGreenCheckBox");
+ App.Tap("ThumbColorGreenCheckBox");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("SwitchControl");
+ App.Tap("SwitchControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Material3)]
+ public void Material3Switch_SetThumbColorAndOnColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ThumbColorRedCheckBox");
+ App.Tap("ThumbColorRedCheckBox");
+ App.WaitForElement("OnColorGreenCheckBox");
+ App.Tap("OnColorGreenCheckBox");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("SwitchControl");
+ App.Tap("SwitchControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/NavigationPageFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/NavigationPageFeatureTests.cs
index 341af2070f0d..dee36853a673 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/NavigationPageFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/NavigationPageFeatureTests.cs
@@ -314,10 +314,6 @@ public void TitleIcon_AddingTwice_DoesNotDuplicate()
[Test, Order(13)]
public void Combine_BarBackgroundColor_TextColor_IconColor_Visual()
{
- if (App is AppiumIOSApp iosApp && HelperExtensions.IsIOS26OrHigher(iosApp))
- {
- Assert.Ignore("Ignored due to a bug issue in iOS 26"); // Issue Link: https://github.com/dotnet/maui/issues/33966
- }
App.WaitForElement("ResetButton");
App.Tap("ResetButton");
// Set bar background color and text color
@@ -331,7 +327,7 @@ public void Combine_BarBackgroundColor_TextColor_IconColor_Visual()
App.WaitForElement("PushPageButton");
App.Tap("PushPageButton");
// Screenshot: Combined bar background, text color and icon color on pushed page
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ VerifyScreenshot();
}
[Test, Order(14)]
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/RefreshViewFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/RefreshViewFeatureTests.cs
index 497ecb526464..6942bcaae343 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/RefreshViewFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/RefreshViewFeatureTests.cs
@@ -215,4 +215,87 @@ public void RefreshView_SetShadowWithCollectionView_VerifyShadowApplied()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
#endif
-}
\ No newline at end of file
+
+#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_CATALYST // In Appium PullToRefresh is not supported on Catalyst and Windows
+
+ [Test, Order(14)]
+ [Category(UITestCategories.RefreshView)]
+ public void RefreshView_RefreshingEvent_DefaultState_NotRaised()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("RefreshView");
+
+ Assert.That(App.FindElement("RefreshingEventLabel").GetText(), Is.EqualTo("Not Raised"));
+ }
+
+ [Test , Order(15)]
+ [Category(UITestCategories.RefreshView)]
+ public void RefreshView_RefreshingEvent_IsRaised()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("RefreshView");
+
+ Assert.That(App.FindElement("RefreshingEventLabel").GetText(), Is.EqualTo("Not Raised"));
+
+ App.ScrollUp("RefreshView");
+
+ App.WaitForElement("RefreshingEventLabel", timeout: TimeSpan.FromSeconds(5));
+
+ Assert.That(App.FindElement("RefreshingEventLabel").GetText(), Is.EqualTo("Raised"));
+ }
+
+ [Test , Order(16)]
+ [Category(UITestCategories.RefreshView)]
+
+ public void RefreshView_Disabled_DoesNotRaiseRefreshingEvent()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("IsEnabledFalseButton");
+ App.Tap("IsEnabledFalseButton");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.WaitForElement("RefreshView");
+
+ App.ScrollUp("RefreshView");
+
+ Assert.That(App.FindElement("RefreshingEventLabel").GetText(), Is.EqualTo("Not Raised"));
+ }
+
+ [Test , Order(17)]
+ [Category(UITestCategories.RefreshView)]
+ public void RefreshView_CollectionViewInteraction_ThenPull_RaisesRefreshingEvent()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.WaitForElement("RefreshView");
+ App.WaitForElement("CollectionViewContentButton");
+
+ Assert.That(App.FindElement("RefreshingEventLabel").GetText(), Is.EqualTo("Not Raised"));
+
+ App.Tap("CollectionViewContentButton");
+
+ Assert.That( App.FindElement("RefreshingEventLabel").GetText(), Is.EqualTo("Not Raised"));
+
+ App.ScrollUp("RefreshView");
+
+ App.WaitForElement("RefreshingEventLabel");
+
+ Assert.That(App.FindElement("RefreshingEventLabel").GetText(), Is.EqualTo("Raised"));
+ }
+
+#endif
+
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/SearchBarMaterial3FeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/SearchBarMaterial3FeatureTests.cs
new file mode 100644
index 000000000000..4509c1716f5b
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/SearchBarMaterial3FeatureTests.cs
@@ -0,0 +1,498 @@
+#if ANDROID
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+// Material3 SearchBar uses TextInputLayout + TextInputEditText instead of SearchView.
+// These tests run only on Android where Material3 SearchBarHandler2 is used.
+public class SearchBarMaterial3FeatureTests : _GalleryUITest
+{
+ public override string GalleryPageName => "Search Bar Material3 Feature Matrix";
+
+ public SearchBarMaterial3FeatureTests(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ [Test, Order(1)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_InitialState_VerifyVisualState()
+ {
+ App.WaitForElement("SearchBar");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(2)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetCancelButtonAndTextColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("CancelButtonOrangeColor");
+ App.Tap("CancelButtonOrangeColor");
+ App.WaitForElement("TextColorRedButton");
+ App.Tap("TextColorRedButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "Search Text");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(3)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetFlowDirection_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FlowDirectionRTL");
+ App.Tap("FlowDirectionRTL");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "Search Text");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(4)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetFontAttributesAndFontFamily_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontAttributesBoldButton");
+ App.Tap("FontAttributesBoldButton");
+ App.WaitForElement("FontFamilyDokdoButton");
+ App.Tap("FontFamilyDokdoButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "Search Text");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(5)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetFontAttributesAndFontSize_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontAttributesItalicButton");
+ App.Tap("FontAttributesItalicButton");
+ App.WaitForElement("FontSizeEntry");
+ App.ClearText("FontSizeEntry");
+ App.EnterText("FontSizeEntry", "20");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "Search Text");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(6)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetFontAttributesAndPlaceholderText_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontAttributesItalicButton");
+ App.Tap("FontAttributesItalicButton");
+ App.WaitForElement("PlaceholderEntry");
+ App.EnterText("PlaceholderEntry", "Placeholder Text");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(7)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetFontAttributesAndText_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontAttributesItalicButton");
+ App.Tap("FontAttributesItalicButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "Search Text");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(8)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetFontAttributesAndTextTransform_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontAttributesBoldButton");
+ App.Tap("FontAttributesBoldButton");
+ App.WaitForElement("TextTransformUppercaseButton");
+ App.Tap("TextTransformUppercaseButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "Search Text");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(9)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetFontFamilyAndFontSize_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontFamilyDokdoButton");
+ App.Tap("FontFamilyDokdoButton");
+ App.WaitForElement("FontSizeEntry");
+ App.ClearText("FontSizeEntry");
+ App.EnterText("FontSizeEntry", "20");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "Search Text");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(10)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetFontFamilyAndPlaceholder_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontFamilyDokdoButton");
+ App.Tap("FontFamilyDokdoButton");
+ App.WaitForElement("PlaceholderEntry");
+ App.EnterText("PlaceholderEntry", "Placeholder Text");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(11)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetFontFamilyAndText_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontFamilyDokdoButton");
+ App.Tap("FontFamilyDokdoButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "Search Text");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(12)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetFontSizeAndPlaceholder_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontSizeEntry");
+ App.ClearText("FontSizeEntry");
+ App.EnterText("FontSizeEntry", "20");
+ App.WaitForElement("PlaceholderEntry");
+ App.EnterText("PlaceholderEntry", "Placeholder Text");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(13)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetFontSizeAndText_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontSizeEntry");
+ App.ClearText("FontSizeEntry");
+ App.EnterText("FontSizeEntry", "20");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "Search Text");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(14)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetHorizontalTextAlignmentAndPlaceholder_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("HorizontalTextAlignmentCenterButton");
+ App.Tap("HorizontalTextAlignmentCenterButton");
+ App.WaitForElement("PlaceholderEntry");
+ App.EnterText("PlaceholderEntry", "Placeholder Text");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(15)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetHorizontalTextAlignmentAndText_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("HorizontalTextAlignmentEndButton");
+ App.Tap("HorizontalTextAlignmentEndButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "Search Text");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(16)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetHorizontalAndVerticalTextAlignment_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("HorizontalTextAlignmentCenterButton");
+ App.Tap("HorizontalTextAlignmentCenterButton");
+ App.WaitForElement("VerticalTextAlignmentEndButton");
+ App.Tap("VerticalTextAlignmentEndButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "Search Text");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+#if TEST_FAILS_ON_ANDROID // Issue Link - https://github.com/dotnet/maui/issues/14566
+ [Test, Order(17)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetIsEnabledFalse_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsEnabledFalseButton");
+ App.Tap("IsEnabledFalseButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+#if TEST_FAILS_ON_ANDROID // Issue Link - https://github.com/dotnet/maui/issues/29547
+ [Tst, Order(18)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetIsReadOnlyAndText_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsReadOnlyTrueButton");
+ App.Tap("IsReadOnlyTrueButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+#endif
+
+#if TEST_FAILS_ON_ANDROID // Issue Link - https://github.com/dotnet/maui/issues/29833
+ [Test, Order(19)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetIsSpellCheckEnabledAndText_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsSpellCheckEnabledFalseButton");
+ App.Tap("IsSpellCheckEnabledFalseButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "Ths is a spleling eror");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ [Test, Order(20)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetIsTextPredictionEnabledAndText_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("IsTextPredictionEnabledFalseButton");
+ App.Tap("IsTextPredictionEnabledFalseButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "t");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+#if TEST_FAILS_ON_ANDROID // Issue Link - https://github.com/dotnet/maui/issues/26968
+ [Test, Order(21)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetKeyboardAndText_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("KeyboardNumericButton");
+ App.Tap("KeyboardNumericButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.Tap("SearchBar");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+ [Test, Order(22)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetPlaceholderAndCharacterSpacing_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("PlaceholderEntry");
+ App.EnterText("PlaceholderEntry", "Placeholder Text");
+ App.WaitForElement("CharacterSpacingEntry");
+ App.ClearText("CharacterSpacingEntry");
+ App.EnterText("CharacterSpacingEntry", "10");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(23)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetPlaceholderAndPlaceholderColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("PlaceholderEntry");
+ App.EnterText("PlaceholderEntry", "Placeholder Text");
+ App.WaitForElement("PlaceholderColorRedButton");
+ App.Tap("PlaceholderColorRedButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(24)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetPlaceholderAndVerticalTextAlignment_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("PlaceholderEntry");
+ App.EnterText("PlaceholderEntry", "Placeholder Text");
+ App.WaitForElement("VerticalTextAlignmentEndButton");
+ App.Tap("VerticalTextAlignmentEndButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(25)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetTextAndVerticalTextAlignment_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("VerticalTextAlignmentStartButton");
+ App.Tap("VerticalTextAlignmentStartButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "Search Text");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(26)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetPlaceholderColorAndTextColor_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TextColorGreenButton");
+ App.Tap("TextColorGreenButton");
+ App.WaitForElement("PlaceholderEntry");
+ App.EnterText("PlaceholderEntry", "Placeholder Text");
+ App.WaitForElement("PlaceholderColorRedButton");
+ App.Tap("PlaceholderColorRedButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(27)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetShadow_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ShadowTrueButton");
+ App.Tap("ShadowTrueButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "Shadow Test");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(28)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetTextAndCharacterSpacing_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("CharacterSpacingEntry");
+ App.ClearText("CharacterSpacingEntry");
+ App.EnterText("CharacterSpacingEntry", "10");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "SearchText");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test, Order(29)]
+ [Category(UITestCategories.Material3)]
+ public void SearchBar_Material3_SetFontAutoScalingFalse_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontAutoScalingFalseRadioButton");
+ App.Tap("FontAutoScalingFalseRadioButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", "Search Text");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ShellNavigationFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ShellNavigationFeatureTests.cs
new file mode 100644
index 000000000000..c866522a3ad2
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ShellNavigationFeatureTests.cs
@@ -0,0 +1,939 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+[Category(UITestCategories.Shell)]
+public class ShellNavigationFeatureTests : _GalleryUITest
+{
+ public const string ShellFeatureMatrix = "Shell Feature Matrix";
+ public override string GalleryPageName => ShellFeatureMatrix;
+
+ public ShellNavigationFeatureTests(TestDevice device) : base(device) { }
+
+ void NavigateToPage2()
+ {
+ App.TapShellFlyoutIcon();
+ App.WaitForElement("Page2");
+ App.Tap("Page2");
+ }
+
+ void NavigateToPage3()
+ {
+ App.TapShellFlyoutIcon();
+ App.WaitForElement("Page3");
+ App.Tap("Page3");
+ }
+
+ // GoToMainButton shares the same AutomationId on all sub-pages — safe because
+ // Shell renders only the active page, so only one instance is in the view tree.
+ void GoBackToMain()
+ {
+ App.WaitForElement("GoToMainButton");
+ App.Tap("GoToMainButton");
+ App.WaitForElement("MainPageIdentityLabel");
+ }
+
+ void NavigateToDetail1AndWait()
+ {
+ App.WaitForElement("NavigateToDetail1Button");
+ App.Tap("NavigateToDetail1Button");
+ App.WaitForElement("Detail1PageIdentityLabel");
+ }
+
+ void NavigateToDetail2AndWait()
+ {
+ App.WaitForElement("NavigateToDetail2Button");
+ App.Tap("NavigateToDetail2Button");
+ App.WaitForElement("Detail2PageIdentityLabel");
+ }
+
+ void NavigateToQuerySenderAndWait()
+ {
+ App.WaitForElement("MainPageIdentityLabel");
+ App.Tap("OpenPassDataDemoButton");
+ App.WaitForElement("QuerySenderPageIdentityLabel");
+ }
+
+ bool iOS26OrHigher => App is AppiumIOSApp iosApp && HelperExtensions.IsIOS26OrHigher(iosApp);
+
+ void TapShellBackArrow(string parentPageTitle)
+ {
+#if ANDROID || WINDOWS
+ App.TapBackArrow();
+#elif IOS || MACCATALYST
+ if (iOS26OrHigher)
+ App.TapBackArrow();
+ else
+ App.TapBackArrow(parentPageTitle);
+#endif
+ }
+
+ // On iOS 26+, the back button accessibility ID changed from the custom text to the static "BackButton".
+ // All other platforms continue to work with the custom identifier.
+ void TapCustomLabelBackArrow(string customLabel)
+ {
+ if (iOS26OrHigher)
+ App.TapBackArrow();
+ else
+ App.TapBackArrow(customLabel);
+ }
+
+ void TapContent1()
+ {
+#if WINDOWS
+ App.TapTab("Content");
+ App.WaitForElement("Content1");
+ App.Tap("Content1");
+#else
+ App.TapTab("Content1");
+#endif
+ }
+
+ void TapContent2()
+ {
+#if WINDOWS
+ App.TapTab("Content");
+ App.WaitForElement("Content2");
+ App.Tap("Content2");
+#else
+ App.TapTab("Content2");
+#endif
+ }
+
+ // ── Shell Properties ─────────────────────────────────────────────────────
+
+ // Shell.CurrentState, CurrentPage, CurrentItem, and Shell.Current reflect the initial Main tab.
+ [Test, Order(1)]
+ public void VerifyShellProperties_InitialState()
+ {
+ App.WaitForElement("ShellNavigationButton");
+ App.Tap("ShellNavigationButton");
+ App.WaitForElement("MainPageIdentityLabel");
+ Assert.That(App.FindElement("CurrentStateLabel").GetText(), Does.Contain("main"));
+ Assert.That(App.FindElement("CurrentPageLabel").GetText(), Is.EqualTo("ShellNavigation"));
+ Assert.That(App.FindElement("CurrentItemLabel").GetText(), Is.EqualTo("Main"));
+ Assert.That(App.FindElement("ShellCurrentLabel").GetText(), Is.EqualTo("ShellNavigationControlPage"));
+ }
+
+ // Shell properties update when switching flyout items (Main → Page2).
+ [Test, Order(2)]
+ public void VerifyShellProperties_FlyoutSwitch()
+ {
+ NavigateToPage2();
+ App.WaitForElement("Page2ContentA1PageLabel");
+ Assert.That(App.FindElement("Page2CurrentStateLabel").GetText(), Does.Contain("page2"));
+ Assert.That(App.FindElement("Page2CurrentPageLabel").GetText(), Is.EqualTo("Page2TabAContentA1"));
+ Assert.That(App.FindElement("Page2CurrentItemLabel").GetText(), Is.EqualTo("Page2"));
+ Assert.That(App.FindElement("Page2ShellCurrentLabel").GetText(), Is.EqualTo("ShellNavigationControlPage"));
+ GoBackToMain();
+ }
+
+ // Shell.CurrentPage updates when switching ShellContent tabs (Content1 → Content2) in Page3.
+ [Test, Order(3)]
+ public void VerifyShellProperties_ContentSwitch()
+ {
+ NavigateToPage3();
+ App.WaitForElement("Page3C1PageLabel");
+ TapContent2();
+ App.WaitForElement("Page3C2PageLabel");
+ Assert.That(App.FindElement("Page3C2CurrentStateLabel").GetText(), Does.Contain("Content2"));
+ Assert.That(App.FindElement("Page3C2CurrentPageLabel").GetText(), Is.EqualTo("Page3Content2"));
+ Assert.That(App.FindElement("Page3C2CurrentItemLabel").GetText(), Is.EqualTo("Page3"));
+ Assert.That(App.FindElement("Page3C2ShellCurrentLabel").GetText(), Is.EqualTo("ShellNavigationControlPage"));
+ TapContent1();
+ GoBackToMain();
+ }
+
+ // Shell.CurrentPage updates when switching ShellSection tabs (TabA → TabB).
+ [Test, Order(4)]
+ public void VerifyShellProperties_TabSwitch()
+ {
+ NavigateToPage2();
+ App.WaitForElement("TabB");
+ App.Tap("TabB");
+ App.WaitForElement("Page2TabBPageLabel");
+ Assert.That(App.FindElement("Page2TabBCurrentStateLabel").GetText(), Does.Contain("page2"));
+ Assert.That(App.FindElement("Page2TabBCurrentPageLabel").GetText(), Is.EqualTo("Page2TabBContentB1"));
+ Assert.That(App.FindElement("Page2TabBCurrentItemLabel").GetText(), Is.EqualTo("Page2"));
+ Assert.That(App.FindElement("Page2TabBShellCurrentLabel").GetText(), Is.EqualTo("ShellNavigationControlPage"));
+ App.WaitForElement("TabA");
+ App.Tap("TabA");
+ GoBackToMain();
+ }
+
+ // ── Back Button ───────────────────────────────────────────────────────────
+
+ // Tapping the back arrow with no BackButtonBehavior command pops the page.
+ [Test, Order(5)]
+ public void BackButton_TapBackArrow_NoCommand_NavigatesBack()
+ {
+ NavigateToDetail1AndWait();
+ TapShellBackArrow("ShellNavigation");
+ App.WaitForElement("MainPageIdentityLabel");
+ }
+
+ // ── Route Navigation ──────────────────────────────────────────────────────
+
+ // Relative route "navtest1" pushes NavTest1 onto the stack from Detail1.
+ [Test, Order(6)]
+ public void RelativeRoute_Detail1ToNavTest1()
+ {
+ App.WaitForElement("MainPageIdentityLabel");
+ NavigateToDetail1AndWait();
+ App.WaitForElement("Detail1RelativeButton");
+ App.Tap("Detail1RelativeButton");
+ App.WaitForElement("NavTest1PageIdentityLabel");
+ }
+
+ // Forward push: NavTest1 → NavTest2 via relative route.
+ [Test, Order(7)]
+ public void ForwardNavigation_NavTest1ToNavTest2()
+ {
+ App.WaitForElement("NavTest1ToNavTest2Button");
+ App.Tap("NavTest1ToNavTest2Button");
+ App.WaitForElement("NavTest2PageIdentityLabel");
+ }
+
+ // "../navtest3" pops NavTest2 then pushes NavTest3 in a single GoToAsync call.
+ [Test, Order(8)]
+ public void BackAndForwardNavigation_NavTest2ToNavTest3()
+ {
+ App.WaitForElement("NavTest2BackForwardButton");
+ App.Tap("NavTest2BackForwardButton");
+ App.WaitForElement("NavTest3PageIdentityLabel");
+ }
+
+ // "../.." pops two levels at once, landing on Detail1.
+ [Test, Order(9)]
+ public void DoubleBackNavigation_NavTest3ToDetail1()
+ {
+ App.WaitForElement("NavTest3MultiBackButton");
+ App.Tap("NavTest3MultiBackButton");
+ App.WaitForElement("Detail1PageIdentityLabel");
+ }
+
+ // Absolute route "//page2" jumps directly across flyout items from Detail1.
+ [Test, Order(10)]
+ public void AbsoluteRoute_Detail1ToPage2()
+ {
+ App.WaitForElement("Detail1AbsoluteButton");
+ App.Tap("Detail1AbsoluteButton");
+ App.WaitForElement("Page2ContentA1PageLabel");
+ }
+
+ // ── Contextual Routes ─────────────────────────────────────────────────────
+
+ // "subdetail" resolves relative to the page that pushed it (Detail1 context).
+ [Test, Order(11)]
+ public void ContextualRoute_FromDetail1_SubDetailShowsDetail1Context()
+ {
+ App.WaitForElement("GoToMainButton");
+ App.Tap("GoToMainButton");
+ App.WaitForElement("MainPageIdentityLabel");
+ NavigateToDetail1AndWait();
+ App.WaitForElement("Detail1ContextualNavButton");
+ App.Tap("Detail1ContextualNavButton");
+ App.WaitForElement("SubDetailPageIdentityLabel");
+ Assert.That(App.FindElement("SubDetailCurrentRouteLabel").GetText(), Does.Contain("detail1"));
+ Assert.That(App.FindElement("SubDetailSourceContextLabel").GetText(), Is.EqualTo("Contextual from: detail1"));
+ App.WaitForElement("SubDetailGoBackButton");
+ App.Tap("SubDetailGoBackButton");
+ App.WaitForElement("Detail1GoBackButton");
+ App.Tap("Detail1GoBackButton");
+ App.WaitForElement("MainPageIdentityLabel");
+ NavigateToDetail2AndWait();
+ }
+
+ // "subdetail" resolves relative to the page that pushed it (Detail2 context).
+ [Test, Order(12)]
+ public void ContextualRoute_FromDetail2_SubDetailShowsDetail2Context()
+ {
+ App.WaitForElement("Detail2ContextualNavButton");
+ App.Tap("Detail2ContextualNavButton");
+ App.WaitForElement("SubDetailPageIdentityLabel");
+ Assert.That(App.FindElement("SubDetailCurrentRouteLabel").GetText(), Does.Contain("detail2"));
+ Assert.That(App.FindElement("SubDetailSourceContextLabel").GetText(), Is.EqualTo("Contextual from: detail2"));
+ App.WaitForElement("SubDetailGoBackButton");
+ App.Tap("SubDetailGoBackButton");
+ App.WaitForElement("Detail2GoBackButton");
+ App.Tap("Detail2GoBackButton");
+ App.WaitForElement("MainPageIdentityLabel");
+ }
+
+ // ── Route Registration ────────────────────────────────────────────────────
+
+ // Unregistering a route prevents navigation to it.
+ [Test, Order(13)]
+ public void RouteRegistration_UnregisterRoute_NavigationFails()
+ {
+ App.WaitForElement("ToggleRouteButton");
+ App.Tap("ToggleRouteButton");
+ App.WaitForElement("RouteStatusLabel");
+ Assert.That(App.FindElement("RouteStatusLabel").GetText(), Is.EqualTo("Unregistered"));
+ }
+
+ // Re-registering a route restores navigation to it.
+ [Test, Order(14)]
+ public void RouteRegistration_ReRegisterRoute_NavigationSucceeds()
+ {
+ App.WaitForElement("ToggleRouteButton");
+ App.Tap("ToggleRouteButton");
+ App.WaitForElement("Detail2PageIdentityLabel");
+ App.WaitForElement("Detail2GoBackButton");
+ App.Tap("Detail2GoBackButton");
+ App.WaitForElement("RouteStatusLabel");
+ Assert.That(App.FindElement("RouteStatusLabel").GetText(), Is.EqualTo("Registered"));
+ Assert.That(App.FindElement("ToggleRouteButton").GetText(), Is.EqualTo("Unregister Route"));
+ }
+
+ // ── Cancel Navigation ─────────────────────────────────────────────────────
+
+ // ShellNavigatingEventArgs.Cancel() blocks the navigation; disabling it resumes normal flow.
+ [Test, Order(15)]
+ public void CancelNavigation_NavigationIsBlocked_StaysOnMain()
+ {
+ App.WaitForElement("MainPageIdentityLabel");
+ App.WaitForElement("CancelNavigationButton");
+ App.Tap("CancelNavigationButton");
+ Assert.That(App.FindElement("CancelNavigationButton").GetText(), Is.EqualTo("CancelNav: True"));
+ App.WaitForElement("NavigateToDetail1Button");
+ App.Tap("NavigateToDetail1Button");
+ App.WaitForElement("MainPageIdentityLabel");
+ Assert.That(App.FindElement("NavigatingCancelledLabel").GetText(), Is.EqualTo("True"));
+ App.Tap("CancelNavigationButton");
+ Assert.That(App.FindElement("CancelNavigationButton").GetText(), Is.EqualTo("CancelNav: False"));
+ App.WaitForElement("NavigateToDetail1Button");
+ App.Tap("NavigateToDetail1Button");
+ App.WaitForElement("Detail1PageIdentityLabel");
+ App.WaitForElement("Detail1GoBackButton");
+ App.Tap("Detail1GoBackButton");
+ App.WaitForElement("MainPageIdentityLabel");
+ }
+
+ // ── Deferral Navigation ───────────────────────────────────────────────────
+
+ // Deferral delays navigation completion; the page still loads once the deferral is resolved.
+ [Test, Order(16)]
+ public void DeferralNavigation_NavigationCompletes_AfterDelay()
+ {
+ App.WaitForElement("MainPageIdentityLabel");
+ App.WaitForElement("EnableDeferralButton");
+ App.Tap("EnableDeferralButton");
+ Assert.That(App.FindElement("EnableDeferralButton").GetText(), Is.EqualTo("Deferral: True"));
+ App.WaitForElement("NavigateToDetail1Button");
+ App.Tap("NavigateToDetail1Button");
+ App.WaitForElement("Detail1PageIdentityLabel", timeout: TimeSpan.FromSeconds(10));
+ App.WaitForElement("Detail1GoBackButton");
+ App.Tap("Detail1GoBackButton");
+ App.WaitForElement("MainPageIdentityLabel");
+ Assert.That(App.FindElement("DeferralStatusLabel").GetText(), Is.EqualTo("Deferral completed"));
+ App.Tap("EnableDeferralButton");
+ Assert.That(App.FindElement("EnableDeferralButton").GetText(), Is.EqualTo("Deferral: False"));
+ }
+
+ // ── Tab Navigation Stack ──────────────────────────────────────────────────
+
+ // Pushing OptionsPage adds it to the stack; count goes from 1 to 2.
+ [Test, Order(17)]
+ public void TabStack_OnPushAsync_EnterOptionsPage_TabStackCountIsTwo()
+ {
+ App.WaitForElement("MainPageIdentityLabel");
+ App.Tap("Apply");
+ App.WaitForElement("OptionsPageIdentityLabel");
+
+ App.WaitForElement("OptionsTabStackLabel");
+ Assert.That(App.FindElement("OptionsTabStackLabel").GetText(),
+ Is.EqualTo("Count=2: ShellNavigation, OptionsPage"));
+ Assert.That(App.FindElement("OptionsGetNavStackLabel").GetText(),
+ Is.EqualTo("Count=2: ShellNavigation, OptionsPage"));
+ Assert.That(App.FindElement("OptionsCurrentPageLabel").GetText(),
+ Is.EqualTo("OptionsPage"));
+ }
+
+ // Pushing SubPage1 from OptionsPage grows the stack to count 3.
+ [Test, Order(18)]
+ public void TabStack_OnPushAsync_SubPage1_TabStackCountIsThree()
+ {
+ App.WaitForElement("OptionsPageIdentityLabel");
+ App.Tap("OptionsPushButton");
+ App.WaitForElement("OptionsSubPage1IdentityLabel");
+
+ Assert.That(App.FindElement("SubPage1TabStackLabel").GetText(),
+ Is.EqualTo("Count=3: ShellNavigation, OptionsPage, SubPage1"));
+ Assert.That(App.FindElement("SubPage1NavStackLabel").GetText(),
+ Is.EqualTo("Count=3: ShellNavigation, OptionsPage, SubPage1"));
+ }
+
+ // Pushing SubPage2 from SubPage1 grows the stack to count 4.
+ [Test, Order(19)]
+ public void TabStack_OnPushAsync_SubPage2_TabStackCountIsFour()
+ {
+ App.WaitForElement("OptionsSubPage1IdentityLabel");
+ App.Tap("SubPushDeeperButton");
+ App.WaitForElement("OptionsSubPage2IdentityLabel");
+
+ Assert.That(App.FindElement("SubPage2TabStackLabel").GetText(),
+ Is.EqualTo("Count=4: ShellNavigation, OptionsPage, SubPage1, SubPage2"));
+ Assert.That(App.FindElement("SubPage2NavStackLabel").GetText(),
+ Is.EqualTo("Count=4: ShellNavigation, OptionsPage, SubPage1, SubPage2"));
+ }
+
+ // Popping SubPage2 shrinks the stack back to count 3.
+ [Test, Order(20)]
+ public void TabStack_OnPopAsync_SubPage2_TabStackCountIsThree()
+ {
+ App.WaitForElement("OptionsSubPage2IdentityLabel");
+ App.Tap("SubPopButton");
+ App.WaitForElement("OptionsSubPage1IdentityLabel");
+
+ Assert.That(App.FindElement("SubPage1TabStackLabel").GetText(),
+ Is.EqualTo("Count=3: ShellNavigation, OptionsPage, SubPage1"));
+ Assert.That(App.FindElement("SubPage1NavStackLabel").GetText(),
+ Is.EqualTo("Count=3: ShellNavigation, OptionsPage, SubPage1"));
+ }
+
+ // PopToRoot clears the entire push stack and returns to the root (Main).
+ [Test, Order(21)]
+ public void TabStack_OnPopToRootAsync_ReturnsToMainPage()
+ {
+ App.WaitForElement("OptionsSubPage1IdentityLabel");
+ App.Tap("SubPopToRootButton");
+ App.WaitForElement("MainPageIdentityLabel");
+ }
+
+ // InsertPageBefore inserts below the current page; we stay on OptionsPage (count = 3).
+ [Test, Order(22)]
+ public void TabStack_OnInsertPageBefore_InsertsPageBelowCurrentPage()
+ {
+ App.WaitForElement("MainPageIdentityLabel");
+ App.Tap("Apply");
+ App.WaitForElement("OptionsPageIdentityLabel");
+
+ App.Tap("OptionsInsertButton");
+ App.WaitForElement("OptionsPageIdentityLabel");
+
+ Assert.That(App.FindElement("OptionsTabStackLabel").GetText(),
+ Is.EqualTo("Count=3: ShellNavigation, InsertedPage1, OptionsPage"));
+ Assert.That(App.FindElement("OptionsGetNavStackLabel").GetText(),
+ Is.EqualTo("Count=3: ShellNavigation, InsertedPage1, OptionsPage"));
+ }
+
+ // RemovePage removes the inserted page, restoring the stack to count 2.
+ [Test, Order(23)]
+ public void TabStack_OnRemovePage_RemovesInsertedPage_CountBackToTwo()
+ {
+ App.WaitForElement("OptionsPageIdentityLabel");
+ App.Tap("OptionsRemoveButton");
+ App.WaitForElement("OptionsPageIdentityLabel");
+
+ Assert.That(App.FindElement("OptionsTabStackLabel").GetText(),
+ Is.EqualTo("Count=2: ShellNavigation, OptionsPage"));
+ Assert.That(App.FindElement("OptionsGetNavStackLabel").GetText(),
+ Is.EqualTo("Count=2: ShellNavigation, OptionsPage"));
+ }
+
+ // RemovePage is a no-op when there is nothing extra to remove.
+ [Test, Order(24)]
+ public void TabStack_OnRemovePage_NoOp_WhenNothingToRemove()
+ {
+ App.WaitForElement("OptionsPageIdentityLabel");
+ App.Tap("OptionsRemoveButton");
+ App.WaitForElement("OptionsPageIdentityLabel");
+
+ Assert.That(App.FindElement("OptionsTabStackLabel").GetText(),
+ Is.EqualTo("Count=2: ShellNavigation, OptionsPage"));
+ }
+
+ // Tab.Stack and INavigation.NavigationStack are identical after mixed insert/push operations.
+ [Test, Order(25)]
+ public void TabStack_GetNavigationStack_TabStackMatchesNavigationStack()
+ {
+ App.WaitForElement("OptionsPageIdentityLabel");
+ App.Tap("OptionsInsertButton");
+ App.WaitForElement("OptionsPushButton");
+ App.Tap("OptionsPushButton");
+ App.WaitForElement("OptionsSubPage1IdentityLabel");
+
+ // Stack: ShellNavigation, InsertedPage, OptionsPage, SubPage
+ var tabText = App.FindElement("SubPage1TabStackLabel").GetText();
+ var navText = App.FindElement("SubPage1NavStackLabel").GetText();
+ Assert.That(tabText, Is.EqualTo("Count=4: ShellNavigation, InsertedPage1, OptionsPage, SubPage1"));
+ Assert.That(navText, Is.EqualTo(tabText));
+
+ App.Tap("SubPopButton");
+ App.WaitForElement("OptionsPageIdentityLabel");
+ App.Tap("OptionsRemoveButton");
+ Assert.That(App.FindElement("OptionsTabStackLabel").GetText(),
+ Is.EqualTo("Count=2: ShellNavigation, OptionsPage"));
+ TapShellBackArrow("ShellNavigation");
+ App.WaitForElement("MainPageIdentityLabel");
+ }
+
+ // ── Navigation Events ─────────────────────────────────────────────────────
+
+ // Navigating event fires with Source=Pop; Current shows the page being left.
+ [Test, Order(26)]
+ public void NavEvents_Pop_NavigatingEvent_SourceIsPopAndCurrentIsPreviousPage()
+ {
+ App.WaitForElement("MainPageIdentityLabel");
+ App.Tap("Apply");
+ App.WaitForElement("OptionsPageIdentityLabel");
+ App.Tap("OptionsPushButton");
+ App.WaitForElement("OptionsSubPage1IdentityLabel");
+ App.Tap("SubPopButton");
+ App.WaitForElement("OptionsPageIdentityLabel");
+
+ Assert.That(App.FindElement("OptionsNavigatingCurrentLabel").GetText(),
+ Is.EqualTo("SubPage1"));
+ Assert.That(App.FindElement("OptionsNavigatingSourceLabel").GetText(),
+ Is.EqualTo("Pop"));
+ Assert.That(App.FindElement("OptionsNavigatingCanCancelLabel").GetText(),
+ Is.EqualTo("True"));
+ Assert.That(App.FindElement("OptionsNavigatingCancelledLabel").GetText(),
+ Is.EqualTo("False"));
+ }
+
+ // Navigated event fires with Source=Pop; Current is the landed page, Previous is the popped page.
+ [Test, Order(27)]
+ public void NavEvents_Pop_NavigatedEvent_CurrentIsLandedPagePreviousIsPopped()
+ {
+ App.WaitForElement("OptionsPageIdentityLabel");
+
+ Assert.That(App.FindElement("OptionsNavigatedCurrentLabel").GetText(),
+ Is.EqualTo("OptionsPage"));
+ Assert.That(App.FindElement("OptionsNavigatedPreviousLabel").GetText(),
+ Is.EqualTo("SubPage1"));
+ Assert.That(App.FindElement("OptionsNavigatedSourceLabel").GetText(),
+ Is.EqualTo("Pop"));
+ }
+
+ // Navigating event fires with Source=Push; Current shows the page being pushed from.
+ [Test, Order(28)]
+ public void NavEvents_Push_NavigatingEvent_SourceIsPushAndCurrentIsPushedFromPage()
+ {
+ App.WaitForElement("OptionsPageIdentityLabel");
+ App.Tap("OptionsPushButton");
+ App.WaitForElement("OptionsSubPage1IdentityLabel");
+
+ Assert.That(App.FindElement("SubNavigatingCurrentLabel").GetText(),
+ Is.EqualTo("OptionsPage"));
+ Assert.That(App.FindElement("SubNavigatingSourceLabel").GetText(),
+ Is.EqualTo("Push"));
+ Assert.That(App.FindElement("SubNavigatingCanCancelLabel").GetText(),
+ Is.EqualTo("True"));
+ Assert.That(App.FindElement("SubNavigatingCancelledLabel").GetText(),
+ Is.EqualTo("False"));
+ }
+
+ // Navigated event fires with Source=Push; Current is the new page, Previous is the source page.
+ [Test, Order(29)]
+ public void NavEvents_Push_NavigatedEvent_CurrentIsNewPagePreviousIsSourcePage()
+ {
+ App.WaitForElement("OptionsSubPage1IdentityLabel");
+
+ Assert.That(App.FindElement("SubNavigatedCurrentLabel").GetText(),
+ Is.EqualTo("SubPage1"));
+ Assert.That(App.FindElement("SubNavigatedPreviousLabel").GetText(),
+ Is.EqualTo("OptionsPage"));
+ Assert.That(App.FindElement("SubNavigatedSourceLabel").GetText(),
+ Is.EqualTo("Push"));
+ }
+
+ // Navigating event fires with Source=ShellItemChanged when switching flyout items.
+ [Test, Order(30)]
+ public void NavEvents_ShellItemChanged_NavigatingEvent_SourceIsShellItemChanged()
+ {
+ App.WaitForElement("OptionsSubPage1IdentityLabel");
+ App.Tap("SubPopButton");
+ App.WaitForElement("OptionsPageIdentityLabel");
+ TapShellBackArrow("ShellNavigation");
+ App.WaitForElement("MainPageIdentityLabel");
+
+ NavigateToPage2();
+ App.WaitForElement("Page2ContentA1PageLabel");
+
+ Assert.That(App.FindElement("Page2NavigatingCurrentLabel").GetText(),
+ Is.EqualTo("ShellNavigation"));
+ Assert.That(App.FindElement("Page2NavigatingSourceLabel").GetText(),
+ Is.EqualTo("ShellItemChanged"));
+ Assert.That(App.FindElement("Page2NavigatingTargetLabel").GetText(),
+ Does.Contain("page2"));
+ }
+
+ // Navigated event fires with Source=ShellItemChanged; Current is the new item's page.
+ [Test, Order(31)]
+ public void NavEvents_ShellItemChanged_NavigatedEvent_CurrentIsPage2PreviousIsMain()
+ {
+ App.WaitForElement("Page2ContentA1PageLabel");
+
+ Assert.That(App.FindElement("Page2NavigatedCurrentLabel").GetText(),
+ Is.EqualTo("Page2TabAContentA1"));
+ Assert.That(App.FindElement("Page2NavigatedPreviousLabel").GetText(),
+ Is.EqualTo("ShellNavigation"));
+ Assert.That(App.FindElement("Page2NavigatedSourceLabel").GetText(),
+ Is.EqualTo("ShellItemChanged"));
+ }
+
+ // GoToAsync source is determined by structural path change, not by the API called.
+ // "//main/MainContent" from Page2 crosses a ShellItem boundary → Source=ShellItemChanged.
+ [Test, Order(32)]
+ public void NavEvents_GoToAsyncAbsoluteRoute_NavigatingEvent_SourceIsShellItemChanged()
+ {
+ App.WaitForElement("Page2ContentA1PageLabel");
+ App.Tap("GoToMainButton");
+ App.WaitForElement("MainPageIdentityLabel");
+
+ Assert.That(App.FindElement("OverrideNavigatingLabel").GetText(),
+ Does.Contain("Source=ShellItemChanged"));
+ }
+
+ // Shell.Navigated also reports ShellItemChanged for the same GoToAsync absolute route.
+ [Test, Order(33)]
+ public void NavEvents_GoToAsyncAbsoluteRoute_NavigatedEvent_SourceIsShellItemChanged()
+ {
+ App.WaitForElement("MainPageIdentityLabel");
+
+ Assert.That(App.FindElement("OverrideNavigatedLabel").GetText(),
+ Does.Contain("Source=ShellItemChanged"));
+ }
+
+#if TEST_FAILS_ON_WINDOWS // Issue Link: https://github.com/dotnet/maui/issues/34318
+ // Navigating event fires with Source=ShellContentChanged when switching ShellContent tabs.
+ [Test, Order(34)]
+ public void NavEvents_ShellContentChanged_NavigatingEvent_SourceIsShellContentChanged()
+ {
+ App.WaitForElement("MainPageIdentityLabel");
+ NavigateToPage3();
+ App.WaitForElement("Page3C1PageLabel");
+
+ TapContent2();
+ App.WaitForElement("Page3C2PageLabel");
+
+ Assert.That(App.FindElement("Page3C2NavigatingCurrentLabel").GetText(),
+ Is.EqualTo("Page3Content1"));
+ Assert.That(App.FindElement("Page3C2NavigatingSourceLabel").GetText(),
+ Is.EqualTo("ShellContentChanged"));
+ Assert.That(App.FindElement("Page3C2NavigatingTargetLabel").GetText(),
+ Does.Contain("Content2"));
+ }
+
+ // Navigated event confirms the content switch with correct Current and Previous pages.
+ [Test, Order(35)]
+ public void NavEvents_ShellContentChanged_NavigatedEvent_CurrentIsContent2PreviousIsContent1()
+ {
+ App.WaitForElement("Page3C2PageLabel");
+
+ Assert.That(App.FindElement("Page3C2NavigatedCurrentLabel").GetText(),
+ Is.EqualTo("Page3Content2"));
+ Assert.That(App.FindElement("Page3C2NavigatedPreviousLabel").GetText(),
+ Is.EqualTo("Page3Content1"));
+ Assert.That(App.FindElement("Page3C2NavigatedSourceLabel").GetText(),
+ Is.EqualTo("ShellContentChanged"));
+ GoBackToMain();
+ }
+#endif
+
+ // Navigating event fires with Source=ShellSectionChanged when switching ShellSection tabs.
+ [Test, Order(36)]
+ public void NavEvents_ShellSectionChanged_NavigatingEvent_SourceIsShellSectionChanged()
+ {
+ NavigateToPage2();
+ App.WaitForElement("Page2ContentA1PageLabel");
+ App.WaitForElement("TabB");
+ App.Tap("TabB");
+ App.WaitForElement("Page2TabBPageLabel");
+
+ Assert.That(App.FindElement("Page2TabBNavigatingCurrentLabel").GetText(),
+ Is.EqualTo("Page2TabAContentA1"));
+ Assert.That(App.FindElement("Page2TabBNavigatingSourceLabel").GetText(),
+ Is.EqualTo("ShellSectionChanged"));
+ Assert.That(App.FindElement("Page2TabBNavigatingTargetLabel").GetText(),
+ Does.Contain("TabB"));
+ }
+
+ // Navigated event confirms the section switch with correct Current and Previous pages.
+ [Test, Order(37)]
+ public void NavEvents_ShellSectionChanged_NavigatedEvent_CurrentIsTabBPreviousIsTabA()
+ {
+ App.WaitForElement("Page2TabBPageLabel");
+
+ Assert.That(App.FindElement("Page2TabBNavigatedCurrentLabel").GetText(),
+ Is.EqualTo("Page2TabBContentB1"));
+ Assert.That(App.FindElement("Page2TabBNavigatedPreviousLabel").GetText(),
+ Is.EqualTo("Page2TabAContentA1"));
+ Assert.That(App.FindElement("Page2TabBNavigatedSourceLabel").GetText(),
+ Is.EqualTo("ShellSectionChanged"));
+
+ GoBackToMain();
+ }
+
+ // Navigating event fires with Source=PopToRoot when the entire push stack is cleared.
+ [Test, Order(38)]
+ public void NavEvents_PopToRoot_NavigatingEvent_SourceIsPopToRoot()
+ {
+ App.WaitForElement("MainPageIdentityLabel");
+ App.Tap("Apply");
+ App.WaitForElement("OptionsPageIdentityLabel");
+ App.Tap("OptionsPushButton");
+ App.WaitForElement("OptionsSubPage1IdentityLabel");
+ App.Tap("SubPopToRootButton");
+ App.WaitForElement("MainPageIdentityLabel");
+
+ Assert.That(App.FindElement("OverrideNavigatingLabel").GetText(),
+ Does.Contain("Source=PopToRoot"));
+ }
+
+ // Navigated event confirms PopToRoot landed on Main with Source=PopToRoot.
+ [Test, Order(39)]
+ public void NavEvents_PopToRoot_NavigatedEvent_CurrentIsMainSourceIsPopToRoot()
+ {
+ App.WaitForElement("MainPageIdentityLabel");
+
+ Assert.That(App.FindElement("OverrideNavigatedLabel").GetText(),
+ Does.Contain("Source=PopToRoot"));
+ }
+
+ // ── Pass Data ─────────────────────────────────────────────────────────────
+ // String query param (?name=...) is received by IQueryAttributable on the detail page.
+ [Test, Order(40)]
+ public void PassData_StringQueryParam_ReceivedByIQueryAttributable()
+ {
+ App.WaitForElement("MainPageIdentityLabel");
+ App.Tap("Reset");
+ NavigateToQuerySenderAndWait();
+
+ App.ClearText("QuerySendNameEntry");
+ App.EnterText("QuerySendNameEntry", "Alice");
+ App.Tap("QuerySendStringButton");
+ App.WaitForElement("QueryDataDetailPageIdentityLabel");
+
+ Assert.That(App.FindElement("QueryPropertyReceivedLabel").GetText(), Is.EqualTo("Alice"));
+ }
+
+ // Dictionary-based params are received by IQueryAttributable on the detail page.
+ [Test, Order(41)]
+ public void PassData_DictionaryParam_ReceivedByIQueryAttributable()
+ {
+ App.WaitForElement("QueryDataDetailPageIdentityLabel");
+ App.Tap("QueryDetailGoBackButton");
+ App.WaitForElement("QuerySenderPageIdentityLabel");
+
+ App.ClearText("QuerySendNameEntry");
+ App.EnterText("QuerySendNameEntry", "Bob");
+ App.Tap("QuerySendDictButton");
+ App.WaitForElement("QueryDataDetailPageIdentityLabel");
+
+ Assert.That(App.FindElement("IQueryAttributableReceivedLabel").GetText(), Is.EqualTo("Bob"));
+ }
+
+ // ShellNavigationQueryParameters (single-use) are received by IQueryAttributable.
+ [Test, Order(42)]
+ public void PassData_SingleUseParams_ReceivedByIQueryAttributable()
+ {
+ App.WaitForElement("QueryDataDetailPageIdentityLabel");
+ App.Tap("QueryDetailGoBackButton");
+ App.WaitForElement("QuerySenderPageIdentityLabel");
+
+ App.ClearText("QuerySendNameEntry");
+ App.EnterText("QuerySendNameEntry", "Carol");
+ App.Tap("QuerySendSingleUseButton");
+ App.WaitForElement("QueryDataDetailPageIdentityLabel");
+
+ Assert.That(App.FindElement("QueryPropertyReceivedLabel").GetText(), Is.EqualTo("Carol"));
+ }
+
+ // Both name and IQA labels receive the same param from string query (both set in ApplyQueryAttributes).
+ [Test, Order(43)]
+ public void PassData_StringQueryParam_BothLabelsReceiveSameValue()
+ {
+ App.WaitForElement("QueryDataDetailPageIdentityLabel");
+ App.Tap("QueryDetailGoBackButton");
+ App.WaitForElement("QuerySenderPageIdentityLabel");
+
+ App.ClearText("QuerySendNameEntry");
+ App.EnterText("QuerySendNameEntry", "Dave");
+ App.Tap("QuerySendStringButton");
+ App.WaitForElement("QueryDataDetailPageIdentityLabel");
+
+ Assert.That(App.FindElement("QueryPropertyReceivedLabel").GetText(), Is.EqualTo("Dave"));
+ Assert.That(App.FindElement("IQueryAttributableReceivedLabel").GetText(), Is.EqualTo("Dave"));
+ }
+
+ // Backwards navigation with data passes ?backvalue to the previous page via IQueryAttributable.
+ [Test, Order(44)]
+ public void PassData_BackwardsNavigation_PassesDataToPreviousPage()
+ {
+ App.WaitForElement("QueryDataDetailPageIdentityLabel");
+ App.Tap("QueryDetailGoBackWithDataButton");
+ App.WaitForElement("QuerySenderPageIdentityLabel");
+
+ Assert.That(App.FindElement("QueryBackValueLabel").GetText(), Is.EqualTo("ReturnedData"));
+
+ // cleanup
+ App.Tap("QuerySenderGoBackButton");
+ App.WaitForElement("MainPageIdentityLabel");
+ }
+
+
+ // Multiple string params (?name=&location=) are each received by IQueryAttributable.
+ [Test, Order(45)]
+ public void PassData_MultipleStringParams_ReceivedByIQueryAttributable()
+ {
+ // After test 45 cleanup we are already on MainPage
+ NavigateToQuerySenderAndWait();
+
+ App.ClearText("QuerySendNameEntry");
+ App.EnterText("QuerySendNameEntry", "Alice");
+ App.ClearText("QuerySendLocationEntry");
+ App.EnterText("QuerySendLocationEntry", "Savannah");
+ App.Tap("QuerySendMultiParamButton");
+ App.WaitForElement("QueryDataDetailPageIdentityLabel");
+
+ Assert.That(App.FindElement("QueryPropertyReceivedLabel").GetText(), Is.EqualTo("Alice"));
+ Assert.That(App.FindElement("QueryPropertyLocationLabel").GetText(), Is.EqualTo("Savannah"));
+ }
+
+ // IQueryAttributable receives decoded values for all data passing methods.
+ [Test, Order(46)]
+ public void PassData_IQueryAttributable_ReceivesDecodedValues()
+ {
+ App.WaitForElement("QueryDataDetailPageIdentityLabel");
+ App.Tap("QueryDetailGoBackButton");
+ App.WaitForElement("QuerySenderPageIdentityLabel");
+
+ App.ClearText("QuerySendNameEntry");
+ App.EnterText("QuerySendNameEntry", "Hello World");
+ App.Tap("QuerySendStringButton");
+ App.WaitForElement("QueryDataDetailPageIdentityLabel");
+
+ Assert.That(App.FindElement("QueryPropertyReceivedLabel").GetText(), Is.EqualTo("Hello World"));
+ Assert.That(App.FindElement("IQueryAttributableReceivedLabel").GetText(), Is.EqualTo("Hello World"));
+ }
+
+ // Dictionary data is retained in memory; navigating forward without data and back re-applies it.
+ [Test, Order(47)]
+ public void PassData_Dictionary_PersistsWhenNavigatingToIntermediatePageAndBack()
+ {
+ App.WaitForElement("QueryDataDetailPageIdentityLabel");
+ App.Tap("QueryDetailGoBackButton");
+ App.WaitForElement("QuerySenderPageIdentityLabel");
+
+ App.ClearText("QuerySendNameEntry");
+ App.EnterText("QuerySendNameEntry", "test");
+ App.Tap("QuerySendDictButton");
+ App.WaitForElement("QueryDataDetailPageIdentityLabel");
+
+ // First arrival: IQA called once (count tracks all ApplyQueryAttributes calls with "name")
+ Assert.That(App.FindElement("DictAppliedCountLabel").GetText(), Is.EqualTo("1"));
+
+ // Navigate to intermediate WITHOUT data — Dictionary data persists in memory
+ App.Tap("QueryDetailGoToIntermediateButton");
+ App.WaitForElement("QueryIntermediatePageIdentityLabel");
+ App.Tap("QueryIntermediateGoBackButton");
+ App.WaitForElement("QueryDataDetailPageIdentityLabel");
+
+ // IQA fired again on return — Dictionary was re-applied automatically
+ Assert.That(App.FindElement("DictAppliedCountLabel").GetText(), Is.EqualTo("2"));
+ Assert.That(App.FindElement("IQueryAttributableReceivedLabel").GetText(), Is.EqualTo("test"));
+
+ // cleanup
+ App.Tap("QueryDetailGoBackButton");
+ App.WaitForElement("QuerySenderPageIdentityLabel");
+ App.Tap("QuerySenderGoBackButton");
+ App.WaitForElement("MainPageIdentityLabel");
+ }
+
+ // ── BackButtonBehavior Properties ─────────────────────────────────────────
+#if TEST_FAILS_ON_WINDOWS // Issue Link: https://github.com/dotnet/maui/issues/1625
+ // BackButtonBehavior.Text replaces the back button label with a custom string.
+ [Test, Order(48)]
+ public void BackButtonBehavior_TextOverride_CustomTextShownOnBackButton()
+ {
+ App.WaitForElement("MainPageIdentityLabel");
+ App.WaitForElement("TextOverrideEntry");
+ App.ClearText("TextOverrideEntry");
+ App.EnterText("TextOverrideEntry", "Go");
+ NavigateToDetail1AndWait();
+ TapCustomLabelBackArrow("Go");
+ App.WaitForElement("MainPageIdentityLabel");
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+ }
+#endif
+
+ // BackButtonBehavior.CommandParameter passes the correct value to the back command.
+ [Test, Order(49)]
+ public void BackButtonBehavior_CommandParameter_CommandFiresWithCorrectParameter()
+ {
+ App.WaitForElement("MainPageIdentityLabel");
+ App.WaitForElement("CommandParameterEntry");
+ App.ClearText("CommandParameterEntry");
+ App.EnterText("CommandParameterEntry", "test");
+ NavigateToDetail1AndWait();
+ TapShellBackArrow("ShellNavigation");
+ App.WaitForElement("MainPageIdentityLabel");
+ Assert.That(App.FindElement("CommandExecutedLabel").GetText(), Is.EqualTo("Executed: test"));
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+ }
+
+ // BackButtonBehavior.IsEnabled=false keeps the back button visible but ignores taps.
+ [Test, Order(50)]
+ public void BackButtonBehavior_IsEnabled_False_BackButtonDoesNotNavigate()
+ {
+ App.WaitForElement("MainPageIdentityLabel");
+ App.WaitForElement("IsEnabledButton");
+ App.Tap("IsEnabledButton");
+ Assert.That(App.FindElement("IsEnabledButton").GetText(), Is.EqualTo("IsEnabled: False"));
+ NavigateToDetail1AndWait();
+ TapShellBackArrow("ShellNavigation");
+ App.WaitForElement("Detail1PageIdentityLabel");
+ App.WaitForElement("Detail1GoBackButton");
+ App.Tap("Detail1GoBackButton");
+ App.WaitForElement("MainPageIdentityLabel");
+ App.Tap("IsEnabledButton");
+ Assert.That(App.FindElement("IsEnabledButton").GetText(), Is.EqualTo("IsEnabled: True"));
+ NavigateToDetail1AndWait();
+ TapShellBackArrow("ShellNavigation");
+ App.WaitForElement("MainPageIdentityLabel");
+ App.Tap("Reset");
+ }
+
+ // BackButtonBehavior.IsVisible=false hides the back button; programmatic navigation still works.
+ [Test, Order(51)]
+ public void BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks()
+ {
+ App.WaitForElement("MainPageIdentityLabel");
+ App.WaitForElement("IsVisibleButton");
+ App.Tap("IsVisibleButton");
+ NavigateToDetail1AndWait();
+ ShellScreenshot();
+ }
+
+#if TEST_FAILS_ON_WINDOWS // Issue Link: https://github.com/dotnet/maui/issues/1625
+ // BackButtonBehavior.IconOverride replaces the default back arrow with a custom icon.
+ [Test, Order(52)]
+ public void BackButtonBehavior_IconOverride_CustomIconShownOnBackButton()
+ {
+ App.WaitForElement("Detail1GoBackButton");
+ App.Tap("Detail1GoBackButton");
+ App.WaitForElement("MainPageIdentityLabel");
+ App.Tap("Reset");
+ App.WaitForElement("MainPageIdentityLabel");
+ App.Tap("IconOverrideBank");
+ NavigateToDetail1AndWait();
+ ShellScreenshot();
+ }
+#endif
+
+ public void ShellScreenshot() // This method is to show titlebar for Screenshot
+ {
+#if WINDOWS
+ VerifyScreenshot(includeTitleBar: true, tolerance: 0.5);
+#else
+ VerifyScreenshot(tolerance: 0.5);
+#endif
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ShellPagesFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ShellPagesFeatureTests.cs
new file mode 100644
index 000000000000..439a4c733853
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/ShellPagesFeatureTests.cs
@@ -0,0 +1,498 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+public class ShellPagesFeatureTests : _GalleryUITest
+{
+ public const string ShellPagesFeatureMatrix = "Shell Feature Matrix";
+ public override string GalleryPageName => ShellPagesFeatureMatrix;
+ public const string Options = "Options";
+ public const string Apply = "Apply";
+
+ public ShellPagesFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+#if TEST_FAILS_ON_CATALYST // Issue Link: https://github.com/dotnet/maui/issues/32125
+ [Test, Order(5)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_TitleColor()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("RedTitleColor");
+ App.Tap("RedTitleColor");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+#endif
+
+#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS // Issue Link: https://github.com/dotnet/maui/issues/32992
+ [Test, Order(2)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_BackgroundColor()
+ {
+ if (App is AppiumIOSApp iosApp && HelperExtensions.IsIOS26OrHigher(iosApp))
+ {
+ Assert.Ignore("Ignored due to a bug issue in iOS 26"); // Issue Link: https://github.com/dotnet/maui/issues/32125
+ }
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("SkyBlueBackgroundColor");
+ App.Tap("SkyBlueBackgroundColor");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+#endif
+
+#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS && TEST_FAILS_ON_WINDOWS // Issue Link: https://github.com/dotnet/maui/issues/5161
+ [Test, Order(3)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_DisabledColor()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("IsEnabledFalse");
+ App.Tap("IsEnabledFalse");
+ App.WaitForElement("VioletDisabledColor");
+ App.Tap("VioletDisabledColor");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+#endif
+
+#if TEST_FAILS_ON_CATALYST // Issue Link: https://github.com/dotnet/maui/issues/32125
+ [Test, Order(4)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_UnselectedColor()
+ {
+ if (App is AppiumIOSApp iosApp && HelperExtensions.IsIOS26OrHigher(iosApp))
+ {
+ Assert.Ignore("Ignored due to a bug issue in iOS 26"); // Issue Link: https://github.com/dotnet/maui/issues/32125
+ }
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("BlueUnselectedColor");
+ App.Tap("BlueUnselectedColor");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+#endif
+
+ [Test, Order(1)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_IsEnabledTrue()
+ {
+ App.WaitForElement("ShellPageButton");
+ App.Tap("ShellPageButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("IsEnabledTrue");
+ App.Tap("IsEnabledTrue");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Tab3");
+ App.Tap("Tab3");
+ App.WaitForElement("Tab3Label");
+ App.WaitForElement("Tab3GoToHomeButton");
+ App.Tap("Tab3GoToHomeButton");
+ }
+
+#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS && TEST_FAILS_ON_WINDOWS // Issue Link: https://github.com/dotnet/maui/issues/5161
+ [Test, Order(6)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_IsEnabledFalse()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("IsEnabledFalse");
+ App.Tap("IsEnabledFalse");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("Tab3");
+ App.Tap("Tab3");
+ VerifyScreenshot();
+ }
+#endif
+
+#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS && TEST_FAILS_ON_WINDOWS // Issue Link: https://github.com/dotnet/maui/issues/32992
+ [Test, Order(7)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_ShowTitleViewWithBackgroundColor()
+ {
+ if (App is AppiumIOSApp iosApp && HelperExtensions.IsIOS26OrHigher(iosApp))
+ {
+ Assert.Ignore("Ignored due to a bug issue in iOS 26"); // Issue Link: https://github.com/dotnet/maui/issues/32125
+ }
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("SkyBlueBackgroundColor");
+ App.Tap("SkyBlueBackgroundColor");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ShowTitleViewButton");
+ App.Tap("ShowTitleViewButton");
+ VerifyScreenshot();
+ }
+#endif
+
+ [Test, Order(8)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_ShowTitleView()
+ {
+ App.WaitForElement(Options); // to reset the old value
+ App.Tap(Options);
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ShowTitleViewButton");
+ App.Tap("ShowTitleViewButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(9)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_ShowTitleViewHidden()
+ {
+ App.WaitForElement("HideTitleViewButton");
+ App.Tap("HideTitleViewButton");
+ VerifyScreenshot();
+ }
+
+#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST // Issue Link: https://github.com/dotnet/maui/issues/17550
+ [Test, Order(10)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_NavBarVisibilityHide()
+ {
+ App.WaitForElement("NavBarHideButton");
+ App.Tap("NavBarHideButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(11)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_NavBarVisibilityShow()
+ {
+ App.WaitForElement("NavBarShowButton");
+ App.Tap("NavBarShowButton");
+ VerifyScreenshot();
+ }
+#endif
+
+#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS && TEST_FAILS_ON_WINDOWS // Issue Link: https://github.com/dotnet/maui/issues/6399
+ [Test, Order(12)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_NavBarHasShadowTrue()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("WhiteBackgroundColor"); // For visible difference when shadow is applied
+ App.Tap("WhiteBackgroundColor");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ShadowTrue");
+ App.Tap("ShadowTrue");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(13)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_NavBarHasShadowFalse()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("WhiteBackgroundColor"); // For visible difference when shadow is removed
+ App.Tap("WhiteBackgroundColor");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement("ShadowFalse");
+ App.Tap("ShadowFalse");
+ VerifyScreenshot();
+ }
+#endif
+
+ [Test, Order(14)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_IsVisibleFalse()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("IsVisibleFalse");
+ App.Tap("IsVisibleFalse");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test, Order(15)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_IsVisibleTrue()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("IsVisibleTrue");
+ App.Tap("IsVisibleTrue");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test, Order(16)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_PresentationModeNotAnimated()
+ {
+ App.WaitForElement("NotAnimatedButton");
+ App.Tap("NotAnimatedButton");
+ App.WaitForElement("GoBackButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(17)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_PresentationModeAnimated()
+ {
+ App.WaitForElement("GoBackButton"); // To go back to controls page
+ App.Tap("GoBackButton");
+ App.WaitForElement("AnimatedButton");
+ App.Tap("AnimatedButton");
+ App.WaitForElement("GoBackButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(18)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_PresentationModeModal()
+ {
+ App.WaitForElement("GoBackButton"); // To go back to controls page
+ App.Tap("GoBackButton");
+ App.WaitForElement("ModalButton");
+ App.Tap("ModalButton");
+ App.WaitForElement("GoBackButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(19)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_PresentationModeModalAnimated()
+ {
+ App.WaitForElement("GoBackButton"); // To go back to controls page
+ App.Tap("GoBackButton");
+ App.WaitForElement("ModalAnimatedButton");
+ App.Tap("ModalAnimatedButton");
+ App.WaitForElement("GoBackButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(20)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_PresentationModeModalNotAnimated()
+ {
+ App.WaitForElement("GoBackButton"); // To go back to controls page
+ App.Tap("GoBackButton");
+ App.WaitForElement("ModalNotAnimatedButton");
+ App.Tap("ModalNotAnimatedButton");
+ App.WaitForElement("GoBackButton");
+ VerifyScreenshot();
+ }
+
+#if TEST_FAILS_ON_CATALYST // Issue Link: https://github.com/dotnet/maui/issues/32125
+ [Test, Order(21)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_ForegroundColor()
+ {
+ App.WaitForElement("GoBackButton"); // To go back to controls page
+ App.Tap("GoBackButton");
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("MagentaForegroundColor");
+ App.Tap("MagentaForegroundColor");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test, Order(22)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_ForegroundColorAndTitleColor()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("MagentaForegroundColor");
+ App.Tap("MagentaForegroundColor");
+ App.WaitForElement("RedTitleColor");
+ App.Tap("RedTitleColor");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+#endif
+
+#if TEST_FAILS_ON_CATALYST // Issue Link: https://github.com/dotnet/maui/issues/32125
+ [Test, Order(23)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_ForegroundColorAndUnselectedColor()
+ {
+ if (App is AppiumIOSApp iosApp && HelperExtensions.IsIOS26OrHigher(iosApp))
+ {
+ Assert.Ignore("Ignored due to a bug issue in iOS 26"); // Issue Link: https://github.com/dotnet/maui/issues/32125
+ }
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("MagentaForegroundColor");
+ App.Tap("MagentaForegroundColor");
+ App.WaitForElement("MaroonUnselectedColor");
+ App.Tap("MaroonUnselectedColor");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+#endif
+
+
+#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS && TEST_FAILS_ON_WINDOWS// Issue Link: https://github.com/dotnet/maui/issues/32992
+ [Test, Order(24)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_BackgroundColorAndForegroundColor()
+ {
+ if (App is AppiumIOSApp iosApp && HelperExtensions.IsIOS26OrHigher(iosApp))
+ {
+ Assert.Ignore("Ignored due to a bug issue in iOS 26"); // Issue Link: https://github.com/dotnet/maui/issues/32125
+ }
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("LightGreenBackgroundColor");
+ App.Tap("LightGreenBackgroundColor");
+ App.WaitForElement("MagentaForegroundColor");
+ App.Tap("MagentaForegroundColor");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test, Order(25)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_BackgroundColorAndTitleColor()
+ {
+ if (App is AppiumIOSApp iosApp && HelperExtensions.IsIOS26OrHigher(iosApp))
+ {
+ Assert.Ignore("Ignored due to a bug issue in iOS 26"); // Issue Link: https://github.com/dotnet/maui/issues/32125
+ }
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("LightGreenBackgroundColor");
+ App.Tap("LightGreenBackgroundColor");
+ App.WaitForElement("RedTitleColor");
+ App.Tap("RedTitleColor");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test, Order(26)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_BackgroundColorAndUnselectedColor()
+ {
+ if (App is AppiumIOSApp iosApp && HelperExtensions.IsIOS26OrHigher(iosApp))
+ {
+ Assert.Ignore("Ignored due to a bug issue in iOS 26"); // Issue Link: https://github.com/dotnet/maui/issues/32125
+ }
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("LightGreenBackgroundColor");
+ App.Tap("LightGreenBackgroundColor");
+ App.WaitForElement("MaroonUnselectedColor");
+ App.Tap("MaroonUnselectedColor");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+#endif
+
+#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS && TEST_FAILS_ON_WINDOWS // Issue Link: https://github.com/dotnet/maui/issues/5161
+ [Test, Order(27)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_ForegroundColorAndDisabledColor()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("MagentaForegroundColor");
+ App.Tap("MagentaForegroundColor");
+ App.WaitForElement("IsEnabledFalse");
+ App.Tap("IsEnabledFalse");
+ App.WaitForElement("VioletDisabledColor");
+ App.Tap("VioletDisabledColor");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+
+ [Test, Order(28)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_DisabledColorAndUnselectedColor()
+ {
+ if (App is AppiumIOSApp iosApp && HelperExtensions.IsIOS26OrHigher(iosApp))
+ {
+ Assert.Ignore("Ignored due to a bug issue in iOS 26"); // Issue Link: https://github.com/dotnet/maui/issues/32125
+ }
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("MaroonUnselectedColor");
+ App.Tap("MaroonUnselectedColor");
+ App.WaitForElement("IsEnabledFalse");
+ App.Tap("IsEnabledFalse");
+ App.WaitForElement("VioletDisabledColor");
+ App.Tap("VioletDisabledColor");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+#endif
+
+#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS// Issue Link: https://github.com/dotnet/maui/issues/33909
+ [Test, Order(30)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_VerifyForegroundColorResetForBackButton()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("MagentaForegroundColor");
+ App.Tap("MagentaForegroundColor");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ VerifyShellScreenshot();
+ }
+#endif
+
+#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS // Issue Link:https://github.com/dotnet/maui/issues/32993
+ [Test, Order(29)]
+ [Category(UITestCategories.Shell)]
+ public void ShellPages_FlowDirectionRTL()
+ {
+ App.WaitForElement(Options);
+ App.Tap(Options);
+ App.WaitForElement("FlowDirectionRTL");
+ App.Tap("FlowDirectionRTL");
+ App.WaitForElement(Apply);
+ App.Tap(Apply);
+ VerifyScreenshot();
+ }
+#endif
+
+ public void VerifyShellScreenshot()
+ {
+#if WINDOWS
+ VerifyScreenshot(includeTitleBar: true);
+#else
+ VerifyScreenshot();
+#endif
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/SliderFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/SliderFeatureTests.cs
index 5ea180442eae..a18f860bc1a8 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/SliderFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/SliderFeatureTests.cs
@@ -374,6 +374,21 @@ public void Slider_ChangeThumbImageSource_VerifyVisualState()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
+ [Test]
+ [Category(UITestCategories.Slider)]
+ public void Slider_SetThumbImageSourceAndReset_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("ThumbImageSourceButton");
+ App.Tap("ThumbImageSourceButton");
+ App.Tap("ThumbImageResetButton");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("SliderControl");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
[Test]
[Category(UITestCategories.Slider)]
public void Slider_SetMinimumAndChangeFlowDirection_RTL()
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/StepperFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/StepperFeatureTests.cs
index 1f90e9fad8b7..fd82c2159810 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/StepperFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/StepperFeatureTests.cs
@@ -25,6 +25,23 @@ public void Stepper_ValidateDefaultValues_VerifyLabels()
Assert.That(App.FindElement("ValueLabel").GetText(), Is.EqualTo("0.00"));
}
+ [Test, Order(2)]
+ public void Stepper_ValueChangedEvent_IsRaised()
+ {
+ App.WaitForElement("Options");
+
+ Assert.That(
+ App.FindElement("ValueChangedEventLabel").GetText(),
+ Is.EqualTo("Not Raised")
+ );
+ App.IncreaseStepper("StepperControl");
+
+ Assert.That(
+ App.FindElement("ValueChangedEventLabel").GetText(),
+ Is.EqualTo("Raised")
+ );
+ }
+
[Test]
public void Stepper_SetMinimumValue_VerifyMinimumLabel()
{
@@ -167,7 +184,6 @@ public void Stepper_SetVisibilityToFalse_VerifyVisualState()
App.WaitForNoElement("StepperControl");
}
-#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST //Related Issue Link : https://github.com/dotnet/maui/issues/29704
[Test]
public void Stepper_ChangeFlowDirection_RTL_VerifyVisualState()
{
@@ -180,7 +196,7 @@ public void Stepper_ChangeFlowDirection_RTL_VerifyVisualState()
App.WaitForElement("Options");
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
-#endif
+
[Test]
public void Stepper_AtMinimumValue_DecrementButtonDisabled()
{
@@ -345,4 +361,137 @@ public void Stepper_DecrementDoesNotGoBelowMinimum()
var currentValue = App.FindElement("ValueLabel").GetText();
Assert.That(currentValue, Is.EqualTo("0.00"));
}
-}
\ No newline at end of file
+
+ [Test]
+ public void Stepper_ValueChanged_ShouldFire_WhenValueIsClampedToMinimum()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.ClearText("MinimumEntry");
+ App.EnterText("MinimumEntry", "5");
+ App.PressEnter();
+
+ App.Tap("Apply");
+ App.WaitForElement("Options");
+
+ Assert.That(App.FindElement("ValueLabel").GetText(), Is.EqualTo("5.00"));
+ Assert.That(App.FindElement("ValueChangedEventLabel").GetText(), Is.EqualTo("Raised"));
+ }
+
+ [Test]
+ public void Stepper_ValueChanged_ShouldFire_WhenValueIsClampedToMaximum()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.ClearText("MaximumEntry");
+ App.EnterText("MaximumEntry", "10");
+ App.PressEnter();
+
+ App.ClearText("ValueEntry");
+ App.EnterText("ValueEntry", "50");
+ App.PressEnter();
+
+ App.Tap("Apply");
+ App.WaitForElement("Options");
+
+ Assert.That(App.FindElement("ValueLabel").GetText(), Is.EqualTo("10.00"));
+ Assert.That(App.FindElement("ValueChangedEventLabel").GetText(), Is.EqualTo("Raised"));
+ }
+
+ [Test]
+ public void Stepper_ValueChanged_Arguments_NotRaised_WhenDisabled()
+ {
+ App.WaitForElement("Options");
+
+ App.Tap("Options");
+ App.WaitForElement("IsEnabledFalseRadio");
+ App.Tap("IsEnabledFalseRadio");
+ App.Tap("Apply");
+
+ App.WaitForElement("Options");
+
+ App.IncreaseStepper("StepperControl");
+
+ Assert.That(App.FindElement("ValueChangedEventLabel").GetText(), Is.EqualTo("Not Raised"));
+ }
+
+
+ [Test]
+ public void Stepper_ValueChanged_Arguments_FirstChange()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ Assert.That(App.FindElement("ValueChangedEventLabel").GetText(), Is.EqualTo("Not Raised"));
+
+ App.IncreaseStepper("StepperControl");
+
+ Assert.That(App.FindElement("ValueChangedEventLabel").GetText(), Is.EqualTo("Raised"));
+
+ Assert.That(App.FindElement("OldValueLabel").GetText(), Is.EqualTo("0.00"));
+ Assert.That(App.FindElement("NewValueLabel").GetText(), Is.EqualTo("1.00"));
+ }
+
+ [Test]
+ public void Stepper_ValueChanged_Arguments_SecondChange()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.IncreaseStepper("StepperControl");
+ App.IncreaseStepper("StepperControl");
+
+ Assert.That(App.FindElement("OldValueLabel").GetText(), Is.EqualTo("1.00"));
+ Assert.That(App.FindElement("NewValueLabel").GetText(), Is.EqualTo("2.00"));
+ }
+
+ [Test]
+ public void Stepper_ValueChanged_Arguments_WhenValueChangedFromOptions()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ Assert.That(
+ App.FindElement("ValueChangedEventLabel").GetText(),
+ Is.EqualTo("Not Raised")
+ );
+
+ App.Tap("Options");
+ App.WaitForElement("ValueEntry");
+
+ App.ClearText("ValueEntry");
+ App.EnterText("ValueEntry", "5");
+ App.PressEnter();
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.WaitForElement("Options");
+
+ Assert.That(
+ App.FindElement("ValueChangedEventLabel").GetText(),
+ Is.EqualTo("Raised")
+ );
+
+ Assert.That(
+ App.FindElement("OldValueLabel").GetText(),
+ Is.EqualTo("0.00")
+ );
+
+ Assert.That(
+ App.FindElement("NewValueLabel").GetText(),
+ Is.EqualTo("5.00")
+ );
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/TriggersFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/TriggersFeatureTests.cs
new file mode 100644
index 000000000000..19d0515ea2d6
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/TriggersFeatureTests.cs
@@ -0,0 +1,463 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests
+{
+ [Category(UITestCategories.Triggers)]
+ public class TriggersFeatureTests : _GalleryUITest
+ {
+ public const string TriggerFeatureMatrix = "Triggers Feature Matrix";
+ public override string GalleryPageName => TriggerFeatureMatrix;
+
+#if IOS
+ private const int CropBottomValue = 1200;
+#elif ANDROID
+ private const int CropBottomValue = 1100;
+#endif
+
+ public TriggersFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+ private void VerifyScreenshotOrSetExceptionWithCroppingBottom(ref Exception? exception, string? name = null, bool isKeyBoardNotShown = false)
+ {
+#if IOS
+ VerifyScreenshotOrSetException(ref exception, name, cropBottom: CropBottomValue, tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+#elif ANDROID
+ if (isKeyBoardNotShown)
+ VerifyScreenshotOrSetException(ref exception, name, tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ else
+ VerifyScreenshotOrSetException(ref exception, name, cropBottom: CropBottomValue, tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+#else
+ VerifyScreenshotOrSetException(ref exception, name, tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+#endif
+ }
+
+ private void VerifyScreenshotOrSetExceptionWithCroppingLeft(ref Exception? exception, string? name = null)
+ {
+#if ANDROID
+ VerifyScreenshotOrSetException(ref exception, name, cropLeft: 125, tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+#else
+ VerifyScreenshotOrSetException(ref exception, name, tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+#endif
+ }
+
+ private void SelectTriggerType(string triggerButtonAutomationId)
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement(triggerButtonAutomationId);
+ App.Tap(triggerButtonAutomationId);
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("Options");
+ }
+
+ [Test]
+ [Order(1)]
+ public void PropertyTriggerChangesBackgroundOnFocus()
+ {
+ Exception? exception = null;
+
+ SelectTriggerType("PropertyTriggerButton");
+
+ // Tap to focus
+ App.WaitForElement("PropertyTriggerEntry");
+ App.Tap("PropertyTriggerEntry");
+ VerifyScreenshotOrSetExceptionWithCroppingBottom(ref exception, "PropertyTrigger_Focused");
+
+ App.WaitForElement("PropertyTriggerDummyEntry");
+ App.Tap("PropertyTriggerDummyEntry");
+ VerifyScreenshotOrSetExceptionWithCroppingBottom(ref exception, "PropertyTrigger_UnFocused");
+
+ if (exception != null)
+ {
+ throw exception;
+ }
+ }
+
+ [Test]
+ [Order(2)]
+ public void DataTriggerEnablesButtonWhenTextEntered()
+ {
+ Exception? exception = null;
+
+ SelectTriggerType("DataTriggerButton");
+
+ // Initial state - button disabled (opacity 0.5)
+ VerifyScreenshotOrSetException(ref exception, "DataTrigger_ButtonDisabled", tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+
+ // Enter text to enable button
+ App.WaitForElement("DataTriggerEntry");
+ App.Tap("DataTriggerEntry");
+ App.EnterText("DataTriggerEntry", "Test");
+ VerifyScreenshotOrSetException(ref exception, "DataTrigger_ButtonEnabled", tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+
+ if (exception != null)
+ {
+ throw exception;
+ }
+ }
+
+ [Test]
+ [Order(3)]
+ public void EventTriggerValidatesNumericInput()
+ {
+ Exception? exception = null;
+
+ SelectTriggerType("EventTriggerButton");
+
+ // Enter valid numeric text
+ App.WaitForElement("EventTriggerEntry");
+ App.Tap("EventTriggerEntry");
+ App.EnterText("EventTriggerEntry", "123");
+ VerifyScreenshotOrSetExceptionWithCroppingBottom(ref exception, "EventTrigger_ValidNumeric", isKeyBoardNotShown: true);
+
+ // Enter invalid text (should trigger validation)
+ App.ClearText("EventTriggerEntry");
+ App.EnterText("EventTriggerEntry", "abc");
+ VerifyScreenshotOrSetExceptionWithCroppingBottom(ref exception, "EventTrigger_InvalidText", isKeyBoardNotShown: true);
+
+ if (exception != null)
+ {
+ throw exception;
+ }
+ }
+
+ [Test]
+ [Order(4)]
+ public void StateTriggerChangesBackgroundOnToggle()
+ {
+ Exception? exception = null;
+
+ SelectTriggerType("StateTriggerButton");
+
+ // Initial state - switch off, background white
+ VerifyScreenshotOrSetException(ref exception, "StateTrigger_SwitchOff", tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+
+ // Toggle switch on - background black
+ App.WaitForElement("StateTriggerSwitch");
+ App.Tap("StateTriggerSwitch");
+ VerifyScreenshotOrSetException(ref exception, "StateTrigger_SwitchOn", tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+
+ if (exception != null)
+ {
+ throw exception;
+ }
+ }
+
+ [Test]
+ [Order(5)]
+ public void CompareStateTriggerChangesBackgroundOnCheck()
+ {
+ Exception? exception = null;
+
+ SelectTriggerType("CompareStateTriggerButton");
+
+ // Initial state - unchecked, background LightGray
+ VerifyScreenshotOrSetException(ref exception, "CompareStateTrigger_Unchecked", tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+
+ // Check the checkbox - background DarkGreen
+ App.WaitForElement("CompareStateCheckBox");
+ App.Tap("CompareStateCheckBox");
+ VerifyScreenshotOrSetException(ref exception, "CompareStateTrigger_Checked", tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+
+ if (exception != null)
+ {
+ throw exception;
+ }
+ }
+
+#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_CATALYST // DeviceStateTrigger is currently not supported on Windows and Catalyst platform
+ [Test]
+ [Order(6)]
+ public void DeviceStateTriggerShowsPlatformSpecificBackground()
+ {
+ SelectTriggerType("DeviceStateTriggerButton");
+ // Verify platform updated with correct background color for current platform
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+#endif
+
+#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS // These platforms do not support orientation changes which is required to test the OrientationStateTrigger
+#if TEST_FAILS_ON_ANDROID // This test is currently failing on Android in Automation, but can be passed manually.
+ [Test]
+ [Order(7)]
+ public void OrientationStateTriggerShowsCorrectBackground()
+ {
+ SelectTriggerType("OrientationStateTriggerButton");
+ App.WaitForElement("OrientationStateTriggerGrid");
+
+ // Verify orientation label is displayed
+ var orientationPortraitText = App.FindElement("OrientationLabel").GetText();
+ Assert.That(orientationPortraitText, Is.EqualTo("Current orientation: Portrait"));
+
+ App.SetOrientationLandscape();
+
+ App.WaitForElement("Options"); // Wait for page to stabilize after orientation change
+ var orientationLandscapeText = App.FindElement("OrientationLabel").GetText();
+ Assert.That(orientationLandscapeText, Is.EqualTo("Current orientation: Landscape"));
+
+ App.SetOrientationPortrait();
+ App.WaitForElement("Options"); // Verify orientation label returns to portrait
+ }
+#endif
+
+ [Test]
+ [Order(8)]
+ public void AdaptiveTriggerChangesOrientation()
+ {
+ Exception? exception = null;
+
+ SelectTriggerType("AdaptiveTriggerButton");
+
+ // Verify initial orientation (should be portrait for narrow width)
+ VerifyScreenshotOrSetException(ref exception, "AdaptiveTrigger_Portrait", tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ App.SetOrientationLandscape();
+
+ App.WaitForElement("Options"); // Wait for page to stabilize after orientation change
+ VerifyScreenshotOrSetExceptionWithCroppingLeft(ref exception, "AdaptiveTrigger_Landscape");
+
+ App.SetOrientationPortrait();
+ App.WaitForElement("Options"); // Wait for page to stabilize after orientation change
+
+ if (exception != null)
+ {
+ throw exception;
+ }
+ }
+#endif
+
+ [Test]
+ [Order(9)]
+ public void MultiTriggerComplexConditions()
+ {
+ Exception? exception = null;
+
+ SelectTriggerType("MultiTriggerButton");
+
+ App.WaitForElement("MultiTriggerEmailEntry");
+ App.Tap("MultiTriggerEmailEntry");
+ App.EnterText("MultiTriggerEmailEntry", "user@test.com");
+ VerifyScreenshotOrSetException(ref exception, "MultiTrigger_EmailOnly_Filled", tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+
+ App.ClearText("MultiTriggerEmailEntry");
+ App.WaitForElement("MultiTriggerPhoneEntry");
+ App.Tap("MultiTriggerPhoneEntry");
+ App.EnterText("MultiTriggerPhoneEntry", "555-1234");
+ VerifyScreenshotOrSetException(ref exception, "MultiTrigger_PhoneOnly_Filled", tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+
+ App.WaitForElement("MultiTriggerEmailEntry");
+ App.Tap("MultiTriggerEmailEntry");
+ App.EnterText("MultiTriggerEmailEntry", "user@test.com");
+ VerifyScreenshotOrSetException(ref exception, "MultiTrigger_Both_Filled", tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+
+ if (exception != null)
+ {
+ throw exception;
+ }
+ }
+
+ [Test]
+ [Order(10)]
+ public void EnterExitActionsTriggerEntryFocusUnfocus()
+ {
+ SelectTriggerType("EnterExitActionsButton");
+
+ // Verify both entries exist
+ App.WaitForElement("EnterExitActionsEntry");
+ App.WaitForElement("EnterExitActionsDummyEntry");
+
+ // Focus the entry - triggers EnterAction (fade animation from 0)
+ App.Tap("EnterExitActionsEntry");
+ App.WaitForElement("EnterExitActionsEntry");
+
+ // Unfocus by tapping dummy entry - triggers ExitAction (fade animation from 1)
+ App.Tap("EnterExitActionsDummyEntry");
+ App.WaitForElement("EnterExitActionsEntry");
+
+ // Verify info label confirms trigger type
+ var infoText = App.FindElement("EnterExitActionsInfo").GetText();
+ Assert.That(infoText, Is.EqualTo("Tests: EnterActions, ExitActions, animations"));
+ }
+
+ [Test]
+ [Order(11)]
+ public void DataTriggerVerifiesButtonTextAndEntryInput()
+ {
+ SelectTriggerType("DataTriggerButton");
+ App.WaitForElement("DataTriggerEntry");
+
+ // Verify save button has correct text
+ var buttonText = App.FindElement("DataTriggerSaveButton").GetText();
+ Assert.That(buttonText, Is.EqualTo("Save"));
+
+ // Enter text and verify entry accepts input
+ App.Tap("DataTriggerEntry");
+ App.EnterText("DataTriggerEntry", "Test");
+ App.WaitForElement("DataTriggerSaveButton");
+
+ // Clear text and verify entry is reset
+ App.ClearText("DataTriggerEntry");
+ App.WaitForElement("DataTriggerSaveButton");
+
+ // Verify info label text
+ var infoText = App.FindElement("DataTriggerInfo").GetText();
+ Assert.That(infoText, Is.EqualTo("Tests: DataTrigger, binding-based condition"));
+ }
+
+ [Test]
+ [Order(12)]
+ public void EventTriggerVerifiesEntryInput()
+ {
+ SelectTriggerType("EventTriggerButton");
+ App.WaitForElement("EventTriggerEntry");
+
+ // Enter valid numeric text and verify entry accepts it
+ App.Tap("EventTriggerEntry");
+ App.EnterText("EventTriggerEntry", "123");
+ App.WaitForElement("EventTriggerEntry");
+
+ // Clear and enter non-numeric text to trigger validation
+ App.ClearText("EventTriggerEntry");
+ App.EnterText("EventTriggerEntry", "abc");
+ App.WaitForElement("EventTriggerEntry");
+
+ // Verify info label text
+ var infoText = App.FindElement("EventTriggerInfo").GetText();
+ Assert.That(infoText, Is.EqualTo("Tests: TextChanged event, custom TriggerAction"));
+ }
+
+ [Test]
+ [Order(13)]
+ public void MultiTriggerVerifiesFieldsAndSubmitButton()
+ {
+ SelectTriggerType("MultiTriggerButton");
+
+ App.WaitForElement("MultiTriggerEmailEntry");
+ App.WaitForElement("MultiTriggerPhoneEntry");
+
+ // Verify submit button text
+ var buttonText = App.FindElement("MultiTriggerSubmitButton").GetText();
+ Assert.That(buttonText, Is.EqualTo("Submit"));
+
+ // Fill email only
+ App.Tap("MultiTriggerEmailEntry");
+ App.EnterText("MultiTriggerEmailEntry", "user@test.com");
+ App.WaitForElement("MultiTriggerSubmitButton");
+
+ // Fill phone field too (both filled - multi-trigger condition met)
+ App.Tap("MultiTriggerPhoneEntry");
+ App.EnterText("MultiTriggerPhoneEntry", "555-1234");
+ App.WaitForElement("MultiTriggerSubmitButton");
+
+ // Clear email (condition no longer met)
+ App.ClearText("MultiTriggerEmailEntry");
+ App.WaitForElement("MultiTriggerSubmitButton");
+
+ // Verify info label text
+ var infoText = App.FindElement("MultiTriggerInfo").GetText();
+ Assert.That(infoText, Is.EqualTo("Tests: Multiple conditions, combined logic"));
+ }
+
+ [Test]
+ [Order(14)]
+ public void StateTriggerVerifiesGridLabelTextAfterToggle()
+ {
+ SelectTriggerType("StateTriggerButton");
+ App.WaitForElement("StateTriggerSwitch");
+
+ // Verify grid label text
+ var labelText = App.FindElement("StateTriggerGridLabel").GetText();
+ Assert.That(labelText, Is.EqualTo("Grid background changes based on switch"));
+
+ // Toggle switch on and verify label still present
+ App.Tap("StateTriggerSwitch");
+ App.WaitForElement("StateTriggerGridLabel");
+
+ // Toggle switch off and verify label text unchanged
+ App.Tap("StateTriggerSwitch");
+ var labelTextAfter = App.FindElement("StateTriggerGridLabel").GetText();
+ Assert.That(labelTextAfter, Is.EqualTo("Grid background changes based on switch"));
+
+ // Verify info label text
+ var infoText = App.FindElement("StateTriggerInfo").GetText();
+ Assert.That(infoText, Is.EqualTo("Tests: StateTrigger with IsActive binding"));
+ }
+
+ [Test]
+ [Order(15)]
+ public void CompareStateTriggerVerifiesLabelAfterCheckToggle()
+ {
+ SelectTriggerType("CompareStateTriggerButton");
+ App.WaitForElement("CompareStateCheckBox");
+
+ // Verify label text
+ var labelText = App.FindElement("CompareStateLabel").GetText();
+ Assert.That(labelText, Is.EqualTo("Check to change background"));
+
+ // Check the checkbox and verify label still present
+ App.Tap("CompareStateCheckBox");
+ App.WaitForElement("CompareStateLabel");
+
+ // Uncheck and verify label text unchanged
+ App.Tap("CompareStateCheckBox");
+ var labelTextAfter = App.FindElement("CompareStateLabel").GetText();
+ Assert.That(labelTextAfter, Is.EqualTo("Check to change background"));
+
+ // Verify info label text
+ var infoText = App.FindElement("CompareStateTriggerInfo").GetText();
+ Assert.That(infoText, Is.EqualTo("Tests: CompareStateTrigger with Property binding"));
+ }
+
+ [Test]
+ [Order(16)]
+ public void PropertyTriggerVerifiesEntryFocusInteraction()
+ {
+ SelectTriggerType("PropertyTriggerButton");
+
+ App.WaitForElement("PropertyTriggerEntry");
+ App.WaitForElement("PropertyTriggerDummyEntry");
+
+ // Verify info label text
+ var infoText = App.FindElement("PropertyTriggerInfo").GetText();
+ Assert.That(infoText, Is.EqualTo("Tests: IsFocused property, multiple setters"));
+
+ // Focus entry (triggers IsFocused=True property trigger)
+ App.Tap("PropertyTriggerEntry");
+ App.WaitForElement("PropertyTriggerEntry");
+
+ // Unfocus by tapping dummy (triggers IsFocused=False, reverts setters)
+ App.Tap("PropertyTriggerDummyEntry");
+ App.WaitForElement("PropertyTriggerEntry");
+
+ // Re-focus to verify trigger can be re-activated
+ App.Tap("PropertyTriggerEntry");
+ App.WaitForElement("PropertyTriggerEntry");
+ }
+ // On Windows AutomationId is not working for BoxView. For more details see: https://github.com/dotnet/maui/issues/4715
+#if TEST_FAILS_ON_WINDOWS
+ [Test]
+ [Order(17)]
+ public void AdaptiveTriggerVerifiesWindowSizeLabelAndElements()
+ {
+ SelectTriggerType("AdaptiveTriggerButton");
+
+ // Verify window size label contains expected text
+ var sizeText = App.FindElement("WindowSizeLabel").GetText();
+ Assert.That(sizeText, Does.Contain("Window size"));
+
+ // Verify all colored boxes exist within the adaptive layout
+ App.WaitForElement("AdaptiveBoxRed");
+ App.WaitForElement("AdaptiveBoxGreen");
+ App.WaitForElement("AdaptiveBoxBlue");
+
+ // Verify info label text
+ var infoText = App.FindElement("AdaptiveTriggerInfo").GetText();
+ Assert.That(infoText, Is.EqualTo("Tests: MinWindowWidth, responsive layout"));
+ }
+#endif
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_ButtonFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_ButtonFeatureTests.cs
new file mode 100644
index 000000000000..2d04d19c42a0
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_ButtonFeatureTests.cs
@@ -0,0 +1,175 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+[Category(UITestCategories.VisualStateManager)]
+public class VisualStateManager_ButtonFeatureTests : _GalleryUITest
+{
+ public const string VisualStateManagerButtonFeatureTests = "VisualStateManager Feature Matrix";
+ public override string GalleryPageName => VisualStateManagerButtonFeatureTests;
+
+ public VisualStateManager_ButtonFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+// PointerOver states cannot currently be reliably covered in CI environments, as hover/pointer interactions are not consistently supported in automated runs. Therefore, these states are validated manually on Mac and Windows, and PointerOver-related tests have not been included in the automated test cases.
+ [Test, Order(1)]
+ public void VerifyVSM_Button_InitialState()
+ {
+ App.WaitForElement("VSMButton");
+ App.Tap("VSMButton");
+ App.WaitForElement("ButtonStateLabel");
+ var stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(2)]
+ public void VerifyVSM_Button_Disable()
+ {
+ App.WaitForElement("ButtonDisable");
+ App.Tap("ButtonDisable");
+ App.WaitForElement("ButtonStateLabel");
+ var stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(3)]
+ public void VerifyVSM_Button_Reset()
+ {
+ App.WaitForElement("ButtonReset");
+ App.Tap("ButtonReset");
+ Assert.That(App.FindElement("DemoButton").IsEnabled(), Is.True);
+ var stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ VerifyScreenshot();
+ }
+
+#if TEST_FAILS_ON_ANDROID // Related issue link: https://github.com/dotnet/maui/issues/19289
+ [Test, Order(4)]
+ public void VerifyVSM_Button_PressedAndReleased()
+ {
+ App.WaitForElement("DemoButton");
+ App.Tap("DemoButton");
+ App.WaitForElement("ButtonStateLabel");
+ var stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal/Released"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(5)]
+ public void VerifyVSM_Button_DisableWhilePressedAndReleased()
+ {
+ App.WaitForElement("ButtonReset");
+ App.Tap("ButtonReset");
+ App.WaitForElement("DemoButton");
+ App.Tap("DemoButton");
+ App.WaitForElement("ButtonStateLabel");
+ var stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal/Released"));
+ App.WaitForElement("ButtonDisable");
+ App.Tap("ButtonDisable");
+ App.WaitForElement("ButtonStateLabel");
+ stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ }
+
+ [Test, Order(6)]
+ public void VerifyVSM_Button_ResetWhilePressedAndReleased()
+ {
+ App.WaitForElement("ButtonReset");
+ App.Tap("ButtonReset");
+ App.WaitForElement("DemoButton");
+ App.Tap("DemoButton");
+ App.WaitForElement("ButtonStateLabel");
+ var stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal/Released"));
+ App.WaitForElement("ButtonReset");
+ App.Tap("ButtonReset");
+ App.WaitForElement("ButtonStateLabel");
+ stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+#endif
+
+ [Test, Order(7)]
+ public void VerifyVSM_Button_ResetWhileDisabled()
+ {
+ App.WaitForElement("ButtonReset");
+ App.Tap("ButtonReset");
+ App.WaitForElement("ButtonDisable");
+ App.Tap("ButtonDisable");
+ App.WaitForElement("ButtonStateLabel");
+ var stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("ButtonReset");
+ App.Tap("ButtonReset");
+ App.WaitForElement("ButtonStateLabel");
+ stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+
+#if TEST_FAILS_ON_ANDROID // Related issue link: https://github.com/dotnet/maui/issues/19289
+ [Test, Order(8)]
+ public void VerifyVSM_Button_PressedAndReleasedWhileDisabled()
+ {
+ App.WaitForElement("ButtonReset");
+ App.Tap("ButtonReset");
+ App.WaitForElement("ButtonDisable");
+ App.Tap("ButtonDisable");
+ App.WaitForElement("ButtonStateLabel");
+ var stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("DemoButton");
+ App.Tap("DemoButton");
+ App.WaitForElement("ButtonStateLabel");
+ stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ }
+#endif
+
+ [Test, Order(9)]
+ public void VerifyVSM_Button_DisableAndEnable()
+ {
+ App.WaitForElement("ButtonReset");
+ App.Tap("ButtonReset");
+ App.WaitForElement("ButtonDisable");
+ App.Tap("ButtonDisable");
+ App.WaitForElement("ButtonStateLabel");
+ var stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("ButtonDisable");
+ App.Tap("ButtonDisable");
+ App.WaitForElement("ButtonStateLabel");
+ stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+
+#if TEST_FAILS_ON_ANDROID // Related issue link: https://github.com/dotnet/maui/issues/19289
+ [Test, Order(10)]
+ public void VerifyVSM_Button_DisableAndEnableWhilePressedAndReleased()
+ {
+ App.WaitForElement("ButtonReset");
+ App.Tap("ButtonReset");
+ App.WaitForElement("DemoButton");
+ App.Tap("DemoButton");
+ App.WaitForElement("ButtonStateLabel");
+ var stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal/Released"));
+ App.WaitForElement("ButtonDisable");
+ App.Tap("ButtonDisable");
+ App.WaitForElement("ButtonStateLabel");
+ stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("ButtonDisable");
+ App.Tap("ButtonDisable");
+ App.WaitForElement("ButtonStateLabel");
+ stateText = App.FindElement("ButtonStateLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+#endif
+}
+
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_CheckBoxFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_CheckBoxFeatureTests.cs
new file mode 100644
index 000000000000..4e9a98c226ab
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_CheckBoxFeatureTests.cs
@@ -0,0 +1,211 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+[Category(UITestCategories.VisualStateManager)]
+public class VisualStateManager_CheckBoxFeatureTests : _GalleryUITest
+{
+ public const string VisualStateManagerCheckBoxFeatureTests = "VisualStateManager Feature Matrix";
+ public override string GalleryPageName => VisualStateManagerCheckBoxFeatureTests;
+
+ public VisualStateManager_CheckBoxFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+ [Test, Order(1)]
+ public void VerifyVSM_CheckBox_InitialState()
+ {
+ App.WaitForElement("VSMCheckBoxButton");
+ App.Tap("VSMCheckBoxButton");
+ App.WaitForElement("CheckBoxState");
+ var stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(2)]
+ public void VerifyVSM_CheckBox_Disable()
+ {
+ App.WaitForElement("CheckBoxDisable");
+ App.Tap("CheckBoxDisable");
+ var stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(3)]
+ public void VerifyVSM_CheckBox_Reset()
+ {
+ App.WaitForElement("CheckBoxReset");
+ App.Tap("CheckBoxReset");
+ var stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(4)]
+ public void VerifyVSM_CheckBox_Checked()
+ {
+ App.WaitForElement("VSMCheckBox");
+ App.Tap("VSMCheckBox");
+ var stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Checked"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(5)]
+ public void VerifyVSM_CheckBox_UnCheckedWhileChecked()
+ {
+ App.WaitForElement("CheckBoxReset");
+ App.Tap("CheckBoxReset");
+ App.WaitForElement("VSMCheckBox");
+ App.Tap("VSMCheckBox");
+ var stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Checked"));
+ App.WaitForElement("VSMCheckBox");
+ App.Tap("VSMCheckBox");
+ stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Unchecked"));
+ }
+
+ [Test, Order(6)]
+ public void VerifyVSM_CheckBox_DisableWhileChecked()
+ {
+ App.WaitForElement("CheckBoxReset");
+ App.Tap("CheckBoxReset");
+ App.WaitForElement("VSMCheckBox");
+ App.Tap("VSMCheckBox");
+ var stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Checked"));
+ App.WaitForElement("CheckBoxDisable");
+ App.Tap("CheckBoxDisable");
+ stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(7)]
+ public void VerifyVSM_CheckBox_CheckedWhileDisable()
+ {
+ App.WaitForElement("CheckBoxReset");
+ App.Tap("CheckBoxReset");
+ App.WaitForElement("CheckBoxDisable");
+ App.Tap("CheckBoxDisable");
+ var stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("VSMCheckBox");
+ App.Tap("VSMCheckBox");
+ stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ }
+
+ [Test, Order(8)]
+ public void VerifyVSM_CheckBox_ResetWhileDisable()
+ {
+ App.WaitForElement("CheckBoxReset");
+ App.Tap("CheckBoxReset");
+ App.WaitForElement("CheckBoxDisable");
+ App.Tap("CheckBoxDisable");
+ var stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("CheckBoxReset");
+ App.Tap("CheckBoxReset");
+ stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+
+ [Test, Order(9)]
+ public void VerifyVSM_CheckBox_UnCheckedWhileDisable()
+ {
+ App.WaitForElement("CheckBoxReset");
+ App.Tap("CheckBoxReset");
+ App.WaitForElement("VSMCheckBox");
+ App.Tap("VSMCheckBox");
+ var stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Checked"));
+ App.WaitForElement("CheckBoxDisable");
+ App.Tap("CheckBoxDisable");
+ var stateText1 = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText1, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("VSMCheckBox");
+ App.Tap("VSMCheckBox");
+ stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ }
+
+ [Test, Order(10)]
+ public void VerifyVSM_CheckBox_DisableAndEnableWhileChecked()
+ {
+ App.WaitForElement("CheckBoxReset");
+ App.Tap("CheckBoxReset");
+ App.WaitForElement("VSMCheckBox");
+ App.Tap("VSMCheckBox");
+ var stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Checked"));
+ App.WaitForElement("CheckBoxDisable");
+ App.Tap("CheckBoxDisable");
+ var stateText1 = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText1, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("CheckBoxDisable");
+ App.Tap("CheckBoxDisable");
+ stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Checked"));
+ }
+
+ [Test, Order(11)]
+ public void VerifyVSM_CheckBox_DisableAndEnableWhileUnchecked()
+ {
+ App.WaitForElement("CheckBoxReset");
+ App.Tap("CheckBoxReset");
+ App.WaitForElement("VSMCheckBox");
+ App.Tap("VSMCheckBox");
+ App.WaitForElement("VSMCheckBox");
+ App.Tap("VSMCheckBox");
+ var stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Unchecked"));
+ App.WaitForElement("CheckBoxDisable");
+ App.Tap("CheckBoxDisable");
+ var stateText1 = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText1, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("CheckBoxDisable");
+ App.Tap("CheckBoxDisable");
+ stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Unchecked"));
+ }
+
+ [Test, Order(12)]
+ public void VerifyVSM_CheckBox_ResetWhileChecked()
+ {
+ App.WaitForElement("CheckBoxReset");
+ App.Tap("CheckBoxReset");
+ App.WaitForElement("VSMCheckBox");
+ App.Tap("VSMCheckBox");
+ var stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Checked"));
+ App.WaitForElement("CheckBoxReset");
+ App.Tap("CheckBoxReset");
+ stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+
+ [Test, Order(13)]
+ public void VerifyVSM_CheckBox_ResetWhileUnchecked()
+ {
+ App.WaitForElement("CheckBoxReset");
+ App.Tap("CheckBoxReset");
+ App.WaitForElement("VSMCheckBox");
+ App.Tap("VSMCheckBox");
+ App.WaitForElement("VSMCheckBox");
+ App.Tap("VSMCheckBox");
+ var stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Unchecked"));
+ App.WaitForElement("CheckBoxReset");
+ App.Tap("CheckBoxReset");
+ stateText = App.FindElement("CheckBoxState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+}
+
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_CollectionViewFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_CollectionViewFeatureTests.cs
new file mode 100644
index 000000000000..11d9f2d997af
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_CollectionViewFeatureTests.cs
@@ -0,0 +1,389 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+[Category(UITestCategories.VisualStateManager)]
+public class VisualStateManager_CollectionViewFeatureTests : _GalleryUITest
+{
+ public const string VisualStateManagerCollectionViewFeatureTests = "VisualStateManager Feature Matrix";
+ public override string GalleryPageName => VisualStateManagerCollectionViewFeatureTests;
+
+ public VisualStateManager_CollectionViewFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+// PointerOver states cannot currently be reliably covered in CI environments, as hover/pointer interactions are not consistently supported in automated runs. Therefore, these states are validated manually on Mac and Windows, and PointerOver-related tests have not been included in the automated test cases.
+ [Test, Order(1)]
+ public void VerifyVSM_CollectionView_InitialState()
+ {
+ App.WaitForElement("VSMCollectionViewButton");
+ App.Tap("VSMCollectionViewButton");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(2)]
+ public void VerifyVSM_CollectionView_Selected()
+ {
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Does.Contain("State: Selected (1)"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(3)]
+ public void VerifyVSM_CollectionView_Normal()
+ {
+ App.WaitForElement("CVNormal");
+ App.Tap("CVNormal");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal/Unselected"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(4)]
+ public void VerifyVSM_CollectionView_Disabled()
+ {
+ App.WaitForElement("CVDisable");
+ App.Tap("CVDisable");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(5)]
+ public void VerifyVSM_CollectionView_Reset()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ VerifyScreenshot();
+ }
+
+#if TEST_FAILS_ON_CATALYST //related issue: https://github.com/dotnet/maui/issues/18028
+ [Test, Order(6)]
+ public void VerifyVSM_CollectionView_Selected_Multiple()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ App.WaitForElement("Cherry");
+ App.Tap("Cherry");
+ App.WaitForElement("Date");
+ App.Tap("Date");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Does.Contain("State: Selected (3)"));
+ VerifyScreenshot();
+ }
+#endif
+
+ [Test, Order(7)]
+ public void VerifyVSM_CollectionView_DisableWhileNormal()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("CVNormal");
+ App.Tap("CVNormal");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal/Unselected"));
+ App.WaitForElement("CVDisable");
+ App.Tap("CVDisable");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ }
+
+ [Test, Order(8)]
+ public void VerifyVSM_CollectionView_DisableWhileSelected()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("CVSelectItem");
+ App.Tap("CVSelectItem");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Does.Contain("State: Selected (1)"));
+ App.WaitForElement("CVDisable");
+ App.Tap("CVDisable");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ }
+
+ [Test, Order(9)]
+ public void VerifyVSM_CollectionView_ResetWhileDisabled()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("CVDisable");
+ App.Tap("CVDisable");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+
+ [Test, Order(10)]
+ public void VerifyVSM_CollectionView_ResetWhileNormal()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("CVNormal");
+ App.Tap("CVNormal");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal/Unselected"));
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+
+ [Test, Order(11)]
+ public void VerifyVSM_CollectionView_ResetWhileSelected()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("CVSelectItem");
+ App.Tap("CVSelectItem");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Does.Contain("State: Selected (1)"));
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+
+#if TEST_FAILS_ON_CATALYST //related issue: https://github.com/dotnet/maui/issues/18028
+ [Test, Order(12)]
+ public void VerifyVSM_CollectionView_ResetWhileMultipleSelected()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ App.WaitForElement("Cherry");
+ App.Tap("Cherry");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Does.Contain("State: Selected (2)"));
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+
+ [Test, Order(13)]
+ public void VerifyVSM_CollectionView_SelectAndUnselectItem_UsingTap()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Does.Contain("State: Selected (1)"));
+ App.Tap("Banana");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+#endif
+
+ [Test, Order(14)]
+ public void VerifyVSM_CollectionView_SelectItem_UsingButton()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("CVSelectItem");
+ App.Tap("CVSelectItem");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Does.Contain("State: Selected (1)"));
+ App.WaitForElement("CVNormal");
+ App.Tap("CVNormal");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal/Unselected"));
+ }
+
+#if TEST_FAILS_ON_CATALYST //related issue: https://github.com/dotnet/maui/issues/18028
+ [Test, Order(15)]
+ public void VerifyVSM_CollectionView_SelectMultipleItems_UsingTap()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ App.WaitForElement("Cherry");
+ App.Tap("Cherry");
+ App.WaitForElement("Grape");
+ App.Tap("Grape");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Does.Contain("State: Selected (3)"));
+ App.Tap("Cherry");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Does.Contain("State: Selected (2)"));
+ }
+#endif
+
+ [Test, Order(16)]
+ [Ignore("Fails on all platforms. Related issue: https://github.com/dotnet/maui/issues/20615")]
+ public void VerifyVSM_CollectionView_DisableAndEnableWhileSelected()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Does.Contain("State: Selected (1)"));
+ App.WaitForElement("CVDisable");
+ App.Tap("CVDisable");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("CVDisable");
+ App.Tap("CVDisable");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText,Is.EqualTo("State: Selected (1)"));
+ }
+
+ [Test, Order(17)]
+ public void VerifyVSM_CollectionView_DisableAndEnableWhileUnselected()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("CVNormal");
+ App.Tap("CVNormal");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal/Unselected"));
+ App.WaitForElement("CVDisable");
+ App.Tap("CVDisable");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("CVDisable");
+ App.Tap("CVDisable");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+
+#if TEST_FAILS_ON_CATALYST //related issue: https://github.com/dotnet/maui/issues/18028
+ [Test, Order(18)]
+ public void VerifyVSM_CollectionView_DisableEnableWhileSelectMultipleItems()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ App.WaitForElement("Honeydew");
+ App.Tap("Honeydew");
+ App.WaitForElement("Grape");
+ App.Tap("Grape");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Does.Contain("State: Selected (3)"));
+ App.WaitForElement("CVDisable");
+ App.Tap("CVDisable");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("CVDisable");
+ App.Tap("CVDisable");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Selected (3)"));
+ }
+#endif
+
+ [Test, Order(19)]
+ [Ignore("Fails on all platforms. Related issue: https://github.com/dotnet/maui/issues/20615")]
+ public void VerifyVSM_CollectionView_SelectedWhileDisabled()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("CVDisable");
+ App.Tap("CVDisable");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ }
+
+ [Test, Order(20)]
+ [Ignore("Fails on all platforms. Related issue: https://github.com/dotnet/maui/issues/20615")]
+ public void VerifyVSM_CollectionView_SelectedMultipleWhileDisabled()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("CVDisable");
+ App.Tap("CVDisable");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ App.WaitForElement("Cherry");
+ App.Tap("Cherry");
+ App.WaitForElement("Fig");
+ App.Tap("Fig");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ }
+
+ [Test, Order(21)]
+ [Ignore("Fails on all platforms. Related issue: https://github.com/dotnet/maui/issues/20615")]
+ public void VerifyVSM_CollectionView_UnselectWhileDisabled()
+ {
+ App.WaitForElement("CVReset");
+ App.Tap("CVReset");
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ App.WaitForElement("CVState");
+ var stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Does.Contain("State: Selected (1)"));
+ App.WaitForElement("CVDisable");
+ App.Tap("CVDisable");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("Banana");
+ App.Tap("Banana");
+ App.WaitForElement("CVState");
+ stateText = App.FindElement("CVState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ }
+}
+
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_EntryFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_EntryFeatureTests.cs
new file mode 100644
index 000000000000..03eb24e068c2
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_EntryFeatureTests.cs
@@ -0,0 +1,403 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+[Category(UITestCategories.VisualStateManager)]
+public class VisualStateManager_EntryFeatureTests : _GalleryUITest
+{
+ public const string VisualStateManagerEntryFeatureTests = "VisualStateManager Feature Matrix";
+ public override string GalleryPageName => VisualStateManagerEntryFeatureTests;
+
+ public VisualStateManager_EntryFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+ [Test, Order(1)]
+ public void VerifyVSM_Entry_InitialState()
+ {
+ App.WaitForElement("VSMEntryButton");
+ App.Tap("VSMEntryButton");
+ App.WaitForElement("EntryState");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(2)]
+ public void VerifyVSM_Entry_Focus()
+ {
+ App.WaitForElement("VSMEntry");
+ App.Tap("VSMEntry");
+ App.WaitForElement("EntryState");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Focused"));
+#if ANDROID
+ VerifyScreenshot(cropBottom: 1150);
+ if(App.IsKeyboardShown())
+ App.DismissKeyboard();
+#elif IOS
+ VerifyScreenshot(cropBottom: 1200);
+ if(App.IsKeyboardShown())
+ App.DismissKeyboard();
+#else
+ VerifyScreenshot();
+#endif
+ }
+
+ [Test, Order(3)]
+ public void VerifyVSM_Entry_Unfocus()
+ {
+ App.WaitForElement("NormalEntryButton");
+ App.Tap("NormalEntryButton");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal/Unfocused"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(4)]
+ public void VerifyVSM_Entry_Disable()
+ {
+ App.WaitForElement("DisableEntryButton");
+ App.Tap("DisableEntryButton");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(5)]
+ public void VerifyVSM_Entry_Reset()
+ {
+ App.WaitForElement("ResetEntryButton");
+ App.Tap("ResetEntryButton");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(6)]
+ public void VerifyVSM_Entry_DisableWhileFocused()
+ {
+ App.WaitForElement("ResetEntryButton");
+ App.Tap("ResetEntryButton");
+ App.WaitForElement("VSMEntry");
+ App.Tap("VSMEntry");
+ App.EnterText("VSMEntry", "Testing");
+ App.WaitForElement("FocusEntryButton");
+#if ANDROID || IOS
+ if (App.IsKeyboardShown())
+ App.DismissKeyboard();
+#endif
+ App.Tap("FocusEntryButton");
+ App.WaitForElement("EntryState");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Focused"));
+ App.WaitForElement("DisableEntryButton");
+ App.Tap("DisableEntryButton");
+ stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(7)]
+ public void VerifyVSM_Entry_DisableWhileUnFocused()
+ {
+ App.WaitForElement("ResetEntryButton");
+ App.Tap("ResetEntryButton");
+ App.WaitForElement("NormalEntryButton");
+ App.Tap("NormalEntryButton");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal/Unfocused"));
+ App.WaitForElement("DisableEntryButton");
+ App.Tap("DisableEntryButton");
+ stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(8)]
+ public void VerifyVSM_Entry_DisableWhileReset()
+ {
+ App.WaitForElement("ResetEntryButton");
+ App.Tap("ResetEntryButton");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ App.WaitForElement("DisableEntryButton");
+ App.Tap("DisableEntryButton");
+ stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(9)]
+ public void VerifyVSM_Entry_DisableAndEnableWhileFocused()
+ {
+ App.WaitForElement("ResetEntryButton");
+ App.Tap("ResetEntryButton");
+ App.WaitForElement("VSMEntry");
+ App.Tap("VSMEntry");
+ App.EnterText("VSMEntry", "Testing");
+ App.WaitForElement("FocusEntryButton");
+#if ANDROID || IOS
+ if (App.IsKeyboardShown())
+ App.DismissKeyboard();
+#endif
+ App.Tap("FocusEntryButton");
+ App.WaitForElement("EntryState");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Focused"));
+ App.WaitForElement("DisableEntryButton");
+ App.Tap("DisableEntryButton");
+ stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("DisableEntryButton");
+ App.Tap("DisableEntryButton");
+ stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(10)]
+ public void VerifyVSM_Entry_DisableAndEnableWhileUnFocused()
+ {
+ App.WaitForElement("ResetEntryButton");
+ App.Tap("ResetEntryButton");
+ App.WaitForElement("NormalEntryButton");
+ App.Tap("NormalEntryButton");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal/Unfocused"));
+ App.WaitForElement("DisableEntryButton");
+ App.Tap("DisableEntryButton");
+ stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("DisableEntryButton");
+ App.Tap("DisableEntryButton");
+ stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(11)]
+ public void VerifyVSM_Entry_FocusedAndUnfocused()
+ {
+ App.WaitForElement("ResetEntryButton");
+ App.Tap("ResetEntryButton");
+ App.WaitForElement("VSMEntry");
+ App.Tap("VSMEntry");
+ App.WaitForElement("EntryState");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Focused"));
+ App.WaitForElement("NormalEntryButton");
+ App.Tap("NormalEntryButton");
+ App.WaitForElement("EntryState");
+ stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal/Unfocused"));
+ }
+
+ [Test, Order(12)]
+ public void VerifyVSM_Entry_ResetWhileDisabled()
+ {
+ App.WaitForElement("ResetEntryButton");
+ App.Tap("ResetEntryButton");
+ App.WaitForElement("DisableEntryButton");
+ App.Tap("DisableEntryButton");
+ App.WaitForElement("EntryState");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("ResetEntryButton");
+ App.Tap("ResetEntryButton");
+ App.WaitForElement("EntryState");
+ stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+
+ [Test, Order(13)]
+ public void VerifyVSM_Entry_FocusedWhileDisabled()
+ {
+ App.WaitForElement("ResetEntryButton");
+ App.Tap("ResetEntryButton");
+ App.WaitForElement("DisableEntryButton");
+ App.Tap("DisableEntryButton");
+ App.WaitForElement("VSMEntry");
+ App.Tap("VSMEntry");
+ App.WaitForElement("EntryState");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ }
+
+ [Test, Order(14)]
+ public void VerifyVSM_Entry_UnfocusedWhileDisabled()
+ {
+ App.WaitForElement("ResetEntryButton");
+ App.Tap("ResetEntryButton");
+ App.WaitForElement("DisableEntryButton");
+ App.Tap("DisableEntryButton");
+ App.WaitForElement("EntryState");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("NormalEntryButton");
+ App.Tap("NormalEntryButton");
+ App.WaitForElement("EntryState");
+ stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ }
+
+ [Test, Order(15)]
+ public void VerifyVSM_Entry_Validate()
+ {
+ App.WaitForElement("ResetValidationEntryButton");
+ App.Tap("ResetValidationEntryButton");
+ App.WaitForElement("ValidationEntry");
+ App.Tap("ValidationEntry");
+ App.EnterText("ValidationEntry", "965-999-9999");
+#if ANDROID || IOS
+ if (App.IsKeyboardShown())
+ App.DismissKeyboard();
+#endif
+ App.WaitForElement("ValidationEntryLabel");
+ var stateText = App.FindElement("ValidationEntryLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Valid"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(16)]
+ public void VerifyVSM_Entry_Invalid()
+ {
+ App.WaitForElement("ResetValidationEntryButton");
+ App.Tap("ResetValidationEntryButton");
+ App.WaitForElement("ValidationEntry");
+ App.Tap("ValidationEntry");
+ App.EnterText("ValidationEntry", "Invalid");
+#if ANDROID || IOS
+ if (App.IsKeyboardShown())
+ App.DismissKeyboard();
+#endif
+ App.WaitForElement("ValidationEntryLabel");
+ var stateText = App.FindElement("ValidationEntryLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Invalid"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(17)]
+ public void VerifyVSM_Entry_ResetValidation()
+ {
+ App.WaitForElement("ResetValidationEntryButton");
+ App.Tap("ResetValidationEntryButton");
+ App.WaitForElement("ValidationEntry");
+ App.Tap("ValidationEntry");
+ App.EnterText("ValidationEntry", "777-777-7777");
+#if ANDROID || IOS
+ if (App.IsKeyboardShown())
+ App.DismissKeyboard();
+#endif
+ App.WaitForElement("ValidationEntryLabel");
+ var stateText = App.FindElement("ValidationEntryLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Valid"));
+ App.WaitForElement("ResetValidationEntryButton");
+ App.Tap("ResetValidationEntryButton");
+ App.WaitForElement("ValidationEntryLabel");
+ stateText = App.FindElement("ValidationEntryLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Invalid"));
+ }
+
+ [Test, Order(18)]
+ public void VerifyVSM_Entry_ValidToInvalid()
+ {
+ App.WaitForElement("ResetValidationEntryButton");
+ App.Tap("ResetValidationEntryButton");
+ App.WaitForElement("ValidationEntry");
+ App.Tap("ValidationEntry");
+ App.EnterText("ValidationEntry", "965-999-9999");
+#if ANDROID || IOS
+ if (App.IsKeyboardShown())
+ App.DismissKeyboard();
+#endif
+ App.WaitForElement("ValidationEntryLabel");
+ var stateText = App.FindElement("ValidationEntryLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Valid"));
+ App.WaitForElement("ValidateEntryButton");
+ App.Tap("ValidateEntryButton");
+ App.ClearText("ValidationEntry");
+ App.EnterText("ValidationEntry", "6789-456-1234");
+#if ANDROID || IOS
+ if (App.IsKeyboardShown())
+ App.DismissKeyboard();
+#endif
+ App.WaitForElement("ValidationEntryLabel");
+ stateText = App.FindElement("ValidationEntryLabel").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Invalid"));
+ }
+#if TEST_FAILS_ON_ANDROID // When PressEnter() is triggered to verify the Completed state, the Entry automatically becomes visually unfocused. Therefore, the test currently fails on Android in automation but passes during manual testing.
+ [Test, Order(19)]
+ public void VerifyVSM_Entry_Completed()
+ {
+ App.WaitForElement("ResetEntryButton");
+ App.Tap("ResetEntryButton");
+ App.WaitForElement("VSMEntry");
+ App.Tap("VSMEntry");
+ App.EnterText("VSMEntry", "Hello");
+ App.PressEnter();
+ App.WaitForElement("EntryState");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Completed"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(20)]
+ public void VerifyVSM_Entry_CompletedAndReset()
+ {
+ App.WaitForElement("ResetEntryButton");
+ App.Tap("ResetEntryButton");
+ App.WaitForElement("VSMEntry");
+ App.Tap("VSMEntry");
+ App.EnterText("VSMEntry", "Hello");
+ App.PressEnter();
+ App.WaitForElement("EntryState");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Completed"));
+ App.WaitForElement("ResetEntryButton");
+ App.Tap("ResetEntryButton");
+ stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+
+ [Test, Order(21)]
+ public void VerifyVSM_Entry_CompletedAndRefocused()
+ {
+ App.WaitForElement("ResetEntryButton");
+ App.Tap("ResetEntryButton");
+ App.WaitForElement("VSMEntry");
+ App.Tap("VSMEntry");
+ App.EnterText("VSMEntry", "Hello");
+ App.PressEnter();
+ App.WaitForElement("EntryState");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Completed"));
+ App.WaitForElement("FocusEntryButton");
+ App.Tap("FocusEntryButton");
+ stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Focused"));
+ }
+
+ [Test, Order(22)]
+ public void VerifyVSM_Entry_DisableWhileCompleted()
+ {
+ App.WaitForElement("ResetEntryButton");
+ App.Tap("ResetEntryButton");
+ App.WaitForElement("VSMEntry");
+ App.Tap("VSMEntry");
+ App.EnterText("VSMEntry", "Hello");
+ App.PressEnter();
+ App.WaitForElement("EntryState");
+ var stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Completed"));
+ App.WaitForElement("DisableEntryButton");
+ App.Tap("DisableEntryButton");
+ stateText = App.FindElement("EntryState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ }
+#endif
+}
+
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_LabelFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_LabelFeatureTests.cs
new file mode 100644
index 000000000000..9ed598b3fb01
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_LabelFeatureTests.cs
@@ -0,0 +1,210 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+[Category(UITestCategories.VisualStateManager)]
+public class VisualStateManager_LabelFeatureTests : _GalleryUITest
+{
+ public const string VisualStateManagerLabelFeatureTests = "VisualStateManager Feature Matrix";
+ public override string GalleryPageName => VisualStateManagerLabelFeatureTests;
+
+ public VisualStateManager_LabelFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+ [Test, Order(1)]
+ public void VerifyVSM_Label_InitialState()
+ {
+ App.WaitForElement("VSMLabelButton");
+ App.Tap("VSMLabelButton");
+ App.WaitForElement("LabelState");
+ var labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Normal"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(2)]
+ public void VerifyVSM_Label_Selected()
+ {
+ App.WaitForElement("SelectableLabelContainer");
+ App.Tap("SelectableLabelContainer");
+ App.WaitForElement("LabelState");
+ var labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Selected"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(3)]
+ public void VerifyVSM_Label_Disabled()
+ {
+ App.WaitForElement("LabelDisable");
+ App.Tap("LabelDisable");
+ App.WaitForElement("LabelState");
+ var labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Disabled"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(4)]
+ public void VerifyVSM_Label_Reset()
+ {
+ App.WaitForElement("LabelReset");
+ App.Tap("LabelReset");
+ App.WaitForElement("LabelState");
+ var labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Normal"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(5)]
+ public void VerifyVSM_Label_DisableWhileSelected()
+ {
+ App.WaitForElement("LabelReset");
+ App.Tap("LabelReset");
+ App.WaitForElement("SelectableLabelContainer");
+ App.Tap("SelectableLabelContainer");
+ App.WaitForElement("LabelState");
+ var labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Selected"));
+ App.WaitForElement("LabelDisable");
+ App.Tap("LabelDisable");
+ App.WaitForElement("LabelState");
+ labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Disabled"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(6)]
+ public void VerifyVSM_Label_DisableWhileNormal()
+ {
+ App.WaitForElement("LabelReset");
+ App.Tap("LabelReset");
+ App.WaitForElement("LabelDisable");
+ App.Tap("LabelDisable");
+ App.WaitForElement("LabelState");
+ var labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Disabled"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(7)]
+ public void VerifyVSM_Label_DisableAndEnableWhileSelected()
+ {
+ App.WaitForElement("LabelReset");
+ App.Tap("LabelReset");
+ App.WaitForElement("SelectableLabelContainer");
+ App.Tap("SelectableLabelContainer");
+ App.WaitForElement("LabelState");
+ var labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Selected"));
+ App.WaitForElement("LabelDisable");
+ App.Tap("LabelDisable");
+ App.WaitForElement("LabelState");
+ labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("LabelDisable");
+ App.Tap("LabelDisable");
+ App.WaitForElement("LabelState");
+ labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Selected"));
+ }
+
+ [Test, Order(8)]
+ public void VerifyVSM_Label_DisableAndEnableWhileNormal()
+ {
+ App.WaitForElement("LabelReset");
+ App.Tap("LabelReset");
+ App.WaitForElement("LabelDisable");
+ App.Tap("LabelDisable");
+ App.WaitForElement("LabelState");
+ var labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("LabelDisable");
+ App.Tap("LabelDisable");
+ App.WaitForElement("LabelState");
+ labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Normal"));
+ }
+
+ [Test, Order(9)]
+ public void VerifyVSM_Label_ResetWhileSelected()
+ {
+ App.WaitForElement("LabelReset");
+ App.Tap("LabelReset");
+ App.WaitForElement("SelectableLabelContainer");
+ App.Tap("SelectableLabelContainer");
+ App.WaitForElement("LabelState");
+ var labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Selected"));
+ App.WaitForElement("LabelReset");
+ App.Tap("LabelReset");
+ App.WaitForElement("LabelState");
+ labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Normal"));
+ }
+
+ [Test, Order(10)]
+ public void VerifyVSM_Label_ResetWhileDisabled()
+ {
+ App.WaitForElement("LabelReset");
+ App.Tap("LabelReset");
+ App.WaitForElement("LabelDisable");
+ App.Tap("LabelDisable");
+ App.WaitForElement("LabelState");
+ var labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("LabelReset");
+ App.Tap("LabelReset");
+ App.WaitForElement("LabelState");
+ labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Normal"));
+ }
+
+ [Test, Order(11)]
+ public void VerifyVSM_Label_SelectedToDisabledToSelected()
+ {
+ App.WaitForElement("LabelReset");
+ App.Tap("LabelReset");
+ App.WaitForElement("SelectableLabelContainer");
+ App.Tap("SelectableLabelContainer");
+ App.WaitForElement("LabelState");
+ var labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Selected"));
+ App.WaitForElement("LabelDisable");
+ App.Tap("LabelDisable");
+ App.WaitForElement("LabelState");
+ labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("LabelDisable");
+ App.Tap("LabelDisable");
+ App.WaitForElement("LabelState");
+ labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Selected"));
+ }
+
+ [Test, Order(12)]
+ public void VerifyVSM_Label_CannotSelectWhileDisabled()
+ {
+ App.WaitForElement("LabelReset");
+ App.Tap("LabelReset");
+ App.WaitForElement("SelectableLabelContainer");
+ App.Tap("SelectableLabelContainer");
+ App.WaitForElement("LabelState");
+ var labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Selected"));
+ App.WaitForElement("LabelDisable");
+ App.Tap("LabelDisable");
+ App.WaitForElement("LabelState");
+ labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("SelectableLabelContainer");
+ App.Tap("SelectableLabelContainer");
+ App.WaitForElement("LabelState");
+ labelText = App.FindElement("LabelState").GetText();
+ Assert.That(labelText, Is.EqualTo("State: Disabled"));
+ }
+}
+
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_SliderFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_SliderFeatureTests.cs
new file mode 100644
index 000000000000..c903e5707878
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_SliderFeatureTests.cs
@@ -0,0 +1,325 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+[Category(UITestCategories.VisualStateManager)]
+public class VisualStateManager_SliderFeatureTests : _GalleryUITest
+{
+ public const string VisualStateManagerSliderFeatureTests = "VisualStateManager Feature Matrix";
+ public override string GalleryPageName => VisualStateManagerSliderFeatureTests;
+
+ public VisualStateManager_SliderFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+ [Test, Order(1)]
+ public void VerifyVSM_Slider_NormalState()
+ {
+ App.WaitForElement("VSMSliderButton");
+ App.Tap("VSMSliderButton");
+ App.WaitForElement("SliderState");
+ var stateText = App.FindElement("SliderState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal | Value: 50"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(2)]
+ public void VerifyVSM_Slider_NormalOrUnfocusedState()
+ {
+ App.WaitForElement("SliderNormal");
+ App.Tap("SliderNormal");
+ App.WaitForElement("SliderState");
+ var stateText1 = App.FindElement("SliderState").GetText();
+ Assert.That(stateText1, Is.EqualTo("State: Normal/Unfocused | Value: 50"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(3)]
+ public void VerifyVSM_Slider_DisabledState()
+ {
+ App.WaitForElement("SliderDisable");
+ App.Tap("SliderDisable");
+ App.WaitForElement("SliderState");
+ var stateText = App.FindElement("SliderState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled | Value: 50"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(4)]
+ public void VerifyVSM_Slider_ResetState()
+ {
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("SliderState");
+ var stateText = App.FindElement("SliderState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal | Value: 50"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(5)]
+ public void VerifyVSM_Slider_FocusedState()
+ {
+ App.WaitForElement("SliderFocus");
+ App.Tap("SliderFocus");
+ App.WaitForElement("SliderState");
+ var stateText = App.FindElement("SliderState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Focused | Value: 65"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(6)]
+ public void VerifyVSM_Slider_ResetWhileFocused()
+ {
+ App.WaitForElement("VSMSlider");
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("SliderFocus");
+ App.Tap("SliderFocus");
+ App.WaitForElement("SliderState");
+ var initialStateText = App.FindElement("SliderState").GetText();
+ Assert.That(initialStateText, Is.EqualTo("State: Focused | Value: 65"));
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("SliderState");
+ var stateText = App.FindElement("SliderState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal | Value: 50"));
+ }
+
+ [Test, Order(7)]
+ public void VerifyVSM_Slider_ResetWhileUnfocused()
+ {
+ App.WaitForElement("VSMSlider");
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("SliderNormal");
+ App.Tap("SliderNormal");
+ App.WaitForElement("SliderState");
+ var stateText = App.FindElement("SliderState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal/Unfocused | Value: 50"));
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("SliderState");
+ var resetStateText = App.FindElement("SliderState").GetText();
+ Assert.That(resetStateText, Is.EqualTo("State: Normal | Value: 50"));
+ }
+
+ [Test, Order(8)]
+ public void VerifyVSM_Slider_ResetWhileDisabled()
+ {
+ App.WaitForElement("VSMSlider");
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("SliderDisable");
+ App.Tap("SliderDisable");
+ App.WaitForElement("SliderState");
+ var stateText = App.FindElement("SliderState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled | Value: 50"));
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("SliderState");
+ var resetStateText = App.FindElement("SliderState").GetText();
+ Assert.That(resetStateText, Is.EqualTo("State: Normal | Value: 50"));
+ }
+
+ [Test, Order(9)]
+ public void VerifyVSM_Slider_DisableWhileFocused()
+ {
+ App.WaitForElement("VSMSlider");
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("SliderFocus");
+ App.Tap("SliderFocus");
+ App.WaitForElement("SliderState");
+ var initialStateText = App.FindElement("SliderState").GetText();
+ Assert.That(initialStateText, Is.EqualTo("State: Focused | Value: 65"));
+ App.WaitForElement("SliderDisable");
+ App.Tap("SliderDisable");
+ App.WaitForElement("SliderState");
+ var stateText = App.FindElement("SliderState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled | Value: 65"));
+ }
+
+ [Test, Order(10)]
+ public void VerifyVSM_Slider_DisableWhileUnfocused()
+ {
+ App.WaitForElement("VSMSlider");
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("SliderNormal");
+ App.Tap("SliderNormal");
+ App.WaitForElement("SliderState");
+ var initialStateText = App.FindElement("SliderState").GetText();
+ Assert.That(initialStateText, Is.EqualTo("State: Normal/Unfocused | Value: 50"));
+ App.WaitForElement("SliderDisable");
+ App.Tap("SliderDisable");
+ App.WaitForElement("SliderState");
+ var stateText = App.FindElement("SliderState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled | Value: 50"));
+ }
+
+ [Test, Order(11)]
+ public void VerifyVSM_Slider_FocusedWhileDisabled()
+ {
+ App.WaitForElement("VSMSlider");
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("SliderFocus");
+ App.Tap("SliderFocus");
+ App.WaitForElement("SliderState");
+ var initialStateText = App.FindElement("SliderState").GetText();
+ Assert.That(initialStateText, Is.EqualTo("State: Focused | Value: 65"));
+ App.WaitForElement("SliderDisable");
+ App.Tap("SliderDisable");
+ App.WaitForElement("SliderState");
+ var stateText = App.FindElement("SliderState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled | Value: 65"));
+ App.WaitForElement("SliderFocus");
+ App.Tap("SliderFocus");
+ App.WaitForElement("SliderState");
+ var focusedDisabledStateText = App.FindElement("SliderState").GetText();
+ Assert.That(focusedDisabledStateText, Is.EqualTo("State: Disabled | Value: 65"));
+ }
+
+ [Test, Order(12)]
+ public void VerifyVSM_Slider_UnfocusToFocus()
+ {
+ App.WaitForElement("VSMSlider");
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("SliderNormal");
+ App.Tap("SliderNormal");
+ App.WaitForElement("SliderState");
+ var initialStateText = App.FindElement("SliderState").GetText();
+ Assert.That(initialStateText, Is.EqualTo("State: Normal/Unfocused | Value: 50"));
+ App.WaitForElement("SliderFocus");
+ App.Tap("SliderFocus");
+ App.WaitForElement("SliderState");
+ var focusedStateText = App.FindElement("SliderState").GetText();
+ Assert.That(focusedStateText, Is.EqualTo("State: Focused | Value: 65"));
+ }
+
+ [Test, Order(13)]
+ public void VerifyVSM_Slider_FocusToUnFocus()
+ {
+ App.WaitForElement("VSMSlider");
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("SliderFocus");
+ App.Tap("SliderFocus");
+ App.WaitForElement("SliderState");
+ var focusedStateText = App.FindElement("SliderState").GetText();
+ Assert.That(focusedStateText, Is.EqualTo("State: Focused | Value: 65"));
+ App.WaitForElement("SliderNormal");
+ App.Tap("SliderNormal");
+ App.WaitForElement("SliderState");
+ var unfocusedStateText = App.FindElement("SliderState").GetText();
+ Assert.That(unfocusedStateText, Is.EqualTo("State: Normal/Unfocused | Value: 65"));
+ }
+
+ [Test, Order(14)]
+ public void VerifyVSM_Slider_DisableAndEnableWhileUnfocused()
+ {
+ App.WaitForElement("VSMSlider");
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("SliderNormal");
+ App.Tap("SliderNormal");
+ App.WaitForElement("SliderState");
+ var unfocusedStateText = App.FindElement("SliderState").GetText();
+ Assert.That(unfocusedStateText, Is.EqualTo("State: Normal/Unfocused | Value: 50"));
+ App.WaitForElement("SliderDisable");
+ App.Tap("SliderDisable");
+ App.WaitForElement("SliderState");
+ var disabledStateText = App.FindElement("SliderState").GetText();
+ Assert.That(disabledStateText, Is.EqualTo("State: Disabled | Value: 50"));
+ App.WaitForElement("SliderDisable");
+ App.Tap("SliderDisable");
+ App.WaitForElement("SliderState");
+ var unfocusedDisabledStateText = App.FindElement("SliderState").GetText();
+ Assert.That(unfocusedDisabledStateText, Is.EqualTo("State: Normal | Value: 50"));
+ }
+
+ [Test, Order(15)]
+ public void VerifyVSM_Slider_DisableAndEnableWhileFocused()
+ {
+ App.WaitForElement("VSMSlider");
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("SliderFocus");
+ App.Tap("SliderFocus");
+ App.WaitForElement("SliderState");
+ var focusedStateText = App.FindElement("SliderState").GetText();
+ Assert.That(focusedStateText, Is.EqualTo("State: Focused | Value: 65"));
+ App.WaitForElement("SliderDisable");
+ App.Tap("SliderDisable");
+ App.WaitForElement("SliderState");
+ var disabledStateText = App.FindElement("SliderState").GetText();
+ Assert.That(disabledStateText, Is.EqualTo("State: Disabled | Value: 65"));
+ App.WaitForElement("SliderDisable");
+ App.Tap("SliderDisable");
+ App.WaitForElement("SliderState");
+ var focusedEnabledStateText = App.FindElement("SliderState").GetText();
+ Assert.That(focusedEnabledStateText, Is.EqualTo("State: Normal | Value: 65"));
+ }
+
+ [Test, Order(16)]
+ public void VerifyVSM_Slider_DisableAndEnableWhileReset()
+ {
+ App.WaitForElement("VSMSlider");
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("SliderState");
+ var unfocusedStateText = App.FindElement("SliderState").GetText();
+ Assert.That(unfocusedStateText, Is.EqualTo("State: Normal | Value: 50"));
+ App.WaitForElement("SliderDisable");
+ App.Tap("SliderDisable");
+ App.WaitForElement("SliderState");
+ var disabledStateText = App.FindElement("SliderState").GetText();
+ Assert.That(disabledStateText, Is.EqualTo("State: Disabled | Value: 50"));
+ App.WaitForElement("SliderDisable");
+ App.Tap("SliderDisable");
+ App.WaitForElement("SliderState");
+ var unfocusedEnabledStateText = App.FindElement("SliderState").GetText();
+ Assert.That(unfocusedEnabledStateText, Is.EqualTo("State: Normal | Value: 50"));
+ }
+
+ [Test, Order(17)]
+ public void VerifyVSM_Slider_DragToFocus()
+ {
+ App.WaitForElement("VSMSlider");
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("VSMSlider");
+ var sliderRect = App.WaitForElement("VSMSlider").GetRect();
+ var startX = sliderRect.X + (sliderRect.Width * 50 / 100);
+ var centerY = sliderRect.Y + (sliderRect.Height / 2);
+ var endX = sliderRect.X + (sliderRect.Width * 35 / 100);
+ App.DragCoordinates(startX, centerY, endX, centerY);
+ App.WaitForElement("SliderState");
+ var focusedStateText = App.FindElement("SliderState").GetText();
+ Assert.That(focusedStateText, Does.Contain("State: Focused"));
+ }
+
+ [Test, Order(18)]
+ public void VerifyVSM_Slider_DragWhileDisabled()
+ {
+ App.WaitForElement("VSMSlider");
+ App.WaitForElement("SliderReset");
+ App.Tap("SliderReset");
+ App.WaitForElement("SliderDisable");
+ App.Tap("SliderDisable");
+ App.WaitForElement("VSMSlider");
+ var sliderRect = App.WaitForElement("VSMSlider").GetRect();
+ var startX = sliderRect.X + (sliderRect.Width * 50 / 100);
+ var centerY = sliderRect.Y + (sliderRect.Height / 2);
+ var endX = sliderRect.X + (sliderRect.Width * 35 / 100);
+ App.DragCoordinates(startX, centerY, endX, centerY);
+ App.WaitForElement("SliderState");
+ var disabledStateText = App.FindElement("SliderState").GetText();
+ Assert.That(disabledStateText, Is.EqualTo("State: Disabled | Value: 50"));
+ }
+}
+
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_SwitchFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_SwitchFeatureTests.cs
new file mode 100644
index 000000000000..aad06bb9f2ff
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualStateManager_SwitchFeatureTests.cs
@@ -0,0 +1,281 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+[Category(UITestCategories.VisualStateManager)]
+public class VisualStateManager_SwitchFeatureTests : _GalleryUITest
+{
+ public const string VisualStateManagerSwitchFeatureTests = "VisualStateManager Feature Matrix";
+ public override string GalleryPageName => VisualStateManagerSwitchFeatureTests;
+
+ public VisualStateManager_SwitchFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+ [Test, Order(1)]
+ public void VerifyVSM_Switch_InitialState()
+ {
+ App.WaitForElement("VSMSwitchButton");
+ App.Tap("VSMSwitchButton");
+ App.WaitForElement("VSMSwitch");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Off"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(2)]
+ public void VerifyVSM_Switch_On()
+ {
+ App.WaitForElement("VSMSwitch");
+ App.Tap("VSMSwitch");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: On"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(3)]
+ public void VerifyVSM_Switch_Off()
+ {
+ App.WaitForElement("VSMSwitch");
+ App.Tap("VSMSwitch");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Off"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(4)]
+ public void VerifyVSM_Switch_Reset()
+ {
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(5)]
+ public void VerifyVSM_Switch_OnToOff()
+ {
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ App.WaitForElement("VSMSwitch");
+ App.Tap("VSMSwitch");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: On"));
+ App.Tap("VSMSwitch");
+ stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Off"));
+ }
+
+ [Test, Order(6)]
+ public void VerifyVSM_Switch_OffToOn()
+ {
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ App.WaitForElement("VSMSwitch");
+ App.Tap("VSMSwitch");
+ App.WaitForElement("VSMSwitch");
+ App.Tap("VSMSwitch");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Off"));
+ App.Tap("VSMSwitch");
+ stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: On"));
+ }
+
+ [Test, Order(7)]
+ public void VerifyVSM_Switch_DisableWhileOn()
+ {
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ App.WaitForElement("VSMSwitch");
+ App.Tap("VSMSwitch");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: On"));
+ App.WaitForElement("SwitchDisable");
+ App.Tap("SwitchDisable");
+ stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(8)]
+ public void VerifyVSM_Switch_DisableWhileOff()
+ {
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ App.WaitForElement("SwitchDisable");
+ App.Tap("SwitchDisable");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ VerifyScreenshot();
+ }
+
+ [Test, Order(9)]
+ public void VerifyVSM_Switch_ResetWhileDisabled()
+ {
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ App.WaitForElement("SwitchDisable");
+ App.Tap("SwitchDisable");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+
+ [Test, Order(10)]
+ public void VerifyVSM_Switch_DisableAndResetWhileOn()
+ {
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ App.WaitForElement("VSMSwitch");
+ App.Tap("VSMSwitch");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: On"));
+ App.WaitForElement("SwitchDisable");
+ App.Tap("SwitchDisable");
+ stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+
+ [Test, Order(11)]
+ public void VerifyVSM_Switch_DisableAndResetWhileOff()
+ {
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ App.WaitForElement("SwitchDisable");
+ App.Tap("SwitchDisable");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+
+ [Test, Order(12)]
+ public void VerifyVSM_Switch_OnWhileDisabled()
+ {
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ App.WaitForElement("SwitchDisable");
+ App.Tap("SwitchDisable");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("VSMSwitch");
+ App.Tap("VSMSwitch");
+ stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ }
+
+ [Test, Order(13)]
+ public void VerifyVSM_Switch_OffWhileDisabled()
+ {
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ App.WaitForElement("VSMSwitch");
+ App.Tap("VSMSwitch");
+ App.WaitForElement("SwitchDisable");
+ App.Tap("SwitchDisable");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("VSMSwitch");
+ App.Tap("VSMSwitch");
+ stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ }
+
+ [Test, Order(14)]
+ public void VerifyVSM_Switch_OnWhileDisableAndEnable()
+ {
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ App.WaitForElement("VSMSwitch");
+ App.Tap("VSMSwitch");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: On"));
+ App.WaitForElement("SwitchDisable");
+ App.Tap("SwitchDisable");
+ stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("SwitchDisable");
+ App.Tap("SwitchDisable");
+ stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: On"));
+ }
+
+ [Test, Order(15)]
+ public void VerifyVSM_Switch_OffWhileDisableAndEnable()
+ {
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ App.WaitForElement("SwitchDisable");
+ App.Tap("SwitchDisable");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("SwitchDisable");
+ App.Tap("SwitchDisable");
+ stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Off"));
+ }
+
+ [Test, Order(16)]
+ public void VerifyVSM_Switch_ResetWhileOnAndDisabled()
+ {
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ App.WaitForElement("VSMSwitch");
+ App.Tap("VSMSwitch");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: On"));
+ App.WaitForElement("SwitchDisable");
+ App.Tap("SwitchDisable");
+ stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+
+ [Test, Order(17)]
+ public void VerifyVSM_Switch_ResetWhileOffAndDisabled()
+ {
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ App.WaitForElement("SwitchDisable");
+ App.Tap("SwitchDisable");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Disabled"));
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+
+ [Test, Order(18)]
+ public void VerifyVSM_Switch_ResetWhileOn()
+ {
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ App.WaitForElement("VSMSwitch");
+ App.Tap("VSMSwitch");
+ var stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: On"));
+ App.WaitForElement("SwitchReset");
+ App.Tap("SwitchReset");
+ stateText = App.FindElement("SwitchState").GetText();
+ Assert.That(stateText, Is.EqualTo("State: Normal"));
+ }
+}
+
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualTransformFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualTransformFeatureTests.cs
new file mode 100644
index 000000000000..51d3b1958219
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/VisualTransformFeatureTests.cs
@@ -0,0 +1,662 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+[Category(UITestCategories.ViewBaseTests)]
+public class VisualTransformFeatureTests : _GalleryUITest
+{
+ public const string VisualTransformFeatureMatrix = "VisualTransform Feature Matrix";
+ public override string GalleryPageName => VisualTransformFeatureMatrix;
+
+ public VisualTransformFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+ // Shadow interaction tests - Currently disabled due to platform bugs:
+ // - iOS/macOS: Issue #32724 - Shadow affects Visual Transform properties
+ // - Android: Issue #32731 - Shadow affects Visual Transform properties
+ // These tests will be enabled once the platform bugs are fixed.
+#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_ANDROID
+ [Test]
+ public void VisualTransform_ScaleXWithShadow()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ScaleXEntry");
+ App.ClearText("ScaleXEntry");
+ App.EnterText("ScaleXEntry", "2");
+
+ App.WaitForElement("ShadowCheckBox");
+ App.Tap("ShadowCheckBox");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_AnchorYWithShadow()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationEntry");
+ App.ClearText("RotationEntry");
+ App.EnterText("RotationEntry", "45");
+
+ App.WaitForElement("AnchorYEntry");
+ App.ClearText("AnchorYEntry");
+ App.EnterText("AnchorYEntry", "1");
+
+ App.WaitForElement("ShadowCheckBox");
+ App.Tap("ShadowCheckBox");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_IsShadow()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ShadowCheckBox");
+ App.Tap("ShadowCheckBox");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_RotationYWithShadow()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationYEntry");
+ App.ClearText("RotationYEntry");
+ App.EnterText("RotationYEntry", "60");
+
+ App.WaitForElement("ShadowCheckBox");
+ App.Tap("ShadowCheckBox");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_ScaleWithShadow()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ScaleEntry");
+ App.ClearText("ScaleEntry");
+ App.EnterText("ScaleEntry", "2");
+
+ App.WaitForElement("ShadowCheckBox");
+ App.Tap("ShadowCheckBox");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+
+ [Test]
+ public void VisualTransform_RotationXWithShadow()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationXEntry");
+ App.ClearText("RotationXEntry");
+ App.EnterText("RotationXEntry", "50");
+
+ App.WaitForElement("ShadowCheckBox");
+ App.Tap("ShadowCheckBox");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_ScaleYWithShadow()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ScaleYEntry");
+ App.ClearText("ScaleYEntry");
+ App.EnterText("ScaleYEntry", "2");
+
+ App.WaitForElement("ShadowCheckBox");
+ App.Tap("ShadowCheckBox");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_AnchorXWithShadow()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationEntry");
+ App.ClearText("RotationEntry");
+ App.EnterText("RotationEntry", "45");
+
+ App.WaitForElement("AnchorXEntry");
+ App.ClearText("AnchorXEntry");
+ App.EnterText("AnchorXEntry", "1");
+
+ App.WaitForElement("ShadowCheckBox");
+ App.Tap("ShadowCheckBox");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_RotationWithShadow()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationEntry");
+ App.ClearText("RotationEntry");
+ App.EnterText("RotationEntry", "45");
+
+ App.WaitForElement("ShadowCheckBox");
+ App.Tap("ShadowCheckBox");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+#endif
+
+ [Test]
+ public void VisualTransform_IsVisible()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("VisibilityCheckBox");
+ App.Tap("VisibilityCheckBox");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_Rotation()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationEntry");
+ App.ClearText("RotationEntry");
+ App.EnterText("RotationEntry", "45");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_RotationWithRotationX()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationEntry");
+ App.ClearText("RotationEntry");
+ App.EnterText("RotationEntry", "45");
+
+ App.WaitForElement("RotationXEntry");
+ App.ClearText("RotationXEntry");
+ App.EnterText("RotationXEntry", "30");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_RotationWithScale()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationEntry");
+ App.ClearText("RotationEntry");
+ App.EnterText("RotationEntry", "55");
+
+ App.WaitForElement("ScaleEntry");
+ App.ClearText("ScaleEntry");
+ App.EnterText("ScaleEntry", "0.5");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_RotationXWithScaleX()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationXEntry");
+ App.ClearText("RotationXEntry");
+ App.EnterText("RotationXEntry", "55");
+
+ App.WaitForElement("ScaleXEntry");
+ App.ClearText("ScaleXEntry");
+ App.EnterText("ScaleXEntry", "1.5");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_RotationXWithRotationY()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationXEntry");
+ App.ClearText("RotationXEntry");
+ App.EnterText("RotationXEntry", "55");
+
+ App.WaitForElement("RotationYEntry");
+ App.ClearText("RotationYEntry");
+ App.EnterText("RotationYEntry", "25");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_RotationYWithScaleY()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationYEntry");
+ App.ClearText("RotationYEntry");
+ App.EnterText("RotationYEntry", "55");
+
+ App.WaitForElement("ScaleYEntry");
+ App.ClearText("ScaleYEntry");
+ App.EnterText("ScaleYEntry", "1.5");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_RotationX()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationXEntry");
+ App.ClearText("RotationXEntry");
+ App.EnterText("RotationXEntry", "45");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_RotationY()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationYEntry");
+ App.ClearText("RotationYEntry");
+ App.EnterText("RotationYEntry", "45");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_Scale()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ScaleEntry");
+ App.ClearText("ScaleEntry");
+ App.EnterText("ScaleEntry", "2");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_ScaleWithAnchorXAndRotationY()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ScaleEntry");
+ App.ClearText("ScaleEntry");
+ App.EnterText("ScaleEntry", "2");
+
+ App.WaitForElement("RotationYEntry");
+ App.ClearText("RotationYEntry");
+ App.EnterText("RotationYEntry", "45");
+
+ App.WaitForElement("AnchorXEntry");
+ App.ClearText("AnchorXEntry");
+ App.EnterText("AnchorXEntry", "1");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_ScaleXWithAnchorYAndRotationY()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ScaleXEntry");
+ App.ClearText("ScaleXEntry");
+ App.EnterText("ScaleXEntry", "2");
+
+ App.WaitForElement("RotationYEntry");
+ App.ClearText("RotationYEntry");
+ App.EnterText("RotationYEntry", "45");
+
+ App.WaitForElement("AnchorYEntry");
+ App.ClearText("AnchorYEntry");
+ App.EnterText("AnchorYEntry", "1");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_ScaleYWithAnchorXAndRotationY()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ScaleYEntry");
+ App.ClearText("ScaleYEntry");
+ App.EnterText("ScaleYEntry", "1.5");
+
+ App.WaitForElement("RotationYEntry");
+ App.ClearText("RotationYEntry");
+ App.EnterText("RotationYEntry", "45");
+
+ App.WaitForElement("AnchorXEntry");
+ App.ClearText("AnchorXEntry");
+ App.EnterText("AnchorXEntry", "1");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_ScaleX()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ScaleXEntry");
+ App.ClearText("ScaleXEntry");
+ App.EnterText("ScaleXEntry", "2");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_ScaleY()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("ScaleYEntry");
+ App.ClearText("ScaleYEntry");
+ App.EnterText("ScaleYEntry", "2");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_AnchorX_ScaleYWithRotation()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationEntry");
+ App.ClearText("RotationEntry");
+ App.EnterText("RotationEntry", "45");
+
+ App.WaitForElement("ScaleYEntry");
+ App.ClearText("ScaleYEntry");
+ App.EnterText("ScaleYEntry", "1.5");
+
+ App.WaitForElement("AnchorXEntry");
+ App.ClearText("AnchorXEntry");
+ App.EnterText("AnchorXEntry", "0.9");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_AnchorY_ScaleWithRotationY()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationYEntry");
+ App.ClearText("RotationYEntry");
+ App.EnterText("RotationYEntry", "45");
+
+ App.WaitForElement("ScaleEntry");
+ App.ClearText("ScaleEntry");
+ App.EnterText("ScaleEntry", "1.5");
+
+ App.WaitForElement("AnchorYEntry");
+ App.ClearText("AnchorYEntry");
+ App.EnterText("AnchorYEntry", "0.7");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_AnchorY_ScaleXWithRotationX()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationXEntry");
+ App.ClearText("RotationXEntry");
+ App.EnterText("RotationXEntry", "45");
+
+ App.WaitForElement("ScaleXEntry");
+ App.ClearText("ScaleXEntry");
+ App.EnterText("ScaleXEntry", "1.5");
+
+ App.WaitForElement("AnchorYEntry");
+ App.ClearText("AnchorYEntry");
+ App.EnterText("AnchorYEntry", "0.7");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+
+ [Test]
+ public void VisualTransform_AnchorXWithAnchorY()
+ {
+ App.WaitForElement("Reset");
+ App.Tap("Reset");
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("RotationEntry");
+ App.ClearText("RotationEntry");
+ App.EnterText("RotationEntry", "45");
+
+ App.WaitForElement("AnchorXEntry");
+ App.ClearText("AnchorXEntry");
+ App.EnterText("AnchorXEntry", "1");
+
+ App.WaitForElement("AnchorYEntry");
+ App.ClearText("AnchorYEntry");
+ App.EnterText("AnchorYEntry", "1");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Bugzilla/Bugzilla29128.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Bugzilla/Bugzilla29128.cs
index 94af2190f335..8a1c5153d60c 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Bugzilla/Bugzilla29128.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Bugzilla/Bugzilla29128.cs
@@ -1,6 +1,4 @@
-#if TEST_FAILS_ON_WINDOWS //Background Color updates on Slider Track
-//For more information : https://github.com/dotnet/maui/issues/25921
-using NUnit.Framework;
+using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
@@ -22,5 +20,4 @@ public void Bugzilla29128Test()
VerifyScreenshot();
}
}
-}
-#endif
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue10509.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue10509.cs
new file mode 100644
index 000000000000..f24d7ea0aebe
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue10509.cs
@@ -0,0 +1,23 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue10509 : _IssuesUITest
+{
+ public Issue10509(TestDevice testDevice) : base(testDevice)
+ {
+ }
+ public override string Issue => "Query parameter is missing after navigation";
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void QueryIsPassedOnNavigation()
+ {
+ App.WaitForElement("Page1Button");
+ App.Tap("Page1Button");
+ var label = App.WaitForElement("Page2Label");
+ Assert.That(label.GetText(), Is.EqualTo("Navigation data: Passed"));
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue11677.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue11677.cs
new file mode 100644
index 000000000000..6dbf822ace7c
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue11677.cs
@@ -0,0 +1,21 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+public class Issue11677 : _IssuesUITest
+{
+ public Issue11677(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "[iOS][maccatalyst] SearchBar BackgroundColor is black when set to transparent";
+
+ [Test]
+ [Category(UITestCategories.SearchBar)]
+ public void VerifySearchBarBackground()
+ {
+ App.WaitForElement("Label");
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue11812.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue11812.cs
new file mode 100644
index 000000000000..94d49e188280
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue11812.cs
@@ -0,0 +1,23 @@
+#if TEST_FAILS_ON_WINDOWS // test fails on windows , see https://github.com/dotnet/maui/issues/29930
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue11812 : _IssuesUITest
+{
+ public Issue11812(TestDevice device) : base(device) { }
+
+ public override string Issue => "Setting Content of ContentView through style would crash on parent change";
+
+ [Test]
+ [Category(UITestCategories.Border)]
+ public void InnerContentViewShouldNotCrashWhenDynamicallyChange()
+ {
+ App.WaitForElement("ChangeInnerContent");
+ App.Tap("ChangeInnerContent");
+ App.WaitForElement("ChangeInnerContent");
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue12131.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue12131.cs
new file mode 100644
index 000000000000..1ae0b8042583
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue12131.cs
@@ -0,0 +1,32 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue12131 : _IssuesUITest
+ {
+ public Issue12131(TestDevice testDevice) : base(testDevice) { }
+
+ public override string Issue => "RefreshView - CollectionView sizing not working correctly inside VerticalStackLayout";
+
+ [Test]
+ [Category(UITestCategories.RefreshView)]
+ public void RefreshViewHasNonZeroHeightInVerticalStackLayout()
+ {
+ App.WaitForElement("RefreshView12131");
+ var rect = App.FindElement("RefreshView12131").GetRect();
+ Assert.That(rect.Height, Is.GreaterThan(0), "RefreshView should have a non-zero height when placed inside a VerticalStackLayout");
+ }
+
+ [Test]
+ [Category(UITestCategories.RefreshView)]
+ public void CollectionViewDoesNotExceedAvailableWidth()
+ {
+ App.WaitForElement("CollectionView12131");
+ var refreshRect = App.FindElement("RefreshView12131").GetRect();
+ var collectionRect = App.FindElement("CollectionView12131").GetRect();
+ Assert.That(collectionRect.Width, Is.LessThanOrEqualTo(refreshRect.Width + 1), "CollectionView width should not exceed RefreshView width");
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue12324.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue12324.cs
new file mode 100644
index 000000000000..0b0402b26adc
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue12324.cs
@@ -0,0 +1,25 @@
+#if TEST_FAILS_ON_CATALYST // TabBar not visible on Catalyst: https://github.com/dotnet/maui/issues/32329
+
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue12324 : _IssuesUITest
+{
+ public Issue12324(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "Tabbedpage should not have visual bug";
+
+ [Test]
+ [Category(UITestCategories.TabbedPage)]
+ public void Issue12324TabbedPageVisualTest()
+ {
+ App.WaitForElement("Label12324");
+ VerifyScreenshot();
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue13258.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue13258.cs
new file mode 100644
index 000000000000..97252542761d
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue13258.cs
@@ -0,0 +1,25 @@
+#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST //Window issue link - https://github.com/dotnet/maui/issues/29125 && iOS and Mac PR - https://github.com/dotnet/maui/pull/34184
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue13258 : _IssuesUITest
+{
+ public Issue13258(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "MAUI Slider thumb image is big on android";
+
+ [Test]
+ [Category(UITestCategories.Slider)]
+ public void SliderThumbImageShouldBeScaled()
+ {
+ App.WaitForElement("ToggleImageBtn");
+ App.Tap("ToggleImageBtn");
+ VerifyScreenshot();
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue14364.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue14364.cs
new file mode 100644
index 000000000000..5ee05d881e08
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue14364.cs
@@ -0,0 +1,22 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue14364 : _IssuesUITest
+{
+ public Issue14364(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "Control size properties are not available during Loaded event";
+
+ [Test]
+ [Category(UITestCategories.Layout)]
+ public void SizePropertiesAvailableDuringLoadedEvent()
+ {
+ var label = App.WaitForElement("labelSize");
+ Assert.That(label.GetText(), Is.Not.EqualTo("Height: -1, Width: -1"));
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue14566.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue14566.cs
new file mode 100644
index 000000000000..a59912c56f5e
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue14566.cs
@@ -0,0 +1,46 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+#if ANDROID
+using OpenQA.Selenium;
+#endif
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue14566 : _IssuesUITest
+ {
+ public Issue14566(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ const string SearchBar = "SearchBar";
+ const string ResultText = "ResultText";
+ const string CheckResultButton = "CheckResultButton";
+
+ public override string Issue => "SearchBar IsEnabled property not functioning";
+
+ [Test]
+ [Category(UITestCategories.SearchBar)]
+ public void SearchBarShouldRespectIsEnabled()
+ {
+ App.WaitForElement(CheckResultButton);
+ App.Tap(SearchBar);
+#if ANDROID // Exception thrown when entering text on disabled SearchBar in Android.
+ try
+ {
+ App.EnterText(SearchBar, "Hello");
+ }
+ catch (InvalidElementStateException)
+ {
+ Assert.Pass("SearchBar is disabled");
+ }
+#else
+ App.EnterText(SearchBar, "Hello");
+#endif
+ App.Tap(CheckResultButton);
+ var resultText = App.WaitForElement(ResultText).GetText();
+ Assert.That(resultText, Is.EqualTo("Success"));
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue15280.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue15280.cs
new file mode 100644
index 000000000000..1f64d6fb5777
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue15280.cs
@@ -0,0 +1,27 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue15280 : _IssuesUITest
+{
+ public Issue15280(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "Swipe gestures attached to rotated controls are rotated on iOS";
+
+ [Test]
+ [Category(UITestCategories.Gestures)]
+ public void SwipeGesturesOnRotatedControlsShouldWorkCorrectly()
+ {
+ App.WaitForElement("RotatedImage");
+
+ App.SwipeLeftToRight("RotatedImage");
+ Assert.That(App.WaitForElement("DirectionLabel").GetText(), Is.EqualTo("Swiped: RIGHT"));
+
+ App.SwipeRightToLeft("RotatedImage");
+ Assert.That(App.WaitForElement("DirectionLabel").GetText(), Is.EqualTo("Swiped: LEFT"));
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue15559.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue15559.cs
new file mode 100644
index 000000000000..beaa8e50097a
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue15559.cs
@@ -0,0 +1,22 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue15559 : _IssuesUITest
+ {
+ public override string Issue => "[iOS] Vertical layout content of label is truncated when a width request is set in the layout";
+
+ public Issue15559(TestDevice device) : base(device)
+ { }
+
+ [Test]
+ [Category(UITestCategories.Layout)]
+ public void LabelDisplayWithoutCroppingInsideVerticalLayout()
+ {
+ App.WaitForElement("Label");
+ VerifyScreenshot();
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue16522.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue16522.cs
new file mode 100644
index 000000000000..33719acd131f
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue16522.cs
@@ -0,0 +1,26 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue16522 : _IssuesUITest
+ {
+ public Issue16522(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "[Android] Tabs briefly display wrong background color when navigating between flyout items";
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void ShellTabBarBackgroundColor()
+ {
+ App.WaitForElement("Label");
+ App.Tap("Settings");
+ App.Tap("Main");
+ App.WaitForElement("Label");
+ VerifyScreenshot();
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17323.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17323.cs
new file mode 100644
index 000000000000..e8ef85c0e205
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17323.cs
@@ -0,0 +1,22 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue17323 : _IssuesUITest
+{
+ public Issue17323(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "Arabic text flows RTL on Android in MAUI, but flows LTR on Windows";
+
+ [Test]
+ [Category(UITestCategories.GraphicsView)]
+ public void ArabicStringShouldBeLeftToRight()
+ {
+ App.WaitForElement("TextLabel");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17550.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17550.cs
new file mode 100644
index 000000000000..0f441dd521de
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17550.cs
@@ -0,0 +1,28 @@
+using NUnit.Framework;
+using NUnit.Framework.Legacy;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+public class Issue17550 : _IssuesUITest
+{
+ public Issue17550(TestDevice device) : base(device) { }
+ public override string Issue => "Changing Shell.NavBarIsVisible does not update the nav bar on Mac / iOS";
+
+ [Test, Order(1)]
+ [Category(UITestCategories.Shell)]
+ public void VerifyNavBarStatusAtInitialLoading()
+ {
+ App.WaitForElement("NavBarToggleButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(2)]
+ [Category(UITestCategories.Shell)]
+ public void VerifyNavBarStatusAtRuntime()
+ {
+ App.WaitForElement("NavBarToggleButton");
+ App.Tap("NavBarToggleButton");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17664.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17664.cs
new file mode 100644
index 000000000000..71874bd0c082
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17664.cs
@@ -0,0 +1,30 @@
+#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_WINDOWS // Android fix: https://github.com/dotnet/maui/pull/31437
+// Windows: The Scrolled event is not consistently triggered in the CI environment during automated
+// scrolling, so the label text is never updated. This is a test infrastructure limitation on Windows;
+// the fix itself is iOS/MacCatalyst-only and works correctly on iOS and MacCatalyst.
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue17664 : _IssuesUITest
+{
+ public Issue17664(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "Incorrect ItemsViewScrolledEventArgs in CollectionView when IsGrouped is set to true";
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyGroupedCollectionViewVisibleItemIndices()
+ {
+ App.WaitForElement("Issue17664ScrollBtn");
+ App.Tap("Issue17664ScrollBtn");
+
+ var resultItem = App.WaitForElement("Issue17664DescriptionLabel").GetText();
+ Assert.That(resultItem, Is.EqualTo("Category C item #2"));
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18011.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18011.cs
new file mode 100644
index 000000000000..3c1122a471f7
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18011.cs
@@ -0,0 +1,20 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue18011 : _IssuesUITest
+{
+ public override string Issue => "RadioButton TextColor for plain Content not working on iOS when Label styles present";
+
+ public Issue18011(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.RadioButton)]
+ public void RadioButtonTextColorShouldNotBeOverriddenByGlobalLabelStyle()
+ {
+ App.WaitForElement("RadioButtonWithTextColor");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18668.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18668.cs
new file mode 100644
index 000000000000..7c5646d86005
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18668.cs
@@ -0,0 +1,24 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue18668 : _IssuesUITest
+ {
+ public override string Issue => "Visual state change for disabled RadioButton";
+
+ public Issue18668(TestDevice device) : base(device){ }
+
+ [Test]
+ [Category(UITestCategories.RadioButton)]
+ public void TestIssue18668()
+ {
+ App.WaitForElement("button");
+ App.Click("button");
+
+ // The test passes if the radio button is visually disabled
+ VerifyScreenshot();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18679.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18679.cs
new file mode 100644
index 000000000000..16eeb313be5b
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18679.cs
@@ -0,0 +1,22 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue18679 : _IssuesUITest
+{
+ public Issue18679(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "Canvas.GetStringSize() is not consistent with actual string size in GraphicsView";
+
+ [Test]
+ [Category(UITestCategories.GraphicsView)]
+ public void DrawTextWithinBounds()
+ {
+ App.WaitForElement("18679DescriptionLabel");
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18933.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18933.cs
new file mode 100644
index 000000000000..bfb4bf69fd86
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18933.cs
@@ -0,0 +1,32 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue18933 : _IssuesUITest
+{
+ public Issue18933(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "ContentView Background Color Not Cleared When Set to Null";
+
+ [Test, Order(1)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void VerifyBackgroundColorCleared()
+ {
+ App.WaitForElement("clearBgBtn");
+ App.Tap("clearBgBtn");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(2)]
+ [Category(UITestCategories.ViewBaseTests)]
+ public void VerifyBackgroundColorSet()
+ {
+ App.WaitForElement("setBgBtn");
+ App.Tap("setBgBtn");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19219.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19219.cs
new file mode 100644
index 000000000000..3863a09d0094
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19219.cs
@@ -0,0 +1,32 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue19219 : _IssuesUITest
+{
+ public override string Issue => "[Android, iOS, macOS] Shell SearchHandler Command Not Executed on Item Selection";
+
+ public Issue19219(TestDevice device) : base(device)
+ {
+ }
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void ShouldExecuteCommandWhenTappingShellSearchItem()
+ {
+ App.WaitForElement("SearchHandlerLabel");
+ var searchHandler = App.GetShellSearchHandler();
+ searchHandler.Tap();
+ searchHandler.SendKeys("Los Angeles");
+#if ANDROID // Android does not support selecting elements in SearchHandler's results so used tap coordinates
+ var y = searchHandler.GetRect().Y + searchHandler.GetRect().Height;
+ App.TapCoordinates(searchHandler.GetRect().X + 10, y + 10);
+#else
+ App.Tap("Los Angeles");
+#endif
+ var text = App.WaitForElement("SearchHandlerLabel").GetText();
+ Assert.That(text, Is.EqualTo("SearchHandler Command Executed when tap on item"));
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19676.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19676.cs
new file mode 100644
index 000000000000..aaf241520660
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19676.cs
@@ -0,0 +1,20 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue19676 : _IssuesUITest
+{
+ public override string Issue => "Android Switch Control Thumb Shadow missing when ThumbColor matches background";
+
+ public Issue19676(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.Switch)]
+ public void SwitchThumbShouldBeVisibleWithShadow()
+ {
+ App.WaitForElement("TestSwitch");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue20596.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue20596.cs
new file mode 100644
index 000000000000..db5e319ffcb5
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue20596.cs
@@ -0,0 +1,23 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue20596 : _IssuesUITest
+{
+ public Issue20596(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "[Android] Button with corner radius shadow broken on Android device";
+
+ [Test]
+ [Category(UITestCategories.Button)]
+ public void ShadowShouldUpdateOnCornerRadiusChange()
+ {
+ App.WaitForElement("UpdateCornerRadiusButton");
+ App.Click("UpdateCornerRadiusButton");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue20855.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue20855.cs
new file mode 100644
index 000000000000..018adde77559
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue20855.cs
@@ -0,0 +1,24 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue20855 : _IssuesUITest
+ {
+
+ public Issue20855(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "Grouped CollectionView items not rendered properly on Android, works on Windows";
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void GroupedCollectionViewItems()
+ {
+ App.WaitForElement("Item 1");
+ VerifyScreenshot("GroupedCollectionViewItems");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue20922.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue20922.cs
new file mode 100644
index 000000000000..49035b17b148
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue20922.cs
@@ -0,0 +1,25 @@
+#if ANDROID || IOS
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue20922 : _IssuesUITest
+ {
+ public Issue20922(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "Shadow Doesn't Work on Grid in scroll view on Android";
+
+ [Test]
+ [Category(UITestCategories.ScrollView)]
+ public void ShadowShouldWork()
+ {
+ App.WaitForElement("Grid");
+ VerifyScreenshot();
+ }
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21037.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21037.cs
new file mode 100644
index 000000000000..31eb1534b398
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21037.cs
@@ -0,0 +1,32 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue21037 : _IssuesUITest
+{
+ public Issue21037(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "Switching to an existing page with SetTitleView used in Flyout menu causes crash on Windows";
+
+ [Test]
+ [Category(UITestCategories.TitleView)]
+ public void NavigatingBetweenPagesWithSetTitleViewShouldNotCrash()
+ {
+ App.WaitForElement("OpenMenuButton");
+ App.Tap("OpenMenuButton");
+ App.WaitForElement("SecondButton");
+ App.Tap("SecondButton");
+
+ App.WaitForElement("OpenMenuButton");
+ App.Tap("OpenMenuButton");
+ App.WaitForElement("HomeButton");
+ App.Tap("HomeButton");
+
+ App.WaitForElement("OpenMenuButton");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21646.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21646.cs
new file mode 100644
index 000000000000..63c873e826b6
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21646.cs
@@ -0,0 +1,24 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue21646: _IssuesUITest
+{
+ public Issue21646(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "Flyout icon should remain visible when a page is pushed onto a NavigationPage with the back button disabled.";
+
+ [Test]
+ [Category(UITestCategories.Navigation)]
+ public void FlyoutIconShouldBeVisibleWithBackButtonDisabledInNavigationPage()
+ {
+ App.WaitForElement("NavigateToNextPageButton");
+ App.Tap("NavigateToNextPageButton");
+ App.WaitForElement("SecondPageLabel");
+ App.WaitForFlyoutIcon(FlyoutIconAutomationId, false);
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21646_ShellFlyout.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21646_ShellFlyout.cs
new file mode 100644
index 000000000000..0cef79a995a3
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21646_ShellFlyout.cs
@@ -0,0 +1,24 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue21646_ShellFlyout: _IssuesUITest
+{
+ public Issue21646_ShellFlyout(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "Flyout icon should remain visible when a page is pushed onto a ShellPage with the back button disabled.";
+
+ [Test]
+ [Category(UITestCategories.Navigation)]
+ public void FlyoutIconShouldBeVisibleWithBackButtonDisabledInShellPage()
+ {
+ App.WaitForElement("NavigateToNextPageButton");
+ App.Tap("NavigateToNextPageButton");
+ App.WaitForElement("SecondPageLabel");
+ App.WaitForFlyoutIcon(FlyoutIconAutomationId);
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21828.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21828.cs
new file mode 100644
index 000000000000..01e7c536cd3c
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue21828.cs
@@ -0,0 +1,23 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue21828 : _IssuesUITest
+{
+ public Issue21828(TestDevice testDevice) : base(testDevice)
+ {
+ }
+ public override string Issue => "Flyout icon disappears after root page replacement and pop";
+
+ [Test]
+ [Category(UITestCategories.FlyoutPage)]
+ public void FlyoutIconRemainsVisible()
+ {
+ App.WaitForElement("InsertAndPopButton");
+ App.Tap("InsertAndPopButton");
+ App.WaitForElement("Page2Label");
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22060.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22060.cs
new file mode 100644
index 000000000000..81c6f2ccb355
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22060.cs
@@ -0,0 +1,26 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue22060 : _IssuesUITest
+{
+ public override string Issue => "Flyout icon and content page title disappeared after focusing on the search handler";
+
+ public Issue22060(TestDevice device) : base(device)
+ {
+ }
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ [Category(UITestCategories.SearchBar)]
+ public void ShouldAppearFlyoutIconAndContentPageTitle()
+ {
+ App.EnterTextInShellSearchHandler("Hello");
+#if IOS // When the search handler is focused, the Cancel button is displayed on the right side of the search bar only on the iOS platform.
+ App.Tap("Cancel");
+#endif
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22565.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22565.cs
new file mode 100644
index 000000000000..7bd8b0ed3196
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22565.cs
@@ -0,0 +1,24 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue22565 : _IssuesUITest
+{
+ public Issue22565(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "A disabled Picker prevents the parent container's GestureRecognizer from being triggered";
+
+ [Test]
+ [Category(UITestCategories.Picker)]
+ public void VerifyGesturePropagationWithDisabledPicker()
+ {
+ App.WaitForElement("22565DescriptionLabel");
+ App.Tap("DisabledPicker");
+ var labelText = App.FindElement("22565DescriptionLabel").GetText();
+ Assert.That(labelText, Is.EqualTo("Parent Gesture recognizer triggered"));
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22887.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22887.cs
new file mode 100644
index 000000000000..db5321a0e11a
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22887.cs
@@ -0,0 +1,26 @@
+#if TEST_FAILS_ON_WINDOWS // Windows issue report - https://github.com/dotnet/maui/issues/31256
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue22887 : _IssuesUITest
+{
+
+ public Issue22887(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "Microsoft.Maui.Controls.ImageSource.FromFile fails in iOS when image in subfolder";
+
+
+ [Test]
+ [Category(UITestCategories.Image)]
+ public void ImageShouldLoadFromSubfolder()
+ {
+ App.WaitForElement("ImageView", timeoutMessage: "Image is not rendered in the view");
+ VerifyScreenshot();
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22938.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22938.cs
new file mode 100644
index 000000000000..b108febd1ed4
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22938.cs
@@ -0,0 +1,78 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue22938 : _IssuesUITest
+{
+ public Issue22938(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "Keyboard focus does not shift to a newly opened modal page";
+
+ [Test]
+ [Category(UITestCategories.Focus)]
+ public void ModalPageShouldReceiveKeyboardFocus()
+ {
+ App.WaitForElement("OpenModalButton");
+
+ // Open the modal page
+ App.Tap("OpenModalButton");
+
+ // Wait for modal to appear — the Entry is the first focusable element
+ App.WaitForElement("ModalEntry");
+
+ // Press Enter — with the fix, focus is on ModalEntry (an Entry control),
+ // so Enter should NOT activate MainPageButton on the page beneath
+ App.PressEnter();
+
+ // Close the modal by tapping the close button explicitly
+ App.Tap("CloseModalButton");
+
+ // Wait for main page to reappear
+ App.WaitForElement("ClickCountLabel");
+
+ // Verify the main page button was NOT clicked by the Enter key
+ var clickCount = App.WaitForElement("ClickCountLabel").GetText();
+ Assert.That(clickCount, Is.EqualTo("0"),
+ "Enter key should not activate buttons on the page beneath a modal");
+ }
+
+ [Test]
+ [Category(UITestCategories.Focus)]
+ public void TabShouldNotCycleToBehindModal()
+ {
+ App.WaitForElement("OpenModalButton");
+
+ // Open the modal page (uses semi-transparent background so underlying page stays in tree)
+ App.Tap("OpenModalButton");
+
+ // Wait for modal to appear
+ App.WaitForElement("ModalEntry");
+
+ // Tab through many times to attempt cycling past the modal into the underlying page.
+ // The modal has 2 focusable elements (Entry + CloseModalButton), so 10 tabs should
+ // cycle through them multiple times. If focus escapes to the underlying page,
+ // one of the tabs could land on MainPageButton.
+ for (int i = 0; i < 10; i++)
+ {
+ App.SendTabKey();
+ }
+
+ // Now press Enter. If focus leaked to MainPageButton, this would click it.
+ App.PressEnter();
+
+ // Close the modal
+ App.Tap("CloseModalButton");
+
+ // Wait for main page to reappear
+ App.WaitForElement("ClickCountLabel");
+
+ // Verify the main page button was NOT clicked during Tab cycling
+ var clickCount = App.WaitForElement("ClickCountLabel").GetText();
+ Assert.That(clickCount, Is.EqualTo("0"),
+ "Tab key should not cycle focus to buttons on the page beneath a modal");
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23014.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23014.cs
new file mode 100644
index 000000000000..32dcdfa941a1
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23014.cs
@@ -0,0 +1,26 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue23014 : _IssuesUITest
+ {
+ public Issue23014(TestDevice device)
+ : base(device)
+ { }
+
+ public override string Issue => "App crashes when calling ItemsView.ScrollTo on unloaded CollectionView";
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void ScrollToOnUnloadedCollectionViewShouldNotCrash()
+ {
+ App.WaitForElement("ScrollToRemovedButton");
+ App.Click("ScrollToRemovedButton");
+ App.WaitForElement("StatusLabel");
+ Assert.That(App.FindElement("StatusLabel").GetText(), Is.EqualTo("Success"));
+ }
+ }
+}
+
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23377.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23377.cs
new file mode 100644
index 000000000000..ae1d6226bb09
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23377.cs
@@ -0,0 +1,22 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+public class Issue23377 : _IssuesUITest
+{
+ public Issue23377(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "Horizontal Item spacing in collectionView";
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void Issue23377ItemSpacing()
+ {
+ App.WaitForElement("ChangeItemSpace");
+ App.Tap("ChangeItemSpace");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23832.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23832.cs
new file mode 100644
index 000000000000..f67acd6c7ab2
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23832.cs
@@ -0,0 +1,24 @@
+#if TEST_FAILS_ON_WINDOWS // The app crashes on Windows on CI causing NoSuchWindowException: Currently selected window has been closed
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue23832 : _IssuesUITest
+{
+ public Issue23832(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "Some HEIC photos are upside down after using PlatformImage.Resize";
+
+ [Test]
+ [Category(UITestCategories.GraphicsView)]
+ public void HEICImageShouldNotRenderUpsideDown()
+ {
+ App.WaitForElement("DrawableLabel");
+ VerifyScreenshot();
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue24252.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue24252.cs
new file mode 100644
index 000000000000..b48b2fce574a
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue24252.cs
@@ -0,0 +1,42 @@
+#if TEST_FAILS_ON_CATALYST //The test fails on Mac because PinchToZoomIn does not work.
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue24252 : _IssuesUITest
+{
+ public override string Issue => "Overlapping gesture recognizers should only fire on the topmost child view on Windows";
+
+ public Issue24252(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.Gestures)]
+ public void OverlappingGesturesShouldOnlyFireChild()
+ {
+ // Pan: drag on the child box
+ App.WaitForElement("PanStatusLabel");
+ var panChild = App.WaitForElement("PanChildBox");
+ var panRect = panChild.GetRect();
+ App.DragCoordinates(panRect.CenterX(), panRect.CenterY(), panRect.CenterX() + 50, panRect.CenterY() + 50);
+
+ Assert.That(App.WaitForElement("PanStatusLabel").GetText(), Is.EqualTo("Child triggered"),
+ "Only the child PanGestureRecognizer should fire when dragging the child view.");
+
+ //// Swipe: swipe right on the child box
+ App.WaitForElement("SwipeStatusLabel");
+ App.SwipeLeftToRight("SwipeChildBox", swipePercentage: 1.5, swipeSpeed: 100);
+
+ Assert.That(App.WaitForElement("SwipeStatusLabel").GetText(), Is.EqualTo("Child triggered"),
+ "Only the child SwipeGestureRecognizer should fire when swiping on the child view.");
+
+ // Pinch: pinch on the child box
+ App.WaitForElement("PinchStatusLabel");
+ App.PinchToZoomIn("PinchChildBox");
+
+ Assert.That(App.WaitForElement("PinchStatusLabel").GetText(), Is.EqualTo("Child triggered"),
+ "Only the child PinchGestureRecognizer should fire when pinching on the child view.");
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25081.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25081.cs
new file mode 100644
index 000000000000..469a9cfce941
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25081.cs
@@ -0,0 +1,34 @@
+#if WINDOWS || MACCATALYST // TitleBar is only supported on Windows and MacCatalyst
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue25081 : _IssuesUITest
+{
+ public override string Issue => "[Windows] The flyout icon and background appear awkward when enabled alongside a TitleBar";
+
+ public Issue25081(TestDevice device)
+ : base(device)
+ { }
+
+ [Test, Order(1)]
+ [Category(UITestCategories.Shell)]
+ public void VerifyFlyoutIconBackgroundColor()
+ {
+ App.WaitForElement("ColorChangeButton");
+ VerifyScreenshot(includeTitleBar: true);
+ }
+
+ [Test, Order(2)]
+ [Category(UITestCategories.Shell)]
+ public void VerifyDynamicFlyoutIconBackgroundColor()
+ {
+
+ App.WaitForElement("ColorChangeButton");
+ App.Tap("ColorChangeButton");
+ VerifyScreenshot(includeTitleBar: true);
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25093.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25093.cs
new file mode 100644
index 000000000000..6e5608ffe503
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25093.cs
@@ -0,0 +1,34 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue25093 : _IssuesUITest
+{
+ public Issue25093(TestDevice testDevice) : base(testDevice) { }
+
+ public override string Issue => "[iOS] TintColor on UIButton image no longer working when button made visible";
+
+ [Test]
+ [Category(UITestCategories.Button)]
+ public void ButtonImageTintColorPreservedAfterResize()
+ {
+ App.WaitForElement("ApplyTintButton");
+ App.Tap("ApplyTintButton");
+
+ // Wait for the layout cycle and verification to complete
+ App.WaitForElement("StatusLabel");
+
+ var statusText = App.WaitForElement("StatusLabel", timeout: TimeSpan.FromSeconds(3)).GetText();
+
+ // The label may still say "Waiting" if SizeChanged hasn't fired yet, so retry
+ if (statusText == "Waiting")
+ {
+ Thread.Sleep(1000);
+ statusText = App.FindElement("StatusLabel").GetText();
+ }
+
+ Assert.That(statusText, Is.EqualTo("PASS"));
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25233.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25233.cs
new file mode 100644
index 000000000000..95b1bd95f12a
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25233.cs
@@ -0,0 +1,32 @@
+// SwipeView gestures are not reliably recognized on Mac Catalyst in UI tests
+#if TEST_FAILS_ON_CATALYST
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue25233 : _IssuesUITest
+ {
+ public Issue25233(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "CollectionView with SwipeView items behaves strangely";
+
+ [Test]
+ [Category(UITestCategories.SwipeView)]
+ public void OnlyManuallySwipedItemShouldBeOpened()
+ {
+ App.WaitForElement("Item1");
+ App.SwipeLeftToRight("Item1");
+ App.SwipeLeftToRight("Item2");
+ App.SwipeLeftToRight("Item3");
+ App.Click("button");
+
+ App.WaitForElement("Item15");
+ VerifyScreenshot();
+ }
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25502.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25502.cs
deleted file mode 100644
index 2468cb08212d..000000000000
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25502.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using NUnit.Framework;
-using UITest.Appium;
-using UITest.Core;
-
-namespace Microsoft.Maui.TestCases.Tests.Issues
-{
- public class Issue25502 : _IssuesUITest
- {
- public Issue25502(TestDevice testDevice) : base(testDevice)
- {
- }
-
- public override string Issue => "Gray Line Appears on the Right Side of GraphicsView with Decimal WidthRequest on iOS Platform";
-
- [Test]
- [Category(UITestCategories.GraphicsView)]
- public void VerifyGraphicsViewWithoutGrayLine()
- {
- App.WaitForElement("ChangeColorButton");
- App.Click("ChangeColorButton");
- VerifyScreenshot();
- }
- }
-}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25728.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25728.cs
new file mode 100644
index 000000000000..bdf5d7af83e5
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25728.cs
@@ -0,0 +1,29 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue25728 : _IssuesUITest
+{
+ public Issue25728(TestDevice device) : base(device) { }
+
+ public override string Issue => "Java.Lang.IllegalArgumentException when clearing Entry text with StringFormat binding on Android";
+
+ [Test]
+ [Category(UITestCategories.Entry)]
+ public void ClearingEntryWithStringFormatBindingShouldNotCrash()
+ {
+ // Wait for the Entry with StringFormat binding to appear (shows "0.00" initially)
+ App.WaitForElement("FloatEntry");
+
+ // Clear all characters from the Entry
+ App.ClearText("FloatEntry");
+
+ // Enter a number — this triggered Java.Lang.IllegalArgumentException on Android
+ App.EnterText("FloatEntry", "42");
+
+ // If we reach here without a crash, the issue is fixed
+ App.WaitForElement("FloatEntry");
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25920.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25920.cs
index 4b15e743556a..bd1bd6888e33 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25920.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25920.cs
@@ -1,8 +1,6 @@
-#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_ANDROID
+#if TEST_FAILS_ON_WINDOWS
// https://github.com/dotnet/maui/issues/26148
// In Windows, the foreground color is not applied to the custom icon, so as of now, the test is not applicable for Windows.
-// https://github.com/dotnet/maui/pull/27502
-// For Android, we have separate PR to fix the issue, so the test is not applicable for Android.
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
@@ -25,6 +23,7 @@ public void ForegroundColorShouldbeSetandCustomIconAlignedProperly()
}
App.WaitForElement("Label");
+ //The test passes on Android if the foreground color is applied to the icon
VerifyScreenshot();
}
}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25921.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25921.cs
new file mode 100644
index 000000000000..1f3b03f0945d
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue25921.cs
@@ -0,0 +1,23 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue25921 : _IssuesUITest
+{
+ public override string Issue => "[Windows] Setting BackgroundColor for Slider updates the Maximum Track Color";
+
+ public Issue25921(TestDevice device)
+ : base(device)
+ { }
+
+ [Test]
+ [Category(UITestCategories.Slider)]
+ public void VerifySliderColors()
+ {
+ App.WaitForElement("ColorChangeButton");
+ App.Tap("ColorChangeButton");
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue26158.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue26158.cs
new file mode 100644
index 000000000000..b709e961fba9
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue26158.cs
@@ -0,0 +1,26 @@
+#if ANDROID || IOS
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue26158 : _IssuesUITest
+ {
+ public Issue26158(TestDevice device) : base(device) { }
+
+ public override string Issue => "SelectionLength property not applied when an entry is focused";
+
+ [Test]
+ [Category(UITestCategories.Entry)]
+ public void SelectionLengthShouldUpdateWhenEntryIsFocused()
+ {
+ App.WaitForElement("entry");
+ App.Click("entry");
+#if ANDROID
+ App.DismissKeyboard();
+#endif
+ VerifyScreenshot();
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue26864.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue26864.cs
new file mode 100644
index 000000000000..f95dd0b23840
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue26864.cs
@@ -0,0 +1,25 @@
+#if MACCATALYST
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue26864 : _IssuesUITest
+{
+ public Issue26864(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "Shell Content Title Not Rendering in Full-Screen Mode on Mac Catalyst";
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void ShellContentTitleNotRendering()
+ {
+ App.WaitForElement("Settings");
+ App.EnterFullScreen();
+ VerifyScreenshot();
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue2708.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue2708.cs
new file mode 100644
index 000000000000..00a62ad70cf4
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue2708.cs
@@ -0,0 +1,40 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue2708 : _IssuesUITest
+ {
+ public Issue2708(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "Prevent tabs from being removed during modal navigation";
+
+ [Test]
+ [Category(UITestCategories.TabbedPage)]
+ public void TabsShouldRemainVisibleDuringModalNavigation()
+ {
+ // Verify we're on the TabbedPage and can see Tab 1 content
+ App.WaitForElement("OpenModalButton");
+
+ // Open modal page
+ App.Tap("OpenModalButton");
+ App.WaitForElement("CloseModalButton");
+
+ // Take screenshot while modal is open to verify tabs visible behind
+ App.Screenshot("ModalOpenWithTabsVisible");
+
+ // Close the modal
+ App.Tap("CloseModalButton");
+
+ // Verify tabs still work after modal dismiss
+ App.WaitForElement("StatusLabel");
+ App.TapTab("Tab 2");
+ App.WaitForElement("Tab2Label");
+ App.TapTab("Tab 1");
+ App.WaitForElement("OpenModalButton");
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27143.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27143.cs
new file mode 100644
index 000000000000..1226de0ba7ed
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27143.cs
@@ -0,0 +1,27 @@
+#if IOS || ANDROID
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue27143 : _IssuesUITest
+ {
+ public Issue27143(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "Not trigger OnNavigatedTo method when hide the navi bar and using swipe";
+
+ [Test]
+ [Category(UITestCategories.Navigation)]
+ public void OnNavigatedToShouldTrigger()
+ {
+ App.WaitForElement("button");
+ App.Click("button");
+ App.SwipeBackNavigation();
+ App.WaitForElement("NavigatedTo event triggers count: 2");
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27377.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27377.cs
new file mode 100644
index 000000000000..de1202feefef
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27377.cs
@@ -0,0 +1,28 @@
+#if ANDROID || IOS
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue27377 : _IssuesUITest
+ {
+ public Issue27377(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "SwipeView: SwipeItem.IconImageSource.FontImageSource color value not honored";
+
+ [Test]
+ [Category(UITestCategories.SwipeView)]
+ public void FontImageSourceShouldHonorColor()
+ {
+ App.WaitForElement("Button");
+ App.Click("Button");
+ // Wait for swipe animation to complete
+ App.WaitForElement("Action");
+ VerifyScreenshot(retryDelay: TimeSpan.FromSeconds(1));
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27427.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27427.cs
new file mode 100644
index 000000000000..d20d13684a62
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27427.cs
@@ -0,0 +1,23 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue27427 : _IssuesUITest
+ {
+ public override string Issue => "iOS SearchBar ignores WidthRequest and HeightRequest property values";
+
+ public Issue27427(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ [Test]
+ [Category(UITestCategories.SearchBar)]
+ public void EnsureSearchBarExplicitSize()
+ {
+ App.WaitForElement("SearchBarDimensionsHeaderLabel");
+ VerifyScreenshot();
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27646.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27646.cs
new file mode 100644
index 000000000000..d6dcca22c4d9
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27646.cs
@@ -0,0 +1,33 @@
+#if WINDOWS // MacCatalyst doesn't support programmatic window resizing. So, ignored test on MacCatalyst.
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue27646 : _IssuesUITest
+{
+ public override string Issue => "AdaptiveTrigger not firing when changing window width programmatically only";
+
+ public Issue27646(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.Window)]
+ public void AdaptiveTriggerShouldFireWhenWindowWidthChangedProgrammatically()
+ {
+ App.WaitForElement("ResizeButton");
+
+ App.Tap("ResizeButton");
+
+ var indicatorAfterFirstClick = App.FindElement("IndicatorLabel");
+ Assert.That(indicatorAfterFirstClick.GetText(), Is.EqualTo("Narrow Window"),
+ "Label should show 'Narrow Window' after resizing to 550px");
+
+ App.Tap("ResizeButton");
+
+ var indicatorAfterSecondClick = App.FindElement("IndicatorLabel");
+ Assert.That(indicatorAfterSecondClick.GetText(), Is.EqualTo("Wide Window"),
+ "Label should show 'Wide Window' after resizing to 650px");
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27799.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27799.cs
new file mode 100644
index 000000000000..21b122ae7820
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27799.cs
@@ -0,0 +1,37 @@
+#if IOS // Navigation from the more page is iOS specific
+using NUnit.Framework;
+using NUnit.Framework.Legacy;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue27799 : _IssuesUITest
+ {
+ public Issue27799(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "[iOS] OnAppearing and OnNavigatedTo does not work when using extended Tabbar";
+
+[Test]
+[Category(UITestCategories.Shell)]
+public void OnAppearingAndOnNavigatedToShouldBeCalled()
+ {
+ App.WaitForElement("More");
+ App.Click("More");
+ App.WaitForElement("Tab6");
+ App.Click("Tab6");
+ App.WaitForElement("GoToSubpage6Button");
+ App.Click("GoToSubpage6Button");
+ App.Click("tab2");
+ App.WaitForElement("OnNavigatedToCountLabel");
+ var onNavigatedToCountLabel = App.FindElement("OnNavigatedToCountLabel").GetText();
+ var onAppearingCountLabel = App.FindElement("OnAppearingCountLabel").GetText();
+
+ ClassicAssert.AreEqual("OnNavigatedTo: 3", onNavigatedToCountLabel);
+ ClassicAssert.AreEqual("OnAppearing: 3", onAppearingCountLabel);
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27800.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27800.cs
new file mode 100644
index 000000000000..326623eefb68
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27800.cs
@@ -0,0 +1,38 @@
+#if IOS // Navigation from the more page is iOS specific
+using NUnit.Framework;
+using NUnit.Framework.Legacy;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue27800 : _IssuesUITest
+ {
+ public Issue27800(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "Shell.BackButtonBehavior does not work when using extended Tabbar";
+
+[Test]
+[Category(UITestCategories.Shell)]
+public void ShellBackButtonBehaviorShouldWorkWithMoreTab()
+ {
+ App.WaitForElement("More");
+ App.Click("More");
+ App.WaitForElement("tab6");
+ App.Click("tab6");
+ App.WaitForElement("button");
+ App.Click("button");
+ App.WaitForElement("Go Back");
+ App.Click("Go Back");
+ App.WaitForElement("OnNavigatedToCountLabel");
+ var onNavigatedToCountLabel = App.FindElement("OnNavigatedToCountLabel").GetText();
+ var onAppearingCountLabel = App.FindElement("OnAppearingCountLabel").GetText();
+
+ ClassicAssert.AreEqual("OnNavigatedTo: 2", onNavigatedToCountLabel);
+ ClassicAssert.AreEqual("OnAppearing: 2", onAppearingCountLabel);
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27846.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27846.cs
new file mode 100644
index 000000000000..e2846659e61e
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27846.cs
@@ -0,0 +1,25 @@
+#if IOS
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue27846 : _IssuesUITest
+{
+ public Issue27846(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "[iOS] More tab doesn't respect shell nav bar customization";
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void MoreTabShouldRespectNavBarCustomization()
+ {
+ App.WaitForElement("More");
+ App.Click("More");
+ VerifyScreenshot();
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28101.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28101.cs
new file mode 100644
index 000000000000..dc44200cfe9a
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28101.cs
@@ -0,0 +1,20 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue28101 : _IssuesUITest
+{
+ public Issue28101(TestDevice device) : base(device)
+ {
+ }
+ public override string Issue => "CollectionView Footer Becomes Scrollable When EmptyView is Active on Android";
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void FooterTemplateShouldNotScrollWhenEmptyViewIsDisplayed()
+ {
+ App.WaitForElement("This Is A Footer");
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28321.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28321.cs
new file mode 100644
index 000000000000..4b7f148436fe
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28321.cs
@@ -0,0 +1,23 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue28321 : _IssuesUITest
+ {
+ public Issue28321(TestDevice device) : base(device) { }
+
+ public override string Issue => "CV RemainingItemsThresholdReachedCommand fires on initial data load";
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void RemainingItemsThresholdReachedCommandShouldNotFireOnInitialDataLoad()
+ {
+ App.WaitForElement("LoadedItemsLabel");
+ App.WaitForElement("Item3");
+ var loadedItemsText = App.FindElement("LoadedItemsLabel").GetText();
+ Assert.That("Loaded items: 4", Is.EqualTo(loadedItemsText));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28656.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28656.cs
new file mode 100644
index 000000000000..32e3bb78d05b
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28656.cs
@@ -0,0 +1,22 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+public class Issue28656 : _IssuesUITest
+{
+ public Issue28656(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "CollectionView CollectionViewHandler2 does not change ItemsLayout on DataTrigger";
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void CollectionViewShouldChangeItemsLayout()
+ {
+ App.WaitForElement("ChangeLayoutButton");
+ App.Click("ChangeLayoutButton");
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28901.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28901.cs
new file mode 100644
index 000000000000..5581b0603d7e
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28901.cs
@@ -0,0 +1,22 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue28901 : _IssuesUITest
+{
+ public override string Issue => "[Windows] Switch control is not sizing properly";
+
+ public Issue28901(TestDevice device)
+ : base(device)
+ { }
+
+ [Test]
+ [Category(UITestCategories.Switch)]
+ public void VerifySwitchControlSize()
+ {
+ App.WaitForElement("SwitchControl");
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28968.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28968.cs
new file mode 100644
index 000000000000..7787454abdea
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28968.cs
@@ -0,0 +1,37 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Tests.Issues;
+
+public class Issue28968 : _IssuesUITest
+{
+ public Issue28968(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "[iOS] ActivityIndicator IsRunning ignores IsVisible when set to true";
+
+ [Test]
+ [Category(UITestCategories.ActivityIndicator)]
+ public void ActivityIndicatorIsRunningDoesNotOverrideIsVisible()
+ {
+ App.WaitForElement("SetRunningButton");
+ App.Tap("SetRunningButton");
+
+ // Wait for the status label to update after the delayed check
+ var statusText = App.WaitForElement("StatusLabel").GetText();
+
+ // Retry a few times since the dispatcher delay in the host app
+ // means the label won't update immediately
+ int retries = 10;
+ while (statusText == "Waiting" && retries-- > 0)
+ {
+ Thread.Sleep(200);
+ statusText = App.WaitForElement("StatusLabel").GetText();
+ }
+
+ Assert.That(statusText, Is.EqualTo("HIDDEN"),
+ "ActivityIndicator should remain hidden when IsVisible=false, even after IsRunning is set to true");
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29036.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29036.cs
new file mode 100644
index 000000000000..9f3631265f9b
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29036.cs
@@ -0,0 +1,20 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue29036 : _IssuesUITest
+{
+ public Issue29036(TestDevice testDevice) : base(testDevice) { }
+
+ public override string Issue => "Button RTL text and image overlap";
+
+ [Test]
+ [Category(UITestCategories.Button)]
+ public void ButtonRTLTextAndImageShouldNotOverlap()
+ {
+ App.WaitForElement("button");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29141.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29141.cs
new file mode 100644
index 000000000000..0d27c0236e08
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29141.cs
@@ -0,0 +1,56 @@
+#if TEST_FAILS_ON_WINDOWS // For Windows: Test excluded on Windows due to unrelated NullReferenceException in test infrastructure - see https://github.com/dotnet/maui/issues/28824.
+// The underlying grouped collection bug (issue #28827) is not Windows-specific;
+// re-enable this test on Windows once issue #28824 is resolved.
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue29141 : _IssuesUITest
+{
+ public override string Issue => "[iOS] Group Header/Footer Repeated for All Items When IsGrouped is True for ObservableCollection in CollectionView";
+ public Issue29141(TestDevice device)
+ : base(device)
+ { }
+
+ [Test, Order(1)]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyCVGroupHFTemplateWithObservableCollection()
+ {
+ App.WaitForElement("collectionView");
+ App.Tap("IsGroupedTrue");
+ App.Tap("GroupHeaderTemplateGrid");
+ App.Tap("GroupFooterTemplateGrid");
+ App.WaitForNoElement("GroupHeaderTemplate");
+ App.WaitForNoElement("GroupFooterTemplate");
+ }
+
+ [Test, Order(2)]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyCVGroupHFTemplateWithStringCollection()
+ {
+ App.WaitForElement("collectionView");
+ App.Tap("SwitchToStringCollection");
+ App.Tap("IsGroupedTrue");
+ App.Tap("GroupHeaderTemplateGrid");
+ App.WaitForNoElement("GroupHeaderTemplate");
+ }
+
+ [Test, Order(3)]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyCVNoSectionCrashOnAddFlatItem()
+ {
+ App.WaitForElement("collectionView");
+ App.Tap("IsGroupedTrue");
+ App.Tap("GroupHeaderTemplateGrid");
+ App.WaitForNoElement("GroupHeaderTemplate");
+
+ // Add flat items — should not crash or produce new sections
+ App.Tap("AddItemButton");
+ App.Tap("AddItemButton");
+ App.Tap("AddItemButton");
+ App.WaitForNoElement("GroupHeaderTemplate");
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29192.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29192.cs
new file mode 100644
index 000000000000..fdb7a11bd27c
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29192.cs
@@ -0,0 +1,22 @@
+#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS // CollectionView MeasureFirstItem sizing not applied on Windows, iOS and macOS) https://github.com/dotnet/maui/issues/29130
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue29192 : _IssuesUITest
+{
+ public override string Issue => "[Android] CollectionView MeasureFirstItem ItemSizingStrategy Not Applied in Horizontal Layouts";
+
+ public Issue29192(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void ShouldMeasureFirstItemInHorizontalLayouts()
+ {
+ App.WaitForElement("CollectionView");
+ VerifyScreenshot();
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29394.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29394.cs
new file mode 100644
index 000000000000..a6909a3aef8e
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29394.cs
@@ -0,0 +1,20 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue29394 : _IssuesUITest
+{
+ public Issue29394(TestDevice device) : base(device) { }
+
+ public override string Issue => "On Android Shadows should not be rendered over fully transparent areas of drawn shapes";
+
+ [Test]
+ [Category(UITestCategories.GraphicsView)]
+ public void TransparentShapeShouldNotDisplayShadow()
+ {
+ App.WaitForElement("label");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29428.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29428.cs
new file mode 100644
index 000000000000..08a18f75f41e
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29428.cs
@@ -0,0 +1,23 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue29428 : _IssuesUITest
+{
+ public override string Issue => "Shell flyout navigation fires NavigatedTo before Loaded event";
+
+ public Issue29428(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void ShellFlyoutNavigationEventOrderShouldBeCorrect()
+ {
+ App.WaitForElement("MainPageLabel");
+ App.TapShellFlyoutIcon();
+ App.Tap("Page 2");
+ var eventOrderText = App.WaitForElement("EventOrderLabel").GetText();
+ Assert.That(eventOrderText, Is.EqualTo("Loaded called first then NavigatedTo called"));
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29484.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29484.cs
new file mode 100644
index 000000000000..8ce7af8eebec
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29484.cs
@@ -0,0 +1,27 @@
+#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_IOS
+// This test is disabled on mobile platforms because they do not support pointer hover states.
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue29484 : _IssuesUITest
+{
+ public Issue29484(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "CollectionView Selected state does not work on the selected item when combined with PointerOver";
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void PointerOverWithSelectedStateShouldWork()
+ {
+ App.WaitForElement("CollectionView");
+ App.Tap("Item 2");
+ App.Tap("PointerOverAndSelectedState");
+ VerifyScreenshot();
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29729.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29729.cs
new file mode 100644
index 000000000000..aa4828ab9c83
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29729.cs
@@ -0,0 +1,31 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+public class Issue29729 : _IssuesUITest
+{
+ public override string Issue => "RadioButton TextTransform Property Does Not Apply on Android and Windows Platforms";
+
+ public Issue29729(TestDevice device)
+ : base(device)
+ { }
+
+ [Test, Order(1)]
+ [Category(UITestCategories.RadioButton)]
+ public void VerifyRadioButtonTextWithLowerTransform()
+ {
+ App.WaitForElement("radioButton");
+ App.Tap("LowerCaseButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(2)]
+ [Category(UITestCategories.RadioButton)]
+ public void VerifyRadioButtonTextWithUpperTransform()
+ {
+ App.WaitForElement("radioButton");
+ App.Tap("UpperCaseButton");
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29764.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29764.cs
new file mode 100644
index 000000000000..dc717dd1a6ff
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29764.cs
@@ -0,0 +1,25 @@
+#if TEST_FAILS_ON_WINDOWS // On Windows, still shadows disappearing permanently after Label opacity is at any time set to 0, Issue Link: https://github.com/dotnet/maui/issues/30383
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue29764 : _IssuesUITest
+{
+ public Issue29764(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "Shadows disappearing permanently in Android and Windows after Label opacity is at any time set to 0";
+
+ [Test]
+ [Category(UITestCategories.Label)]
+ public void LabelShadowRemainsAfterOpacityChange()
+ {
+ App.WaitForElement("MainButton");
+ App.Click("MainButton");
+ VerifyScreenshot();
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29921.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29921.cs
new file mode 100644
index 000000000000..d0bebe9a58a2
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29921.cs
@@ -0,0 +1,22 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue29921 : _IssuesUITest
+{
+ public Issue29921(TestDevice testDevice) : base(testDevice)
+ {
+ }
+ public override string Issue => "Flyout icon not replaced after root page change";
+
+ [Test]
+ [Category(UITestCategories.FlyoutPage)]
+ public void FlyoutIconUpdatedAfterInsertPageBefore()
+ {
+ App.WaitForElement("InsertPageButton");
+ App.Tap("InsertPageButton");
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29930.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29930.cs
new file mode 100644
index 000000000000..a8d8e4bd49c8
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29930.cs
@@ -0,0 +1,23 @@
+#if TEST_FAILS_ON_ANDROID // Test fails on Android , see https://github.com/dotnet/maui/issues/11812
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue29930 : _IssuesUITest
+{
+ public Issue29930(TestDevice device) : base(device) { }
+
+ public override string Issue => "[Windows] Setting a ContentView with a content of StaticResource Style Causes a System.Runtime.InteropServices.COMException.";
+
+ [Test]
+ [Category(UITestCategories.Border)]
+ public void InnerContentViewShouldNotCrashWhenDynamicallyChange()
+ {
+ App.WaitForElement("ChangeInnerContent");
+ App.Tap("ChangeInnerContent");
+ App.WaitForElement("ChangeInnerContent");
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30004.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30004.cs
new file mode 100644
index 000000000000..4b0d6553e23d
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30004.cs
@@ -0,0 +1,21 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+public class Issue30004 : _IssuesUITest
+{
+ public override string Issue => "FontImageSource not center-aligned inside Image control";
+
+ public Issue30004(TestDevice device)
+ : base(device)
+ { }
+
+ [Test]
+ [Category(UITestCategories.Image)]
+ public void VerifyFontImageAreCenterAlign()
+ {
+ App.WaitForElement("FontImage");
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30363.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30363.cs
new file mode 100644
index 000000000000..d833da85da81
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30363.cs
@@ -0,0 +1,23 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue30363 : _IssuesUITest
+{
+ public override string Issue => "[iOS] CollectionView does not clear selection when SelectedItem is set to null";
+
+ public Issue30363(TestDevice device)
+ : base(device)
+ { }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void CollectionViewSelectionShouldClear()
+ {
+ App.WaitForElement("cvItem");
+ App.Tap("cvItem");
+ VerifyScreenshot(retryTimeout: TimeSpan.FromSeconds(2));
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30837.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30837.cs
new file mode 100644
index 000000000000..b73bf8c0f693
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30837.cs
@@ -0,0 +1,22 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue30837 : _IssuesUITest
+{
+ public override string Issue => "[iOS] TimePicker AM/PM frequently changes when the app is closed and reopened";
+
+ public Issue30837(TestDevice device)
+ : base(device)
+ { }
+
+ [Test]
+ [Category(UITestCategories.TimePicker)]
+ public void VerifyTimePickerFormat()
+ {
+ App.WaitForElement("TimePickerLabel");
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30888.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30888.cs
new file mode 100644
index 000000000000..073597e52850
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30888.cs
@@ -0,0 +1,22 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue30888 : _IssuesUITest
+{
+ public Issue30888(TestDevice device)
+ : base(device)
+ { }
+
+ public override string Issue => "Flyout page toolbar items not rendered on iOS";
+
+ [Test]
+ [Category(UITestCategories.FlyoutPage)]
+ public void VerifyFlyoutPageToolbarItemsRender()
+ {
+ App.WaitForElement("DetailContent");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31109.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31109.cs
new file mode 100644
index 000000000000..98cd54676156
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31109.cs
@@ -0,0 +1,73 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ [Category(UITestCategories.Layout)]
+ public class Issue31109 : _IssuesUITest
+ {
+ public Issue31109(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "FlexLayout items with Dynamic Width are not updating correctly on orientation change or scroll in Android";
+
+ [Test]
+ public void DynamicWidthRequestUpdatesFlexLayoutItems()
+ {
+ // Wait for the initial layout to render
+ App.WaitForElement("Item1");
+ App.WaitForElement("Item2");
+ App.WaitForElement("Item3");
+
+ // Get initial widths
+ var item1Before = App.WaitForElement("Item1").GetRect();
+ var item2Before = App.WaitForElement("Item2").GetRect();
+
+ // All items should have different initial widths
+ Assert.That(item1Before.Width, Is.LessThan(item2Before.Width),
+ "Item1 should be narrower than Item2 initially");
+
+ // Change all widths to the same value
+ App.WaitForElement("ChangeWidthsButton");
+ App.Tap("ChangeWidthsButton");
+
+ // Wait until Item1 width has actually changed to ensure the layout update completed
+ var timeout = System.TimeSpan.FromSeconds(5);
+ var pollDelay = System.TimeSpan.FromMilliseconds(100);
+ var stopwatch = System.Diagnostics.Stopwatch.StartNew();
+
+ while (stopwatch.Elapsed < timeout)
+ {
+ var currentRect = App.FindElement("Item1").GetRect();
+ if (System.Math.Abs(currentRect.Width - item1Before.Width) > 1)
+ {
+ break;
+ }
+
+ System.Threading.Thread.Sleep(pollDelay);
+ }
+
+ // Verify the width actually changed; fail here rather than in downstream assertions
+ var item1Check = App.FindElement("Item1").GetRect();
+ Assert.That(item1Check.Width, Is.Not.EqualTo(item1Before.Width).Within(1),
+ "Item1 width should have changed within timeout after tapping ChangeWidths");
+
+ // After changing widths, all items should have equal width
+ var item1After = App.WaitForElement("Item1").GetRect();
+ var item2After = App.WaitForElement("Item2").GetRect();
+ var item3After = App.WaitForElement("Item3").GetRect();
+
+ // All items should now have the same width (within tolerance for density conversion)
+ Assert.That(item1After.Width, Is.EqualTo(item2After.Width).Within(2),
+ "After width change, Item1 and Item2 should have equal widths");
+ Assert.That(item2After.Width, Is.EqualTo(item3After.Width).Within(2),
+ "After width change, Item2 and Item3 should have equal widths");
+
+ // Widths should be larger than the initial smallest width
+ Assert.That(item1After.Width, Is.GreaterThan(item1Before.Width),
+ "Item1 should be wider after changing WidthRequest");
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31121.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31121.cs
new file mode 100644
index 000000000000..af555780da63
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31121.cs
@@ -0,0 +1,32 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+public class Issue31121 : _IssuesUITest
+{
+ public Issue31121(TestDevice testDevice) : base(testDevice) { }
+
+ public override string Issue => "[iOS, Mac] TabbedPage FlowDirection Property Renders Opposite Layout Direction When Set via ViewModel Binding";
+
+ [Test]
+ [Category(UITestCategories.TabbedPage)]
+ public void TabbedPageFlowDirectionUpdatesOnRuntimeChange()
+ {
+ Exception? exception = null;
+ App.WaitForElement("LeftToRightButton");
+ VerifyScreenshotOrSetException(ref exception, "TabbedPageFlowDirection_DefaultRightToLeftLayout");
+ App.Tap("LeftToRightButton");
+ VerifyScreenshotOrSetException(ref exception, "TabbedPageFlowDirection_AfterChangingToLeftToRight");
+ App.WaitForElement("RightToLeftButton");
+ App.Tap("RightToLeftButton");
+ App.WaitForElement("LeftToRightButton");
+ App.Tap("LeftToRightButton");
+ VerifyScreenshotOrSetException(ref exception, "TabbedPageFlowDirection_AfterChangingBackToLeftToRight");
+
+ if (exception != null)
+ {
+ throw exception;
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31140.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31140.cs
new file mode 100644
index 000000000000..efa50ecc7681
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31140.cs
@@ -0,0 +1,22 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue31140 : _IssuesUITest
+{
+ public override string Issue => "Setting both IndicatorSize and Shadow properties on IndicatorView causes some dots to be invisible";
+
+ public Issue31140(TestDevice device)
+ : base(device)
+ { }
+
+ [Test]
+ [Category(UITestCategories.IndicatorView)]
+ public void VerifyAllIndicatorDotsShowShadowsWhenIndicatorSize()
+ {
+ App.WaitForElement("carouselview");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31145.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31145.cs
new file mode 100644
index 000000000000..2029bd6296e9
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31145.cs
@@ -0,0 +1,23 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue31145 : _IssuesUITest
+{
+ public Issue31145(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "MaximumVisible Property Not Working with IndicatorTemplate in IndicatorView";
+
+ [Test]
+ [Category(UITestCategories.IndicatorView)]
+ public void VerifyIndicatorViewMaximumVisibleWithTemplate()
+ {
+ App.WaitForElement("UpdateMaximumVisibleBtn");
+ App.Tap("UpdateMaximumVisibleBtn");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31239.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31239.cs
new file mode 100644
index 000000000000..23e82f2ef428
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31239.cs
@@ -0,0 +1,27 @@
+#if TEST_FAILS_ON_ANDROID // https://github.com/dotnet/maui/issues/19568
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue31239(TestDevice testDevice) : _IssuesUITest(testDevice)
+{
+ const string _changeBackgroundButtonId = "changeBackgroundButton";
+ public override string Issue => "[iOS, Mac, Windows] GraphicsView does not change the Background/BackgroundColor";
+
+ [Test]
+ [Category(UITestCategories.GraphicsView)]
+ public void GraphicsViewBackgroundShouldBeAppliedAndChanged()
+ {
+ App.WaitForElement(_changeBackgroundButtonId);
+
+ // Verify initial background is applied
+ VerifyScreenshot("GraphicsViewBackgroundShouldBeApplied");
+
+ // Change backgrounds and verify they update
+ App.Tap(_changeBackgroundButtonId);
+ VerifyScreenshot("GraphicsViewBackgroundShouldBeChanged");
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31551.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31551.cs
new file mode 100644
index 000000000000..0663f4090c63
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31551.cs
@@ -0,0 +1,24 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue31551 : _IssuesUITest
+{
+ public Issue31551(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "ArgumentOutOfRangeException thrown by ScrollTo when group index is invalid";
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyNoExceptionOnInvalidGroupIndex()
+ {
+ App.WaitForElement("Issue31551ScrollBtn");
+ App.Tap("Issue31551ScrollBtn");
+
+ App.WaitForElement("Issue31551CollectionView");
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32016.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32016.cs
new file mode 100644
index 000000000000..d96b91a80930
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32016.cs
@@ -0,0 +1,26 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue32016 : _IssuesUITest
+{
+ public override string Issue => "iOS 26 MaxLength not enforced on Entry";
+
+ public Issue32016(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.Entry)]
+ public void EntryMaxLengthEnforcedOnIOS26()
+ {
+ App.WaitForElement("TestEntry");
+
+ // Type characters up to MaxLength
+ App.Tap("TestEntry");
+ App.EnterText("TestEntry", "1234567890x");
+
+ var text = App.FindElement("TestEntry").GetText();
+ Assert.That(text, Is.EqualTo("1234567890"), "Text should be '1234567890' - the 'x' should be blocked by MaxLength");
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32200.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32200.cs
new file mode 100644
index 000000000000..cbb124a7cb9f
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32200.cs
@@ -0,0 +1,19 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue32200 : _IssuesUITest
+{
+ public override string Issue => "NavigationPage TitleView iOS 26";
+ public Issue32200(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.Navigation)]
+ public void NavigationPageTitleViewShouldRespectMargins()
+ {
+ App.WaitForElement("Label");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32243.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32243.cs
new file mode 100644
index 000000000000..3b89108b2f95
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32243.cs
@@ -0,0 +1,33 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+[Category(UITestCategories.CollectionView)]
+public class Issue32243 : _IssuesUITest
+{
+ public Issue32243(TestDevice testDevice) : base(testDevice) { }
+
+ public override string Issue => "CollectionView does not disconnect handlers when DataTemplateSelector changes template";
+
+ [Test]
+ public void CollectionViewDisconnectsHandlersAfterNavigationBack()
+ {
+ App.WaitForElement("NavigateButton");
+ App.Tap("NavigateButton");
+
+ App.WaitForElement("SwitchTemplateButton");
+ App.Tap("SwitchTemplateButton");
+
+ App.WaitForElement("NavigateBackButton");
+ App.Tap("NavigateBackButton");
+
+ App.WaitForElement("CheckHandlersButton");
+ App.Tap("CheckHandlersButton");
+
+ var result = App.WaitForElement("HandlerCountLabel").GetText();
+ Assert.That(result, Is.EqualTo("✓ No labels have connected handlers (all cleaned up!)"),
+ "CollectionView should disconnect handlers from views belonging to the old DataTemplate after navigating back.");
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32266.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32266.cs
new file mode 100644
index 000000000000..7df55eaa2f02
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32266.cs
@@ -0,0 +1,35 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue32266 : _IssuesUITest
+{
+ public Issue32266(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "TimePicker on Windows Defaults to Midnight When Time Value Is Null";
+
+ [Test, Order(1)]
+ [Category(UITestCategories.TimePicker)]
+ public void VerifyTimePickerIsNullOnInitialLoad()
+ {
+ App.WaitForElement("Issue32266TimePicker");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(2)]
+ [Category(UITestCategories.TimePicker)]
+ public void VerifyClearedTimeDoesNotShowMidnight()
+ {
+ App.WaitForElement("SetTimeButton");
+ App.Tap("SetTimeButton");
+
+ App.WaitForElement("ClearTimeButton");
+ App.Tap("ClearTimeButton");
+
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32316.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32316.cs
new file mode 100644
index 000000000000..d3bf31669c26
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32316.cs
@@ -0,0 +1,24 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue32316 : _IssuesUITest
+{
+
+ public Issue32316(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "RTL mode: Padding for the label is not mirroring properly";
+
+ [Test]
+ [Category(UITestCategories.Label)]
+ public void RTLModePaddingShouldWork()
+ {
+ App.WaitForElement("ToggleFlowDirectionButton");
+ App.Tap("ToggleFlowDirectionButton");
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32406.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32406.cs
new file mode 100644
index 000000000000..431d724351bb
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32406.cs
@@ -0,0 +1,21 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue32406 : _IssuesUITest
+{
+ public override string Issue => "LayoutCycleException caused by nested Borders in ControlTemplates";
+
+ public Issue32406(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.Border)]
+ public void NestedBordersShouldNotCauseLayoutCycle()
+ {
+ // If the app crashes with a LayoutCycleException, we'll never reach this point.
+ // Wait for the Loaded handler to set "Success" text, avoiding a race with element existence.
+ App.WaitForTextToBePresentInElement("ResultLabel", "Success", timeout: TimeSpan.FromSeconds(30));
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32416.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32416.cs
new file mode 100644
index 000000000000..3ba55fe71eec
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32416.cs
@@ -0,0 +1,24 @@
+#if TEST_FAILS_ON_ANDROID // Issue Link - https://github.com/dotnet/maui/issues/32477
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue32416 : _IssuesUITest
+{
+ public Issue32416(TestDevice testDevice) : base(testDevice)
+ {
+ }
+ public override string Issue => "Shell.FlyoutVerticalScrollMode Disabled does not disable scrolling";
+
+ [Test]
+ [Category(UITestCategories.FlyoutPage)]
+ public void VerifyFlyoutVerticalScrollModeDisabled()
+ {
+ App.WaitForElement("Item 1");
+ App.ScrollDown("Item 1", ScrollStrategy.Gesture, swipePercentage: 0.95, swipeSpeed: 100);
+ VerifyScreenshot();
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32791.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32791.cs
new file mode 100644
index 000000000000..506921cc3455
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32791.cs
@@ -0,0 +1,29 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue32791 : _IssuesUITest
+{
+ public Issue32791(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "Setting the IsEnabled property of the CarouselView to false does not prevent swiping through items";
+
+ [Test]
+ [Category(UITestCategories.CarouselView)]
+ public void VerifyCarouselViewPreventsSwipingWhenDisabled()
+ {
+ App.WaitForElement("DisabledCarouselView");
+ App.ScrollRight("DisabledCarouselView");
+
+#if MACCATALYST
+ // MacCatalyst: Wait for item transition to complete after scroll gesture
+ Thread.Sleep(1000);
+#endif
+ var statusText = App.WaitForElement("Issue32791StatusLabel").GetText();
+ Assert.That(statusText, Is.EqualTo("Success"));
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32983.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32983.cs
new file mode 100644
index 000000000000..bb2c9168b8c2
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32983.cs
@@ -0,0 +1,26 @@
+// This test is iOS-only: The test sample uses UISheetPresentationController with a custom detent to present a bottom sheet sized to the measured content height. This API is not available on Android or Windows, and on MacCatalyst it ignores custom detents and always presents as a full-height modal.
+#if IOS
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue32983 : _IssuesUITest
+{
+ public override string Issue => "CollectionView messes up Measure operation on Views";
+
+ public Issue32983(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void BottomSheetDetentHeightIsCorrectWhenCollectionViewIsMeasuredBeforeMount()
+ {
+ App.WaitForElement("ShowBottomSheetButton");
+ App.Tap("ShowBottomSheetButton");
+
+ App.WaitForElement("BottomSheetCollectionView");
+ VerifyScreenshot();
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32989.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32989.cs
new file mode 100644
index 000000000000..1ddd70df55dd
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32989.cs
@@ -0,0 +1,24 @@
+#if WINDOWS //This is a Windows-specific issue
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+public class Issue32989 : _IssuesUITest
+{
+ public override string Issue => "Exception thrown on .NET 10 Windows when calling Permissions.CheckStatusAsync()";
+
+ public Issue32989(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.Essentials)]
+ public void MicrophonePermissionCheckDoesNotCrash()
+ {
+ App.WaitForElement("CheckPermissionButton");
+ App.Tap("CheckPermissionButton");
+ // Verify that the status was updated (permission check completed)
+ var statusText = App.FindElement("StatusLabel").GetText();
+ Assert.That(statusText, Is.EqualTo("Test Passed"));
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32995.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32995.cs
new file mode 100644
index 000000000000..55278f5989ae
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32995.cs
@@ -0,0 +1,24 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue32995 : _IssuesUITest
+{
+ public Issue32995(TestDevice device) : base(device) { }
+
+ public override string Issue => "TabBarDisabledColor not applied to disabled tabs on iOS";
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void TabBarDisabledColorAppliedToDisabledTab()
+ {
+ App.WaitForElement("Tab2");
+ VerifyScreenshot("DisabledTabWithGreenColor");
+
+ App.Tap("EnableButton");
+ App.WaitForElement("Tab2");
+ VerifyScreenshot("EnabledTabWithNormalColor");
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33037.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33037.cs
new file mode 100644
index 000000000000..3727caa50848
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33037.cs
@@ -0,0 +1,34 @@
+#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_WINDOWS
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue33037 : _IssuesUITest
+{
+ public Issue33037(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "iOS Large Title display disappears when scrolling in Shell";
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void LargeTitleTransitionsOnScroll()
+ {
+ // Verify page loaded with large title visible
+ App.WaitForElement("PageTitle");
+
+ // Take a screenshot before scrolling to verify large title is shown
+ VerifyScreenshot("Issue33037_BeforeScroll");
+
+ // Scroll down to trigger the large title → standard title transition
+ App.ScrollDown("TestScrollView");
+ App.ScrollDown("TestScrollView");
+
+ // Take screenshot after scrolling — title should have transitioned to standard size
+ VerifyScreenshot("Issue33037_AfterScroll", retryTimeout: TimeSpan.FromSeconds(2));
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33304.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33304.cs
new file mode 100644
index 000000000000..460776bc7b30
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33304.cs
@@ -0,0 +1,27 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue33304 : _IssuesUITest
+ {
+ public Issue33304(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "Shell TitleView disappears when switching tabs on Android";
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void TitleViewPersistsAfterSwitchingTabs()
+ {
+ App.WaitForElement("HomeTabLabel");
+ App.Tap("Search");
+ App.WaitForElement("SearchTabLabel");
+ App.Tap("Home");
+ App.WaitForElement("HomeTabLabel");
+ App.WaitForElement("HomeTitleView");
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33351.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33351.cs
new file mode 100644
index 000000000000..89196830f37a
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33351.cs
@@ -0,0 +1,34 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue33351 : _IssuesUITest
+{
+ public Issue33351(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "Changing Shell Tab Visibility when navigating back multiple pages ignores Shell Tab Visibility";
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void TabBarVisibilityAfterMultiLevelPopToRoot()
+ {
+ App.WaitForElement("Tab 1");
+ App.Tap("Tab 1");
+
+ App.WaitForElement("PushPage1Button");
+ App.Tap("PushPage1Button");
+
+ App.WaitForElement("PushPage2Button");
+ App.Tap("PushPage2Button");
+
+ App.WaitForElement("PopToRootButton");
+ App.Tap("PopToRootButton");
+ App.WaitForElement("TabBarVisibleLabel");
+
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33375.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33375.cs
new file mode 100644
index 000000000000..5fe3304bd29f
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33375.cs
@@ -0,0 +1,51 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue33375 : _IssuesUITest
+ {
+ public Issue33375(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "SwipeGestureRecognizer triggers while scrolling CollectionView horizontally on iOS";
+
+ [Test]
+ [Category(UITestCategories.Gestures)]
+ public void SwipeGestureRecognizerShouldNotTriggerWhenScrollingCollectionView()
+ {
+ App.WaitForElement("TestCollectionView");
+ var statusLabel = App.WaitForElement("StatusLabel");
+ Assert.That(statusLabel.GetText(), Is.EqualTo("No swipe detected"));
+ var collectionView = App.FindElement("TestCollectionView");
+
+ // Scroll right in the CollectionView
+ App.DragCoordinates(
+ collectionView.GetRect().CenterX(),
+ collectionView.GetRect().CenterY(),
+ collectionView.GetRect().X + 50,
+ collectionView.GetRect().CenterY()
+ );
+
+ App.WaitForElement("StatusLabel");
+ statusLabel = App.FindElement("StatusLabel");
+ Assert.That(statusLabel.GetText(), Is.EqualTo("No swipe detected"),
+ "SwipeGestureRecognizer should not trigger when scrolling CollectionView");
+
+ // Scroll back left to original position
+ App.DragCoordinates(
+ collectionView.GetRect().X + 50,
+ collectionView.GetRect().CenterY(),
+ collectionView.GetRect().CenterX(),
+ collectionView.GetRect().CenterY()
+ );
+
+ App.WaitForElement("StatusLabel");
+ statusLabel = App.FindElement("StatusLabel");
+ Assert.That(statusLabel.GetText(), Is.EqualTo("No swipe detected"),
+ "SwipeGestureRecognizer should not trigger when scrolling back");
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33400.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33400.cs
new file mode 100644
index 000000000000..eac3e7a49bf4
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33400.cs
@@ -0,0 +1,43 @@
+#if ANDROID || WINDOWS // https://github.com/dotnet/maui/issues/7767#issuecomment-1792186959
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue33400 : _IssuesUITest
+{
+ public Issue33400(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "Runtime Scrollbar visibility not updating correctly on Android platform";
+
+ [Test, Order(1)]
+ [Category(UITestCategories.ScrollView)]
+ public void Issue33400ScrollbarVisibilityNever()
+ {
+ App.WaitForElement("Issue33400HorizontalNeverButton");
+ App.Tap("Issue33400HorizontalNeverButton");
+ VerifyScreenshot("Issue33400_Never");
+ }
+
+ [Test, Order(2)]
+ [Category(UITestCategories.ScrollView)]
+ public void Issue33400ScrollbarVisibilityAlways()
+ {
+ App.WaitForElement("Issue33400HorizontalAlwaysButton");
+ App.Tap("Issue33400HorizontalAlwaysButton");
+ VerifyScreenshot("Issue33400_Always");
+ }
+
+ [Test, Order(3)]
+ [Category(UITestCategories.ScrollView)]
+ public void Issue33400ScrollbarVisibilityDefault()
+ {
+ App.WaitForElement("Issue33400HorizontalDefaultButton");
+ App.Tap("Issue33400HorizontalDefaultButton");
+ VerifyScreenshot("Issue33400_Default");
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33407.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33407.cs
new file mode 100644
index 000000000000..a43ba8b7f7f3
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33407.cs
@@ -0,0 +1,45 @@
+#if IOS || ANDROID // Orientation change is not supported on Windows and MacCatalyst
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue33407 : _IssuesUITest
+{
+ public Issue33407(TestDevice device) : base(device) { }
+
+ public override string Issue => "Focusing and entering texts on entry control causes a gap at the top after rotating simulator.";
+
+ [TearDown]
+ public void TearDown()
+ {
+ App.SetOrientationPortrait();
+ }
+
+ [Test]
+ [Category(UITestCategories.Entry)]
+ public void EntryFocusedShouldNotCauseGapAfterRotation()
+ {
+ App.WaitForElement("CategoryE");
+ App.Tap("CategoryE");
+
+ App.WaitForElement("TestE1");
+ App.Tap("TestE1");
+
+ App.WaitForElement("Entry1");
+ App.Tap("Entry1");
+
+ // Rotate while keyboard is visible — RestorePosition() incorrectly used the portrait Y
+ // in landscape space before the fix, causing a gap at the top.
+ App.SetOrientationLandscape();
+
+ // Dismiss keyboard before screenshot to avoid cursor flakiness.
+ App.DismissKeyboard();
+
+ // Use retryTimeout to wait for the keyboard-dismiss/restore animation to fully settle
+ // before asserting, avoiding flaky mid-transition screenshots.
+ VerifyScreenshot(retryTimeout: TimeSpan.FromSeconds(2));
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33415.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33415.cs
new file mode 100644
index 000000000000..78c477aef22e
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33415.cs
@@ -0,0 +1,68 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue33415 : _IssuesUITest
+ {
+ public override string Issue => "ApplyQueryAttributes gets called with empty Dictionary on back";
+
+ public Issue33415(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void ApplyQueryAttributesShouldNotBeCalledWithEmptyDictionaryOnBack()
+ {
+ // Start on landing page - tap to navigate TO MainPage WITH parameters
+ App.WaitForElement("NavigateToMainPageButton");
+ App.Tap("NavigateToMainPageButton");
+
+ // Wait for main page to appear
+ App.WaitForElement("StatusLabel");
+ App.WaitForElement("CallCountLabel");
+
+ // Verify ApplyQueryAttributes was called with parameters
+ var callCountAfterNav = App.FindElement("CallCountLabel").GetText();
+ var statusAfterNav = App.FindElement("StatusLabel").GetText();
+ Assert.That(callCountAfterNav, Is.EqualTo("Call count: 1"), "ApplyQueryAttributes should be called once with parameters");
+ Assert.That(statusAfterNav, Does.Contain("1 parameter"), "Status should indicate parameters were received");
+
+ // Navigate to second page
+ App.Tap("NavigateButton");
+
+ // Wait for second page to appear
+ App.WaitForElement("ReceivedLabel");
+
+ // Verify second page received the parameter (passed through from MainPage navigation)
+ var receivedText = App.FindElement("ReceivedLabel").GetText();
+ Assert.That(receivedText, Is.EqualTo("Received: TestValue"), "Second page should receive the parameter");
+
+ // Go back to main page
+ App.Tap("BackButton");
+
+ // Wait for main page to reappear
+ App.WaitForElement("StatusLabel");
+ App.WaitForElement("CallCountLabel");
+
+ // BUG: According to documentation, after calling query.Clear() in ApplyQueryAttributes,
+ // the method should NOT be called again when navigating back.
+ // However, it IS called with an empty dictionary.
+ var finalCallCount = App.FindElement("CallCountLabel").GetText();
+ var finalStatus = App.FindElement("StatusLabel").GetText();
+
+ // This assertion will FAIL (demonstrating the bug)
+ // Expected: Call count should still be 1 (ApplyQueryAttributes should not be called on back)
+ // Actual: Call count will be 2 (ApplyQueryAttributes IS called with empty dictionary)
+ Assert.That(finalCallCount, Is.EqualTo("Call count: 1"),
+ "ApplyQueryAttributes should NOT be called when navigating back after query.Clear()");
+
+ // If the bug exists, this will show it was called with empty dictionary
+ if (finalCallCount != "Call count: 1")
+ {
+ Assert.That(finalStatus, Is.EqualTo("Status: ApplyQueryAttributes called with EMPTY dictionary"),
+ "If ApplyQueryAttributes is incorrectly called, it's called with empty dictionary");
+ }
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33420.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33420.cs
new file mode 100644
index 000000000000..6661abbfa4c3
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33420.cs
@@ -0,0 +1,31 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue33420 : _IssuesUITest
+{
+ public override string Issue => "System.InvalidCastException when using QueryPropertyAttribute with nullable types";
+
+ public Issue33420(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void NullableQueryPropertyNavigationShouldNotCrash()
+ {
+ // Wait for main page to load
+ App.WaitForElement("MainLabel");
+
+ // Tap navigation button with nullable parameter
+ App.Tap("NavigateButton");
+
+ // If the bug is present, the app will crash
+ // If fixed, we should navigate successfully to the details page
+ App.WaitForElement("ResultLabel");
+
+ // Verify the nullable value was passed correctly
+ var resultText = App.FindElement("ResultLabel").GetText();
+ Assert.That(resultText, Is.EqualTo("Success: ID=1"));
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33523.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33523.cs
index d148110b1651..f0eccafee18b 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33523.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33523.cs
@@ -1,5 +1,3 @@
-#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS // Issue: https://github.com/dotnet/maui/issues/34190
-
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
@@ -26,19 +24,20 @@ public void OnBackButtonPressedShouldFireForShellNavigationBarButton()
// Verify initial state
var statusLabel = App.WaitForElement("StatusLabel");
Assert.That(statusLabel.GetText(), Is.EqualTo("OnBackButtonPressed not called"));
-
- // Tap the navigation bar back button
- // Note: This uses the Shell's navigation bar back button, not the system back button
- App.TapBackArrow();
-
- // Wait a moment for the event to fire
+ if (App is AppiumIOSApp iosApp && HelperExtensions.IsIOS26OrHigher(iosApp))
+ {
+ App.TapBackArrow(); // In iOS 26, the previous page title is not shown along with the back arrow, so we use the default back arrow
+ }
+ else
+ {
+ App.TapBackArrow(Device is TestDevice.iOS or TestDevice.Mac ? "Main Page" : "");
+ }
App.WaitForElement("StatusLabel");
// Verify OnBackButtonPressed was called
statusLabel = App.FindElement("StatusLabel");
- Assert.That(statusLabel.GetText(), Is.EqualTo("OnBackButtonPressed was called"),
+ Assert.That(statusLabel.GetText(), Is.EqualTo("OnBackButtonPressed was called"),
"OnBackButtonPressed should be called when tapping the Shell Navigation Bar back button");
}
}
}
-#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33547.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33547.cs
new file mode 100644
index 000000000000..31b290a54609
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33547.cs
@@ -0,0 +1,27 @@
+#if IOS || ANDROID // This test is only for Android and iOS because the issue is specifically related to on-screen keyboard behavior which is only available on mobile platforms.
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue33547 : _IssuesUITest
+{
+ public override string Issue => "[iOS] Shell Page gets moved partially outside of viewport when focusing element on page load";
+
+ public Issue33547(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void PageShouldNotMoveOutsideViewportWhenEntryFocusedOnPageLoad()
+ {
+ App.WaitForElement("NavigationPushButton");
+ App.Tap("NavigationPushButton");
+
+ App.WaitForElement("TestEntry");
+ App.DismissKeyboard();
+
+ VerifyScreenshot();
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33604.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33604.cs
new file mode 100644
index 000000000000..537e535562be
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33604.cs
@@ -0,0 +1,35 @@
+// iOS only: Android CI devices lack notch/cutout, making SafeArea screenshot verification ineffective.
+#if IOS
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue33604 : _IssuesUITest
+ {
+ public override string Issue => "CollectionView does not respect content SafeAreaEdges choices";
+
+ public Issue33604(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void CollectionViewItemsShouldRespectSafeAreaEdges()
+ {
+ App.WaitForElement("TestCollectionView");
+ App.SetOrientationLandscape();
+
+ // Allow layout to settle after orientation change
+ App.WaitForElement("TopLabel");
+
+ VerifyScreenshot(retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ App.SetOrientationPortrait();
+ }
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33614.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33614.cs
new file mode 100644
index 000000000000..ade6d7440373
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33614.cs
@@ -0,0 +1,22 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue33614 : _IssuesUITest
+{
+ public override string Issue => "CollectionView Scrolled event reports incorrect FirstVisibleItemIndex after programmatic ScrollTo";
+
+ public Issue33614(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void FirstVisibleItemIndexShouldBeCorrectAfterScrollTo()
+ {
+ App.WaitForElement("ScrollToButton");
+ App.Tap("ScrollToButton");
+ var firstIndexText = App.FindElement("FirstIndexLabel").GetText();
+ Assert.That(firstIndexText, Is.EqualTo("FirstVisibleItemIndex: 15"));
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33706.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33706.cs
new file mode 100644
index 000000000000..2f9080c971ca
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33706.cs
@@ -0,0 +1,35 @@
+#if ANDROID
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue33706 : _IssuesUITest
+ {
+ public override string Issue => "MediaPicker gets stuck if LaunchMode is SingleTask";
+
+ public Issue33706(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.LifeCycle)]
+ public void Issue33706Test()
+ {
+ // Wait for the page to load
+ App.WaitForElement("PickMediaButton");
+ App.WaitForElement("StatusLabel");
+ var initialStatus = App.FindElement("StatusLabel").GetText();
+ Assert.That(initialStatus, Is.EqualTo("Ready"), "Initial status should be 'Ready'");
+ App.Tap("PickMediaButton");
+ App.BackgroundApp();
+ App.ForegroundApp();
+ App.WaitForElement("ResumedLabel");
+ App.WaitForElement("StatusLabel");
+ App.WaitForElement("PickMediaButton");
+ var finalStatus = App.FindElement("StatusLabel").GetText();
+ Assert.That(finalStatus, Is.Not.EqualTo("Picking..."),
+ "Status should not still be 'Picking...' after returning from background - this indicates MediaPicker async call is stuck");
+ }
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33769.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33769.cs
new file mode 100644
index 000000000000..cdd0cbd2fda7
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33769.cs
@@ -0,0 +1,28 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue33769 : _IssuesUITest
+{
+ public Issue33769(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "Stepper control fails to reach maximum value when increment exceeds remaining threshold";
+
+ [Test]
+ [Category(UITestCategories.Stepper)]
+ public void ValidateStepperReachesMinMax()
+ {
+ App.WaitForElement("Issue33769_StepperStatusLabel");
+ App.IncreaseStepper("Issue33769_Stepper");
+ var result = App.WaitForElement("Issue33769_StepperStatusLabel").GetText();
+ Assert.That(result, Is.EqualTo("Success"));
+
+ App.DecreaseStepper("Issue33769_Stepper");
+ result = App.WaitForElement("Issue33769_StepperStatusLabel").GetText();
+ Assert.That(result, Is.EqualTo("Success"));
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33772.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33772.cs
new file mode 100644
index 000000000000..77adb6901743
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33772.cs
@@ -0,0 +1,31 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue33772 : _IssuesUITest
+{
+ public Issue33772(TestDevice testDevice) : base(testDevice) { }
+
+ public override string Issue => "Shell SearchHandler SearchBoxVisibility does not update when changed dynamically";
+
+ [Test, Order(1)]
+ [Category(UITestCategories.Shell)]
+ public void SearchHandlerVisibilityChangesToExpanded()
+ {
+ App.WaitForElement("TitleLabel");
+ App.Tap("ExpandButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(2)]
+ [Category(UITestCategories.Shell)]
+ public void SearchHandlerVisibilityChangesToCollapsible()
+ {
+ // Wait for the page to load
+ App.WaitForElement("TitleLabel");
+ App.Tap("CollapsibleButton");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33904.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33904.cs
new file mode 100644
index 000000000000..08e0c8a3461f
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33904.cs
@@ -0,0 +1,22 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue33904 : _IssuesUITest
+{
+ public Issue33904(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "CharacterSpacing applied to Label is not inherited by Span elements in FormattedString";
+
+ [Test]
+ [Category(UITestCategories.Label)]
+ public void VerifySpanInheritsLabelCharacterSpacing()
+ {
+ App.WaitForElement("InheritedCharacterSpacingLabel");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33909.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33909.cs
new file mode 100644
index 000000000000..025924ae882b
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33909.cs
@@ -0,0 +1,27 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue33909 : _IssuesUITest
+{
+ public override string Issue => "[iOS, Android, Catalyst] Shell.ForegroundColor does not reset correctly for the back button";
+
+ public Issue33909(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void Issue33909ForegroundColorReset()
+ {
+ App.WaitForElement("ApplyColorButton");
+ App.Tap("ApplyColorButton");
+ App.WaitForElement("RemoveColorButton");
+ App.Tap("RemoveColorButton");
+ App.WaitForElement("NavigateButton");
+ App.Tap("NavigateButton");
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34073.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34073.cs
new file mode 100644
index 000000000000..c25b09364b4f
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34073.cs
@@ -0,0 +1,39 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue34073 : _IssuesUITest
+{
+ public override string Issue => "OnNavigatingFrom is reporting wrong DestinationPage";
+
+ public Issue34073(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void OnNavigatingFromShouldReportCorrectDestinationPage()
+ {
+ // Wait for Page A to load
+ App.WaitForElement("NavigateButton");
+
+ // Navigate A → B, which triggers OnNavigatingFrom on PageA
+ // The bug: DestinationPage is PageA (self) instead of PageB
+ App.Tap("NavigateButton");
+
+ // Wait for Page B to appear
+ App.WaitForElement("GoBackButton");
+
+ // Navigate back to Page A so we can read the captured result label
+ App.Tap("GoBackButton");
+
+ // Wait for Page A to reappear
+ App.WaitForElement("NavigatingFromResult");
+
+ // DestinationPage should be Issue34073PageB (the page we navigated TO)
+ // Bug causes it to report Issue34073PageA (the current/source page)
+ var result = App.FindElement("NavigatingFromResult").GetText();
+ Assert.That(result, Is.EqualTo("Issue34073PageB"),
+ $"OnNavigatingFrom.DestinationPage should be PageB but was: {result}");
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34083.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34083.cs
new file mode 100644
index 000000000000..9f4d578494ce
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34083.cs
@@ -0,0 +1,24 @@
+#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_WINDOWS // Android Issue Link: https://github.com/dotnet/maui/issues/24676, Windows Issue Link: https://github.com/dotnet/maui/issues/34071
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue34083 : _IssuesUITest
+{
+ public Issue34083(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "Toolbar Items Do Not Reflect Shell ForegroundColor";
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void VerifyShellForegroundColorIsAppliedToToolbarItems()
+ {
+ App.WaitForElement("Issue34083_DescriptionLabel");
+ VerifyScreenshot();
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34114.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34114.cs
new file mode 100644
index 000000000000..0f5b80b7a509
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34114.cs
@@ -0,0 +1,24 @@
+#if TEST_FAILS_ON_WINDOWS // This test fails on Windows because of a known issue with clipping in WinUI.
+
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue34114 : _IssuesUITest
+{
+ public Issue34114(TestDevice device) : base(device) { }
+
+ public override string Issue => "Label with background clip is not working properly";
+
+ [Test]
+ [Category(UITestCategories.Label)]
+ public void VerifyLabelBackgroundIsClippedWithRectangleGeometry()
+ {
+ App.WaitForElement("ClippedLabel");
+ App.Tap("ChangeClip");
+ VerifyScreenshot();
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34122.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34122.cs
new file mode 100644
index 000000000000..5929d9df3506
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34122.cs
@@ -0,0 +1,57 @@
+#if TEST_FAILS_ON_WINDOWS // EmptyView elements are not accessible via Automation on Windows. Issue Link: https://github.com/dotnet/maui/issues/28022
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue34122 : _IssuesUITest
+{
+ const string BasicEmptyViewButtonId = "BasicEmptyViewButton";
+ const string AdvancedEmptyViewButtonId = "AdvancedEmptyViewButton";
+
+ public override string Issue => "I5_EmptyView_Swap - Continuously turning the Toggle EmptyViews on and off would cause an item from the list to show up";
+
+ public Issue34122(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void EmptyViewSwapShouldNotRevealFilteredOutItems()
+ {
+ // Verify items are visible initially
+ App.WaitForElement("Baboon");
+
+ // Clear all items one-by-one (matches the original ManualTests FilterCommand),
+ // which fires multiple CollectionChanged events and puts RecyclerView into
+ // the state that triggers the EmptyView-swap bug.
+ App.WaitForElement("FilterButton");
+ App.Tap("FilterButton");
+ AssertEmptyViewState(AdvancedEmptyViewButtonId, BasicEmptyViewButtonId);
+
+ App.WaitForElement("ToggleEmptyViewButton");
+
+ for (int i = 1; i <= 8; i++)
+ {
+ App.Tap("ToggleEmptyViewButton");
+
+ if (i % 2 == 1) // odd → BasicEmptyView
+ {
+ AssertEmptyViewState(BasicEmptyViewButtonId, AdvancedEmptyViewButtonId);
+ }
+ else // even → AdvancedEmptyView
+ {
+ AssertEmptyViewState(AdvancedEmptyViewButtonId, BasicEmptyViewButtonId);
+ }
+ }
+ }
+
+ void AssertEmptyViewState(string expectedEmptyViewButtonId, string unexpectedEmptyViewButtonId)
+ {
+ App.WaitForElement(expectedEmptyViewButtonId,
+ $"Expected empty view '{expectedEmptyViewButtonId}' was not visible.");
+
+ App.WaitForNoElement(unexpectedEmptyViewButtonId,
+ $"'{unexpectedEmptyViewButtonId}' should not be visible while '{expectedEmptyViewButtonId}' is active.");
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34143.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34143.cs
new file mode 100644
index 000000000000..3bb3749f41ed
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34143.cs
@@ -0,0 +1,34 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue34143 : _IssuesUITest
+{
+ public override string Issue => "Tab bar ghosting issue after navigating from modal via GoToAsync";
+
+ public Issue34143(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void TabBarShouldBeVisibleAfterNavigatingFromModalViaGoToAsync()
+ {
+ // Start on Home page
+ App.WaitForElement("Issue34143PushModal");
+
+ // Push a modal page
+ App.Tap("Issue34143PushModal");
+
+ // Verify modal is shown
+ App.WaitForElement("Issue34143GoToTabBar");
+
+ // Navigate from modal to the tab bar using GoToAsync
+ App.Tap("Issue34143GoToTabBar");
+
+ // Verify we landed on Tab 1 and the tab bar tabs are interactable (not ghosted)
+ App.WaitForElement("Issue34143TabContent");
+
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs
new file mode 100644
index 000000000000..3a2a8227dae8
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34165.cs
@@ -0,0 +1,31 @@
+#if ANDROID || IOS // ScrollRight with ScrollStrategy.Gesture is not supported on Windows and MacCatalyst
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+[Category(UITestCategories.RefreshView)]
+public class Issue34165 : _IssuesUITest
+{
+ public Issue34165(TestDevice testDevice) : base(testDevice) { }
+
+ public override string Issue => "CollectionView is scrolling left/right when the collection is empty and inside a RefreshView";
+
+ [Test]
+ public void EmptyCollectionViewInsideRefreshViewShouldNotScrollHorizontally()
+ {
+ App.WaitForElement("CollectionView");
+
+ var rectBefore = App.WaitForElement("EmptyViewLabel").GetRect();
+
+ App.ScrollRight("CollectionView", ScrollStrategy.Gesture, swipePercentage: 0.8, swipeSpeed: 300);
+
+ var rectAfter = App.WaitForElement("EmptyViewLabel").GetRect();
+
+ Assert.That(rectAfter.X, Is.EqualTo(rectBefore.X).Within(1),
+ $"EmptyViewLabel X position changed from {rectBefore.X} to {rectAfter.X}. " +
+ "CollectionView must NOT scroll horizontally when empty inside a RefreshView.");
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34210.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34210.cs
new file mode 100644
index 000000000000..6a88e4b6954a
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34210.cs
@@ -0,0 +1,24 @@
+#if TEST_FAILS_ON_WINDOWS // Cannot open programmatically a SwipeView on Windows.
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue34210 : _IssuesUITest
+{
+ public override string Issue => "SwipeItem ignores FontImageSource rendered size on Android";
+
+ public Issue34210(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.SwipeView)]
+ public void SwipeItemFontImageSourceSizeIsRespected()
+ {
+ App.WaitForElement("OpenSwipeViewButton");
+ App.Tap("OpenSwipeViewButton");
+ App.WaitForElement("Action");
+ VerifyScreenshot();
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34211.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34211.cs
new file mode 100644
index 000000000000..7118cea9b716
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34211.cs
@@ -0,0 +1,49 @@
+#if ANDROID // Android-only: display-density change can only be triggered via adb shell wm density.
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue34211 : _IssuesUITest
+{
+ public Issue34211(TestDevice device) : base(device) { }
+
+ public override string Issue => "Android display-size change causes parent and drawable children mismatch in .NET MAUI";
+
+ [Test]
+ [Category(UITestCategories.GraphicsView)]
+ public void DrawableDirtyRectMatchesGraphicsViewSizeAfterDisplayDensityChange()
+ {
+ App.WaitForElement("Issue34211_GraphicsView");
+
+ string originalDensity = ShellHelper.ExecuteShellCommandWithOutput("adb shell wm density").Trim();
+ try
+ {
+ App.BackgroundApp();
+ ShellHelper.ExecuteShellCommand($"adb shell wm density {(originalDensity.Contains("320", StringComparison.Ordinal) ? "280" : "320")}");
+ App.ForegroundApp();
+
+ // adb shell wm density triggers an Activity recreation because density is not listed
+ // in MAUI's android:configChanges. The app restarts at the list page, so re-navigate.
+ App.WaitForElement("SearchBar");
+ App.ClearText("SearchBar");
+ App.EnterText("SearchBar", Issue);
+ App.WaitForElement("GoToTestButton");
+ App.Tap("GoToTestButton");
+
+ var el = App.WaitForElement(() =>
+ {
+ var e = App.FindElement("Issue34211_StatusLabel");
+ var text = e?.GetText() ?? string.Empty;
+ return text.StartsWith("PASS", StringComparison.Ordinal) || text.StartsWith("FAIL", StringComparison.Ordinal) ? e : null;
+ }, "Timed out waiting for draw after density change");
+ Assert.That(el.GetText(), Does.StartWith("PASS"), "GraphicsView and drawable sizes diverged after display-density change");
+ }
+ finally
+ {
+ ShellHelper.ExecuteShellCommand("adb shell wm density reset");
+ }
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34247.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34247.cs
new file mode 100644
index 000000000000..c9799d08cda6
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34247.cs
@@ -0,0 +1,26 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue34247 : _IssuesUITest
+ {
+ public Issue34247(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "CollectionView with HeaderTemplate and SelectionMode.Single crashes on selection";
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void SelectingItemInCollectionViewWithHeaderTemplateDoesNotCrash()
+ {
+ App.WaitForElement("TestCollectionView");
+ App.WaitForElement("Item 1");
+ App.Tap("Item 1");
+ var result = App.WaitForElement("ResultLabel").GetText();
+ Assert.That(result, Is.EqualTo("Success"));
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34273.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34273.cs
new file mode 100644
index 000000000000..16fcfcea7421
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34273.cs
@@ -0,0 +1,46 @@
+#if IOS || ANDROID //The test fails on Windows and MacCatalyst because the SetOrientation method, which is intended to change the device orientation, is only supported on mobile platforms iOS and Android.
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue34273 : _IssuesUITest
+{
+ public Issue34273(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "Rotating simulator makes Stepper and Label overlap";
+
+ [Test]
+ [Category(UITestCategories.Editor)]
+ public void EditorNoOverlapAfterRotateToLandscape()
+ {
+ App.WaitForElement("TestEditor");
+
+ App.SetOrientationLandscape();
+ // Wait for the layout to re-render after orientation change before verifying
+ App.WaitForElement("TestEditor");
+
+ VerifyScreenshot(retryTimeout: TimeSpan.FromSeconds(2));
+ }
+
+ [Test]
+ [Category(UITestCategories.Editor)]
+ public void EditorNoOverlapAfterRotateToPortrait()
+ {
+ App.WaitForElement("TestEditor");
+
+ App.SetOrientationLandscape(); // rotate to landscape first to trigger the bug scenario
+ // Wait for the layout to re-render after orientation change before continuing
+ App.WaitForElement("TestEditor");
+
+ App.SetOrientationPortrait(); // rotate back to portrait to verify layout recovers
+ // Wait for the layout to re-render after orientation change before verifying
+ App.WaitForElement("TestEditor");
+
+ VerifyScreenshot(retryTimeout: TimeSpan.FromSeconds(2));
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34336.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34336.cs
new file mode 100644
index 000000000000..4cac3ecc11d6
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34336.cs
@@ -0,0 +1,31 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue34336 : _IssuesUITest
+{
+ public override string Issue => "[iOS] CollectionView has excessive height if ObservableCollection source delayed in loading";
+
+ public Issue34336(TestDevice device) : base(device) { }
+
+ // Verifies that a horizontal CollectionView inside a Grid row set to Auto correctly
+ // resizes to fit its content after a delayed ObservableCollection population.
+ // Bug: On iOS the CollectionView retains excessive height (full-screen) instead of
+ // shrinking to match its items once they arrive after a 3-second delay.
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void CollectionViewHeightIsCorrectAfterDelayedLoad()
+ {
+ // Wait for page to load
+ App.WaitForElement("BelowCollectionViewLabel");
+
+ // Wait for items to appear after the delayed load.
+ App.WaitForElement("ITEM 0", timeout: TimeSpan.FromSeconds(3));
+
+ // Using screenshot instead of assert checks as a safer approach,
+ // since pixel-level differences can occur across platforms.
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34343.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34343.cs
new file mode 100644
index 000000000000..94df2a6576c3
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34343.cs
@@ -0,0 +1,37 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue34343 : _IssuesUITest
+{
+ public override string Issue => "TabBar displays wrong tabs after first tab becomes invisible";
+
+ public Issue34343(TestDevice device) : base(device) { }
+
+#if ANDROID
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void TabBarShouldDisplayCorrectTabsAfterFirstTabBecomesInvisible()
+ {
+ App.WaitForElement("HideAndNavigateButton");
+ App.Tap("HideAndNavigateButton");
+ App.WaitForElement("Tab5Content");
+ VerifyScreenshot();
+ }
+#endif
+
+#if IOS || MACCATALYST
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void SubPageNavigationShouldWorkAfterFirstTabBecomesInvisible()
+ {
+ App.WaitForElement("HideAndNavigateButton");
+ App.Tap("HideAndNavigateButton");
+ App.WaitForElement("Tab5Content");
+ App.Tap("NavigateToPage51Button");
+ VerifyScreenshot();
+ }
+#endif
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34464.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34464.cs
new file mode 100644
index 000000000000..177438acfa14
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34464.cs
@@ -0,0 +1,20 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue34464 : _IssuesUITest
+{
+ public Issue34464(TestDevice device) : base(device) { }
+
+ public override string Issue => "FlexLayout with BindableLayout and Label text display";
+
+ [Test]
+ [Category(UITestCategories.Layout)]
+ public void FlexLayoutWithBindableLayoutDisplaysLabels()
+ {
+ App.WaitForElement("HeaderLabel");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8486.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8486.cs
new file mode 100644
index 000000000000..bc6c2c90995d
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8486.cs
@@ -0,0 +1,23 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue8486 : _IssuesUITest
+ {
+ public Issue8486(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "GraphicsView DrawString not rendering in iOS";
+
+ [Test]
+ [Category(UITestCategories.GraphicsView)]
+ public void DrawStringShouldDrawText()
+ {
+ App.WaitForElement("GraphicsView");
+ VerifyScreenshot();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/IssueShell6738.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/IssueShell6738.cs
index 2a48770352eb..2f60dfd1f50a 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/IssueShell6738.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/IssueShell6738.cs
@@ -1,6 +1,5 @@
-#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_ANDROID
+#if TEST_FAILS_ON_WINDOWS
// Windows: The snapshot does not capture the FlyoutIcon applied to the TitleBar.
-// Android: The fix has not been implemented, so the icon defaults to white.
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
@@ -32,7 +31,7 @@ public void EnsureFlyoutIconWithForegroundColor()
App.WaitForElement(changeColor);
App.Tap(changeColor);
App.WaitForElement(changeColor);
- VerifyScreenshot();
+ VerifyScreenshot();
App.WaitForElement(defaultColor);
App.Tap(defaultColor);
App.WaitForElement(defaultColor);
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/XFIssue/Issue17969.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/XFIssue/Issue17969.cs
index 12f5fa3e1ded..513451b231c0 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/XFIssue/Issue17969.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/XFIssue/Issue17969.cs
@@ -2,36 +2,41 @@
using UITest.Appium;
using UITest.Core;
-namespace Microsoft.Maui.TestCases.Tests.Issues
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue17969 : _IssuesUITest
{
- public class Issue17969 : _IssuesUITest
- {
- public Issue17969(TestDevice device)
- : base(device)
- { }
+ public Issue17969(TestDevice device)
+ : base(device)
+ { }
- public override string Issue => "CollectionView duplicates group headers/footers when adding a new item to a group or crashes when adding a new group with empty view";
+ public override string Issue => "CollectionView duplicates group headers/footers when adding a new item to a group or crashes when adding a new group with empty view";
- [Test]
- [Category(UITestCategories.CollectionView)]
- [FailsOnWindowsWhenRunningOnXamarinUITest]
- public void CollectionViewDuplicateViewsWhenAddItemToGroup()
- {
- App.WaitForElement("collectionView");
- App.Tap("addItem");
- VerifyScreenshot();
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void CollectionViewDuplicateViewsWhenAddItemToGroup()
+ {
+ App.WaitForElement("collectionView");
+ App.Tap("addItem");
+#if !WINDOWS
+ VerifyScreenshot();
+#else
+ App.WaitForElement("Asian Black Bear"); // Verify that the item is actually added.
+#endif
- }
+ }
- [Test]
- [Category(UITestCategories.CollectionView)]
- [FailsOnWindowsWhenRunningOnXamarinUITest]
- public void CollectionViewAddGroupWhenViewIsEmpty()
- {
- App.WaitForElement("collectionView");
- App.Tap("addGroup");
- VerifyScreenshot();
- }
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void CollectionViewAddGroupWhenViewIsEmpty()
+ {
+ App.WaitForElement("collectionView");
+ App.Tap("addGroup");
+#if !WINDOWS
+ VerifyScreenshot();
+#else
+ App.WaitForElement("collectionView"); // Verify that the CollectionView is still present and has not crashed.
+#endif
}
}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/XFIssue/Issue2818.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/XFIssue/Issue2818.cs
index 33a8c2f828c9..f75526c505f8 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/XFIssue/Issue2818.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/XFIssue/Issue2818.cs
@@ -1,6 +1,5 @@
-#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS
+#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_CATALYST
// Orientation not supported in Catalyst and Windows
-// On iOS FlyoutPage RTL is not working as expected, Issue: https://github.com/dotnet/maui/issues/26726
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
@@ -35,8 +34,8 @@ public void RootViewMovesAndContentIsVisible()
Assert.That(positionStart, Is.Not.EqualTo(secondPosition));
}
- [Test]
- public void RootViewSizeDoesntChangeAfterBackground()
+ [Test]
+ public async Task RootViewSizeDoesntChangeAfterBackground()
{
var idiom = App.WaitForElement("Idiom");
App.SetOrientationLandscape();
@@ -52,6 +51,17 @@ public void RootViewSizeDoesntChangeAfterBackground()
App.WaitForNoElement("RootLayout");
App.ForegroundApp();
var newWindowSize = App.WaitForElement("RootLayout");
+
+ // Poll until the width stabilizes. After foregrounding, some platforms (esp. Android/iOS)
+ // may momentarily report an intermediate layout size while the window / flyout re-applies
+ // RTL + orientation constraints. This loop prevents test flakiness by waiting for the
+ // final (restored) size instead of asserting too early
+ int retries = 50;
+ while (newWindowSize.GetRect().Width != windowSize.GetRect().Width && retries-- > 0)
+ {
+ await Task.Delay(100);
+ }
+
Assert.That(newWindowSize.GetRect().Width, Is.EqualTo(windowSize.GetRect().Width));
Assert.That(newWindowSize.GetRect().Height, Is.EqualTo(windowSize.GetRect().Height));
}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/ScrollViewUITests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/ScrollViewUITests.cs
index a4d5fb65fda3..cefdcfec72e6 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/ScrollViewUITests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/ScrollViewUITests.cs
@@ -79,9 +79,6 @@ public void ScrollToY()
// ScrollToYTwice (src\Compatibility\ControlGallery\src\UITests.Shared\Tests\ScrollViewUITests.cs)
[Test]
[Description("ScrollTo Y = 100")]
- [FailsOnIOSWhenRunningOnXamarinUITest("This test is failing, likely due to product issue, More Information: https://github.com/dotnet/maui/issues/27250")]
- [FailsOnMacWhenRunningOnXamarinUITest("This test is failing, likely due to product issue, More Information: https://github.com/dotnet/maui/issues/27250")]
- [FailsOnWindowsWhenRunningOnXamarinUITest("This test is failing, likely due to product issue, More Information: https://github.com/dotnet/maui/issues/27250")]
public void ScrollToYTwice()
{
App.WaitForElement("WaitForStubControl");
diff --git a/src/Controls/tests/TestCases.Shared.Tests/UITestCategories.cs b/src/Controls/tests/TestCases.Shared.Tests/UITestCategories.cs
index 6246fcc6aaf2..ec98beddad96 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/UITestCategories.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/UITestCategories.cs
@@ -75,6 +75,9 @@ internal static class UITestCategories
public const string GraphicsView = "GraphicsView";
public const string Fonts = "Fonts";
public const string SafeAreaEdges = "SafeAreaEdges";
+ public const string Essentials = "Essentials";
public const string Material3 = "Material3";
+ public const string Triggers = "Triggers";
+ public const string VisualStateManager = "VisualStateManager";
}
}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ArabicStringShouldBeLeftToRight.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ArabicStringShouldBeLeftToRight.png
new file mode 100644
index 000000000000..0223e667530f
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ArabicStringShouldBeLeftToRight.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks.png
new file mode 100644
index 000000000000..b794bd913174
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_BackgroundColor.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_BackgroundColor.png
new file mode 100644
index 000000000000..b40c78362ff6
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_BackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipNull_NoCrash.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipNull_NoCrash.png
new file mode 100644
index 000000000000..597a85d37b57
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipNull_NoCrash.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithRotation.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithRotation.png
new file mode 100644
index 000000000000..b8a7ffe566b1
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithRotationAndScale.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithRotationAndScale.png
new file mode 100644
index 000000000000..a8c49021f2e0
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithRotationAndScale.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithStrokeColorBlue.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithStrokeColorBlue.png
new file mode 100644
index 000000000000..915ca55e5327
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithStrokeColorBlue.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithStrokeColorGreen.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithStrokeColorGreen.png
new file mode 100644
index 000000000000..e428ed0b9b54
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithStrokeColorGreen.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithStrokeShapeRoundRectangle.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithStrokeShapeRoundRectangle.png
new file mode 100644
index 000000000000..304d1a988ace
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithStrokeShapeRoundRectangle.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithStrokeThickness.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithStrokeThickness.png
new file mode 100644
index 000000000000..c2f1d6884ca4
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ClipWithStrokeThickness.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_DefaultValues.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_DefaultValues.png
new file mode 100644
index 000000000000..b1a57c044429
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_DefaultValues.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_PaddingWithContent_Image.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_PaddingWithContent_Image.png
new file mode 100644
index 000000000000..6a7fbc29a578
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_PaddingWithContent_Image.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_PaddingWithContent_Label.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_PaddingWithContent_Label.png
index 71728c8e38dc..1c998f442ea3 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_PaddingWithContent_Label.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_PaddingWithContent_Label.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_PolygonShapeWithStrokeLineJoin_Bevel.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_PolygonShapeWithStrokeLineJoin_Bevel.png
index 411260056b7a..4a2e470cac97 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_PolygonShapeWithStrokeLineJoin_Bevel.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_PolygonShapeWithStrokeLineJoin_Bevel.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ShadowWithColor.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ShadowWithColor.png
new file mode 100644
index 000000000000..2b2d01ac9b19
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ShadowWithColor.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithContent_Button.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithContent_Button.png
new file mode 100644
index 000000000000..03593c274eeb
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithContent_Button.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithDashArrayAndOffset.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithDashArrayAndOffset.png
index 6b83e20a4773..6465cf6f2cd9 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithDashArrayAndOffset.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithDashArrayAndOffset.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithRed.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithRed.png
new file mode 100644
index 000000000000..8369efec71b5
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithRed.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithStrokeLineJoin_Round.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithStrokeLineJoin_Round.png
index 690dc84c30c7..da9c86f599d1 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithStrokeLineJoin_Round.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithStrokeLineJoin_Round.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithStrokeShape_RoundRectangle.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithStrokeShape_RoundRectangle.png
index 4c6498e510c8..c98e3055af7f 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithStrokeShape_RoundRectangle.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithStrokeShape_RoundRectangle.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithStrokeThickness.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithStrokeThickness.png
index a8ab56f92356..d57c52cd548c 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithStrokeThickness.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeColorWithStrokeThickness.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeDashArrayWithStrokeColor.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeDashArrayWithStrokeColor.png
index c6f289746446..fe083a5a959b 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeDashArrayWithStrokeColor.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeDashArrayWithStrokeColor.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeGradientBrush.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeGradientBrush.png
new file mode 100644
index 000000000000..c3c70cb3c7f5
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeGradientBrush.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeMiterLimitWithStrokeLineJoin_Miter.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeMiterLimitWithStrokeLineJoin_Miter.png
new file mode 100644
index 000000000000..9e21d0f054ff
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeMiterLimitWithStrokeLineJoin_Miter.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeRectangle_AfterChange.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeRectangle_AfterChange.png
new file mode 100644
index 000000000000..59bb36882d60
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeRectangle_AfterChange.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeWithDashArray_Path.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeWithDashArray_Path.png
index 52562caca14f..eab5a26cf05d 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeWithDashArray_Path.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeWithDashArray_Path.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeWithPolygon.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeWithPolygon.png
new file mode 100644
index 000000000000..cd6c52cd6992
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeWithPolygon.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeWithStrokeLineJoin_Bevel.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeWithStrokeLineJoin_Bevel.png
index c65259a6f036..6cae1b6d474a 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeWithStrokeLineJoin_Bevel.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeWithStrokeLineJoin_Bevel.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeWithStrokeThickness_Ellipse.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeWithStrokeThickness_Ellipse.png
index 94c5f3c92789..069297424dd6 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeWithStrokeThickness_Ellipse.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShapeWithStrokeThickness_Ellipse.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShape_Path.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShape_Path.png
new file mode 100644
index 000000000000..fff2326104bf
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeShape_Path.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeThicknessWithStrokeLineJoin_Bevel.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeThicknessWithStrokeLineJoin_Bevel.png
index bb7dfc4ed10a..13584c753a1c 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeThicknessWithStrokeLineJoin_Bevel.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_StrokeThicknessWithStrokeLineJoin_Bevel.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ZeroPadding.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ZeroPadding.png
new file mode 100644
index 000000000000..33d32583cff4
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Border_ZeroPadding.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/BoxView_ClipWithColorGreen.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/BoxView_ClipWithColorGreen.png
new file mode 100644
index 000000000000..5992ad9da9e1
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/BoxView_ClipWithColorGreen.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/BoxView_ClipWithCornerRadius.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/BoxView_ClipWithCornerRadius.png
new file mode 100644
index 000000000000..e4933eeee192
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/BoxView_ClipWithCornerRadius.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/BoxView_ClipWithRotation.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/BoxView_ClipWithRotation.png
new file mode 100644
index 000000000000..a9630fe7954d
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/BoxView_ClipWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Bugzilla29128Test.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Bugzilla29128Test.png
new file mode 100644
index 000000000000..f18c25a56959
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Bugzilla29128Test.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ButtonRTLTextAndImageShouldNotOverlap.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ButtonRTLTextAndImageShouldNotOverlap.png
new file mode 100644
index 000000000000..210132c472f1
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ButtonRTLTextAndImageShouldNotOverlap.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Button_ClipNull_NoCrash.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Button_ClipNull_NoCrash.png
new file mode 100644
index 000000000000..7e617c8b8f61
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Button_ClipNull_NoCrash.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Button_ClipWithImageSource.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Button_ClipWithImageSource.png
new file mode 100644
index 000000000000..66b9661f96cc
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Button_ClipWithImageSource.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Button_ClipWithScale.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Button_ClipWithScale.png
new file mode 100644
index 000000000000..a2b4f3860052
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Button_ClipWithScale.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Button_ClipWithText.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Button_ClipWithText.png
new file mode 100644
index 000000000000..7e7ffdde852d
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Button_ClipWithText.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CheckBox_ChangeColor_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CheckBox_ChangeColor_VerifyVisualState.png
index 6f262d5efe03..14e23e7fe6ea 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CheckBox_ChangeColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CheckBox_ChangeColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png
index e1f23bab0dfc..5fc5512bc98a 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CheckBox_VerifyColorAfterReset.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CheckBox_VerifyColorAfterReset.png
new file mode 100644
index 000000000000..adc369f962d3
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CheckBox_VerifyColorAfterReset.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CheckBox_VerifyWithShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CheckBox_VerifyWithShadow.png
new file mode 100644
index 000000000000..71385a7343a3
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CheckBox_VerifyWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CollectionViewAddGroupWhenViewIsEmpty.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CollectionViewAddGroupWhenViewIsEmpty.png
new file mode 100644
index 000000000000..2cf28ea31e44
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CollectionViewAddGroupWhenViewIsEmpty.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CollectionViewDuplicateViewsWhenAddItemToGroup.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CollectionViewDuplicateViewsWhenAddItemToGroup.png
new file mode 100644
index 000000000000..9c6829935f2c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CollectionViewDuplicateViewsWhenAddItemToGroup.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CollectionViewShouldChangeItemsLayout.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CollectionViewShouldChangeItemsLayout.png
new file mode 100644
index 000000000000..7f0b5bd4810e
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CollectionViewShouldChangeItemsLayout.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CompareStateTrigger_Checked.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CompareStateTrigger_Checked.png
new file mode 100644
index 000000000000..b3729e3c9d53
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CompareStateTrigger_Checked.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CompareStateTrigger_Unchecked.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CompareStateTrigger_Unchecked.png
new file mode 100644
index 000000000000..ff62f3303323
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/CompareStateTrigger_Unchecked.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ContentView_ClipNull_NoCrash.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ContentView_ClipNull_NoCrash.png
new file mode 100644
index 000000000000..78f91e8ca12e
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ContentView_ClipNull_NoCrash.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ContentView_ClipWithEllipseGeometry.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ContentView_ClipWithEllipseGeometry.png
new file mode 100644
index 000000000000..e9a71c73fbe8
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ContentView_ClipWithEllipseGeometry.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ContentView_ClipWithNestedClippedContent.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ContentView_ClipWithNestedClippedContent.png
new file mode 100644
index 000000000000..3a631788e98e
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ContentView_ClipWithNestedClippedContent.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ContentView_ClipWithRectangleGeometry.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ContentView_ClipWithRectangleGeometry.png
new file mode 100644
index 000000000000..19c40e211add
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ContentView_ClipWithRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ContentView_ClipWithRoundRectangleGeometry.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ContentView_ClipWithRoundRectangleGeometry.png
new file mode 100644
index 000000000000..789535f96e4e
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ContentView_ClipWithRoundRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/DataTrigger_ButtonDisabled.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/DataTrigger_ButtonDisabled.png
new file mode 100644
index 000000000000..52eabf6423ed
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/DataTrigger_ButtonDisabled.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/DataTrigger_ButtonEnabled.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/DataTrigger_ButtonEnabled.png
new file mode 100644
index 000000000000..9b9f5a2cba5d
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/DataTrigger_ButtonEnabled.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/DrawTextWithinBounds.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/DrawTextWithinBounds.png
new file mode 100644
index 000000000000..ed2e9dded753
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/DrawTextWithinBounds.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/EnsureSearchBarExplicitSize.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/EnsureSearchBarExplicitSize.png
new file mode 100644
index 000000000000..24436607618c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/EnsureSearchBarExplicitSize.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/EventTrigger_InvalidText.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/EventTrigger_InvalidText.png
new file mode 100644
index 000000000000..643c739b173c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/EventTrigger_InvalidText.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/EventTrigger_ValidNumeric.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/EventTrigger_ValidNumeric.png
new file mode 100644
index 000000000000..54467fd94617
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/EventTrigger_ValidNumeric.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/FlexLayoutWithBindableLayoutDisplaysLabels.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/FlexLayoutWithBindableLayoutDisplaysLabels.png
new file mode 100644
index 000000000000..409437adb1bd
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/FlexLayoutWithBindableLayoutDisplaysLabels.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/FlyoutIconRemainsVisible.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/FlyoutIconRemainsVisible.png
new file mode 100644
index 000000000000..f254a7acf394
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/FlyoutIconRemainsVisible.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/FlyoutIconUpdatedAfterInsertPageBefore.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/FlyoutIconUpdatedAfterInsertPageBefore.png
new file mode 100644
index 000000000000..6209512d91b0
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/FlyoutIconUpdatedAfterInsertPageBefore.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/GraphicsViewBackgroundShouldBeApplied.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/GraphicsViewBackgroundShouldBeApplied.png
new file mode 100644
index 000000000000..98b44c62e3f1
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/GraphicsViewBackgroundShouldBeApplied.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/GraphicsViewBackgroundShouldBeChanged.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/GraphicsViewBackgroundShouldBeChanged.png
new file mode 100644
index 000000000000..724635e10a25
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/GraphicsViewBackgroundShouldBeChanged.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/GraphicsViewShouldNotWrapText.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/GraphicsViewShouldNotWrapText.png
index a8fe312a0351..d1be45ddd901 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/GraphicsViewShouldNotWrapText.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/GraphicsViewShouldNotWrapText.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/GroupedCollectionViewItems.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/GroupedCollectionViewItems.png
new file mode 100644
index 000000000000..0b7dbd29016b
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/GroupedCollectionViewItems.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageButton_ClipWithEllipseGeometry.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageButton_ClipWithEllipseGeometry.png
new file mode 100644
index 000000000000..5c6fbff05497
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageButton_ClipWithEllipseGeometry.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageButton_ClipWithRectangleGeometry.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageButton_ClipWithRectangleGeometry.png
new file mode 100644
index 000000000000..4a3ce633952c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageButton_ClipWithRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageButton_ClipWithRoundRectangleGeometry.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageButton_ClipWithRoundRectangleGeometry.png
new file mode 100644
index 000000000000..f628cfd0822b
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageButton_ClipWithRoundRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageButton_ClipWithScale.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageButton_ClipWithScale.png
new file mode 100644
index 000000000000..13247a4c0504
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageButton_ClipWithScale.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageUITests_Source_FontImageSource_FontAwesome.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageUITests_Source_FontImageSource_FontAwesome.png
index d6bf1ef6d0d0..e07ea4fcb8c0 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageUITests_Source_FontImageSource_FontAwesome.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageUITests_Source_FontImageSource_FontAwesome.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageUITests_Source_FontImageSource_Ionicons.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageUITests_Source_FontImageSource_Ionicons.png
index f2d99244e9d2..99e2d82f9b15 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageUITests_Source_FontImageSource_Ionicons.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ImageUITests_Source_FontImageSource_Ionicons.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipNull_NoCrash.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipNull_NoCrash.png
new file mode 100644
index 000000000000..a2b4ae8b731a
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipNull_NoCrash.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithArcSegmentPath.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithArcSegmentPath.png
new file mode 100644
index 000000000000..d9ec0f38c1aa
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithArcSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithBezierSegmentPath.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithBezierSegmentPath.png
new file mode 100644
index 000000000000..f13b434be4f8
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithBezierSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithComplexPolyBezierAndRotation.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithComplexPolyBezierAndRotation.png
new file mode 100644
index 000000000000..ca80b20701a2
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithComplexPolyBezierAndRotation.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithEllipseGeometry.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithEllipseGeometry.png
new file mode 100644
index 000000000000..99cfee5cbab6
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithEllipseGeometry.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithGeometryGroup.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithGeometryGroup.png
new file mode 100644
index 000000000000..056a62da9553
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithGeometryGroup.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithLineSegmentPath.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithLineSegmentPath.png
new file mode 100644
index 000000000000..c32d433afb8e
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithLineSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithPolyBezierSegmentPath.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithPolyBezierSegmentPath.png
new file mode 100644
index 000000000000..7079b973b41e
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithPolyBezierSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithPolyLineSegmentPath.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithPolyLineSegmentPath.png
new file mode 100644
index 000000000000..aa035c7c83f1
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithPolyLineSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithPolyQuadraticBezierSegmentPath.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithPolyQuadraticBezierSegmentPath.png
new file mode 100644
index 000000000000..c1f29c699f7e
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithPolyQuadraticBezierSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithQuadraticBezierSegmentPath.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithQuadraticBezierSegmentPath.png
new file mode 100644
index 000000000000..c66cac8daa20
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithQuadraticBezierSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithRectangleGeometry.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithRectangleGeometry.png
new file mode 100644
index 000000000000..d86f08311781
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithRotation.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithRotation.png
new file mode 100644
index 000000000000..aa5f126392e9
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithRoundRectangleGeometry.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithRoundRectangleGeometry.png
new file mode 100644
index 000000000000..3c5147d0a1e1
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithRoundRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithScale.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithScale.png
new file mode 100644
index 000000000000..c37b51cb5dae
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Image_ClipWithScale.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue12324TabbedPageVisualTest.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue12324TabbedPageVisualTest.png
new file mode 100644
index 000000000000..777765fe61d2
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue12324TabbedPageVisualTest.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue21202Test.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue21202Test.png
index 4c81a19de320..5f1ab03c9958 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue21202Test.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue21202Test.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue22433_Default.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue22433_Default.png
index 3b121c472ce4..152a869f145e 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue22433_Default.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue22433_Default.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue22433_Layout.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue22433_Layout.png
index 13e153d2ee57..d46ec1c29d81 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue22433_Layout.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue22433_Layout.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue22433_Spacing.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue22433_Spacing.png
index 8637899a5e82..f4120fe54481 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue22433_Spacing.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue22433_Spacing.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue23377ItemSpacing.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue23377ItemSpacing.png
new file mode 100644
index 000000000000..fa30ab8ecfe4
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue23377ItemSpacing.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue33400_Never.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue33400_Never.png
new file mode 100644
index 000000000000..06e3c33f6f54
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue33400_Never.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue33909ForegroundColorReset.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue33909ForegroundColorReset.png
new file mode 100644
index 000000000000..836710eeef23
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue33909ForegroundColorReset.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/LabelDisplayWithoutCroppingInsideVerticalLayout.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/LabelDisplayWithoutCroppingInsideVerticalLayout.png
new file mode 100644
index 000000000000..62291d373b92
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/LabelDisplayWithoutCroppingInsideVerticalLayout.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MultiTrigger_Both_Filled.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MultiTrigger_Both_Filled.png
new file mode 100644
index 000000000000..c7c4192c1290
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MultiTrigger_Both_Filled.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MultiTrigger_EmailOnly_Filled.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MultiTrigger_EmailOnly_Filled.png
new file mode 100644
index 000000000000..40e614c6624e
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MultiTrigger_EmailOnly_Filled.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MultiTrigger_PhoneOnly_Filled.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MultiTrigger_PhoneOnly_Filled.png
new file mode 100644
index 000000000000..dd5a237d78d5
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MultiTrigger_PhoneOnly_Filled.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/NavigatingBetweenPagesWithSetTitleViewShouldNotCrash.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/NavigatingBetweenPagesWithSetTitleViewShouldNotCrash.png
new file mode 100644
index 000000000000..7579b3c7b161
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/NavigatingBetweenPagesWithSetTitleViewShouldNotCrash.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/NavigationPageTitleViewShouldRespectMargins.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/NavigationPageTitleViewShouldRespectMargins.png
new file mode 100644
index 000000000000..b12c45a48a9c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/NavigationPageTitleViewShouldRespectMargins.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/OnlyManuallySwipedItemShouldBeOpened.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/OnlyManuallySwipedItemShouldBeOpened.png
new file mode 100644
index 000000000000..01942bb440bd
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/OnlyManuallySwipedItemShouldBeOpened.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/PointerOverWithSelectedStateShouldWork.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/PointerOverWithSelectedStateShouldWork.png
new file mode 100644
index 000000000000..7876e8ffbeea
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/PointerOverWithSelectedStateShouldWork.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/PropertyTrigger_Focused.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/PropertyTrigger_Focused.png
new file mode 100644
index 000000000000..f7c5cc2122bd
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/PropertyTrigger_Focused.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/PropertyTrigger_UnFocused.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/PropertyTrigger_UnFocused.png
new file mode 100644
index 000000000000..56541d64cd3b
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/PropertyTrigger_UnFocused.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RTLModePaddingShouldWork.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RTLModePaddingShouldWork.png
new file mode 100644
index 000000000000..0bc488eb1f83
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RTLModePaddingShouldWork.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RadioButtonTextColorShouldNotBeOverriddenByGlobalLabelStyle.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RadioButtonTextColorShouldNotBeOverriddenByGlobalLabelStyle.png
new file mode 100644
index 000000000000..2b2376dc04cf
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RadioButtonTextColorShouldNotBeOverriddenByGlobalLabelStyle.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchHandlerVisibilityChangesToCollapsible.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchHandlerVisibilityChangesToCollapsible.png
new file mode 100644
index 000000000000..e35dbe19a47d
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchHandlerVisibilityChangesToCollapsible.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchHandlerVisibilityChangesToExpanded.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchHandlerVisibilityChangesToExpanded.png
new file mode 100644
index 000000000000..90e93b31b165
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchHandlerVisibilityChangesToExpanded.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShadowShouldUpdateOnCornerRadiusChange.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShadowShouldUpdateOnCornerRadiusChange.png
new file mode 100644
index 000000000000..c5cc39af725a
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShadowShouldUpdateOnCornerRadiusChange.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_FlowDirectionRTL.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_FlowDirectionRTL.png
new file mode 100644
index 000000000000..31ab40e67e9d
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_FlowDirectionRTL.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_ForegroundColor.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_ForegroundColor.png
new file mode 100644
index 000000000000..994447b573b4
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_ForegroundColor.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_ForegroundColorAndTitleColor.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_ForegroundColorAndTitleColor.png
new file mode 100644
index 000000000000..f1b8745d6e6f
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_ForegroundColorAndTitleColor.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_ForegroundColorAndUnselectedColor.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_ForegroundColorAndUnselectedColor.png
new file mode 100644
index 000000000000..eb3c13859924
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_ForegroundColorAndUnselectedColor.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_IsVisibleFalse.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_IsVisibleFalse.png
new file mode 100644
index 000000000000..41f07703d1b6
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_IsVisibleFalse.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_IsVisibleTrue.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_IsVisibleTrue.png
new file mode 100644
index 000000000000..4cb3d15ce08c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_IsVisibleTrue.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_NavBarVisibilityHide.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_NavBarVisibilityHide.png
new file mode 100644
index 000000000000..a6e3382c8efc
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_NavBarVisibilityHide.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_NavBarVisibilityShow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_NavBarVisibilityShow.png
new file mode 100644
index 000000000000..e81cc2c335fd
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_NavBarVisibilityShow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_PresentationModeAnimated.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_PresentationModeAnimated.png
new file mode 100644
index 000000000000..dcbe9e902457
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_PresentationModeAnimated.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_PresentationModeModal.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_PresentationModeModal.png
new file mode 100644
index 000000000000..f98d329dee80
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_PresentationModeModal.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_PresentationModeModalAnimated.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_PresentationModeModalAnimated.png
new file mode 100644
index 000000000000..603698b32a23
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_PresentationModeModalAnimated.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_PresentationModeModalNotAnimated.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_PresentationModeModalNotAnimated.png
new file mode 100644
index 000000000000..cd58446c7d9c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_PresentationModeModalNotAnimated.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_PresentationModeNotAnimated.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_PresentationModeNotAnimated.png
new file mode 100644
index 000000000000..3d427837c613
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_PresentationModeNotAnimated.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_ShowTitleView.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_ShowTitleView.png
new file mode 100644
index 000000000000..b9b2ee28aa43
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_ShowTitleView.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_ShowTitleViewHidden.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_ShowTitleViewHidden.png
new file mode 100644
index 000000000000..d6a2e5192d78
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_ShowTitleViewHidden.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_TitleColor.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_TitleColor.png
new file mode 100644
index 000000000000..d664a8c6c4e7
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_TitleColor.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_UnselectedColor.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_UnselectedColor.png
new file mode 100644
index 000000000000..a78baeec347e
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellPages_UnselectedColor.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellTabBarBackgroundColor.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellTabBarBackgroundColor.png
new file mode 100644
index 000000000000..e11ce5be7381
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShellTabBarBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShouldAppearFlyoutIconAndContentPageTitle.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShouldAppearFlyoutIconAndContentPageTitle.png
new file mode 100644
index 000000000000..56e3eb4a2b5a
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ShouldAppearFlyoutIconAndContentPageTitle.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_ChangeBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_ChangeBackgroundColor_VerifyVisualState.png
index b7aae5414d0c..bcf19d4efe21 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_ChangeBackgroundColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_ChangeBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetBackgroundColorAndIsEnable_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetBackgroundColorAndIsEnable_VerifyVisualState.png
index 22b085a5623e..4e7e6521909d 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetBackgroundColorAndIsEnable_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetBackgroundColorAndIsEnable_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetBackgroundColorAndMaxTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetBackgroundColorAndMaxTrackColor_VerifyVisualState.png
index cbbad1db8895..8a19b8c046a7 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetBackgroundColorAndMaxTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetBackgroundColorAndMaxTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetBackgroundColorAndMinTrackColor_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetBackgroundColorAndMinTrackColor_VerifyVisualState.png
index 04b8c89d09a3..853a3c2305dd 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetBackgroundColorAndMinTrackColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetBackgroundColorAndMinTrackColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetBackgroundColorAndThumbColor_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetBackgroundColorAndThumbColor_VerifyVisualState.png
index 31328881a028..0323c2b60142 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetBackgroundColorAndThumbColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetBackgroundColorAndThumbColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetIsEnableAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetIsEnableAndBackgroundColor_VerifyVisualState.png
index dcb384caf7a0..7959f5202a80 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetIsEnableAndBackgroundColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetIsEnableAndBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetIsVisibleAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetIsVisibleAndBackgroundColor_VerifyVisualState.png
index f92de483c0ce..acd328e6ee06 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetIsVisibleAndBackgroundColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetIsVisibleAndBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetMaxTrackAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetMaxTrackAndBackgroundColor_VerifyVisualState.png
index aeff07f752f1..5b735bf4a4de 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetMaxTrackAndBackgroundColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetMaxTrackAndBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetMinTrackAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetMinTrackAndBackgroundColor_VerifyVisualState.png
index 0fc9de30888c..5835d8467181 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetMinTrackAndBackgroundColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetMinTrackAndBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetThumbAndBackgroundColor_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetThumbAndBackgroundColor_VerifyVisualState.png
index 4b109eab8915..cc917addf07d 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetThumbAndBackgroundColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetThumbAndBackgroundColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetThumbImageSourceAndReset_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetThumbImageSourceAndReset_VerifyVisualState.png
new file mode 100644
index 000000000000..bb33a5365a00
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Slider_SetThumbImageSourceAndReset_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/StateTrigger_SwitchOff.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/StateTrigger_SwitchOff.png
new file mode 100644
index 000000000000..a6595feb2078
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/StateTrigger_SwitchOff.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/StateTrigger_SwitchOn.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/StateTrigger_SwitchOn.png
new file mode 100644
index 000000000000..aef918bd2316
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/StateTrigger_SwitchOn.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Stepper_ChangeFlowDirection_RTL_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Stepper_ChangeFlowDirection_RTL_VerifyVisualState.png
index 56f3f4511f18..5792ecb1bc34 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Stepper_ChangeFlowDirection_RTL_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Stepper_ChangeFlowDirection_RTL_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SwitchThumbShouldBeVisibleWithShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SwitchThumbShouldBeVisibleWithShadow.png
new file mode 100644
index 000000000000..46fba05be8e2
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SwitchThumbShouldBeVisibleWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_Click_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_Click_VerifyVisualState.png
index cab25280763a..1610de6c8d64 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_Click_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_Click_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_InitialState_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_InitialState_VerifyVisualState.png
index a47e07be6c73..dc09bb1b554e 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_InitialState_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_InitialState_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_SetFlowDirectionAndToggled_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_SetFlowDirectionAndToggled_VerifyVisualState.png
index 95154fa648e3..e44e95895d90 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_SetFlowDirectionAndToggled_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_SetFlowDirectionAndToggled_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_SetOnColorAndThumbColor_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_SetOnColorAndThumbColor_VerifyVisualState.png
index 8df8384133a5..576ae78bfa5c 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_SetOnColorAndThumbColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_SetOnColorAndThumbColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_SetThumbColorAndOnColor_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_SetThumbColorAndOnColor_VerifyVisualState.png
index 8d5afb63d105..5e15168f440b 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_SetThumbColorAndOnColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Switch_SetThumbColorAndOnColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TabBarVisibilityAfterMultiLevelPopToRoot.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TabBarVisibilityAfterMultiLevelPopToRoot.png
new file mode 100644
index 000000000000..0dd0900859eb
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TabBarVisibilityAfterMultiLevelPopToRoot.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TabbedPageFlowDirection_AfterChangingBackToLeftToRight.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TabbedPageFlowDirection_AfterChangingBackToLeftToRight.png
new file mode 100644
index 000000000000..0ea142d0896d
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TabbedPageFlowDirection_AfterChangingBackToLeftToRight.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TabbedPageFlowDirection_AfterChangingToLeftToRight.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TabbedPageFlowDirection_AfterChangingToLeftToRight.png
new file mode 100644
index 000000000000..b497d12351ca
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TabbedPageFlowDirection_AfterChangingToLeftToRight.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TabbedPageFlowDirection_DefaultRightToLeftLayout.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TabbedPageFlowDirection_DefaultRightToLeftLayout.png
new file mode 100644
index 000000000000..c48adb2f2044
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TabbedPageFlowDirection_DefaultRightToLeftLayout.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TestIssue18668.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TestIssue18668.png
new file mode 100644
index 000000000000..7349182d9b6a
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TestIssue18668.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ToolbarItemFontIconSourceChangesAtRunTime.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ToolbarItemFontIconSourceChangesAtRunTime.png
index a4ad67fefb4e..2e3d85d26379 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ToolbarItemFontIconSourceChangesAtRunTime.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/ToolbarItemFontIconSourceChangesAtRunTime.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TransparentShapeShouldNotDisplayShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TransparentShapeShouldNotDisplayShadow.png
new file mode 100644
index 000000000000..607854ad63fe
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TransparentShapeShouldNotDisplayShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/UpdatedIsEnabledProperty.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/UpdatedIsEnabledProperty.png
index e110b6ac4a78..2339e90360ad 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/UpdatedIsEnabledProperty.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/UpdatedIsEnabledProperty.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyAllIndicatorDotsShowShadowsWhenIndicatorSize.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyAllIndicatorDotsShowShadowsWhenIndicatorSize.png
new file mode 100644
index 000000000000..1a5357ba644c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyAllIndicatorDotsShowShadowsWhenIndicatorSize.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyBackgroundColorCleared.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyBackgroundColorCleared.png
new file mode 100644
index 000000000000..ac75bc7b5c4b
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyBackgroundColorCleared.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyBackgroundColorSet.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyBackgroundColorSet.png
new file mode 100644
index 000000000000..c9c925a3b80e
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyBackgroundColorSet.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyClearedTimeDoesNotShowMidnight.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyClearedTimeDoesNotShowMidnight.png
new file mode 100644
index 000000000000..e4bd9014ceae
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyClearedTimeDoesNotShowMidnight.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyDynamicFlyoutIconBackgroundColor.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyDynamicFlyoutIconBackgroundColor.png
new file mode 100644
index 000000000000..d7d591d6fdf0
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyDynamicFlyoutIconBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollection.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollection.png
new file mode 100644
index 000000000000..4e42fc1fea45
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollection.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollection.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollection.png
new file mode 100644
index 000000000000..6df2f944f1f7
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollection.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutIconBackgroundColor.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutIconBackgroundColor.png
new file mode 100644
index 000000000000..51f8435028c0
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutIconBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPageToolbarItemsRender.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPageToolbarItemsRender.png
new file mode 100644
index 000000000000..bd63630f6db3
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPageToolbarItemsRender.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPage_BackgroundColor.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPage_BackgroundColor.png
index f678d82122ea..8a212035cebf 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPage_BackgroundColor.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPage_BackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPage_FlyoutLayoutBehavior_Split.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPage_FlyoutLayoutBehavior_Split.png
index b0835557f06a..b8bc543f626e 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPage_FlyoutLayoutBehavior_Split.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPage_FlyoutLayoutBehavior_Split.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPage_IsFlowDirectionRTL.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPage_IsFlowDirectionRTL.png
index 99280438d986..01e590ce6310 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPage_IsFlowDirectionRTL.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPage_IsFlowDirectionRTL.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPage_Title.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPage_Title.png
index 036a2635ca9d..42d2d2848a2e 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPage_Title.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPage_Title.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutVerticalScrollModeDisabled.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutVerticalScrollModeDisabled.png
new file mode 100644
index 000000000000..ab3280e96960
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutVerticalScrollModeDisabled.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFontImageAreCenterAlign.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFontImageAreCenterAlign.png
new file mode 100644
index 000000000000..d06c927fa784
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFontImageAreCenterAlign.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFontImageWithFontColorGreen.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFontImageWithFontColorGreen.png
index 9becee47aed2..cc9112dbc518 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFontImageWithFontColorGreen.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFontImageWithFontColorGreen.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyGraphicsViewWithoutGrayLine.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyGraphicsViewWithoutGrayLine.png
deleted file mode 100644
index 6ab678231fc9..000000000000
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyGraphicsViewWithoutGrayLine.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_AspectFillWithImageSourceFromFile.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_AspectFillWithImageSourceFromFile.png
new file mode 100644
index 000000000000..d47b054f9c85
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_AspectFillWithImageSourceFromFile.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_AspectFillWithImageSourceFromFontImage.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_AspectFillWithImageSourceFromFontImage.png
new file mode 100644
index 000000000000..f29b0baa8d1a
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_AspectFillWithImageSourceFromFontImage.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_AspectFillWithImageSourceFromStream.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_AspectFillWithImageSourceFromStream.png
new file mode 100644
index 000000000000..7e8844846fce
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_AspectFillWithImageSourceFromStream.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_AspectFillWithImageSourceFromUri.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_AspectFillWithImageSourceFromUri.png
new file mode 100644
index 000000000000..ad437724c276
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_AspectFillWithImageSourceFromUri.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_AspectFitWithImageSourceFromFontImage.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_AspectFitWithImageSourceFromFontImage.png
index d13b0cfe2588..a220bd3b8c95 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_AspectFitWithImageSourceFromFontImage.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_AspectFitWithImageSourceFromFontImage.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_CenterWithImageSourceFromFontImage.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_CenterWithImageSourceFromFontImage.png
index 69f402965cd4..7b3fe6c29e86 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_CenterWithImageSourceFromFontImage.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_CenterWithImageSourceFromFontImage.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_CenterWithImageSourceFromUri.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_CenterWithImageSourceFromUri.png
index 01cd04dd7c2f..64c650e07733 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_CenterWithImageSourceFromUri.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_CenterWithImageSourceFromUri.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_FillWithImageSourceFromFontImage.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_FillWithImageSourceFromFontImage.png
index 809438b58d9a..f282b7821de8 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_FillWithImageSourceFromFontImage.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyImageAspect_FillWithImageSourceFromFontImage.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyIndicatorViewMaximumVisibleWithTemplate.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyIndicatorViewMaximumVisibleWithTemplate.png
new file mode 100644
index 000000000000..6586080f45ef
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyIndicatorViewMaximumVisibleWithTemplate.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyNavBarStatusAtInitialLoading.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyNavBarStatusAtInitialLoading.png
new file mode 100644
index 000000000000..53c3f0ebea9c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyNavBarStatusAtInitialLoading.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyNavBarStatusAtRuntime.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyNavBarStatusAtRuntime.png
new file mode 100644
index 000000000000..3653171f21e0
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyNavBarStatusAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyRadioButtonTextWithLowerTransform.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyRadioButtonTextWithLowerTransform.png
new file mode 100644
index 000000000000..e3653fe3c4e4
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyRadioButtonTextWithLowerTransform.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyRadioButtonTextWithUpperTransform.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyRadioButtonTextWithUpperTransform.png
new file mode 100644
index 000000000000..2c4ba0810ef2
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyRadioButtonTextWithUpperTransform.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifySearchBarBackground.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifySearchBarBackground.png
new file mode 100644
index 000000000000..256536cd1081
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifySearchBarBackground.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifySliderColors.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifySliderColors.png
new file mode 100644
index 000000000000..0754c4ea2ebf
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifySliderColors.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifySpanInheritsLabelCharacterSpacing.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifySpanInheritsLabelCharacterSpacing.png
new file mode 100644
index 000000000000..fb54338149a0
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifySpanInheritsLabelCharacterSpacing.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifySwitchControlSize.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifySwitchControlSize.png
new file mode 100644
index 000000000000..f52878304394
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifySwitchControlSize.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyTimePickerFormat.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyTimePickerFormat.png
new file mode 100644
index 000000000000..b1b180083309
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyTimePickerFormat.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyTimePickerIsNullOnInitialLoad.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyTimePickerIsNullOnInitialLoad.png
new file mode 100644
index 000000000000..93c50126cd9b
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyTimePickerIsNullOnInitialLoad.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Button_Disable.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Button_Disable.png
new file mode 100644
index 000000000000..c43807271f70
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Button_Disable.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Button_InitialState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Button_InitialState.png
new file mode 100644
index 000000000000..72d5658dff23
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Button_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Button_PressedAndReleased.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Button_PressedAndReleased.png
new file mode 100644
index 000000000000..40fa0fec2baa
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Button_PressedAndReleased.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Button_Reset.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Button_Reset.png
new file mode 100644
index 000000000000..d4adf9875032
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Button_Reset.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CheckBox_Checked.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CheckBox_Checked.png
new file mode 100644
index 000000000000..a7471dfb1bd4
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CheckBox_Checked.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CheckBox_Disable.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CheckBox_Disable.png
new file mode 100644
index 000000000000..8c458ebda384
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CheckBox_Disable.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CheckBox_DisableWhileChecked.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CheckBox_DisableWhileChecked.png
new file mode 100644
index 000000000000..de5864c43aa4
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CheckBox_DisableWhileChecked.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CheckBox_InitialState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CheckBox_InitialState.png
new file mode 100644
index 000000000000..d7ddd9c9030c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CheckBox_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CheckBox_Reset.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CheckBox_Reset.png
new file mode 100644
index 000000000000..422fb69ebb43
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CheckBox_Reset.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_Disabled.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_Disabled.png
new file mode 100644
index 000000000000..73b3d02a27d3
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_Disabled.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_InitialState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_InitialState.png
new file mode 100644
index 000000000000..512d0702934c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_Normal.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_Normal.png
new file mode 100644
index 000000000000..bdc8ee719892
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_Normal.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_Reset.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_Reset.png
new file mode 100644
index 000000000000..8a277921af90
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_Reset.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_Selected.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_Selected.png
new file mode 100644
index 000000000000..3875b2638f18
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_Selected.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_Selected_Multiple.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_Selected_Multiple.png
new file mode 100644
index 000000000000..b8ac55e2102c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_CollectionView_Selected_Multiple.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Completed.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Completed.png
new file mode 100644
index 000000000000..10c0d00504f9
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Completed.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Disable.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Disable.png
new file mode 100644
index 000000000000..f94d34391aeb
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Disable.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_DisableAndEnableWhileFocused.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_DisableAndEnableWhileFocused.png
new file mode 100644
index 000000000000..4f28fce3749d
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_DisableAndEnableWhileFocused.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_DisableAndEnableWhileUnFocused.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_DisableAndEnableWhileUnFocused.png
new file mode 100644
index 000000000000..dffb7c168100
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_DisableAndEnableWhileUnFocused.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_DisableWhileFocused.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_DisableWhileFocused.png
new file mode 100644
index 000000000000..b388e9748936
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_DisableWhileFocused.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_DisableWhileReset.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_DisableWhileReset.png
new file mode 100644
index 000000000000..ff42a6e22c88
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_DisableWhileReset.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_DisableWhileUnFocused.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_DisableWhileUnFocused.png
new file mode 100644
index 000000000000..1ba68aac114e
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_DisableWhileUnFocused.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Focus.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Focus.png
new file mode 100644
index 000000000000..121eb115a603
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Focus.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_InitialState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_InitialState.png
new file mode 100644
index 000000000000..2aee00ca214f
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Invalid.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Invalid.png
new file mode 100644
index 000000000000..3088af5c910b
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Invalid.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Reset.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Reset.png
new file mode 100644
index 000000000000..10679a5c9df5
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Reset.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Unfocus.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Unfocus.png
new file mode 100644
index 000000000000..96045e1b8557
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Unfocus.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Validate.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Validate.png
new file mode 100644
index 000000000000..c9cffb111356
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Entry_Validate.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_DisableWhileNormal.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_DisableWhileNormal.png
new file mode 100644
index 000000000000..c34aafef585d
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_DisableWhileNormal.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_DisableWhileSelected.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_DisableWhileSelected.png
new file mode 100644
index 000000000000..f081b045c7f7
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_DisableWhileSelected.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_Disabled.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_Disabled.png
new file mode 100644
index 000000000000..2d890164881a
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_Disabled.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_InitialState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_InitialState.png
new file mode 100644
index 000000000000..8509f8bce962
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_Reset.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_Reset.png
new file mode 100644
index 000000000000..9724dc5a6525
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_Reset.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_Selected.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_Selected.png
new file mode 100644
index 000000000000..caac733d8e29
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Label_Selected.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Slider_DisabledState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Slider_DisabledState.png
new file mode 100644
index 000000000000..bcf9164f8ae8
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Slider_DisabledState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Slider_FocusedState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Slider_FocusedState.png
new file mode 100644
index 000000000000..cdf3123c6600
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Slider_FocusedState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Slider_NormalOrUnfocusedState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Slider_NormalOrUnfocusedState.png
new file mode 100644
index 000000000000..9333043a77ae
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Slider_NormalOrUnfocusedState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Slider_NormalState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Slider_NormalState.png
new file mode 100644
index 000000000000..40b3c2b835d8
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Slider_NormalState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Slider_ResetState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Slider_ResetState.png
new file mode 100644
index 000000000000..9aa438e472ad
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Slider_ResetState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_DisableWhileOff.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_DisableWhileOff.png
new file mode 100644
index 000000000000..aaab0ef1f8a6
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_DisableWhileOff.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_DisableWhileOn.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_DisableWhileOn.png
new file mode 100644
index 000000000000..fb732ad107da
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_DisableWhileOn.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_InitialState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_InitialState.png
new file mode 100644
index 000000000000..9b9412e6c7f3
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_Off.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_Off.png
new file mode 100644
index 000000000000..1b71a840598b
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_Off.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_On.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_On.png
new file mode 100644
index 000000000000..73d0943a1351
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_On.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_Reset.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_Reset.png
new file mode 100644
index 000000000000..644b95ee6edb
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyVSM_Switch_Reset.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorXWithAnchorY.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorXWithAnchorY.png
new file mode 100644
index 000000000000..65d83febbb5b
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorXWithAnchorY.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorXWithShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorXWithShadow.png
new file mode 100644
index 000000000000..9ea0a52cb46d
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorXWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorX_ScaleYWithRotation.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorX_ScaleYWithRotation.png
new file mode 100644
index 000000000000..eeb4f686b248
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorX_ScaleYWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorYWithShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorYWithShadow.png
new file mode 100644
index 000000000000..c8dbbb1fba1e
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorYWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorY_ScaleWithRotationY.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorY_ScaleWithRotationY.png
new file mode 100644
index 000000000000..7155c4a127d2
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorY_ScaleWithRotationY.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorY_ScaleXWithRotationX.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorY_ScaleXWithRotationX.png
new file mode 100644
index 000000000000..d1c09c7b977c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_AnchorY_ScaleXWithRotationX.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_IsShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_IsShadow.png
new file mode 100644
index 000000000000..66772d2f1490
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_IsShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_IsVisible.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_IsVisible.png
new file mode 100644
index 000000000000..6a84b358f3cb
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_IsVisible.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_Rotation.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_Rotation.png
new file mode 100644
index 000000000000..615c7af0d0bd
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_Rotation.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationWithRotationX.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationWithRotationX.png
new file mode 100644
index 000000000000..e0648a619789
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationWithRotationX.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationWithScale.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationWithScale.png
new file mode 100644
index 000000000000..11f2a929cea6
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationWithScale.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationWithShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationWithShadow.png
new file mode 100644
index 000000000000..d1fd1f04dc9c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationX.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationX.png
new file mode 100644
index 000000000000..050aac6957b1
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationX.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationXWithRotationY.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationXWithRotationY.png
new file mode 100644
index 000000000000..0ec8edd9e53a
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationXWithRotationY.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationXWithScaleX.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationXWithScaleX.png
new file mode 100644
index 000000000000..f0a4534b22ce
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationXWithScaleX.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationXWithShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationXWithShadow.png
new file mode 100644
index 000000000000..efe9c76da7f9
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationXWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationY.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationY.png
new file mode 100644
index 000000000000..db5c1fcae239
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationY.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationYWithScaleY.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationYWithScaleY.png
new file mode 100644
index 000000000000..2e6984a38d74
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationYWithScaleY.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationYWithShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationYWithShadow.png
new file mode 100644
index 000000000000..1f43b540e576
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_RotationYWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_Scale.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_Scale.png
new file mode 100644
index 000000000000..dceb54e5ed3d
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_Scale.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleWithAnchorXAndRotationY.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleWithAnchorXAndRotationY.png
new file mode 100644
index 000000000000..08be1c6216a1
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleWithAnchorXAndRotationY.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleWithShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleWithShadow.png
new file mode 100644
index 000000000000..c694dbe158e7
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleX.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleX.png
new file mode 100644
index 000000000000..c558f43fb0c0
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleX.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleXWithAnchorYAndRotationY.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleXWithAnchorYAndRotationY.png
new file mode 100644
index 000000000000..dd6cd47a4114
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleXWithAnchorYAndRotationY.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleXWithShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleXWithShadow.png
new file mode 100644
index 000000000000..b870d4753bd4
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleXWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleY.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleY.png
new file mode 100644
index 000000000000..94508fc86dcf
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleY.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleYWithAnchorXAndRotationY.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleYWithAnchorXAndRotationY.png
new file mode 100644
index 000000000000..c5d202616360
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleYWithAnchorXAndRotationY.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleYWithShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleYWithShadow.png
new file mode 100644
index 000000000000..d09b0061ec29
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VisualTransform_ScaleYWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/AdaptiveTrigger_Landscape.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/AdaptiveTrigger_Landscape.png
new file mode 100644
index 000000000000..7fd03e94279f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/AdaptiveTrigger_Landscape.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/AdaptiveTrigger_Portrait.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/AdaptiveTrigger_Portrait.png
new file mode 100644
index 000000000000..f79fb9907a11
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/AdaptiveTrigger_Portrait.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BackButtonBehavior_IconOverride_CustomIconShownOnBackButton.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BackButtonBehavior_IconOverride_CustomIconShownOnBackButton.png
new file mode 100644
index 000000000000..852a40272265
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BackButtonBehavior_IconOverride_CustomIconShownOnBackButton.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks.png
new file mode 100644
index 000000000000..93b7bcfa80be
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_BackgroundColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_BackgroundColor.png
new file mode 100644
index 000000000000..9ced24036587
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_BackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_DefaultValues.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_DefaultValues.png
new file mode 100644
index 000000000000..4f4cd5890a3a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_DefaultValues.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_PaddingWithContent_Image.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_PaddingWithContent_Image.png
new file mode 100644
index 000000000000..dd33909ed6e3
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_PaddingWithContent_Image.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_PaddingWithContent_Label.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_PaddingWithContent_Label.png
index 6ac9c4b707fb..20c7e1e2628a 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_PaddingWithContent_Label.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_PaddingWithContent_Label.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_PolygonShapeWithStrokeLineCap_Round.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_PolygonShapeWithStrokeLineCap_Round.png
index 183318248f2c..eccd35c579f8 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_PolygonShapeWithStrokeLineCap_Round.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_PolygonShapeWithStrokeLineCap_Round.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_Shadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_Shadow.png
index b93efb8ac290..a5af3b6bd7f7 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_Shadow.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_Shadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_ShadowWithColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_ShadowWithColor.png
new file mode 100644
index 000000000000..9f14582797a0
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_ShadowWithColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithContent_Button.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithContent_Button.png
new file mode 100644
index 000000000000..deb28b2d8516
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithContent_Button.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithDashArrayAndOffset.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithDashArrayAndOffset.png
index 9169de07277e..d074965da98d 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithDashArrayAndOffset.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithDashArrayAndOffset.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithPaddingAndContent_Image.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithPaddingAndContent_Image.png
deleted file mode 100644
index 44e4f20f5732..000000000000
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithPaddingAndContent_Image.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithRed.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithRed.png
new file mode 100644
index 000000000000..479fbace504b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithRed.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithStrokeShape_RoundRectangle.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithStrokeShape_RoundRectangle.png
index b64e3ca611e3..02bea8f3a9dd 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithStrokeShape_RoundRectangle.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithStrokeShape_RoundRectangle.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithStrokeThickness.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithStrokeThickness.png
index c258b1abe22c..35536bcd2125 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithStrokeThickness.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeColorWithStrokeThickness.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithDashOffset.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithDashOffset.png
new file mode 100644
index 000000000000..246de2b1e0aa
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithDashOffset.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithDashOffsetAndStrokeLineCapRound.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithDashOffsetAndStrokeLineCapRound.png
index 93aaac12b105..d92eae22ac6e 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithDashOffsetAndStrokeLineCapRound.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithDashOffsetAndStrokeLineCapRound.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithEllipseShapeAndStrokeLineCap_Square.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithEllipseShapeAndStrokeLineCap_Square.png
new file mode 100644
index 000000000000..626773be9e16
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithEllipseShapeAndStrokeLineCap_Square.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithStrokeColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithStrokeColor.png
new file mode 100644
index 000000000000..d5f70d51f492
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithStrokeColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithStrokeLineCap_Flat.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithStrokeLineCap_Flat.png
new file mode 100644
index 000000000000..711e8bdc687e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithStrokeLineCap_Flat.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithStrokeLineCap_Round.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithStrokeLineCap_Round.png
new file mode 100644
index 000000000000..00fa93d71f44
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithStrokeLineCap_Round.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithStrokeLineCap_Square.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithStrokeLineCap_Square.png
new file mode 100644
index 000000000000..f52aa5fe8e08
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeDashArrayWithStrokeLineCap_Square.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeGradientBrush.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeGradientBrush.png
new file mode 100644
index 000000000000..358c3af668ed
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeGradientBrush.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeShapeRectangle_AfterChange.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeShapeRectangle_AfterChange.png
new file mode 100644
index 000000000000..22f0baeca9e9
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeShapeRectangle_AfterChange.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeShapeWithDashArray_Path.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeShapeWithDashArray_Path.png
new file mode 100644
index 000000000000..60703e15a68b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeShapeWithDashArray_Path.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeShapeWithPolygon.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeShapeWithPolygon.png
new file mode 100644
index 000000000000..9125c31a463a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeShapeWithPolygon.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeShapeWithStrokeThickness_Ellipse.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeShapeWithStrokeThickness_Ellipse.png
index fc80d56b391b..53a110b2fac7 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeShapeWithStrokeThickness_Ellipse.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeShapeWithStrokeThickness_Ellipse.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeShape_Path.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeShape_Path.png
new file mode 100644
index 000000000000..5c3dacb0098b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeShape_Path.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeThicknessWithDashArray.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeThicknessWithDashArray.png
new file mode 100644
index 000000000000..d906d176f7cf
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_StrokeThicknessWithDashArray.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_ZeroPadding.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_ZeroPadding.png
new file mode 100644
index 000000000000..4431804bdb5e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Border_ZeroPadding.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BottomSheetDetentHeightIsCorrectWhenCollectionViewIsMeasuredBeforeMount.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BottomSheetDetentHeightIsCorrectWhenCollectionViewIsMeasuredBeforeMount.png
new file mode 100644
index 000000000000..6e8a9ef37955
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BottomSheetDetentHeightIsCorrectWhenCollectionViewIsMeasuredBeforeMount.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_BlueColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_BlueColor.png
new file mode 100644
index 000000000000..df4735172c1d
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_BlueColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_Color.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_Color.png
new file mode 100644
index 000000000000..c6b70db43f5d
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_Color.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_ColorWithOpacity.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_ColorWithOpacity.png
index a6298b489655..7b6ced7b8a7c 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_ColorWithOpacity.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_ColorWithOpacity.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_ColorWithOpacityAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_ColorWithOpacityAndShadow.png
index dbffb4d20e61..951127a82c93 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_ColorWithOpacityAndShadow.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_ColorWithOpacityAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_CornerRadiusWithColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_CornerRadiusWithColor.png
index 8e084ea0f549..827f7737cd95 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_CornerRadiusWithColor.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_CornerRadiusWithColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_CornerRadiusWithColorAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_CornerRadiusWithColorAndShadow.png
index a1dab508f582..841a2e6240c6 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_CornerRadiusWithColorAndShadow.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_CornerRadiusWithColorAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_CornerRadiusWithFlowDirection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_CornerRadiusWithFlowDirection.png
index 91a16d9aa556..a6ac0c636f19 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_CornerRadiusWithFlowDirection.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_CornerRadiusWithFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_CornerRadiusWithOpacityAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_CornerRadiusWithOpacityAndShadow.png
index b5e7a8527eda..cf1ce85c11de 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_CornerRadiusWithOpacityAndShadow.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_CornerRadiusWithOpacityAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_GreenColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_GreenColor.png
new file mode 100644
index 000000000000..bd00c4a06a4f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_GreenColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_IsVisible.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_IsVisible.png
index a7558659bf49..4c180905bfc0 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_IsVisible.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_IsVisible.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_OpacityZero.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_OpacityZero.png
new file mode 100644
index 000000000000..02b61887de59
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_OpacityZero.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_Reset.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_Reset.png
new file mode 100644
index 000000000000..72fb6f1e8067
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_Reset.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_UniformCornerRadius.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_UniformCornerRadius.png
new file mode 100644
index 000000000000..bbb1be1a3dbf
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_UniformCornerRadius.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_WidthAndHeight.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_WidthAndHeight.png
new file mode 100644
index 000000000000..53fc442e1336
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/BoxView_WidthAndHeight.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CheckBox_ChangeColor_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CheckBox_ChangeColor_VerifyVisualState.png
index 6bb1071e14e5..252b6f61ab97 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CheckBox_ChangeColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CheckBox_ChangeColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png
index 5cbb3c90ccb4..fe6c89ce448d 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CheckBox_VerifyWithShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CheckBox_VerifyWithShadow.png
new file mode 100644
index 000000000000..c98aef830639
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CheckBox_VerifyWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CollectionViewHeightIsCorrectAfterDelayedLoad.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CollectionViewHeightIsCorrectAfterDelayedLoad.png
new file mode 100644
index 000000000000..bbdf30bf0751
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CollectionViewHeightIsCorrectAfterDelayedLoad.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Combine_BarBackgroundColor_TextColor_IconColor_Visual.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Combine_BarBackgroundColor_TextColor_IconColor_Visual.png
new file mode 100644
index 000000000000..a9a0e58591b6
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Combine_BarBackgroundColor_TextColor_IconColor_Visual.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CompareStateTrigger_Checked.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CompareStateTrigger_Checked.png
new file mode 100644
index 000000000000..71f64bbd0e6e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CompareStateTrigger_Checked.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CompareStateTrigger_Unchecked.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CompareStateTrigger_Unchecked.png
new file mode 100644
index 000000000000..0149af0a493f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/CompareStateTrigger_Unchecked.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/DataTrigger_ButtonDisabled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/DataTrigger_ButtonDisabled.png
new file mode 100644
index 000000000000..c71c4b9e7581
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/DataTrigger_ButtonDisabled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/DataTrigger_ButtonEnabled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/DataTrigger_ButtonEnabled.png
new file mode 100644
index 000000000000..5f3912f80e64
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/DataTrigger_ButtonEnabled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/DeviceStateTriggerShowsPlatformSpecificBackground.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/DeviceStateTriggerShowsPlatformSpecificBackground.png
new file mode 100644
index 000000000000..9bc469e3b747
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/DeviceStateTriggerShowsPlatformSpecificBackground.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/DrawStringShouldDrawText.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/DrawStringShouldDrawText.png
new file mode 100644
index 000000000000..74bd01aaf9ae
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/DrawStringShouldDrawText.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/DrawTextWithinBounds.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/DrawTextWithinBounds.png
new file mode 100644
index 000000000000..ab2076258116
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/DrawTextWithinBounds.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/EditorNoOverlapAfterRotateToLandscape.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/EditorNoOverlapAfterRotateToLandscape.png
new file mode 100644
index 000000000000..914fe2ea113b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/EditorNoOverlapAfterRotateToLandscape.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/EditorNoOverlapAfterRotateToPortrait.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/EditorNoOverlapAfterRotateToPortrait.png
new file mode 100644
index 000000000000..c572b25f7974
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/EditorNoOverlapAfterRotateToPortrait.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/EntryFocusedShouldNotCauseGapAfterRotation.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/EntryFocusedShouldNotCauseGapAfterRotation.png
new file mode 100644
index 000000000000..e945f6f554a3
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/EntryFocusedShouldNotCauseGapAfterRotation.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/EventTrigger_InvalidText.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/EventTrigger_InvalidText.png
new file mode 100644
index 000000000000..7c3529a681cb
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/EventTrigger_InvalidText.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/EventTrigger_ValidNumeric.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/EventTrigger_ValidNumeric.png
new file mode 100644
index 000000000000..ac62424c0eb1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/EventTrigger_ValidNumeric.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FlyoutIconRemainsVisible.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FlyoutIconRemainsVisible.png
new file mode 100644
index 000000000000..7c8e9aba6a5f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FlyoutIconRemainsVisible.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FontImageSourceShouldHonorColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FontImageSourceShouldHonorColor.png
new file mode 100644
index 000000000000..ea6bc43557b1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FontImageSourceShouldHonorColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/GraphicsViewBackgroundShouldBeApplied.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/GraphicsViewBackgroundShouldBeApplied.png
new file mode 100644
index 000000000000..bd0ae5efdef4
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/GraphicsViewBackgroundShouldBeApplied.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/GraphicsViewBackgroundShouldBeChanged.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/GraphicsViewBackgroundShouldBeChanged.png
new file mode 100644
index 000000000000..b01008ccda0e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/GraphicsViewBackgroundShouldBeChanged.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/GraphicsViewShouldNotWrapText.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/GraphicsViewShouldNotWrapText.png
index 1d9545542446..9a166f3203b9 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/GraphicsViewShouldNotWrapText.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/GraphicsViewShouldNotWrapText.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Issue12324TabbedPageVisualTest.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Issue12324TabbedPageVisualTest.png
new file mode 100644
index 000000000000..81fc701f5fe8
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Issue12324TabbedPageVisualTest.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Issue23325Test.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Issue23325Test.png
new file mode 100644
index 000000000000..3d33137688e1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Issue23325Test.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Issue33037_AfterScroll.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Issue33037_AfterScroll.png
new file mode 100644
index 000000000000..e1625fc7307a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Issue33037_AfterScroll.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Issue33037_BeforeScroll.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Issue33037_BeforeScroll.png
new file mode 100644
index 000000000000..5153b091fc0b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Issue33037_BeforeScroll.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Issue33909ForegroundColorReset.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Issue33909ForegroundColorReset.png
new file mode 100644
index 000000000000..d4ac22e13f15
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Issue33909ForegroundColorReset.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Map_IsVisible.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Map_IsVisible.png
new file mode 100644
index 000000000000..841f6de3f3f7
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Map_IsVisible.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/MoreTabShouldRespectNavBarCustomization.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/MoreTabShouldRespectNavBarCustomization.png
new file mode 100644
index 000000000000..7010cd6b8d7b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/MoreTabShouldRespectNavBarCustomization.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/MultiTrigger_Both_Filled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/MultiTrigger_Both_Filled.png
new file mode 100644
index 000000000000..d5924a045ec1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/MultiTrigger_Both_Filled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/MultiTrigger_EmailOnly_Filled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/MultiTrigger_EmailOnly_Filled.png
new file mode 100644
index 000000000000..5dd143ec3083
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/MultiTrigger_EmailOnly_Filled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/MultiTrigger_PhoneOnly_Filled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/MultiTrigger_PhoneOnly_Filled.png
new file mode 100644
index 000000000000..98ba1530eab0
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/MultiTrigger_PhoneOnly_Filled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/NavigationPageTitleViewShouldRespectMargins.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/NavigationPageTitleViewShouldRespectMargins.png
new file mode 100644
index 000000000000..edcccefc3dad
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/NavigationPageTitleViewShouldRespectMargins.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/OnlyManuallySwipedItemShouldBeOpened.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/OnlyManuallySwipedItemShouldBeOpened.png
new file mode 100644
index 000000000000..03464f295655
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/OnlyManuallySwipedItemShouldBeOpened.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ProgressBar_ChangeFlowDirection_RTL_VerifyLabel.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ProgressBar_ChangeFlowDirection_RTL_VerifyLabel.png
new file mode 100644
index 000000000000..cab594c66e8c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ProgressBar_ChangeFlowDirection_RTL_VerifyLabel.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/PropertyTrigger_Focused.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/PropertyTrigger_Focused.png
new file mode 100644
index 000000000000..83b51318ced9
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/PropertyTrigger_Focused.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/PropertyTrigger_UnFocused.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/PropertyTrigger_UnFocused.png
new file mode 100644
index 000000000000..12c15bb414e1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/PropertyTrigger_UnFocused.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButtonTextColorShouldNotBeOverriddenByGlobalLabelStyle.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButtonTextColorShouldNotBeOverriddenByGlobalLabelStyle.png
new file mode 100644
index 000000000000..2d4e3e371713
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButtonTextColorShouldNotBeOverriddenByGlobalLabelStyle.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RefreshView_SetShadowWithCollectionView_VerifyShadowApplied.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RefreshView_SetShadowWithCollectionView_VerifyShadowApplied.png
index 01321e4e4eb2..ec5fd3408317 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RefreshView_SetShadowWithCollectionView_VerifyShadowApplied.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RefreshView_SetShadowWithCollectionView_VerifyShadowApplied.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RefreshView_SetShadow_VerifyShadowApplied.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RefreshView_SetShadow_VerifyShadowApplied.png
index e72da51d6e18..1f72599954ff 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RefreshView_SetShadow_VerifyShadowApplied.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RefreshView_SetShadow_VerifyShadowApplied.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SelectionLengthShouldUpdateWhenEntryIsFocused.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SelectionLengthShouldUpdateWhenEntryIsFocused.png
new file mode 100644
index 000000000000..876821a5401c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SelectionLengthShouldUpdateWhenEntryIsFocused.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShadowShouldUpdateOnCornerRadiusChange.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShadowShouldUpdateOnCornerRadiusChange.png
new file mode 100644
index 000000000000..16b48c06eb18
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShadowShouldUpdateOnCornerRadiusChange.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShadowShouldWork.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShadowShouldWork.png
new file mode 100644
index 000000000000..4b55119c475f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShadowShouldWork.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_ForegroundColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_ForegroundColor.png
new file mode 100644
index 000000000000..256bcdd7d8fd
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_ForegroundColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_ForegroundColorAndTitleColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_ForegroundColorAndTitleColor.png
new file mode 100644
index 000000000000..e2c633e3684d
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_ForegroundColorAndTitleColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_IsVisibleFalse.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_IsVisibleFalse.png
new file mode 100644
index 000000000000..f89107e88aba
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_IsVisibleFalse.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_IsVisibleTrue.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_IsVisibleTrue.png
new file mode 100644
index 000000000000..c16bfb1ae64b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_IsVisibleTrue.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_PresentationModeAnimated.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_PresentationModeAnimated.png
new file mode 100644
index 000000000000..6fc102c3609b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_PresentationModeAnimated.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_PresentationModeModal.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_PresentationModeModal.png
new file mode 100644
index 000000000000..0493fd842cbb
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_PresentationModeModal.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_PresentationModeModalAnimated.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_PresentationModeModalAnimated.png
new file mode 100644
index 000000000000..f0f80f6bda61
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_PresentationModeModalAnimated.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_PresentationModeModalNotAnimated.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_PresentationModeModalNotAnimated.png
new file mode 100644
index 000000000000..435129f6fa56
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_PresentationModeModalNotAnimated.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_PresentationModeNotAnimated.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_PresentationModeNotAnimated.png
new file mode 100644
index 000000000000..82a91f1cf89a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_PresentationModeNotAnimated.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_ShowTitleView.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_ShowTitleView.png
new file mode 100644
index 000000000000..ce7034ad7800
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_ShowTitleView.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_ShowTitleViewHidden.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_ShowTitleViewHidden.png
new file mode 100644
index 000000000000..3c28b66397af
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_ShowTitleViewHidden.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_TitleColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_TitleColor.png
new file mode 100644
index 000000000000..0eaa031195c5
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ShellPages_TitleColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SliderShouldChangeThumbImageAndResetIt.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SliderShouldChangeThumbImageAndResetIt.png
new file mode 100644
index 000000000000..4b102b24223d
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SliderShouldChangeThumbImageAndResetIt.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Slider_SetThumbImageSourceAndReset_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Slider_SetThumbImageSourceAndReset_VerifyVisualState.png
new file mode 100644
index 000000000000..2e024480e91f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Slider_SetThumbImageSourceAndReset_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/StateTrigger_SwitchOff.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/StateTrigger_SwitchOff.png
new file mode 100644
index 000000000000..95d5155af261
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/StateTrigger_SwitchOff.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/StateTrigger_SwitchOn.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/StateTrigger_SwitchOn.png
new file mode 100644
index 000000000000..b63abdfe815a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/StateTrigger_SwitchOn.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Stepper_ChangeFlowDirection_RTL_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Stepper_ChangeFlowDirection_RTL_VerifyVisualState.png
new file mode 100644
index 000000000000..1bf0dffbd089
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/Stepper_ChangeFlowDirection_RTL_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SubPageNavigationShouldWorkAfterFirstTabBecomesInvisible.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SubPageNavigationShouldWorkAfterFirstTabBecomesInvisible.png
new file mode 100644
index 000000000000..2a6a5500440a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SubPageNavigationShouldWorkAfterFirstTabBecomesInvisible.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SwipeItemFontImageSourceSizeIsRespected.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SwipeItemFontImageSourceSizeIsRespected.png
new file mode 100644
index 000000000000..1aae1e0be0dd
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SwipeItemFontImageSourceSizeIsRespected.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TabBarShouldBeVisibleAfterNavigatingFromModalViaGoToAsync.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TabBarShouldBeVisibleAfterNavigatingFromModalViaGoToAsync.png
new file mode 100644
index 000000000000..9e14c0ba716c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TabBarShouldBeVisibleAfterNavigatingFromModalViaGoToAsync.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TabBarVisibilityAfterMultiLevelPopToRoot.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TabBarVisibilityAfterMultiLevelPopToRoot.png
new file mode 100644
index 000000000000..9395c36ba223
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TabBarVisibilityAfterMultiLevelPopToRoot.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TestIssue18668.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TestIssue18668.png
new file mode 100644
index 000000000000..02d6745beb01
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TestIssue18668.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TransparentShapeShouldNotDisplayShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TransparentShapeShouldNotDisplayShadow.png
new file mode 100644
index 000000000000..f5fc2b10d3b6
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TransparentShapeShouldNotDisplayShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyDefaultScrollToRequested.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyDefaultScrollToRequested.png
new file mode 100644
index 000000000000..69fb34134af6
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyDefaultScrollToRequested.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_BackgroundColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_BackgroundColor.png
index 3bccc1dc1d36..35b900a3d8e8 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_BackgroundColor.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_BackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_DetailPageIconImageSource.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_DetailPageIconImageSource.png
index 7bb503d73655..87e48986d55e 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_DetailPageIconImageSource.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_DetailPageIconImageSource.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_DetailPageIconImageSource_FlyoutLayoutBehavior.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_DetailPageIconImageSource_FlyoutLayoutBehavior.png
index 3c0aee0bc28f..2db13b5da36d 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_DetailPageIconImageSource_FlyoutLayoutBehavior.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_DetailPageIconImageSource_FlyoutLayoutBehavior.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_FlyoutLayoutBehaviorPopover.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_FlyoutLayoutBehaviorPopover.png
index 9edd930593f3..9938c56b6940 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_FlyoutLayoutBehaviorPopover.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_FlyoutLayoutBehaviorPopover.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_Title.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_Title.png
index 500ed978ba53..a6616b0655b1 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_Title.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutPage_Title.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyGraphicsViewWithoutGrayLine.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyGraphicsViewWithoutGrayLine.png
deleted file mode 100644
index 00b38305fcc5..000000000000
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyGraphicsViewWithoutGrayLine.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyLabelBackgroundIsClippedWithRectangleGeometry.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyLabelBackgroundIsClippedWithRectangleGeometry.png
new file mode 100644
index 000000000000..862e293b35a5
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyLabelBackgroundIsClippedWithRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyMeasureAllItemsWithGroupedList.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyMeasureAllItemsWithGroupedList.png
index 4be025b0f7b2..e6670123ddbf 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyMeasureAllItemsWithGroupedList.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyMeasureAllItemsWithGroupedList.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyMeasureAllItemsWithObservableCollection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyMeasureAllItemsWithObservableCollection.png
index 9e40e21a92df..3f2ba2a95ad9 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyMeasureAllItemsWithObservableCollection.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyMeasureAllItemsWithObservableCollection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByIndexWithCenterPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByIndexWithCenterPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..a77dfa03a756
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByIndexWithCenterPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByIndexWithEndPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByIndexWithEndPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..8402a421399e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByIndexWithEndPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..6b452a6079f9
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByItemWithCenterPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByItemWithCenterPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..e9c92eac3dde
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByItemWithCenterPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByItemWithEndPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByItemWithEndPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..27f0406764f3
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByItemWithEndPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByItemWithMakeVisiblePositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByItemWithMakeVisiblePositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..0a05b35eb4c1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollToByItemWithMakeVisiblePositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellForegroundColorIsAppliedToToolbarItems.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellForegroundColorIsAppliedToToolbarItems.png
new file mode 100644
index 000000000000..62f6c79ee6ec
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellForegroundColorIsAppliedToToolbarItems.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyTimePickerFormat.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyTimePickerFormat.png
new file mode 100644
index 000000000000..9dba5ca44d7e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyTimePickerFormat.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_AnchorXWithAnchorY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_AnchorXWithAnchorY.png
new file mode 100644
index 000000000000..84a60d950224
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_AnchorXWithAnchorY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_AnchorX_ScaleYWithRotation.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_AnchorX_ScaleYWithRotation.png
new file mode 100644
index 000000000000..e91aaf70d423
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_AnchorX_ScaleYWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_AnchorY_ScaleWithRotationY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_AnchorY_ScaleWithRotationY.png
new file mode 100644
index 000000000000..eeb5b53cb918
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_AnchorY_ScaleWithRotationY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_AnchorY_ScaleXWithRotationX.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_AnchorY_ScaleXWithRotationX.png
new file mode 100644
index 000000000000..41767982bd94
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_AnchorY_ScaleXWithRotationX.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_IsVisible.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_IsVisible.png
new file mode 100644
index 000000000000..06179953d643
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_IsVisible.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_Rotation.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_Rotation.png
new file mode 100644
index 000000000000..4a8f9159a34d
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_Rotation.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationWithRotationX.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationWithRotationX.png
new file mode 100644
index 000000000000..53b91f518ddb
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationWithRotationX.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationWithScale.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationWithScale.png
new file mode 100644
index 000000000000..aa92ae16d4de
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationWithScale.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationX.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationX.png
new file mode 100644
index 000000000000..0f6c6aac5d21
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationX.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationXWithRotationY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationXWithRotationY.png
new file mode 100644
index 000000000000..8bed1f36f9b7
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationXWithRotationY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationXWithScaleX.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationXWithScaleX.png
new file mode 100644
index 000000000000..4fd129b2fc77
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationXWithScaleX.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationY.png
new file mode 100644
index 000000000000..887a1414415c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationYWithScaleY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationYWithScaleY.png
new file mode 100644
index 000000000000..77af966bb9aa
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_RotationYWithScaleY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_Scale.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_Scale.png
new file mode 100644
index 000000000000..b5db62ec6199
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_Scale.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_ScaleWithAnchorXAndRotationY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_ScaleWithAnchorXAndRotationY.png
new file mode 100644
index 000000000000..6f421540dc45
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_ScaleWithAnchorXAndRotationY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_ScaleX.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_ScaleX.png
new file mode 100644
index 000000000000..63a33023c739
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_ScaleX.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_ScaleXWithAnchorYAndRotationY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_ScaleXWithAnchorYAndRotationY.png
new file mode 100644
index 000000000000..dcee91c63a90
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_ScaleXWithAnchorYAndRotationY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_ScaleY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_ScaleY.png
new file mode 100644
index 000000000000..b3b8e69f9205
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_ScaleY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_ScaleYWithAnchorXAndRotationY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_ScaleYWithAnchorXAndRotationY.png
new file mode 100644
index 000000000000..74a6fce96603
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VisualTransform_ScaleYWithAnchorXAndRotationY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/AdaptiveTrigger_Landscape.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/AdaptiveTrigger_Landscape.png
new file mode 100644
index 000000000000..d5d37c4d08a5
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/AdaptiveTrigger_Landscape.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/AdaptiveTrigger_Portrait.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/AdaptiveTrigger_Portrait.png
new file mode 100644
index 000000000000..4845ce33aa0e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/AdaptiveTrigger_Portrait.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ArabicStringShouldBeLeftToRight.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ArabicStringShouldBeLeftToRight.png
new file mode 100644
index 000000000000..9e930b0a3ae8
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ArabicStringShouldBeLeftToRight.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BackButtonBehavior_IconOverride_CustomIconShownOnBackButton.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BackButtonBehavior_IconOverride_CustomIconShownOnBackButton.png
new file mode 100644
index 000000000000..dabfb3b6ccb2
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BackButtonBehavior_IconOverride_CustomIconShownOnBackButton.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks.png
new file mode 100644
index 000000000000..3a0930bdcf7b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_BackgroundColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_BackgroundColor.png
new file mode 100644
index 000000000000..629cfb821a3b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_BackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipNull_NoCrash.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipNull_NoCrash.png
new file mode 100644
index 000000000000..d5727b7b1f83
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipNull_NoCrash.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithNestedContent.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithNestedContent.png
new file mode 100644
index 000000000000..7b0b4184fa6f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithNestedContent.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithRotation.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithRotation.png
new file mode 100644
index 000000000000..b5d29a9f1c3f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithRotationAndScale.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithRotationAndScale.png
new file mode 100644
index 000000000000..8d8d357868a9
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithRotationAndScale.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithShadow.png
new file mode 100644
index 000000000000..3be35af982d6
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithStrokeColorBlue.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithStrokeColorBlue.png
new file mode 100644
index 000000000000..82dde06fab4d
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithStrokeColorBlue.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithStrokeColorGreen.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithStrokeColorGreen.png
new file mode 100644
index 000000000000..f44bf8abfca1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithStrokeColorGreen.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithStrokeShapeRoundRectangle.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithStrokeShapeRoundRectangle.png
new file mode 100644
index 000000000000..987dc42be054
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithStrokeShapeRoundRectangle.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithStrokeThickness.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithStrokeThickness.png
new file mode 100644
index 000000000000..23abf3af28b5
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ClipWithStrokeThickness.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_DefaultValues.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_DefaultValues.png
new file mode 100644
index 000000000000..13d3e7b15c05
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_DefaultValues.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_PaddingWithContent_Image.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_PaddingWithContent_Image.png
new file mode 100644
index 000000000000..5e12735f2dbf
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_PaddingWithContent_Image.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_PaddingWithContent_Label.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_PaddingWithContent_Label.png
index b1efad8590b3..c7a5c5a6d6a4 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_PaddingWithContent_Label.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_PaddingWithContent_Label.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_PolygonShapeWithStrokeLineCap_Round.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_PolygonShapeWithStrokeLineCap_Round.png
index 183318248f2c..c8eab228f656 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_PolygonShapeWithStrokeLineCap_Round.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_PolygonShapeWithStrokeLineCap_Round.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_Shadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_Shadow.png
index ab1c7f962c1b..79b95741e920 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_Shadow.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_Shadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ShadowWithColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ShadowWithColor.png
new file mode 100644
index 000000000000..c196a4cd8c42
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ShadowWithColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithContent_Button.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithContent_Button.png
new file mode 100644
index 000000000000..e385f237e955
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithContent_Button.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithDashArrayAndOffset.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithDashArrayAndOffset.png
index 9169de07277e..81ed662343a2 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithDashArrayAndOffset.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithDashArrayAndOffset.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithPaddingAndContent_Image.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithPaddingAndContent_Image.png
deleted file mode 100644
index 22a1bc09f98a..000000000000
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithPaddingAndContent_Image.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithRed.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithRed.png
new file mode 100644
index 000000000000..c4a0e444af0f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithRed.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithStrokeShape_RoundRectangle.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithStrokeShape_RoundRectangle.png
index 84574135ae9b..7521825c1eb4 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithStrokeShape_RoundRectangle.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithStrokeShape_RoundRectangle.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithStrokeThickness.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithStrokeThickness.png
index d1b988b92957..f5c87a998f5a 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithStrokeThickness.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeColorWithStrokeThickness.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithDashOffset.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithDashOffset.png
new file mode 100644
index 000000000000..db14c6033657
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithDashOffset.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithDashOffsetAndStrokeLineCapRound.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithDashOffsetAndStrokeLineCapRound.png
index 93aaac12b105..040165135db3 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithDashOffsetAndStrokeLineCapRound.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithDashOffsetAndStrokeLineCapRound.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithEllipseShapeAndStrokeLineCap_Square.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithEllipseShapeAndStrokeLineCap_Square.png
new file mode 100644
index 000000000000..eb0097424988
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithEllipseShapeAndStrokeLineCap_Square.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithStrokeColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithStrokeColor.png
new file mode 100644
index 000000000000..04eb708ac144
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithStrokeColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithStrokeLineCap_Flat.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithStrokeLineCap_Flat.png
new file mode 100644
index 000000000000..4538a74033b8
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithStrokeLineCap_Flat.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithStrokeLineCap_Round.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithStrokeLineCap_Round.png
new file mode 100644
index 000000000000..105b0619923a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithStrokeLineCap_Round.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithStrokeLineCap_Square.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithStrokeLineCap_Square.png
new file mode 100644
index 000000000000..cc2d1e3d9461
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeDashArrayWithStrokeLineCap_Square.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeGradientBrush.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeGradientBrush.png
new file mode 100644
index 000000000000..ca2e8a25a897
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeGradientBrush.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeShapeRectangle_AfterChange.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeShapeRectangle_AfterChange.png
new file mode 100644
index 000000000000..dc9329ce0c7c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeShapeRectangle_AfterChange.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeShapeWithDashArray_Path.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeShapeWithDashArray_Path.png
new file mode 100644
index 000000000000..17d4f5d5ca2d
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeShapeWithDashArray_Path.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeShapeWithPolygon.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeShapeWithPolygon.png
new file mode 100644
index 000000000000..a396094675f5
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeShapeWithPolygon.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeShapeWithStrokeThickness_Ellipse.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeShapeWithStrokeThickness_Ellipse.png
index 9f74023e10bf..41986fc40402 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeShapeWithStrokeThickness_Ellipse.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeShapeWithStrokeThickness_Ellipse.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeShape_Path.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeShape_Path.png
new file mode 100644
index 000000000000..b2f49079202c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeShape_Path.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeThicknessWithDashArray.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeThicknessWithDashArray.png
new file mode 100644
index 000000000000..2c9f4ee89e2f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_StrokeThicknessWithDashArray.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ZeroPadding.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ZeroPadding.png
new file mode 100644
index 000000000000..86a5b2cf3294
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Border_ZeroPadding.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BottomSheetDetentHeightIsCorrectWhenCollectionViewIsMeasuredBeforeMount.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BottomSheetDetentHeightIsCorrectWhenCollectionViewIsMeasuredBeforeMount.png
new file mode 100644
index 000000000000..320359c1c88f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BottomSheetDetentHeightIsCorrectWhenCollectionViewIsMeasuredBeforeMount.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_BlueColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_BlueColor.png
new file mode 100644
index 000000000000..e38eea799024
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_BlueColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ClipWithColorGreen.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ClipWithColorGreen.png
new file mode 100644
index 000000000000..615202180027
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ClipWithColorGreen.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ClipWithCornerRadius.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ClipWithCornerRadius.png
new file mode 100644
index 000000000000..082b9d81a957
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ClipWithCornerRadius.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ClipWithRotation.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ClipWithRotation.png
new file mode 100644
index 000000000000..2b158746571e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ClipWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ClipWithShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ClipWithShadow.png
new file mode 100644
index 000000000000..a036862d2356
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ClipWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_Color.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_Color.png
new file mode 100644
index 000000000000..0e80e70a33bc
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_Color.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ColorWithOpacity.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ColorWithOpacity.png
index 5c9c682ac35a..b08f5494ad7a 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ColorWithOpacity.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ColorWithOpacity.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ColorWithOpacityAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ColorWithOpacityAndShadow.png
index 2d8c1134d86e..273c51aab501 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ColorWithOpacityAndShadow.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_ColorWithOpacityAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_CornerRadiusWithColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_CornerRadiusWithColor.png
index 75b7a0270ca9..e578fec66d09 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_CornerRadiusWithColor.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_CornerRadiusWithColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_CornerRadiusWithColorAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_CornerRadiusWithColorAndShadow.png
index 621ba538681b..ac6b23650ada 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_CornerRadiusWithColorAndShadow.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_CornerRadiusWithColorAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_CornerRadiusWithFlowDirection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_CornerRadiusWithFlowDirection.png
index 5b2f9bb77d22..7c3d23cbcbee 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_CornerRadiusWithFlowDirection.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_CornerRadiusWithFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_CornerRadiusWithOpacityAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_CornerRadiusWithOpacityAndShadow.png
index e13cbdd77cbb..2001a465c782 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_CornerRadiusWithOpacityAndShadow.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_CornerRadiusWithOpacityAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_GreenColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_GreenColor.png
new file mode 100644
index 000000000000..967334ad3a70
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_GreenColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_IsVisible.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_IsVisible.png
index da7ce5e9f560..329b617a59d7 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_IsVisible.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_IsVisible.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_OpacityZero.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_OpacityZero.png
new file mode 100644
index 000000000000..815ce411a936
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_OpacityZero.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_Reset.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_Reset.png
new file mode 100644
index 000000000000..4485a853c783
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_Reset.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_UniformCornerRadius.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_UniformCornerRadius.png
new file mode 100644
index 000000000000..6cbc33dd8aa0
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_UniformCornerRadius.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_WidthAndHeight.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_WidthAndHeight.png
new file mode 100644
index 000000000000..27401e571a9b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/BoxView_WidthAndHeight.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ButtonRTLTextAndImageShouldNotOverlap.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ButtonRTLTextAndImageShouldNotOverlap.png
new file mode 100644
index 000000000000..162f6962b4ff
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ButtonRTLTextAndImageShouldNotOverlap.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Button_ClipNull_NoCrash.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Button_ClipNull_NoCrash.png
new file mode 100644
index 000000000000..c679cde227f7
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Button_ClipNull_NoCrash.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Button_ClipWithImageSource.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Button_ClipWithImageSource.png
new file mode 100644
index 000000000000..847a4778a46a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Button_ClipWithImageSource.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Button_ClipWithScale.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Button_ClipWithScale.png
new file mode 100644
index 000000000000..9b1038124ebb
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Button_ClipWithScale.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Button_ClipWithShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Button_ClipWithShadow.png
new file mode 100644
index 000000000000..311991de9496
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Button_ClipWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Button_ClipWithText.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Button_ClipWithText.png
new file mode 100644
index 000000000000..b9faadf59197
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Button_ClipWithText.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CheckBox_ChangeColor_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CheckBox_ChangeColor_VerifyVisualState.png
index 4f00408865c9..b8d8e14666b1 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CheckBox_ChangeColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CheckBox_ChangeColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png
index 66a2ffd0f330..f861dd5e181b 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CheckBox_SetIsCheckedAndColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CheckBox_VerifyWithShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CheckBox_VerifyWithShadow.png
new file mode 100644
index 000000000000..09524a21209f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CheckBox_VerifyWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewHeightIsCorrectAfterDelayedLoad.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewHeightIsCorrectAfterDelayedLoad.png
new file mode 100644
index 000000000000..034f3a7a1ec7
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewHeightIsCorrectAfterDelayedLoad.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewItemsShouldRespectSafeAreaEdges.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewItemsShouldRespectSafeAreaEdges.png
new file mode 100644
index 000000000000..bb3cac59fef5
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewItemsShouldRespectSafeAreaEdges.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewSelectionShouldClear.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewSelectionShouldClear.png
new file mode 100644
index 000000000000..74241465a1e6
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewSelectionShouldClear.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewShouldChangeItemsLayout.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewShouldChangeItemsLayout.png
new file mode 100644
index 000000000000..4708354a75fe
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewShouldChangeItemsLayout.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CompareStateTrigger_Checked.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CompareStateTrigger_Checked.png
new file mode 100644
index 000000000000..1f375c1666cf
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CompareStateTrigger_Checked.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CompareStateTrigger_Unchecked.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CompareStateTrigger_Unchecked.png
new file mode 100644
index 000000000000..ed512e69e332
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CompareStateTrigger_Unchecked.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipNull_NoCrash.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipNull_NoCrash.png
new file mode 100644
index 000000000000..d6af5dee76fb
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipNull_NoCrash.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipWithEllipseGeometry.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipWithEllipseGeometry.png
new file mode 100644
index 000000000000..8991f44c9560
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipWithEllipseGeometry.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipWithNestedClippedContent.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipWithNestedClippedContent.png
new file mode 100644
index 000000000000..e9f9a9d28fed
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipWithNestedClippedContent.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipWithRectangleGeometry.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipWithRectangleGeometry.png
new file mode 100644
index 000000000000..df403c95fc4e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipWithRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipWithRoundRectangleGeometry.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipWithRoundRectangleGeometry.png
new file mode 100644
index 000000000000..fb91d2c3f53d
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipWithRoundRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipWithShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipWithShadow.png
new file mode 100644
index 000000000000..652c164ea081
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ContentView_ClipWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DataTrigger_ButtonDisabled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DataTrigger_ButtonDisabled.png
new file mode 100644
index 000000000000..e95a3e321ce5
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DataTrigger_ButtonDisabled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DataTrigger_ButtonEnabled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DataTrigger_ButtonEnabled.png
new file mode 100644
index 000000000000..8a809574b8c6
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DataTrigger_ButtonEnabled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DeviceStateTriggerShowsPlatformSpecificBackground.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DeviceStateTriggerShowsPlatformSpecificBackground.png
new file mode 100644
index 000000000000..d766268205f0
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DeviceStateTriggerShowsPlatformSpecificBackground.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DisabledTabWithGreenColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DisabledTabWithGreenColor.png
new file mode 100644
index 000000000000..6b740b531dec
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DisabledTabWithGreenColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DrawStringShouldDrawText.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DrawStringShouldDrawText.png
new file mode 100644
index 000000000000..74bd01aaf9ae
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DrawStringShouldDrawText.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DrawTextWithinBounds.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DrawTextWithinBounds.png
new file mode 100644
index 000000000000..c2598f3a3deb
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/DrawTextWithinBounds.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EditorNoOverlapAfterRotateToLandscape.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EditorNoOverlapAfterRotateToLandscape.png
new file mode 100644
index 000000000000..d3652dbcc9f0
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EditorNoOverlapAfterRotateToLandscape.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EditorNoOverlapAfterRotateToPortrait.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EditorNoOverlapAfterRotateToPortrait.png
new file mode 100644
index 000000000000..86a77ac3d5de
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EditorNoOverlapAfterRotateToPortrait.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EnabledTabWithNormalColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EnabledTabWithNormalColor.png
new file mode 100644
index 000000000000..218e9a14ec53
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EnabledTabWithNormalColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EnsureSearchBarExplicitSize.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EnsureSearchBarExplicitSize.png
new file mode 100644
index 000000000000..6ae410e29e48
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EnsureSearchBarExplicitSize.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EntryFocusedShouldNotCauseGapAfterRotation.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EntryFocusedShouldNotCauseGapAfterRotation.png
new file mode 100644
index 000000000000..52d86e9e87c4
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EntryFocusedShouldNotCauseGapAfterRotation.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EventTrigger_InvalidText.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EventTrigger_InvalidText.png
new file mode 100644
index 000000000000..81bb9ecd7aaa
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EventTrigger_InvalidText.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EventTrigger_ValidNumeric.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EventTrigger_ValidNumeric.png
new file mode 100644
index 000000000000..16da85403edc
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/EventTrigger_ValidNumeric.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FlexLayoutWithBindableLayoutDisplaysLabels.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FlexLayoutWithBindableLayoutDisplaysLabels.png
new file mode 100644
index 000000000000..78ab9f1a5815
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FlexLayoutWithBindableLayoutDisplaysLabels.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FlyoutIconRemainsVisible.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FlyoutIconRemainsVisible.png
new file mode 100644
index 000000000000..f7fc231766aa
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FlyoutIconRemainsVisible.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FlyoutIconUpdatedAfterInsertPageBefore.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FlyoutIconUpdatedAfterInsertPageBefore.png
new file mode 100644
index 000000000000..112a4f1c0e1a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FlyoutIconUpdatedAfterInsertPageBefore.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FontImageSourceShouldHonorColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FontImageSourceShouldHonorColor.png
new file mode 100644
index 000000000000..ea6bc43557b1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FontImageSourceShouldHonorColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/GraphicsViewBackgroundShouldBeApplied.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/GraphicsViewBackgroundShouldBeApplied.png
new file mode 100644
index 000000000000..9487dbf95576
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/GraphicsViewBackgroundShouldBeApplied.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/GraphicsViewBackgroundShouldBeChanged.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/GraphicsViewBackgroundShouldBeChanged.png
new file mode 100644
index 000000000000..0bb8c5f0490e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/GraphicsViewBackgroundShouldBeChanged.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/GraphicsViewShouldNotWrapText.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/GraphicsViewShouldNotWrapText.png
index 1d9545542446..c2a09c604b51 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/GraphicsViewShouldNotWrapText.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/GraphicsViewShouldNotWrapText.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/GroupedCollectionViewItems.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/GroupedCollectionViewItems.png
new file mode 100644
index 000000000000..2c7a2b5e4a22
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/GroupedCollectionViewItems.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/HEICImageShouldNotRenderUpsideDown.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/HEICImageShouldNotRenderUpsideDown.png
new file mode 100644
index 000000000000..fa7b0394fb31
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/HEICImageShouldNotRenderUpsideDown.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageButton_ClipWithEllipseGeometry.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageButton_ClipWithEllipseGeometry.png
new file mode 100644
index 000000000000..49d584f68d80
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageButton_ClipWithEllipseGeometry.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageButton_ClipWithRectangleGeometry.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageButton_ClipWithRectangleGeometry.png
new file mode 100644
index 000000000000..4fa693649567
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageButton_ClipWithRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageButton_ClipWithRoundRectangleGeometry.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageButton_ClipWithRoundRectangleGeometry.png
new file mode 100644
index 000000000000..fe0b4c6c6cf3
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageButton_ClipWithRoundRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageButton_ClipWithScale.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageButton_ClipWithScale.png
new file mode 100644
index 000000000000..f7b99159a051
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageButton_ClipWithScale.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageButton_ClipWithShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageButton_ClipWithShadow.png
new file mode 100644
index 000000000000..38c941fc252f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageButton_ClipWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageShouldLoadFromSubfolder.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageShouldLoadFromSubfolder.png
new file mode 100644
index 000000000000..e70a6fabeca7
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ImageShouldLoadFromSubfolder.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipNull_NoCrash.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipNull_NoCrash.png
new file mode 100644
index 000000000000..1c2e78807534
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipNull_NoCrash.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithArcSegmentPath.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithArcSegmentPath.png
new file mode 100644
index 000000000000..a6fa3dc69840
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithArcSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithBezierSegmentPath.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithBezierSegmentPath.png
new file mode 100644
index 000000000000..5b08f5223712
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithBezierSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithComplexPolyBezierAndRotation.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithComplexPolyBezierAndRotation.png
new file mode 100644
index 000000000000..d42a3b6b2ebd
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithComplexPolyBezierAndRotation.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithComplexPolyLineGeometry.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithComplexPolyLineGeometry.png
new file mode 100644
index 000000000000..157b331bedb0
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithComplexPolyLineGeometry.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithEllipseGeometry.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithEllipseGeometry.png
new file mode 100644
index 000000000000..be10126bbde1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithEllipseGeometry.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithGeometryGroup.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithGeometryGroup.png
new file mode 100644
index 000000000000..7083135708a2
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithGeometryGroup.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithLineSegmentPath.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithLineSegmentPath.png
new file mode 100644
index 000000000000..c59e4106c394
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithLineSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithPolyBezierSegmentPath.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithPolyBezierSegmentPath.png
new file mode 100644
index 000000000000..5bd0273cd8be
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithPolyBezierSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithPolyLineSegmentPath.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithPolyLineSegmentPath.png
new file mode 100644
index 000000000000..8b5683bf266c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithPolyLineSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithPolyQuadraticBezierSegmentPath.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithPolyQuadraticBezierSegmentPath.png
new file mode 100644
index 000000000000..637106c112d5
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithPolyQuadraticBezierSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithQuadraticBezierSegmentPath.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithQuadraticBezierSegmentPath.png
new file mode 100644
index 000000000000..15ba41d72c87
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithQuadraticBezierSegmentPath.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithRectangleGeometry.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithRectangleGeometry.png
new file mode 100644
index 000000000000..0069efd53441
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithRotation.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithRotation.png
new file mode 100644
index 000000000000..a6deeae41791
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithRoundRectangleGeometry.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithRoundRectangleGeometry.png
new file mode 100644
index 000000000000..6e319f7f26e6
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithRoundRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithScale.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithScale.png
new file mode 100644
index 000000000000..af0a2c7dde00
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Image_ClipWithScale.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Issue12324TabbedPageVisualTest.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Issue12324TabbedPageVisualTest.png
new file mode 100644
index 000000000000..bdef7cfc5866
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Issue12324TabbedPageVisualTest.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Issue23377ItemSpacing.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Issue23377ItemSpacing.png
new file mode 100644
index 000000000000..e232292247c8
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Issue23377ItemSpacing.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Issue33037_AfterScroll.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Issue33037_AfterScroll.png
new file mode 100644
index 000000000000..e1625fc7307a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Issue33037_AfterScroll.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Issue33037_BeforeScroll.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Issue33037_BeforeScroll.png
new file mode 100644
index 000000000000..5153b091fc0b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Issue33037_BeforeScroll.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Issue33909ForegroundColorReset.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Issue33909ForegroundColorReset.png
new file mode 100644
index 000000000000..149b6240cffc
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Issue33909ForegroundColorReset.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/LabelDisplayWithoutCroppingInsideVerticalLayout.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/LabelDisplayWithoutCroppingInsideVerticalLayout.png
new file mode 100644
index 000000000000..1429a0937a55
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/LabelDisplayWithoutCroppingInsideVerticalLayout.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/LabelShadowRemainsAfterOpacityChange.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/LabelShadowRemainsAfterOpacityChange.png
new file mode 100644
index 000000000000..d2f409cc1bba
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/LabelShadowRemainsAfterOpacityChange.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Map_IsVisible.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Map_IsVisible.png
new file mode 100644
index 000000000000..38138bcaa9a8
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Map_IsVisible.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/MoreTabShouldRespectNavBarCustomization.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/MoreTabShouldRespectNavBarCustomization.png
new file mode 100644
index 000000000000..1bef5d55646c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/MoreTabShouldRespectNavBarCustomization.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/MultiTrigger_Both_Filled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/MultiTrigger_Both_Filled.png
new file mode 100644
index 000000000000..92f0ba6d65a2
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/MultiTrigger_Both_Filled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/MultiTrigger_EmailOnly_Filled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/MultiTrigger_EmailOnly_Filled.png
new file mode 100644
index 000000000000..d474a806d32e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/MultiTrigger_EmailOnly_Filled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/MultiTrigger_PhoneOnly_Filled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/MultiTrigger_PhoneOnly_Filled.png
new file mode 100644
index 000000000000..48c66f1c9e08
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/MultiTrigger_PhoneOnly_Filled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/NavigatingBetweenPagesWithSetTitleViewShouldNotCrash.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/NavigatingBetweenPagesWithSetTitleViewShouldNotCrash.png
new file mode 100644
index 000000000000..f9f7d47817b3
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/NavigatingBetweenPagesWithSetTitleViewShouldNotCrash.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/NavigationPageTitleViewShouldRespectMargins.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/NavigationPageTitleViewShouldRespectMargins.png
new file mode 100644
index 000000000000..edcccefc3dad
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/NavigationPageTitleViewShouldRespectMargins.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/OnlyManuallySwipedItemShouldBeOpened.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/OnlyManuallySwipedItemShouldBeOpened.png
new file mode 100644
index 000000000000..ffe5d663d473
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/OnlyManuallySwipedItemShouldBeOpened.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/PageShouldNotMoveOutsideViewportWhenEntryFocusedOnPageLoad.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/PageShouldNotMoveOutsideViewportWhenEntryFocusedOnPageLoad.png
new file mode 100644
index 000000000000..6fd06404c1e3
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/PageShouldNotMoveOutsideViewportWhenEntryFocusedOnPageLoad.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/PropertyTrigger_Focused.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/PropertyTrigger_Focused.png
new file mode 100644
index 000000000000..0a250707a71c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/PropertyTrigger_Focused.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/PropertyTrigger_UnFocused.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/PropertyTrigger_UnFocused.png
new file mode 100644
index 000000000000..6bbed43e9f34
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/PropertyTrigger_UnFocused.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RTLModePaddingShouldWork.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RTLModePaddingShouldWork.png
new file mode 100644
index 000000000000..d33119240099
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RTLModePaddingShouldWork.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RadioButtonTextColorShouldNotBeOverriddenByGlobalLabelStyle.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RadioButtonTextColorShouldNotBeOverriddenByGlobalLabelStyle.png
new file mode 100644
index 000000000000..713b010b6668
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RadioButtonTextColorShouldNotBeOverriddenByGlobalLabelStyle.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RefreshView_SetShadowWithCollectionView_VerifyShadowApplied.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RefreshView_SetShadowWithCollectionView_VerifyShadowApplied.png
index 4bb7eccd71c6..c545bb563eb5 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RefreshView_SetShadowWithCollectionView_VerifyShadowApplied.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RefreshView_SetShadowWithCollectionView_VerifyShadowApplied.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RefreshView_SetShadow_VerifyShadowApplied.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RefreshView_SetShadow_VerifyShadowApplied.png
index 23e1f14c7484..a3909dce731f 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RefreshView_SetShadow_VerifyShadowApplied.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RefreshView_SetShadow_VerifyShadowApplied.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RightToLeftFlowDirectionShouldWork.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RightToLeftFlowDirectionShouldWork.png
index 8118ea02e5ec..29909fd34686 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RightToLeftFlowDirectionShouldWork.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RightToLeftFlowDirectionShouldWork.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerVisibilityChangesToCollapsible.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerVisibilityChangesToCollapsible.png
new file mode 100644
index 000000000000..c57168244fff
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerVisibilityChangesToCollapsible.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerVisibilityChangesToExpanded.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerVisibilityChangesToExpanded.png
new file mode 100644
index 000000000000..6821a1571178
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerVisibilityChangesToExpanded.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SelectionLengthShouldUpdateWhenEntryIsFocused.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SelectionLengthShouldUpdateWhenEntryIsFocused.png
new file mode 100644
index 000000000000..b86f68c6330e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SelectionLengthShouldUpdateWhenEntryIsFocused.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShadowShouldUpdateOnCornerRadiusChange.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShadowShouldUpdateOnCornerRadiusChange.png
new file mode 100644
index 000000000000..c43ec701a9d1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShadowShouldUpdateOnCornerRadiusChange.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShadowShouldWork.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShadowShouldWork.png
new file mode 100644
index 000000000000..0367702c4402
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShadowShouldWork.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_ForegroundColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_ForegroundColor.png
new file mode 100644
index 000000000000..c345cd6e1151
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_ForegroundColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_ForegroundColorAndTitleColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_ForegroundColorAndTitleColor.png
new file mode 100644
index 000000000000..06a13382dc2a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_ForegroundColorAndTitleColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_ForegroundColorAndUnselectedColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_ForegroundColorAndUnselectedColor.png
new file mode 100644
index 000000000000..6aad595e7396
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_ForegroundColorAndUnselectedColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_IsVisibleFalse.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_IsVisibleFalse.png
new file mode 100644
index 000000000000..211520701a36
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_IsVisibleFalse.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_IsVisibleTrue.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_IsVisibleTrue.png
new file mode 100644
index 000000000000..3560812b7f75
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_IsVisibleTrue.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_PresentationModeAnimated.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_PresentationModeAnimated.png
new file mode 100644
index 000000000000..a076c268d473
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_PresentationModeAnimated.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_PresentationModeModal.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_PresentationModeModal.png
new file mode 100644
index 000000000000..6b97e12605f1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_PresentationModeModal.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_PresentationModeModalAnimated.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_PresentationModeModalAnimated.png
new file mode 100644
index 000000000000..5a714f439ce6
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_PresentationModeModalAnimated.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_PresentationModeModalNotAnimated.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_PresentationModeModalNotAnimated.png
new file mode 100644
index 000000000000..901b8e8cb299
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_PresentationModeModalNotAnimated.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_PresentationModeNotAnimated.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_PresentationModeNotAnimated.png
new file mode 100644
index 000000000000..07b0ecea52ce
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_PresentationModeNotAnimated.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_ShowTitleView.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_ShowTitleView.png
new file mode 100644
index 000000000000..d56fc46b5e3c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_ShowTitleView.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_ShowTitleViewHidden.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_ShowTitleViewHidden.png
new file mode 100644
index 000000000000..7e8df68840dd
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_ShowTitleViewHidden.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_TitleColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_TitleColor.png
new file mode 100644
index 000000000000..ac23a67b2d81
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_TitleColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_UnselectedColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_UnselectedColor.png
new file mode 100644
index 000000000000..761c2b1f0f3e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellPages_UnselectedColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellTabBarBackgroundColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellTabBarBackgroundColor.png
new file mode 100644
index 000000000000..f55a444cbc56
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShellTabBarBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShouldAppearFlyoutIconAndContentPageTitle.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShouldAppearFlyoutIconAndContentPageTitle.png
new file mode 100644
index 000000000000..25839e1bd540
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ShouldAppearFlyoutIconAndContentPageTitle.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_Reenabled_Normal_State.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_Reenabled_Normal_State.png
new file mode 100644
index 000000000000..02f78a477188
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_Reenabled_Normal_State.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_Reset_Disabled_State.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_Reset_Disabled_State.png
new file mode 100644
index 000000000000..a28c00fe715b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_Reset_Disabled_State.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_SetThumbImageSourceAndReset_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_SetThumbImageSourceAndReset_VerifyVisualState.png
new file mode 100644
index 000000000000..b59668b7abea
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Slider_SetThumbImageSourceAndReset_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/StateTrigger_SwitchOff.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/StateTrigger_SwitchOff.png
new file mode 100644
index 000000000000..1a8cce9dddd1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/StateTrigger_SwitchOff.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/StateTrigger_SwitchOn.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/StateTrigger_SwitchOn.png
new file mode 100644
index 000000000000..e455aaf878cc
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/StateTrigger_SwitchOn.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Stepper_ChangeFlowDirection_RTL_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Stepper_ChangeFlowDirection_RTL_VerifyVisualState.png
new file mode 100644
index 000000000000..9ddfd6a31c9b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/Stepper_ChangeFlowDirection_RTL_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SubPageNavigationShouldWorkAfterFirstTabBecomesInvisible.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SubPageNavigationShouldWorkAfterFirstTabBecomesInvisible.png
new file mode 100644
index 000000000000..1bc4c0153811
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SubPageNavigationShouldWorkAfterFirstTabBecomesInvisible.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SwitchThumbShouldBeVisibleWithShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SwitchThumbShouldBeVisibleWithShadow.png
new file mode 100644
index 000000000000..64778b07ab31
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SwitchThumbShouldBeVisibleWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabBarShouldBeVisibleAfterNavigatingFromModalViaGoToAsync.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabBarShouldBeVisibleAfterNavigatingFromModalViaGoToAsync.png
new file mode 100644
index 000000000000..c23bb1d35ddb
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabBarShouldBeVisibleAfterNavigatingFromModalViaGoToAsync.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabBarVisibilityAfterMultiLevelPopToRoot.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabBarVisibilityAfterMultiLevelPopToRoot.png
new file mode 100644
index 000000000000..93f501b8ecf0
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabBarVisibilityAfterMultiLevelPopToRoot.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabbedPageFlowDirection_AfterChangingBackToLeftToRight.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabbedPageFlowDirection_AfterChangingBackToLeftToRight.png
new file mode 100644
index 000000000000..8d56e886f68f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabbedPageFlowDirection_AfterChangingBackToLeftToRight.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabbedPageFlowDirection_AfterChangingToLeftToRight.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabbedPageFlowDirection_AfterChangingToLeftToRight.png
new file mode 100644
index 000000000000..ddabda9cffaf
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabbedPageFlowDirection_AfterChangingToLeftToRight.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabbedPageFlowDirection_DefaultRightToLeftLayout.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabbedPageFlowDirection_DefaultRightToLeftLayout.png
new file mode 100644
index 000000000000..3df2b7568d53
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabbedPageFlowDirection_DefaultRightToLeftLayout.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TestIssue18668.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TestIssue18668.png
new file mode 100644
index 000000000000..02d6745beb01
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TestIssue18668.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TransparentShapeShouldNotDisplayShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TransparentShapeShouldNotDisplayShadow.png
new file mode 100644
index 000000000000..eefc0b7e7012
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TransparentShapeShouldNotDisplayShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyAllIndicatorDotsShowShadowsWhenIndicatorSize.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyAllIndicatorDotsShowShadowsWhenIndicatorSize.png
new file mode 100644
index 000000000000..77311baf21e5
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyAllIndicatorDotsShowShadowsWhenIndicatorSize.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyBackgroundColorCleared.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyBackgroundColorCleared.png
new file mode 100644
index 000000000000..c81d073b6b59
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyBackgroundColorCleared.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyBackgroundColorSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyBackgroundColorSet.png
new file mode 100644
index 000000000000..aa00e8693d67
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyBackgroundColorSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyClearedTimeDoesNotShowMidnight.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyClearedTimeDoesNotShowMidnight.png
new file mode 100644
index 000000000000..56602449ce37
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyClearedTimeDoesNotShowMidnight.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png
new file mode 100644
index 000000000000..3258105f8a01
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png
new file mode 100644
index 000000000000..0dca66aabb0c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png
new file mode 100644
index 000000000000..8ecb512221bd
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithLeftToRightFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png
new file mode 100644
index 000000000000..b8842d525b78
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomSizedEmptyViewDisplaysCorrectly_WithRightToLeftFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png
new file mode 100644
index 000000000000..828e76923a38
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png
new file mode 100644
index 000000000000..6e25cd17b549
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomSizedEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyDefaultScrollToRequested.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyDefaultScrollToRequested.png
new file mode 100644
index 000000000000..a1ee3016f99e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyDefaultScrollToRequested.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png
new file mode 100644
index 000000000000..200db72a945e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEmptyViewTemplateDisplaysCorrectly_WithLeftToRightFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png
new file mode 100644
index 000000000000..1cd2358734bb
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEmptyViewTemplateDisplaysCorrectly_WithRightToLeftFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedList.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedList.png
new file mode 100644
index 000000000000..ee1a9c96fb96
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlowDirectionLTRAndMeasureAllItemsWithGroupedList.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollection.png
new file mode 100644
index 000000000000..1da7f894d656
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlowDirectionLTRAndMeasureAllItemsWithObservableCollection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedList.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedList.png
new file mode 100644
index 000000000000..8a86ea739cac
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlowDirectionRTLAndMeasureAllItemsWithGroupedList.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollection.png
new file mode 100644
index 000000000000..a4d1ae17b82c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlowDirectionRTLAndMeasureAllItemsWithObservableCollection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPageToolbarItemsRender.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPageToolbarItemsRender.png
new file mode 100644
index 000000000000..653cc33017fa
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPageToolbarItemsRender.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_BackgroundColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_BackgroundColor.png
index 11c40ffbba17..cc8bfd21e103 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_BackgroundColor.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_BackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_DetailPageIconImageSource.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_DetailPageIconImageSource.png
index 57cfff5c0109..09784e8979cb 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_DetailPageIconImageSource.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_DetailPageIconImageSource.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_DetailPageIconImageSource_FlyoutLayoutBehavior.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_DetailPageIconImageSource_FlyoutLayoutBehavior.png
index 7c8860473383..31eb21c16355 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_DetailPageIconImageSource_FlyoutLayoutBehavior.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_DetailPageIconImageSource_FlyoutLayoutBehavior.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_FlyoutLayoutBehaviorPopover.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_FlyoutLayoutBehaviorPopover.png
index ca3ebd16206d..a1386ac4fea8 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_FlyoutLayoutBehaviorPopover.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_FlyoutLayoutBehaviorPopover.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_Title.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_Title.png
index 6df4eb4e30a0..d0f2cd3b6597 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_Title.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutPage_Title.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutVerticalScrollModeDisabled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutVerticalScrollModeDisabled.png
new file mode 100644
index 000000000000..3cd358fb8753
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutVerticalScrollModeDisabled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFontImageAreCenterAlign.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFontImageAreCenterAlign.png
new file mode 100644
index 000000000000..359b2b77537c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFontImageAreCenterAlign.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyGraphicsViewWithoutGrayLine.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyGraphicsViewWithoutGrayLine.png
deleted file mode 100644
index 00b38305fcc5..000000000000
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyGraphicsViewWithoutGrayLine.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyIndicatorColorWithFlowDirection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyIndicatorColorWithFlowDirection.png
index dae98004484a..a4683efef9ed 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyIndicatorColorWithFlowDirection.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyIndicatorColorWithFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyIndicatorHideSingleIsFalse.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyIndicatorHideSingleIsFalse.png
index e6244cfea2af..c0499f05b0c0 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyIndicatorHideSingleIsFalse.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyIndicatorHideSingleIsFalse.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyIndicatorViewMaximumVisibleWithTemplate.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyIndicatorViewMaximumVisibleWithTemplate.png
new file mode 100644
index 000000000000..f96ce2855f2e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyIndicatorViewMaximumVisibleWithTemplate.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyLabelBackgroundIsClippedWithRectangleGeometry.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyLabelBackgroundIsClippedWithRectangleGeometry.png
new file mode 100644
index 000000000000..001a9ab26966
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyLabelBackgroundIsClippedWithRectangleGeometry.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyMeasureAllItemsWithGroupedList.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyMeasureAllItemsWithGroupedList.png
index 8e760faf29f4..0d3db3e8d84d 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyMeasureAllItemsWithGroupedList.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyMeasureAllItemsWithGroupedList.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyMeasureAllItemsWithObservableCollection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyMeasureAllItemsWithObservableCollection.png
index 8b4c1ad4253a..40ff21a8d06c 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyMeasureAllItemsWithObservableCollection.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyMeasureAllItemsWithObservableCollection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyNavBarStatusAtInitialLoading.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyNavBarStatusAtInitialLoading.png
new file mode 100644
index 000000000000..f1378b09a6ec
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyNavBarStatusAtInitialLoading.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyNavBarStatusAtRuntime.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyNavBarStatusAtRuntime.png
new file mode 100644
index 000000000000..41d73c893593
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyNavBarStatusAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyRadioButtonTextWithLowerTransform.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyRadioButtonTextWithLowerTransform.png
new file mode 100644
index 000000000000..8c61409e3f22
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyRadioButtonTextWithLowerTransform.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyRadioButtonTextWithUpperTransform.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyRadioButtonTextWithUpperTransform.png
new file mode 100644
index 000000000000..66e841e1ce2a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyRadioButtonTextWithUpperTransform.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByIndexWithCenterPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByIndexWithCenterPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..dd2af1f8f302
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByIndexWithCenterPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByIndexWithEndPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByIndexWithEndPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..b57e69d21580
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByIndexWithEndPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..f846dcae55a2
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByIndexWithMakeVisiblePositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByItemWithCenterPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByItemWithCenterPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..00a4a9658ad5
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByItemWithCenterPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByItemWithEndPositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByItemWithEndPositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..fd4ec70840f2
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByItemWithEndPositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByItemWithMakeVisiblePositionAndVerticalList_Carrot.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByItemWithMakeVisiblePositionAndVerticalList_Carrot.png
new file mode 100644
index 000000000000..17ebc07926d8
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollToByItemWithMakeVisiblePositionAndVerticalList_Carrot.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySearchBarBackground.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySearchBarBackground.png
new file mode 100644
index 000000000000..bdd15a7649f6
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySearchBarBackground.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySelectedIndicatorColorWithFlowDirection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySelectedIndicatorColorWithFlowDirection.png
index 972d73c934bd..60c68a5cd6bc 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySelectedIndicatorColorWithFlowDirection.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySelectedIndicatorColorWithFlowDirection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySelectedIndicatorColorWithIndicatorColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySelectedIndicatorColorWithIndicatorColor.png
index 0c8243175a28..0ffba4a1c7fc 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySelectedIndicatorColorWithIndicatorColor.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySelectedIndicatorColorWithIndicatorColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellForegroundColorIsAppliedToToolbarItems.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellForegroundColorIsAppliedToToolbarItems.png
new file mode 100644
index 000000000000..bfb2fd03887e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellForegroundColorIsAppliedToToolbarItems.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySliderColors.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySliderColors.png
new file mode 100644
index 000000000000..5aa3943a1ff3
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySliderColors.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySpanInheritsLabelCharacterSpacing.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySpanInheritsLabelCharacterSpacing.png
new file mode 100644
index 000000000000..52a6c8cd7982
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySpanInheritsLabelCharacterSpacing.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwitchControlSize.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwitchControlSize.png
new file mode 100644
index 000000000000..56b75e2e5c3b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwitchControlSize.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyTimePickerFormat.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyTimePickerFormat.png
new file mode 100644
index 000000000000..7f79c4faa343
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyTimePickerFormat.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyTimePickerIsNullOnInitialLoad.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyTimePickerIsNullOnInitialLoad.png
new file mode 100644
index 000000000000..16c9a06adfb3
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyTimePickerIsNullOnInitialLoad.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_Disable.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_Disable.png
new file mode 100644
index 000000000000..66690f582936
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_Disable.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_DisableWhilePressedAndReleased.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_DisableWhilePressedAndReleased.png
new file mode 100644
index 000000000000..2a0a97b53688
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_DisableWhilePressedAndReleased.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_InitialState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_InitialState.png
new file mode 100644
index 000000000000..267e091c62e4
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_PressedAndReleased.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_PressedAndReleased.png
new file mode 100644
index 000000000000..0352852efc5b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_PressedAndReleased.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_Reset.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_Reset.png
new file mode 100644
index 000000000000..c9ef011e08fa
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_Reset.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_ResetWhileDisabled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_ResetWhileDisabled.png
new file mode 100644
index 000000000000..241fe1d3fb6e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Button_ResetWhileDisabled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CheckBox_Checked.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CheckBox_Checked.png
new file mode 100644
index 000000000000..7192e2706675
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CheckBox_Checked.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CheckBox_Disable.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CheckBox_Disable.png
new file mode 100644
index 000000000000..3b8d37440ef2
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CheckBox_Disable.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CheckBox_DisableWhileChecked.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CheckBox_DisableWhileChecked.png
new file mode 100644
index 000000000000..72cadc0f8db2
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CheckBox_DisableWhileChecked.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CheckBox_InitialState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CheckBox_InitialState.png
new file mode 100644
index 000000000000..8d7ed306edfd
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CheckBox_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CheckBox_Reset.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CheckBox_Reset.png
new file mode 100644
index 000000000000..cd1b99f45454
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CheckBox_Reset.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_Disabled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_Disabled.png
new file mode 100644
index 000000000000..32ba35f8f728
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_Disabled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_InitialState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_InitialState.png
new file mode 100644
index 000000000000..df03cdd0d8ae
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_Normal.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_Normal.png
new file mode 100644
index 000000000000..3b76a57a0a13
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_Normal.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_Reset.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_Reset.png
new file mode 100644
index 000000000000..20222ca7bdaa
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_Reset.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_Selected.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_Selected.png
new file mode 100644
index 000000000000..659d34a362ae
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_Selected.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_Selected_Multiple.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_Selected_Multiple.png
new file mode 100644
index 000000000000..06c83cec4410
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_CollectionView_Selected_Multiple.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Completed.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Completed.png
new file mode 100644
index 000000000000..6697c3200a94
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Completed.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Disable.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Disable.png
new file mode 100644
index 000000000000..e7c300a3eba3
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Disable.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_DisableAndEnableWhileFocused.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_DisableAndEnableWhileFocused.png
new file mode 100644
index 000000000000..1f0e4e742585
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_DisableAndEnableWhileFocused.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_DisableAndEnableWhileUnFocused.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_DisableAndEnableWhileUnFocused.png
new file mode 100644
index 000000000000..756200f69464
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_DisableAndEnableWhileUnFocused.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_DisableWhileFocused.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_DisableWhileFocused.png
new file mode 100644
index 000000000000..d9e3baa9a182
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_DisableWhileFocused.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_DisableWhileReset.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_DisableWhileReset.png
new file mode 100644
index 000000000000..9234161c00d2
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_DisableWhileReset.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_DisableWhileUnFocused.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_DisableWhileUnFocused.png
new file mode 100644
index 000000000000..c37975c368a8
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_DisableWhileUnFocused.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Focus.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Focus.png
new file mode 100644
index 000000000000..b9292dc6823a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Focus.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_InitialState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_InitialState.png
new file mode 100644
index 000000000000..e1f270533ce7
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Invalid.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Invalid.png
new file mode 100644
index 000000000000..a8b85d3592d2
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Invalid.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Reset.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Reset.png
new file mode 100644
index 000000000000..4bf01ce96f59
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Reset.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Unfocus.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Unfocus.png
new file mode 100644
index 000000000000..475f4e082663
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Unfocus.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Validate.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Validate.png
new file mode 100644
index 000000000000..a37222394d7d
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Entry_Validate.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_DisableWhileNormal.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_DisableWhileNormal.png
new file mode 100644
index 000000000000..bb9af788ff90
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_DisableWhileNormal.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_DisableWhileSelected.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_DisableWhileSelected.png
new file mode 100644
index 000000000000..f6dea0a69e67
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_DisableWhileSelected.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_Disabled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_Disabled.png
new file mode 100644
index 000000000000..9e81f768a68d
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_Disabled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_InitialState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_InitialState.png
new file mode 100644
index 000000000000..ecdf0cd4b2d3
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_Reset.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_Reset.png
new file mode 100644
index 000000000000..b1c0b49d2619
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_Reset.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_Selected.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_Selected.png
new file mode 100644
index 000000000000..5a29f67808c0
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Label_Selected.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Slider_DisabledState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Slider_DisabledState.png
new file mode 100644
index 000000000000..31c8bd59aab3
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Slider_DisabledState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Slider_FocusedState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Slider_FocusedState.png
new file mode 100644
index 000000000000..73a3f2ffe2a1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Slider_FocusedState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Slider_NormalOrUnfocusedState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Slider_NormalOrUnfocusedState.png
new file mode 100644
index 000000000000..8e2879ea501a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Slider_NormalOrUnfocusedState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Slider_NormalState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Slider_NormalState.png
new file mode 100644
index 000000000000..5ed7fe64e3fd
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Slider_NormalState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Slider_ResetState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Slider_ResetState.png
new file mode 100644
index 000000000000..7fe5c53fc46f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Slider_ResetState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_DisableWhileOff.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_DisableWhileOff.png
new file mode 100644
index 000000000000..100f0051aa65
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_DisableWhileOff.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_DisableWhileOn.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_DisableWhileOn.png
new file mode 100644
index 000000000000..586766a485ab
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_DisableWhileOn.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_InitialState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_InitialState.png
new file mode 100644
index 000000000000..2435ef2a0fcd
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_InitialState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_Off.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_Off.png
new file mode 100644
index 000000000000..ad1eb0d32771
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_Off.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_On.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_On.png
new file mode 100644
index 000000000000..026d8ef81f9c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_On.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_Reset.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_Reset.png
new file mode 100644
index 000000000000..0d011da41b82
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyVSM_Switch_Reset.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_AnchorXWithAnchorY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_AnchorXWithAnchorY.png
new file mode 100644
index 000000000000..3427fb55546d
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_AnchorXWithAnchorY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_AnchorX_ScaleYWithRotation.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_AnchorX_ScaleYWithRotation.png
new file mode 100644
index 000000000000..6a7a426f147a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_AnchorX_ScaleYWithRotation.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_AnchorY_ScaleWithRotationY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_AnchorY_ScaleWithRotationY.png
new file mode 100644
index 000000000000..26a49f757fc8
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_AnchorY_ScaleWithRotationY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_AnchorY_ScaleXWithRotationX.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_AnchorY_ScaleXWithRotationX.png
new file mode 100644
index 000000000000..e684be66a1d1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_AnchorY_ScaleXWithRotationX.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_IsVisible.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_IsVisible.png
new file mode 100644
index 000000000000..88ad58d69fd9
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_IsVisible.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_Rotation.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_Rotation.png
new file mode 100644
index 000000000000..65577fa13e00
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_Rotation.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationWithRotationX.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationWithRotationX.png
new file mode 100644
index 000000000000..5c68b55b9683
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationWithRotationX.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationWithScale.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationWithScale.png
new file mode 100644
index 000000000000..d29b2b791601
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationWithScale.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationX.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationX.png
new file mode 100644
index 000000000000..13a25639f660
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationX.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationXWithRotationY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationXWithRotationY.png
new file mode 100644
index 000000000000..8fea04e19355
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationXWithRotationY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationXWithScaleX.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationXWithScaleX.png
new file mode 100644
index 000000000000..e769443834eb
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationXWithScaleX.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationY.png
new file mode 100644
index 000000000000..18a27e3df036
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationYWithScaleY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationYWithScaleY.png
new file mode 100644
index 000000000000..255b3d59b766
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_RotationYWithScaleY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_Scale.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_Scale.png
new file mode 100644
index 000000000000..55bfe6d6d75e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_Scale.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_ScaleWithAnchorXAndRotationY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_ScaleWithAnchorXAndRotationY.png
new file mode 100644
index 000000000000..9da91449c64a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_ScaleWithAnchorXAndRotationY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_ScaleX.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_ScaleX.png
new file mode 100644
index 000000000000..9f17dca70e28
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_ScaleX.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_ScaleXWithAnchorYAndRotationY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_ScaleXWithAnchorYAndRotationY.png
new file mode 100644
index 000000000000..5158ab039b9d
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_ScaleXWithAnchorYAndRotationY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_ScaleY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_ScaleY.png
new file mode 100644
index 000000000000..d105b3e6aec2
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_ScaleY.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_ScaleYWithAnchorXAndRotationY.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_ScaleYWithAnchorXAndRotationY.png
new file mode 100644
index 000000000000..c66fd6a700af
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VisualTransform_ScaleYWithAnchorXAndRotationY.png differ
diff --git a/src/Controls/tests/Xaml.UnitTests/HotReloadStaticResourceException.xaml.cs b/src/Controls/tests/Xaml.UnitTests/HotReloadStaticResourceException.xaml.cs
index 0aaf9243289e..525413e5079f 100644
--- a/src/Controls/tests/Xaml.UnitTests/HotReloadStaticResourceException.xaml.cs
+++ b/src/Controls/tests/Xaml.UnitTests/HotReloadStaticResourceException.xaml.cs
@@ -47,10 +47,14 @@ internal void MissingResourceExceptionAreHandled(XamlInflator inflator)
handled = true;
Assert.Equal(13, xpe.XmlInfo.LinePosition);
}
-
};
- var page = new HotReloadStaticResourceException(inflator);
- Assert.True(handled, "Exception was not handled");
+
+ // Now expect the exception to be thrown (after handler is invoked)
+ var exception = Assert.Throws(() => new HotReloadStaticResourceException(inflator));
+
+ // Verify handler was invoked and exception details are correct
+ Assert.True(handled, "Exception handler was not invoked");
+ Assert.Contains("StaticResource not found for key MissingResource", exception.Message, System.StringComparison.Ordinal);
}
#else
[Fact(Skip = "This test runs only in debug")]
diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui33291.xaml b/src/Controls/tests/Xaml.UnitTests/Issues/Maui33291.xaml
new file mode 100644
index 000000000000..6061d31b71c0
--- /dev/null
+++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui33291.xaml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui33291.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Maui33291.xaml.cs
new file mode 100644
index 000000000000..a84f158c3c0f
--- /dev/null
+++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui33291.xaml.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Windows.Input;
+using Microsoft.Maui.Dispatching;
+using Microsoft.Maui.UnitTests;
+using Xunit;
+
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+///
+/// Regression test for https://github.com/dotnet/maui/issues/33291
+///
+/// Scenario:
+/// A TapGestureRecognizer inside a CollectionView DataTemplate binds its Command
+/// to the *page* BindingContext using Source={x:Reference thisPage}, while the
+/// CommandParameter binds to the current DataTemplate item.
+///
+/// The DataTemplate has x:DataType="local:Maui33291Item" (the item model type).
+///
+/// Bug (regressed in .NET 10 with MauiEnableXamlCBindingWithSourceCompilation):
+/// The source generator incorrectly compiled the Command binding using
+/// x:DataType="Maui33291Item" as the source type, producing a TypedBinding
+/// that could not find BindingContext.ItemTappedCommand on the item model.
+/// The Command was null at runtime, so tapping an item did nothing.
+///
+/// Fix:
+/// When a binding has Source={x:Reference ...}, skip the compiled binding path
+/// and use the fallback string-based binding instead, just as is done for
+/// Source={RelativeSource ...} (PR #33248 / #33247).
+///
+public partial class Maui33291 : ContentPage
+{
+ public ObservableCollection Items { get; } = new()
+ {
+ new Maui33291Item { Name = "Item A" },
+ new Maui33291Item { Name = "Item B" },
+ };
+
+ public ICommand ItemTappedCommand { get; } = new Command(_ => { });
+
+ public Maui33291()
+ {
+ InitializeComponent();
+ BindingContext = this;
+ }
+
+ [Collection("Issue")]
+ public class Tests : IDisposable
+ {
+ public Tests() => DispatcherProvider.SetCurrent(new DispatcherProviderStub());
+ public void Dispose() => DispatcherProvider.SetCurrent(null);
+
+ [Theory]
+ [XamlInflatorData]
+ internal void TapGestureCommandBindsToPageCommandViaXReference(XamlInflator inflator)
+ {
+ var page = new Maui33291(inflator);
+ // The inflator constructor bypasses the user-defined constructor which sets
+ // BindingContext = this, so we set it explicitly here to simulate the real
+ // app scenario where BindingContext is assigned to the page's view-model.
+ page.BindingContext = page;
+
+ // Create a DataTemplate item — this is what CollectionView does at scroll time.
+ var itemLayout = page.TheCollectionView.ItemTemplate.CreateContent() as VerticalStackLayout;
+ Assert.NotNull(itemLayout);
+
+ var tapGesture = itemLayout.GestureRecognizers[0] as TapGestureRecognizer;
+ Assert.NotNull(tapGesture);
+
+ // Command must NOT be null — this is the key assertion.
+ // When the bug is present, the SourceGen compiles the binding against the item
+ // model type (Maui33291Item), fails to find BindingContext.ItemTappedCommand on
+ // it, and the Command ends up null.
+ Assert.NotNull(tapGesture.Command);
+
+ // The resolved Command must be the same instance exposed by the page.
+ Assert.Same(page.ItemTappedCommand, tapGesture.Command);
+ }
+ }
+}
+
+public class Maui33291Item
+{
+ public string Name { get; set; } = string.Empty;
+}
diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui33293.xaml b/src/Controls/tests/Xaml.UnitTests/Issues/Maui33293.xaml
new file mode 100644
index 000000000000..662dd94b9ec6
--- /dev/null
+++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui33293.xaml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui33293.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Maui33293.xaml.cs
new file mode 100644
index 000000000000..79e7e1e2d651
--- /dev/null
+++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui33293.xaml.cs
@@ -0,0 +1,74 @@
+using System;
+using Microsoft.Maui.ApplicationModel;
+using Microsoft.Maui.Controls.Core.UnitTests;
+using Microsoft.Maui.Controls.Xaml.Diagnostics;
+using Microsoft.Maui.Dispatching;
+using Microsoft.Maui.UnitTests;
+using Xunit;
+
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+public class Maui33293Product
+{
+ public string Name { get; set; } = string.Empty;
+ public string Price { get; set; } = string.Empty;
+ public bool IsSelected { get; set; }
+}
+
+public partial class Maui33293
+{
+ public Maui33293() => InitializeComponent();
+
+ [Collection("Issue")]
+ public class Maui33293Tests : IDisposable
+ {
+ bool bindingFailureReported = false;
+
+ public Maui33293Tests()
+ {
+ Application.SetCurrentApplication(new MockApplication());
+ DispatcherProvider.SetCurrent(new DispatcherProviderStub());
+ RuntimeFeature.EnableMauiDiagnostics = true;
+ BindingDiagnostics.BindingFailed += BindingFailed;
+ }
+
+ public void Dispose()
+ {
+ BindingDiagnostics.BindingFailed -= BindingFailed;
+ DispatcherProvider.SetCurrent(null);
+ AppInfo.SetCurrent(null);
+ }
+
+ void BindingFailed(object sender, BindingBaseErrorEventArgs args) =>
+ bindingFailureReported = true;
+
+ [Theory]
+ [XamlInflatorData]
+ internal void RadioButtonXReferenceBindingInDataTemplateShouldResolve(XamlInflator inflator)
+ {
+ if (inflator == XamlInflator.SourceGen)
+ {
+ // SourceGen is out of scope for this runtime fix
+ return;
+ }
+
+ var page = new Maui33293(inflator);
+
+ // Provide one product so the DataTemplate instantiates a RadioButton
+ var product = new Maui33293Product { Name = "Option 1", Price = "$0.99", IsSelected = true };
+ BindableLayout.SetItemsSource(page.ProductStack, new[] { product });
+
+ // Walk the visual tree: ProductStack → RadioButton → Content (HorizontalStackLayout) → Labels
+ var radioButton = (RadioButton)page.ProductStack.Children[0];
+ var hsl = (HorizontalStackLayout)radioButton.Content;
+ var priceLabel = (Label)hsl.Children[0];
+ var nameLabel = (Label)hsl.Children[1];
+
+ // Labels must show product data — binding resolved successfully through x:Reference
+ Assert.Equal("$0.99", priceLabel.Text);
+ Assert.Equal("Option 1", nameLabel.Text);
+
+ // No false-positive type-mismatch diagnostic must be raised
+ Assert.False(bindingFailureReported);
+ }
+ }
+}
diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui33417.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Maui33417.cs
new file mode 100644
index 000000000000..9efb50e2e70c
--- /dev/null
+++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui33417.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Linq;
+using Xunit;
+using static Microsoft.Maui.Controls.Xaml.UnitTests.MockSourceGenerator;
+
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public class Maui33417
+{
+ const string InvalidBindingXaml = """
+
+
+
+
+
+
+ """;
+
+ [Fact]
+ public void Maui33417_Test()
+ {
+ var result = CreateMauiCompilation()
+ .RunMauiSourceGenerator(new AdditionalXamlFile("Maui33417_InvalidBinding.xaml", InvalidBindingXaml));
+ Assert.NotEmpty(result.Diagnostics);
+ var hasTypeError = result.Diagnostics.Any(d =>
+ d.Id == "MAUIX2000" && d.GetMessage().Contains("Foo", StringComparison.Ordinal));
+ Assert.True(hasTypeError,
+ $"Should report type resolution error (MAUIX2000) for 'local:Foo'. Found diagnostics: {string.Join(", ", result.Diagnostics.Select(d => $"{d.Id}: {d.GetMessage()}"))}");
+ }
+}
diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui34021.xaml b/src/Controls/tests/Xaml.UnitTests/Issues/Maui34021.xaml
new file mode 100644
index 000000000000..95366cc326c4
--- /dev/null
+++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui34021.xaml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui34021.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Maui34021.xaml.cs
new file mode 100644
index 000000000000..65b3b617ffce
--- /dev/null
+++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui34021.xaml.cs
@@ -0,0 +1,83 @@
+using System;
+using Xunit;
+
+using static Microsoft.Maui.Controls.Xaml.UnitTests.MockSourceGenerator;
+
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public enum Maui34021MyEnum { Value1, Value2, Value3 }
+
+internal static class Maui34021MyEnumExtension
+{
+ public static string ToFriendlyString(this Maui34021MyEnum value) => value.ToString();
+}
+
+public class Maui34021DataObject : View where T : Enum
+{
+}
+
+public partial class Maui34021 : ContentPage
+{
+ public Maui34021() => InitializeComponent();
+
+ [Collection("Issue")]
+ public class Tests : IDisposable
+ {
+ public void Dispose() => Application.Current = null;
+
+ [Theory]
+ [InlineData(XamlInflator.Runtime)]
+ [InlineData(XamlInflator.XamlC)]
+ [InlineData(XamlInflator.SourceGen)]
+ internal void SourceGenResolvesEnumTypeNotExtensionClass(XamlInflator inflator)
+ {
+ if (inflator == XamlInflator.SourceGen)
+ {
+ const string xaml = """
+
+
+
+
+""";
+
+ const string csharp = """
+using System;
+using Microsoft.Maui.Controls;
+
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public enum Maui34021MyEnum { Value1, Value2, Value3 }
+
+internal static class Maui34021MyEnumExtension
+{
+ public static string ToFriendlyString(this Maui34021MyEnum value) => value.ToString();
+}
+
+public class Maui34021DataObject : View where T : Enum
+{
+}
+
+public partial class Maui34021SourceGenRepro : ContentPage
+{
+ public Maui34021SourceGenRepro() => InitializeComponent();
+}
+""";
+
+ var result = CreateMauiCompilation()
+ .WithAdditionalSource(csharp)
+ .RunMauiSourceGenerator(new AdditionalXamlFile("Issues/Maui34021SourceGenRepro.xaml", xaml, TargetFramework: "net10.0"));
+ var generated = result.GeneratedInitializeComponent();
+ Assert.DoesNotContain("Maui34021MyEnumExtension", generated, StringComparison.Ordinal);
+ Assert.Contains("Maui34021MyEnum", generated, StringComparison.Ordinal);
+ }
+ else
+ {
+ var page = new Maui34021(inflator);
+ Assert.IsType>(page.Content);
+ }
+ }
+ }
+}
diff --git a/src/Core/AndroidNative/maui/src/main/java/com/microsoft/maui/PlatformInterop.java b/src/Core/AndroidNative/maui/src/main/java/com/microsoft/maui/PlatformInterop.java
index 546d40fe1cb9..5ea39a481ae2 100644
--- a/src/Core/AndroidNative/maui/src/main/java/com/microsoft/maui/PlatformInterop.java
+++ b/src/Core/AndroidNative/maui/src/main/java/com/microsoft/maui/PlatformInterop.java
@@ -53,6 +53,8 @@
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
+import com.google.android.material.textfield.TextInputEditText;
+import com.google.android.material.textfield.TextInputLayout;
import com.microsoft.maui.glide.MauiCustomTarget;
import com.microsoft.maui.glide.MauiCustomViewTarget;
@@ -134,6 +136,12 @@ public static void setContentDescriptionForAutomationId(View view, String descri
public static View getSemanticPlatformElement(View view) {
if (view instanceof SearchView) {
view = view.findViewById(androidx.appcompat.R.id.search_src_text);
+ } else if (view instanceof TextInputLayout) {
+ EditText editText = ((TextInputLayout) view).getEditText();
+ if (editText instanceof TextInputEditText) {
+ // TextInputLayout wraps the EditText, but Appium cannot detect Entry directly during UI tests. Return the inner TextInputEditText so automation tools can properly interact with the control.
+ view = editText;
+ }
}
return view;
diff --git a/src/Core/maps/src/Handlers/Map/MapHandler.Android.cs b/src/Core/maps/src/Handlers/Map/MapHandler.Android.cs
index 81aca876c1eb..be84b030bc5f 100644
--- a/src/Core/maps/src/Handlers/Map/MapHandler.Android.cs
+++ b/src/Core/maps/src/Handlers/Map/MapHandler.Android.cs
@@ -36,6 +36,7 @@ public partial class MapHandler : ViewHandler
List? _polylines;
List? _polygons;
List? _circles;
+ List? _trackedMapElements;
public GoogleMap? Map { get; private set; }
@@ -466,6 +467,16 @@ void AddPins(IList pins)
void ClearMapElements()
{
+ // Clear MapElementId from tracked elements (not VirtualView.Elements,
+ // which returns an empty snapshot after ObservableCollection.Clear())
+ if (_trackedMapElements != null)
+ {
+ foreach (var element in _trackedMapElements)
+ element.MapElementId = null;
+
+ _trackedMapElements = null;
+ }
+
if (_polylines != null)
{
for (int i = 0; i < _polylines.Count; i++)
@@ -498,8 +509,13 @@ void AddMapElements(IList mapElements)
if (Map == null || MauiContext == null)
return;
+ _trackedMapElements = new List();
+
foreach (var element in mapElements)
{
+ if (element is IMapElement me)
+ _trackedMapElements.Add(me);
+
if (element is IGeoPathMapElement geoPath)
{
if (element is IFilledMapElement)
diff --git a/src/Core/maps/src/Platform/iOS/MauiMKMapView.cs b/src/Core/maps/src/Platform/iOS/MauiMKMapView.cs
index 9c2137dee456..ed8255cebfcb 100644
--- a/src/Core/maps/src/Platform/iOS/MauiMKMapView.cs
+++ b/src/Core/maps/src/Platform/iOS/MauiMKMapView.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections;
+using System.Collections.Generic;
using System.Linq;
using CoreLocation;
using MapKit;
@@ -15,6 +16,7 @@ public class MauiMKMapView : MKMapView
WeakReference _handlerRef;
object? _lastTouchedView;
UITapGestureRecognizer? _mapClickedGestureRecognizer;
+ List? _trackedMapElements;
public MauiMKMapView(IMapHandler handler)
{
@@ -139,6 +141,16 @@ internal void ClearMapElements()
if (elements == null)
return;
+ // Clear MapElementId from tracked elements (not Handler.VirtualView.Elements,
+ // which returns an empty snapshot after ObservableCollection.Clear())
+ if (_trackedMapElements != null)
+ {
+ foreach (var element in _trackedMapElements)
+ element.MapElementId = null;
+
+ _trackedMapElements = null;
+ }
+
foreach (IMKOverlay overlay in elements)
{
RemoveOverlay(overlay);
@@ -147,8 +159,12 @@ internal void ClearMapElements()
internal void AddElements(IList elements)
{
+ _trackedMapElements = new List();
+
foreach (IMapElement element in elements)
{
+ _trackedMapElements.Add(element);
+
IMKOverlay? overlay = null;
switch (element)
{
diff --git a/src/Core/src/Handlers/ActivityIndicator/ActivityIndicatorHandler.Android.cs b/src/Core/src/Handlers/ActivityIndicator/ActivityIndicatorHandler.Android.cs
index eb643d760148..ae9ace1cd561 100644
--- a/src/Core/src/Handlers/ActivityIndicator/ActivityIndicatorHandler.Android.cs
+++ b/src/Core/src/Handlers/ActivityIndicator/ActivityIndicatorHandler.Android.cs
@@ -1,4 +1,6 @@
-using Android.Widget;
+using System;
+using Android.Widget;
+using Microsoft.Maui.Graphics;
namespace Microsoft.Maui.Handlers
{
@@ -16,4 +18,41 @@ public static partial void MapColor(IActivityIndicatorHandler handler, IActivity
handler.PlatformView?.UpdateColor(activityIndicator);
}
}
-}
\ No newline at end of file
+
+ // TODO: material3 - make it public in .net 11
+ internal class ActivityIndicatorHandler2 : ActivityIndicatorHandler
+ {
+ protected override MaterialActivityIndicator CreatePlatformView()
+ {
+ return new MaterialActivityIndicator(Context)
+ {
+ Indeterminate = true
+ };
+ }
+
+ public override void PlatformArrange(Rect frame)
+ {
+ if (Context == null || PlatformView == null)
+ {
+ return;
+ }
+
+ // Get the child's desired size (what it measured at)
+ var desiredWidth = VirtualView?.DesiredSize.Width ?? frame.Width;
+ var desiredHeight = VirtualView?.DesiredSize.Height ?? frame.Height;
+
+ // Constrain to desired size (don't let parent stretch us)
+ var constrainedWidth = Math.Min(frame.Width, desiredWidth);
+ var constrainedHeight = Math.Min(frame.Height, desiredHeight);
+
+ // Create new frame with constrained size, centered if necessary
+ var arrangeFrame = new Rect(
+ frame.X + (frame.Width - constrainedWidth) / 2,
+ frame.Y + (frame.Height - constrainedHeight) / 2,
+ constrainedWidth,
+ constrainedHeight);
+
+ base.PlatformArrange(arrangeFrame);
+ }
+ }
+}
diff --git a/src/Core/src/Handlers/ActivityIndicator/ActivityIndicatorHandler.cs b/src/Core/src/Handlers/ActivityIndicator/ActivityIndicatorHandler.cs
index 57033052b059..10e9986a6fee 100644
--- a/src/Core/src/Handlers/ActivityIndicator/ActivityIndicatorHandler.cs
+++ b/src/Core/src/Handlers/ActivityIndicator/ActivityIndicatorHandler.cs
@@ -22,8 +22,8 @@ public partial class ActivityIndicatorHandler : IActivityIndicatorHandler
{
[nameof(IActivityIndicator.Color)] = MapColor,
[nameof(IActivityIndicator.IsRunning)] = MapIsRunning,
-#if __ANDROID__
- // Android does not have the concept of IsRunning, so we are leveraging the Visibility
+#if __ANDROID__ || __IOS__ || MACCATALYST
+ // Android/iOS do not respect both properties independently, so we handle Visibility explicitly
[nameof(IActivityIndicator.Visibility)] = MapIsRunning,
#endif
#if WINDOWS
@@ -91,4 +91,4 @@ public ActivityIndicatorHandler(IPropertyMapper? mapper, CommandMapper? commandM
public static partial void MapBackground(IActivityIndicatorHandler handler, IActivityIndicator activityIndicator);
#endif
}
-}
\ No newline at end of file
+}
diff --git a/src/Core/src/Handlers/Border/BorderHandler.Android.cs b/src/Core/src/Handlers/Border/BorderHandler.Android.cs
index 73d6c3971247..d80411153057 100644
--- a/src/Core/src/Handlers/Border/BorderHandler.Android.cs
+++ b/src/Core/src/Handlers/Border/BorderHandler.Android.cs
@@ -43,7 +43,12 @@ static partial void UpdateContent(IBorderHandler handler)
handler.PlatformView.RemoveAllViews();
if (handler.VirtualView.PresentedContent is IView view)
- handler.PlatformView.AddView(view.ToPlatform(handler.MauiContext));
+ {
+ var platformView = view.ToPlatform(handler.MauiContext);
+ // Ensure the view is detached from any existing parent before adding it
+ platformView.RemoveFromParent();
+ handler.PlatformView.AddView(platformView);
+ }
}
public static partial void MapHeight(IBorderHandler handler, IBorderView border)
diff --git a/src/Core/src/Handlers/Border/BorderHandler.Windows.cs b/src/Core/src/Handlers/Border/BorderHandler.Windows.cs
index 62d60f019438..d9a32ce0caa0 100644
--- a/src/Core/src/Handlers/Border/BorderHandler.Windows.cs
+++ b/src/Core/src/Handlers/Border/BorderHandler.Windows.cs
@@ -24,7 +24,12 @@ static partial void UpdateContent(IBorderHandler handler)
handler.PlatformView.EnsureBorderPath();
if (handler.VirtualView.PresentedContent is IView view)
+ {
+ // Detach the old handler if it exists (prevents WinUI COM exception on reuse)
+ view.Handler?.DisconnectHandler();
handler.PlatformView.Content = view.ToPlatform(handler.MauiContext);
+ }
+
}
protected override ContentPanel CreatePlatformView()
diff --git a/src/Core/src/Handlers/Border/BorderHandler.cs b/src/Core/src/Handlers/Border/BorderHandler.cs
index 5f66af4af534..b4b3ed87cc76 100644
--- a/src/Core/src/Handlers/Border/BorderHandler.cs
+++ b/src/Core/src/Handlers/Border/BorderHandler.cs
@@ -44,7 +44,9 @@ public partial class BorderHandler : IBorderHandler
{
};
+#if !WINDOWS
private Size _lastSize;
+#endif
public BorderHandler() : base(Mapper, CommandMapper)
{
@@ -65,16 +67,22 @@ public BorderHandler(IPropertyMapper? mapper, CommandMapper? commandMapper)
PlatformView IBorderHandler.PlatformView => PlatformView;
+ // On Windows, ContentPanel.ContentPanelSizeChanged already handles size-based
+ // shape updates. Calling UpdateValue(Shape) during PlatformArrange on Windows
+ // causes Path.Data to be set during the WinUI arrange pass, which invalidates
+ // layout and can cause LayoutCycleException with many nested Borders (#32406).
///
public override void PlatformArrange(Rect rect)
{
base.PlatformArrange(rect);
+#if !WINDOWS
if (_lastSize != rect.Size)
{
_lastSize = rect.Size;
UpdateValue(nameof(IBorderStroke.Shape));
}
+#endif
}
///
diff --git a/src/Core/src/Handlers/Border/BorderHandler.iOS.cs b/src/Core/src/Handlers/Border/BorderHandler.iOS.cs
index 2d86123db589..8324f96f6de5 100644
--- a/src/Core/src/Handlers/Border/BorderHandler.iOS.cs
+++ b/src/Core/src/Handlers/Border/BorderHandler.iOS.cs
@@ -48,6 +48,7 @@ static partial void UpdateContent(IBorderHandler handler)
if (handler.VirtualView.PresentedContent is IView content)
{
var platformContent = content.ToPlatform(handler.MauiContext);
+ platformContent.RemoveFromSuperview();
// If the content is a UIScrollView, we need a container to handle masks and clip shapes effectively.
if (platformContent is UIScrollView)
diff --git a/src/Core/src/Handlers/Button/ButtonHandler.Android.cs b/src/Core/src/Handlers/Button/ButtonHandler.Android.cs
index bd69e420f11f..32d77810e857 100644
--- a/src/Core/src/Handlers/Button/ButtonHandler.Android.cs
+++ b/src/Core/src/Handlers/Button/ButtonHandler.Android.cs
@@ -85,6 +85,11 @@ public static void MapStrokeThickness(IButtonHandler handler, IButton button)
public static void MapCornerRadius(IButtonHandler handler, IButton button)
{
handler.PlatformView?.UpdateCornerRadius(button);
+
+ if (button.Shadow is not null)
+ {
+ handler.UpdateValue(nameof(IButton.Shadow));
+ }
}
public static void MapText(IButtonHandler handler, IText button)
diff --git a/src/Core/src/Handlers/Button/ButtonHandler.Windows.cs b/src/Core/src/Handlers/Button/ButtonHandler.Windows.cs
index d5141a1b4edd..c0fb0f0d0fb3 100644
--- a/src/Core/src/Handlers/Button/ButtonHandler.Windows.cs
+++ b/src/Core/src/Handlers/Button/ButtonHandler.Windows.cs
@@ -59,6 +59,11 @@ public static void MapStrokeThickness(IButtonHandler handler, IButtonStroke butt
public static void MapCornerRadius(IButtonHandler handler, IButtonStroke buttonStroke)
{
handler.PlatformView?.UpdateCornerRadius(buttonStroke);
+
+ if (handler.VirtualView.Shadow is not null)
+ {
+ handler.UpdateValue(nameof(IButton.Shadow));
+ }
}
public static void MapText(IButtonHandler handler, IText button)
diff --git a/src/Core/src/Handlers/ContentView/ContentViewHandler.Android.cs b/src/Core/src/Handlers/ContentView/ContentViewHandler.Android.cs
index 88cacde6a1b1..a02de4ca3287 100644
--- a/src/Core/src/Handlers/ContentView/ContentViewHandler.Android.cs
+++ b/src/Core/src/Handlers/ContentView/ContentViewHandler.Android.cs
@@ -39,7 +39,12 @@ static void UpdateContent(IContentViewHandler handler)
handler.PlatformView.RemoveAllViews();
if (handler.VirtualView.PresentedContent is IView view)
- handler.PlatformView.AddView(view.ToPlatform(handler.MauiContext));
+ {
+ var platformView = view.ToPlatform(handler.MauiContext);
+ // Ensure the view is detached from any existing parent before adding it
+ platformView.RemoveFromParent();
+ handler.PlatformView.AddView(platformView);
+ }
}
public static partial void MapContent(IContentViewHandler handler, IContentView page)
diff --git a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs
index ee6641b10820..e62ecf46af3a 100644
--- a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs
+++ b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs
@@ -25,6 +25,8 @@ static void UpdateContent(IContentViewHandler handler)
if (handler.VirtualView.PresentedContent is IView view)
{
+ // Detach the old handler if it exists (prevents WinUI COM exception on reuse)
+ view.Handler?.DisconnectHandler();
handler.PlatformView.CachedChildren.Add(view.ToPlatform(handler.MauiContext));
}
}
diff --git a/src/Core/src/Handlers/ContentView/ContentViewHandler.iOS.cs b/src/Core/src/Handlers/ContentView/ContentViewHandler.iOS.cs
index 067a01cc9003..023a40b2befe 100644
--- a/src/Core/src/Handlers/ContentView/ContentViewHandler.iOS.cs
+++ b/src/Core/src/Handlers/ContentView/ContentViewHandler.iOS.cs
@@ -38,6 +38,7 @@ static void UpdateContent(IContentViewHandler handler)
if (handler.VirtualView.PresentedContent is IView view)
{
var platformView = view.ToPlatform(handler.MauiContext);
+ platformView.RemoveFromSuperview();
handler.PlatformView.AddSubview(platformView);
if (view.FlowDirection == FlowDirection.MatchParent)
diff --git a/src/Core/src/Handlers/DatePicker/DatePickerHandler2.Android.cs b/src/Core/src/Handlers/DatePicker/DatePickerHandler2.Android.cs
new file mode 100644
index 000000000000..c61a033e197b
--- /dev/null
+++ b/src/Core/src/Handlers/DatePicker/DatePickerHandler2.Android.cs
@@ -0,0 +1,351 @@
+using System;
+using System.Collections.Generic;
+using Android.Content;
+using AndroidX.Fragment.App;
+using Google.Android.Material.DatePicker;
+
+namespace Microsoft.Maui.Handlers;
+
+// TODO: material3 - make it public in .net 11
+internal class DatePickerHandler2 : ViewHandler
+{
+ internal MaterialDatePicker? _dialog;
+ internal bool _isUpdatingIsOpen;
+ internal MaterialDatePickerPositiveButtonClickListener? _positiveButtonClickListener;
+ internal MaterialDatePickerDismissListener? _dismissListener;
+ public static PropertyMapper Mapper =
+ new(ViewMapper)
+ {
+ [nameof(IDatePicker.Background)] = MapBackground,
+ [nameof(IDatePicker.CharacterSpacing)] = MapCharacterSpacing,
+ [nameof(IDatePicker.Date)] = MapDate,
+ [nameof(IDatePicker.Font)] = MapFont,
+ [nameof(IDatePicker.Format)] = MapFormat,
+ [nameof(IDatePicker.MaximumDate)] = MapMaximumDate,
+ [nameof(IDatePicker.MinimumDate)] = MapMinimumDate,
+ [nameof(IDatePicker.TextColor)] = MapTextColor,
+ [nameof(IDatePicker.IsOpen)] = MapIsOpen,
+
+ };
+
+ public static CommandMapper CommandMapper = new(ViewCommandMapper)
+ {
+ };
+
+ public DatePickerHandler2() : base(Mapper, CommandMapper)
+ {
+ }
+
+ protected override MauiMaterialDatePicker CreatePlatformView()
+ {
+ return new MauiMaterialDatePicker(Context);
+ }
+
+ protected override void ConnectHandler(MauiMaterialDatePicker platformView)
+ {
+ base.ConnectHandler(platformView);
+
+ _positiveButtonClickListener = new MaterialDatePickerPositiveButtonClickListener(this);
+ _dismissListener = new MaterialDatePickerDismissListener(this);
+
+ platformView.ShowPicker = ShowPickerDialog;
+ platformView.HidePicker = HidePickerDialog;
+ }
+
+ protected override void DisconnectHandler(MauiMaterialDatePicker platformView)
+ {
+ if (_dialog is not null)
+ {
+ RemoveListeners();
+
+ if (_dialog.IsAdded)
+ {
+ _dialog.DismissAllowingStateLoss();
+ }
+
+ _dialog = null;
+ }
+
+ _positiveButtonClickListener?.Dispose();
+ _positiveButtonClickListener = null;
+ _dismissListener?.Dispose();
+ _dismissListener = null;
+
+ platformView.ShowPicker = null;
+ platformView.HidePicker = null;
+
+ base.DisconnectHandler(platformView);
+ }
+
+ static void MapBackground(DatePickerHandler2 handler, IDatePicker datePicker)
+ {
+ handler.PlatformView?.UpdateBackground(datePicker);
+ }
+
+ static void MapIsOpen(DatePickerHandler2 handler, IDatePicker picker)
+ {
+ if (handler.IsConnected() && handler is DatePickerHandler2 platformHandler && !platformHandler._isUpdatingIsOpen)
+ {
+ if (picker.IsOpen)
+ {
+ platformHandler.ShowPickerDialog();
+ }
+ else
+ {
+ platformHandler.HidePickerDialog();
+ }
+ }
+ }
+
+ static void MapTextColor(DatePickerHandler2 handler, IDatePicker picker)
+ {
+ handler.PlatformView?.UpdateTextColor(picker);
+ }
+
+ // Material3 MaterialDatePicker uses immutable CalendarConstraints.
+ // Min/Max dates cannot be updated after Build().
+ // A new picker is created each time the dialog is shown, so
+ // min/max validation is handled via CalendarConstraints during creation.
+ // No additional runtime validation is required.
+ static void MapMinimumDate(DatePickerHandler2 handler, IDatePicker picker)
+ {
+ }
+
+ static void MapMaximumDate(DatePickerHandler2 handler, IDatePicker picker)
+ {
+ }
+
+ static void MapFormat(DatePickerHandler2 handler, IDatePicker picker)
+ {
+ handler.PlatformView?.UpdateFormat(picker);
+ }
+
+ static void MapFont(DatePickerHandler2 handler, IDatePicker picker)
+ {
+ var fontManager = handler.GetRequiredService();
+
+ handler.PlatformView?.UpdateFont(picker, fontManager);
+ }
+
+ static void MapDate(DatePickerHandler2 handler, IDatePicker picker)
+ {
+ handler.PlatformView?.UpdateDate(picker);
+ }
+
+ static void MapCharacterSpacing(DatePickerHandler2 handler, IDatePicker picker)
+ {
+ handler.PlatformView?.UpdateCharacterSpacing(picker);
+ }
+
+ protected virtual MaterialDatePicker? CreateDatePickerDialog(int year, int month, int day)
+ {
+ long selection = GetUtcMilliseconds(year, month, day);
+
+ var builder = MaterialDatePicker.Builder.DatePicker()
+ .SetSelection(selection)
+ .SetInputMode(MaterialDatePicker.InputModeCalendar);
+
+ var constraints = BuildCalendarConstraints();
+ if (constraints is not null)
+ {
+ builder.SetCalendarConstraints(constraints);
+ }
+
+ var dialog = builder.Build();
+
+ if (_positiveButtonClickListener is not null && _dismissListener is not null)
+ {
+ dialog.AddOnPositiveButtonClickListener(_positiveButtonClickListener);
+ dialog.AddOnDismissListener(_dismissListener);
+ }
+
+ return dialog;
+ }
+
+ CalendarConstraints? BuildCalendarConstraints()
+ {
+ var minDate = VirtualView?.MinimumDate;
+ var maxDate = VirtualView?.MaximumDate;
+
+ if (!minDate.HasValue && !maxDate.HasValue)
+ {
+ return null;
+ }
+
+ var constraintsBuilder = new CalendarConstraints.Builder();
+ var validators = new List(2);
+
+ if (minDate.HasValue)
+ {
+ long minMillis = GetUtcMilliseconds(minDate.Value.Year, minDate.Value.Month, minDate.Value.Day);
+ constraintsBuilder.SetStart(minMillis);
+ validators.Add(DateValidatorPointForward.From(minMillis));
+ }
+
+ if (maxDate.HasValue)
+ {
+ long maxMillis = GetUtcMilliseconds(maxDate.Value.Year, maxDate.Value.Month, maxDate.Value.Day);
+ constraintsBuilder.SetEnd(maxMillis);
+ validators.Add(DateValidatorPointBackward.Before(maxMillis));
+ }
+
+ if (validators.Count > 0)
+ {
+ var validator = validators.Count == 1
+ ? validators[0]
+ : CompositeDateValidator.AllOf(validators);
+ constraintsBuilder.SetValidator(validator);
+ }
+
+ return constraintsBuilder.Build();
+ }
+
+ static long GetUtcMilliseconds(int year, int month, int day)
+ {
+ var date = new DateTimeOffset(year, month, day, 0, 0, 0, TimeSpan.Zero);
+ return date.ToUnixTimeMilliseconds();
+ }
+
+ void ShowPickerDialog()
+ {
+ if (VirtualView is null)
+ {
+ return;
+ }
+
+ ShowPickerDialog(VirtualView.Date);
+ }
+
+ void ShowPickerDialog(DateTime? date)
+ {
+ // Get FragmentActivity - MaterialDatePicker requires AndroidX FragmentManager
+ if (Context?.GetActivity() is not FragmentActivity fragmentActivity ||
+ fragmentActivity.IsDestroyed ||
+ fragmentActivity.IsFinishing)
+ {
+ return;
+ }
+
+ var fragmentManager = fragmentActivity.SupportFragmentManager;
+ if (fragmentManager is null || fragmentManager.IsStateSaved)
+ {
+ return;
+ }
+
+ // Prevent duplicate dialogs
+ if (_dialog is not null && (_dialog.IsVisible || _dialog.IsAdded))
+ {
+ return;
+ }
+
+ var year = date?.Year ?? DateTime.Today.Year;
+ var month = date?.Month ?? DateTime.Today.Month;
+ var day = date?.Day ?? DateTime.Today.Day;
+
+ _dialog = CreateDatePickerDialog(year, month, day);
+ _dialog?.Show(fragmentManager, "MaterialDatePicker");
+
+ UpdateIsOpenState(true);
+ }
+
+ internal void HidePickerDialog()
+ {
+ if (_dialog is null)
+ {
+ UpdateIsOpenState(false);
+ return;
+ }
+
+ RemoveListeners();
+
+ if (_dialog.IsAdded)
+ {
+ _dialog.DismissAllowingStateLoss();
+ }
+
+ _dialog = null;
+ UpdateIsOpenState(false);
+ }
+
+ void RemoveListeners()
+ {
+ if (_dialog is not null)
+ {
+ if (_dismissListener is not null)
+ {
+ _dialog.RemoveOnDismissListener(_dismissListener);
+ }
+ if (_positiveButtonClickListener is not null)
+ {
+ _dialog.RemoveOnPositiveButtonClickListener(_positiveButtonClickListener);
+ }
+ }
+ }
+
+ internal void UpdateIsOpenState(bool isOpen)
+ {
+ if (VirtualView is null || _isUpdatingIsOpen)
+ {
+ return;
+ }
+
+ _isUpdatingIsOpen = true;
+ VirtualView.IsOpen = isOpen;
+ _isUpdatingIsOpen = false;
+ }
+}
+
+// TODO: material3 - make it public in .net 11
+internal class MaterialDatePickerPositiveButtonClickListener : Java.Lang.Object, IMaterialPickerOnPositiveButtonClickListener
+{
+ readonly WeakReference _handler;
+
+ public MaterialDatePickerPositiveButtonClickListener(DatePickerHandler2 handler)
+ {
+ _handler = new WeakReference(handler);
+ }
+
+ public void OnPositiveButtonClick(Java.Lang.Object? selection)
+ {
+ if (!_handler.TryGetTarget(out var handler) || handler.VirtualView is null)
+ {
+ return;
+ }
+
+ // Get the selected date from the dialog
+ if (selection is Java.Lang.Long selectionLong)
+ {
+ var dateTimeOffset = DateTimeOffset.FromUnixTimeMilliseconds(selectionLong.LongValue());
+ handler.VirtualView.Date = dateTimeOffset.UtcDateTime.Date;
+ }
+
+ handler.VirtualView.IsFocused = false;
+
+ // HidePickerDialog removes all listeners and dismisses properly
+ handler.HidePickerDialog();
+ }
+}
+
+// TODO: material3 - make it public in .net 11
+internal class MaterialDatePickerDismissListener : Java.Lang.Object, IDialogInterfaceOnDismissListener
+{
+ readonly WeakReference _handler;
+
+ public MaterialDatePickerDismissListener(DatePickerHandler2 handler)
+ {
+ _handler = new WeakReference(handler);
+ }
+
+ public void OnDismiss(IDialogInterface? dialog)
+ {
+ if (!_handler.TryGetTarget(out var handler))
+ {
+ return;
+ }
+
+ // Dialog was dismissed (back button, outside tap, cancel button, etc.)
+ // Clean up without trying to dismiss again
+ handler._dialog = null;
+ handler.UpdateIsOpenState(false);
+ }
+}
\ No newline at end of file
diff --git a/src/Core/src/Handlers/Editor/EditorHandler2.Android.cs b/src/Core/src/Handlers/Editor/EditorHandler2.Android.cs
index e8794a320301..0b3aff71b8c2 100644
--- a/src/Core/src/Handlers/Editor/EditorHandler2.Android.cs
+++ b/src/Core/src/Handlers/Editor/EditorHandler2.Android.cs
@@ -45,7 +45,7 @@ public EditorHandler2() : base(Mapper, CommandMapper)
protected override MauiMaterialEditText CreatePlatformView()
{
- var editText = new MauiMaterialEditText(Context)
+ var editText = new MauiMaterialEditText(MauiMaterialContextThemeWrapper.Create(Context))
{
ImeOptions = ImeAction.Done,
Gravity = GravityFlags.Top,
diff --git a/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs b/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs
index ae11b863557a..728499247c66 100644
--- a/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs
+++ b/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs
@@ -136,7 +136,14 @@ public void Connect(IEntry virtualView, MauiTextField platformView)
platformView.EditingChanged += OnEditingChanged;
platformView.EditingDidEnd += OnEditingEnded;
platformView.TextPropertySet += OnTextPropertySet;
- platformView.ShouldChangeCharacters += OnShouldChangeCharacters;
+ if (OperatingSystem.IsIOSVersionAtLeast(26) || OperatingSystem.IsMacCatalystVersionAtLeast(26))
+ {
+ platformView.ShouldChangeCharactersInRanges += ShouldChangeCharactersInRanges;
+ }
+ else
+ {
+ platformView.ShouldChangeCharacters += OnShouldChangeCharacters;
+ }
}
public void Disconnect(MauiTextField platformView)
@@ -148,7 +155,14 @@ public void Disconnect(MauiTextField platformView)
platformView.EditingChanged -= OnEditingChanged;
platformView.EditingDidEnd -= OnEditingEnded;
platformView.TextPropertySet -= OnTextPropertySet;
- platformView.ShouldChangeCharacters -= OnShouldChangeCharacters;
+ if (OperatingSystem.IsIOSVersionAtLeast(26) || OperatingSystem.IsMacCatalystVersionAtLeast(26))
+ {
+ platformView.ShouldChangeCharactersInRanges -= ShouldChangeCharactersInRanges;
+ }
+ else
+ {
+ platformView.ShouldChangeCharacters -= OnShouldChangeCharacters;
+ }
if (_set)
platformView.SelectionChanged -= OnSelectionChanged;
@@ -217,6 +231,56 @@ void OnTextPropertySet(object? sender, EventArgs e)
}
}
+ bool ShouldChangeCharactersInRanges(UITextField textField, NSValue[] ranges, string replacementString)
+ {
+ if (ranges == null || ranges.Length == 0)
+ return true;
+
+ var maxLength = VirtualView?.MaxLength ?? -1;
+ if (maxLength < 0)
+ return true;
+
+ // Handle null replacement string defensively
+ replacementString ??= string.Empty;
+
+ var currentText = textField.Text ?? string.Empty;
+
+ // Copy and sort ranges (existing code is correct)
+ var count = ranges.Length;
+ var rangeArray = new NSRange[count];
+ for (int i = 0; i < count; i++)
+ rangeArray[i] = ranges[i].RangeValue;
+
+ Array.Sort(rangeArray, (a, b) => (int)(b.Location - a.Location));
+
+ // Simulate all range replacements (existing code is correct)
+ for (int i = 0; i < count; i++)
+ {
+ var range = rangeArray[i];
+ var start = (int)range.Location;
+ var length = (int)range.Length;
+
+ if (start < 0 || length < 0 || start > currentText.Length || start + length > currentText.Length)
+ return false;
+
+ var before = start > 0 ? currentText.Substring(0, start) : string.Empty;
+ var afterIndex = start + length;
+ var after = afterIndex < currentText.Length ? currentText.Substring(afterIndex) : string.Empty;
+ currentText = before + replacementString + after;
+ }
+
+ var shouldChange = currentText.Length <= maxLength;
+
+ // Paste truncation feature (matches pre-iOS 26 behavior)
+ if (VirtualView is not null && !shouldChange && !string.IsNullOrWhiteSpace(replacementString) &&
+ replacementString.Length >= maxLength)
+ {
+ VirtualView.Text = replacementString.AsSpan(0, maxLength).ToString();
+ }
+
+ return shouldChange;
+ }
+
bool OnShouldChangeCharacters(UITextField textField, NSRange range, string replacementString) =>
VirtualView?.TextWithinMaxLength(textField.Text, range, replacementString) ?? false;
@@ -236,4 +300,4 @@ void OnSelectionChanged(object? sender, EventArgs e)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Core/src/Handlers/Entry/EntryHandler2.Android.cs b/src/Core/src/Handlers/Entry/EntryHandler2.Android.cs
new file mode 100644
index 000000000000..24051d8baa4c
--- /dev/null
+++ b/src/Core/src/Handlers/Entry/EntryHandler2.Android.cs
@@ -0,0 +1,382 @@
+using System;
+using Android.Content.Res;
+using Android.Text;
+using Android.Views;
+using Android.Views.InputMethods;
+using Google.Android.Material.TextField;
+using static Android.Views.View;
+using static Android.Widget.TextView;
+
+namespace Microsoft.Maui.Handlers;
+
+// TODO: Material3: Make it public in .NET 11
+internal class EntryHandler2 : ViewHandler
+{
+ ClearButtonClickListener? _clearButtonClickListener;
+ ColorStateList? _defaultHintTextColors;
+
+ public static PropertyMapper Mapper =
+ new(ViewMapper)
+ {
+ [nameof(IEntry.Background)] = MapBackground,
+ [nameof(IEntry.Text)] = MapText,
+ [nameof(IEntry.TextColor)] = MapTextColor,
+ [nameof(IEntry.IsPassword)] = MapIsPassword,
+ [nameof(IEntry.HorizontalTextAlignment)] = MapHorizontalTextAlignment,
+ [nameof(IEntry.VerticalTextAlignment)] = MapVerticalTextAlignment,
+ [nameof(IEntry.IsTextPredictionEnabled)] = MapIsTextPredictionEnabled,
+ [nameof(IEntry.IsSpellCheckEnabled)] = MapIsSpellCheckEnabled,
+ [nameof(IEntry.MaxLength)] = MapMaxLength,
+ [nameof(IEntry.Placeholder)] = MapPlaceholder,
+ [nameof(IEntry.PlaceholderColor)] = MapPlaceholderColor,
+ [nameof(IEntry.Font)] = MapFont,
+ [nameof(IEntry.IsReadOnly)] = MapIsReadOnly,
+ [nameof(IEntry.Keyboard)] = MapKeyboard,
+ [nameof(IEntry.ReturnType)] = MapReturnType,
+ [nameof(IEntry.CharacterSpacing)] = MapCharacterSpacing,
+ [nameof(IEntry.CursorPosition)] = MapCursorPosition,
+ [nameof(IEntry.SelectionLength)] = MapSelectionLength,
+ [nameof(IEntry.ClearButtonVisibility)] = MapClearButtonVisibility,
+ [nameof(IView.FlowDirection)] = MapFlowDirection,
+ };
+
+ public static CommandMapper CommandMapper =
+ new(ViewCommandMapper)
+ {
+ [nameof(IEntry.Focus)] = MapFocus
+ };
+
+ public EntryHandler2() : base(Mapper, CommandMapper)
+ {
+ }
+
+ protected override MauiMaterialTextInputLayout CreatePlatformView()
+ {
+ var layout = new MauiMaterialTextInputLayout(Context);
+ layout.BoxBackgroundMode = TextInputLayout.BoxBackgroundOutline;
+ layout.AddView(new MauiMaterialEditText(layout.Context!));
+
+ // Store the original default hint colors before any customization
+ _defaultHintTextColors = layout.DefaultHintTextColor;
+
+ return layout;
+ }
+
+ public override void SetVirtualView(IView view)
+ {
+ base.SetVirtualView(view);
+
+ if (PlatformView.EditText is MauiMaterialEditText _editText)
+ {
+ _editText.SelectionChanged -= OnSelectionChanged;
+ _editText.SelectionChanged += OnSelectionChanged;
+ }
+ }
+
+ protected override void ConnectHandler(MauiMaterialTextInputLayout platformView)
+ {
+ platformView.ViewAttachedToWindow += OnViewAttachedToWindow;
+ platformView.EditText?.TextChanged += OnTextChanged;
+ platformView.EditText?.FocusChange += OnFocusedChange;
+
+ _clearButtonClickListener ??= new ClearButtonClickListener(this);
+ platformView.SetEndIconOnClickListener(_clearButtonClickListener);
+
+ platformView.EditText?.EditorAction += OnEditorAction;
+ }
+
+ protected override void DisconnectHandler(MauiMaterialTextInputLayout platformView)
+ {
+ platformView.ViewAttachedToWindow -= OnViewAttachedToWindow;
+
+ if (platformView.EditText is MauiMaterialEditText _editText)
+ {
+ _editText.SelectionChanged -= OnSelectionChanged;
+ }
+
+ platformView.EditText?.EditorAction -= OnEditorAction;
+ platformView.EditText?.TextChanged -= OnTextChanged;
+ platformView.EditText?.FocusChange -= OnFocusedChange;
+ platformView.SetEndIconOnClickListener(null);
+
+ _clearButtonClickListener?.Dispose();
+ _clearButtonClickListener = null;
+
+ _defaultHintTextColors = null;
+ }
+
+ void OnViewAttachedToWindow(object? sender, ViewAttachedToWindowEventArgs e)
+ {
+ if (PlatformView is null || VirtualView is null)
+ {
+ return;
+ }
+
+ PlatformView.EditText?.UpdateReturnType(VirtualView);
+ }
+
+ public static void MapBackground(EntryHandler2 handler, IEntry entry) =>
+ handler.PlatformView?.UpdateBackground(entry);
+
+ public static void MapText(EntryHandler2 handler, IEntry entry) =>
+ handler.PlatformView.EditText?.UpdateText(entry);
+
+ public static void MapTextColor(EntryHandler2 handler, IEntry entry) =>
+ handler.PlatformView.EditText?.UpdateTextColor(entry);
+
+ public static void MapIsPassword(EntryHandler2 handler, IEntry entry)
+ {
+ handler.UpdateValue(nameof(IEntry.Text));
+ handler.PlatformView.EditText?.UpdateIsPassword(entry);
+
+ // Password toggle takes precedence over clear button
+ if (entry.IsPassword)
+ {
+ handler.PlatformView.EndIconMode = TextInputLayout.EndIconPasswordToggle;
+ }
+ else
+ {
+ // Save cursor position before EndIconMode change, which resets it
+ var cursorPosition = handler.PlatformView.EditText?.SelectionStart ?? 0;
+
+ // Re-evaluate clear button visibility
+ MapClearButtonVisibility(handler, entry);
+
+ // Material's EndIconPasswordToggle overrides the EditText's TransformationMethod.
+ // When IsPassword is turned off, explicitly reset it so text becomes visible.
+ if (handler.PlatformView.EditText is { } editText)
+ {
+ editText.TransformationMethod = null;
+ editText.SetSelection(Math.Min(cursorPosition, editText.Length()));
+ }
+ }
+ }
+
+ public static void MapHorizontalTextAlignment(EntryHandler2 handler, IEntry entry) =>
+ handler.PlatformView.EditText?.UpdateHorizontalTextAlignment(entry);
+
+ public static void MapVerticalTextAlignment(EntryHandler2 handler, IEntry entry) =>
+ handler?.PlatformView.EditText?.UpdateVerticalTextAlignment(entry);
+
+ public static void MapIsTextPredictionEnabled(EntryHandler2 handler, IEntry entry) =>
+ handler.PlatformView.EditText?.UpdateIsTextPredictionEnabled(entry);
+
+ public static void MapIsSpellCheckEnabled(EntryHandler2 handler, IEntry entry) =>
+ handler.PlatformView.EditText?.UpdateIsSpellCheckEnabled(entry);
+
+ public static void MapMaxLength(EntryHandler2 handler, IEntry entry) =>
+ handler.PlatformView.EditText?.UpdateMaxLength(entry);
+
+ public static void MapPlaceholder(EntryHandler2 handler, IEntry entry)
+ {
+ handler.PlatformView.Hint = entry.Placeholder;
+ }
+
+ public static void MapPlaceholderColor(EntryHandler2 handler, IEntry entry)
+ {
+ if (entry.PlaceholderColor is not null)
+ {
+ // Set both to ensure color applies in both focused (floating) and unfocused (inline) states
+ handler.PlatformView.DefaultHintTextColor = ColorStateList.ValueOf(entry.PlaceholderColor.ToPlatform());
+ }
+ else
+ {
+ // Reset to the original default Material Design hint colors stored during creation
+ handler.PlatformView.DefaultHintTextColor = handler._defaultHintTextColors;
+ }
+ }
+
+ public static void MapFont(EntryHandler2 handler, IEntry entry)
+ {
+ var fontManager = handler.GetRequiredService();
+ handler.PlatformView.EditText?.UpdateFont(entry, fontManager);
+
+ // TextInputLayout has its own Typeface for the hint/placeholder text,
+ // separate from the EditText's Typeface which only affects input text.
+ handler.PlatformView.Typeface = fontManager.GetTypeface(entry.Font);
+ }
+
+ public static void MapIsReadOnly(EntryHandler2 handler, IEntry entry)
+ {
+ handler.UpdateValue(nameof(IEntry.Text));
+
+ handler.PlatformView.EditText?.UpdateIsReadOnly(entry);
+ }
+
+ public static void MapKeyboard(EntryHandler2 handler, IEntry entry)
+ {
+ handler.UpdateValue(nameof(IEntry.Text));
+
+ handler.PlatformView.EditText?.UpdateKeyboard(entry);
+ }
+
+ public static void MapReturnType(EntryHandler2 handler, IEntry entry) =>
+ handler.PlatformView.EditText?.UpdateReturnType(entry);
+
+ public static void MapCharacterSpacing(EntryHandler2 handler, IEntry entry) =>
+ handler.PlatformView.EditText?.UpdateCharacterSpacing(entry);
+
+ public static void MapFlowDirection(EntryHandler2 handler, IEntry entry)
+ {
+ handler.PlatformView.UpdateFlowDirection(entry);
+ handler.PlatformView.EditText?.UpdateFlowDirection(entry);
+ }
+
+ public static void MapCursorPosition(EntryHandler2 handler, IEntry entry) =>
+ handler.PlatformView.EditText?.UpdateCursorPosition(entry);
+
+ public static void MapSelectionLength(EntryHandler2 handler, IEntry entry) =>
+ handler.PlatformView.EditText?.UpdateSelectionLength(entry);
+
+ public static void MapClearButtonVisibility(EntryHandler2 handler, IEntry entry)
+ {
+ // Password toggle takes precedence
+ if (entry.IsPassword)
+ {
+ return;
+ }
+
+ bool shouldShowClearButton = entry.ClearButtonVisibility == ClearButtonVisibility.WhileEditing &&
+ !string.IsNullOrEmpty(entry.Text) &&
+ handler.PlatformView.HasFocus;
+
+ var targetMode = shouldShowClearButton
+ ? TextInputLayout.EndIconClearText
+ : TextInputLayout.EndIconNone;
+
+ // Only update if mode actually changed to avoid unnecessary native updates
+ if (handler.PlatformView.EndIconMode != targetMode)
+ {
+ handler.PlatformView.EndIconMode = targetMode;
+ }
+ }
+
+ static void MapFocus(EntryHandler2 handler, IEntry entry, object? args)
+ {
+ if (args is FocusRequest request)
+ {
+ // Focus the EditText directly, not the TextInputLayout container
+ handler.PlatformView.EditText?.Focus(request);
+ }
+ }
+
+ void OnTextChanged(object? sender, TextChangedEventArgs e)
+ {
+ if (VirtualView is null)
+ {
+ return;
+ }
+
+ // Let the mapping know that the update is coming from changes to the platform control
+ DataFlowDirection = DataFlowDirection.FromPlatform;
+
+ VirtualView.UpdateText(e);
+
+ // Reset to the default direction
+ DataFlowDirection = DataFlowDirection.ToPlatform;
+
+ MapClearButtonVisibility(this, VirtualView);
+ }
+
+ void OnFocusedChange(object? sender, FocusChangeEventArgs e)
+ {
+ if (VirtualView is null || PlatformView is null)
+ {
+ return;
+ }
+
+ VirtualView.IsFocused = e.HasFocus;
+ MapClearButtonVisibility(this, VirtualView);
+ }
+
+ void OnEditorAction(object? sender, EditorActionEventArgs e)
+ {
+ var returnType = VirtualView?.ReturnType;
+
+ // Inside of the android implementations that map events to listeners, the default return value for "Handled" is always true
+ // This means, just by subscribing to EditorAction/KeyPressed/etc.. you change the behavior of the control
+ // So, we are setting handled to false here in order to maintain default behavior
+ bool handled = false;
+ if (returnType is not null)
+ {
+ var actionId = e.ActionId;
+ var evt = e.Event;
+ ImeAction currentInputImeFlag = PlatformView.EditText?.ImeOptions ?? ImeAction.None;
+
+ // On API 34 it looks like they fixed the issue where the actionId is ImeAction.ImeNull when using a keyboard
+ // so I'm just setting the actionId here to the current ImeOptions so the logic can all be simplified
+ if (actionId == ImeAction.ImeNull && evt?.KeyCode == Keycode.Enter)
+ {
+ actionId = currentInputImeFlag;
+ }
+
+ // keyboard path
+ if (evt?.KeyCode == Keycode.Enter && evt?.Action == KeyEventActions.Down)
+ {
+ handled = true;
+ }
+ else if (evt?.KeyCode == Keycode.Enter && evt?.Action == KeyEventActions.Up)
+ {
+ VirtualView?.Completed();
+ }
+ // InputPaneView Path
+ else if (evt?.KeyCode is null && (actionId == ImeAction.Done || actionId == currentInputImeFlag))
+ {
+ VirtualView?.Completed();
+ // In case of Search, Go, Send the EditorAction will be invoked for KeyEventActions which will cause Completed to invoke twice
+ //So for these setting handled to true
+ if (actionId == ImeAction.Search ||
+ actionId == ImeAction.Go ||
+ actionId == ImeAction.Send)
+ {
+ handled = true;
+ }
+ }
+ }
+
+ e.Handled = handled;
+ }
+
+ void OnSelectionChanged(object? sender, EventArgs e)
+ {
+ if (VirtualView is null)
+ {
+ return;
+ }
+
+ var cursorPosition = PlatformView.EditText?.GetCursorPosition() ?? 0;
+ var selectedTextLength = PlatformView.EditText?.GetSelectedTextLength() ?? 0;
+
+ if (VirtualView.CursorPosition != cursorPosition)
+ {
+ VirtualView.CursorPosition = cursorPosition;
+ }
+
+ if (VirtualView.SelectionLength != selectedTextLength)
+ {
+ VirtualView.SelectionLength = selectedTextLength;
+ }
+ }
+
+ class ClearButtonClickListener : Java.Lang.Object, IOnClickListener
+ {
+ readonly WeakReference _handlerRef;
+
+ public ClearButtonClickListener(EntryHandler2 handler)
+ {
+ _handlerRef = new WeakReference(handler);
+ }
+
+ public void OnClick(global::Android.Views.View? v)
+ {
+ if (_handlerRef.TryGetTarget(out var handler) &&
+ handler.VirtualView is not null &&
+ handler.PlatformView.EditText is not null)
+ {
+ // Clear the text — setting EditText.Text fires OnTextChanged,
+ // which propagates the value to VirtualView via UpdateText.
+ handler.PlatformView.EditText.Text = string.Empty;
+ }
+ }
+ }
+}
diff --git a/src/Core/src/Handlers/GraphicsView/GraphicsViewHandler.Android.cs b/src/Core/src/Handlers/GraphicsView/GraphicsViewHandler.Android.cs
index dc6d328e4fab..e1fd97cfde51 100644
--- a/src/Core/src/Handlers/GraphicsView/GraphicsViewHandler.Android.cs
+++ b/src/Core/src/Handlers/GraphicsView/GraphicsViewHandler.Android.cs
@@ -9,11 +9,8 @@ public partial class GraphicsViewHandler : ViewHandler VirtualView?.Background is not null || base.NeedsContainer;
+
private protected override void OnConnectHandler(FrameworkElement platformView)
{
base.OnConnectHandler(platformView);
@@ -27,10 +31,9 @@ private protected override void OnDisconnectHandler(FrameworkElement platformVie
public static void MapBackground(IGraphicsViewHandler handler, IGraphicsView graphicsView)
{
- if (graphicsView.Background is not null)
- {
- handler.PlatformView?.Invalidate();
- }
+ handler.UpdateValue(nameof(IViewHandler.ContainerView));
+ handler.ToPlatform().UpdateBackground(graphicsView);
+ handler.PlatformView?.Invalidate();
}
public static void MapDrawable(IGraphicsViewHandler handler, IGraphicsView graphicsView)
diff --git a/src/Core/src/Handlers/GraphicsView/GraphicsViewHandler.iOS.cs b/src/Core/src/Handlers/GraphicsView/GraphicsViewHandler.iOS.cs
index 0b649c3926ae..54cd8df603b6 100644
--- a/src/Core/src/Handlers/GraphicsView/GraphicsViewHandler.iOS.cs
+++ b/src/Core/src/Handlers/GraphicsView/GraphicsViewHandler.iOS.cs
@@ -12,10 +12,8 @@ protected override PlatformTouchGraphicsView CreatePlatformView()
public static void MapBackground(IGraphicsViewHandler handler, IGraphicsView graphicsView)
{
- if (graphicsView.Background is not null)
- {
- handler.PlatformView?.InvalidateDrawable();
- }
+ handler.PlatformView?.UpdateBackground(graphicsView);
+ handler.PlatformView?.InvalidateDrawable();
}
public static void MapDrawable(IGraphicsViewHandler handler, IGraphicsView graphicsView)
diff --git a/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.cs b/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.cs
index 0ed4846412ae..652164ccf494 100644
--- a/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.cs
+++ b/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.cs
@@ -83,7 +83,7 @@ public partial class HybridWebViewHandler : IHybridWebViewHandler
public static IPropertyMapper Mapper = new PropertyMapper(ViewHandler.ViewMapper)
{
-#if WINDOWS
+#if WINDOWS || IOS || MACCATALYST
[nameof(IView.FlowDirection)] = MapFlowDirection,
#endif
};
diff --git a/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.iOS.cs b/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.iOS.cs
index a3e47eef5278..148720ee070f 100644
--- a/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.iOS.cs
+++ b/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.iOS.cs
@@ -77,6 +77,15 @@ internal static void EvaluateJavaScript(IHybridWebViewHandler handler, IHybridWe
handler.PlatformView.EvaluateJavaScript(request);
}
+ internal static void MapFlowDirection(IHybridWebViewHandler handler, IHybridWebView hybridWebView)
+ {
+ var scrollView = handler.PlatformView?.ScrollView;
+ if (scrollView == null)
+ return;
+
+ scrollView.UpdateFlowDirectionForScrollView(hybridWebView);
+ }
+
public static void MapSendRawMessage(IHybridWebViewHandler handler, IHybridWebView hybridWebView, object? arg)
{
if (arg is not HybridWebViewRawMessage hybridWebViewRawMessage || handler.PlatformView is not IHybridPlatformWebView hybridPlatformWebView)
diff --git a/src/Core/src/Handlers/Image/ImageHandler.Windows.cs b/src/Core/src/Handlers/Image/ImageHandler.Windows.cs
index 9cbcc2253d8f..3654c88dc98e 100644
--- a/src/Core/src/Handlers/Image/ImageHandler.Windows.cs
+++ b/src/Core/src/Handlers/Image/ImageHandler.Windows.cs
@@ -25,6 +25,7 @@ protected override void ConnectHandler(WImage platformView)
protected override void DisconnectHandler(WImage platformView)
{
platformView.ImageOpened -= OnImageOpened;
+ platformView.Loaded -= OnImageLoaded;
base.DisconnectHandler(platformView);
SourceLoader.Reset();
@@ -139,13 +140,48 @@ public static void MapBackground(IImageHandler handler, IImage image)
/// The associated instance.
public static void MapAspect(IImageHandler handler, IImage image)
{
- handler.UpdateValue(nameof(IViewHandler.ContainerView));
- handler.PlatformView?.UpdateAspect(image);
+ if (handler.PlatformView is null)
+ {
+ return;
+ }
+
+ if (handler is ImageHandler imghandler)
+ {
+ var platformView = imghandler.PlatformView;
+ if (platformView.IsLoaded)
+ {
+ handler.UpdateValue(nameof(IViewHandler.ContainerView));
+ }
+ else
+ {
+ // Prevent stacking multiple Loaded handlers if MapAspect is called before view loads
+ platformView.Loaded -= imghandler.OnImageLoaded;
+ platformView.Loaded += imghandler.OnImageLoaded;
+ }
+ }
+
+ handler.PlatformView.UpdateAspect(image);
// Aspect changes may affect whether we cap to intrinsic size
if (handler is ImageHandler ih)
ih.UpdatePlatformMaxConstraints();
}
+ void OnImageLoaded(object sender, RoutedEventArgs e)
+ {
+ if (sender is WImage platformView)
+ {
+ platformView.Loaded -= OnImageLoaded;
+
+ // Guard against calling after handler disconnect
+ if (!this.IsConnected())
+ {
+ return;
+ }
+
+ UpdateValue(nameof(IViewHandler.ContainerView));
+ }
+ }
+
///
/// Maps the abstract property to the platform-specific implementations.
///
diff --git a/src/Core/src/Handlers/Image/ImageHandler2.Android.cs b/src/Core/src/Handlers/Image/ImageHandler2.Android.cs
new file mode 100644
index 000000000000..9c526dce6957
--- /dev/null
+++ b/src/Core/src/Handlers/Image/ImageHandler2.Android.cs
@@ -0,0 +1,20 @@
+using Google.Android.Material.ImageView;
+
+namespace Microsoft.Maui.Handlers;
+
+// TODO: make it public in .net 11
+internal class ImageHandler2 : ImageHandler
+{
+ protected override ShapeableImageView CreatePlatformView()
+ {
+ var imageView = new ShapeableImageView(MauiMaterialContextThemeWrapper.Create(Context));
+
+ // Enable view bounds adjustment on measure.
+ // This allows the ImageView's OnMeasure method to account for the image's intrinsic
+ // aspect ratio during measurement, which gives us more useful values during constrained
+ // measurement passes.
+ imageView.SetAdjustViewBounds(true);
+
+ return imageView;
+ }
+}
\ No newline at end of file
diff --git a/src/Core/src/Handlers/ImageButton/ImageButtonHandler.Android.cs b/src/Core/src/Handlers/ImageButton/ImageButtonHandler.Android.cs
index b71e98aa8706..3466388f126d 100644
--- a/src/Core/src/Handlers/ImageButton/ImageButtonHandler.Android.cs
+++ b/src/Core/src/Handlers/ImageButton/ImageButtonHandler.Android.cs
@@ -9,7 +9,11 @@ public partial class ImageButtonHandler : ViewHandler Mapper = new PropertyMapper(ViewHandler.ViewMapper)
{
[nameof(IProgress.Progress)] = MapProgress,
- [nameof(IProgress.ProgressColor)] = MapProgressColor
+ [nameof(IProgress.ProgressColor)] = MapProgressColor,
+#if __IOS__ || MACCATALYST
+ [nameof(IView.FlowDirection)] = MapFlowDirection,
+#endif
};
public static CommandMapper CommandMapper = new(ViewCommandMapper)
diff --git a/src/Core/src/Handlers/ProgressBar/ProgressBarHandler.iOS.cs b/src/Core/src/Handlers/ProgressBar/ProgressBarHandler.iOS.cs
index fc30d91f9958..4f15afc6ffde 100644
--- a/src/Core/src/Handlers/ProgressBar/ProgressBarHandler.iOS.cs
+++ b/src/Core/src/Handlers/ProgressBar/ProgressBarHandler.iOS.cs
@@ -1,4 +1,5 @@
-using ObjCRuntime;
+using System;
+using ObjCRuntime;
using UIKit;
namespace Microsoft.Maui.Handlers
@@ -19,5 +20,53 @@ public static void MapProgressColor(IProgressBarHandler handler, IProgress progr
{
handler.PlatformView?.UpdateProgressColor(progress);
}
+
+ internal static void MapFlowDirection(IProgressBarHandler handler, IProgress progress)
+ {
+ var progressbar = handler.PlatformView;
+ if (progressbar is null)
+ {
+ return;
+ }
+
+ UISemanticContentAttribute contentAttribute = GetSemanticContentAttribute(progress);
+ progressbar.SemanticContentAttribute = contentAttribute;
+
+ // On iOS 26, UIProgressView no longer applies the SemanticContentAttribute to its internal subviews, so update
+ // each subview explicitly to keep flow direction consistent.
+ if (OperatingSystem.IsIOSVersionAtLeast(26) || OperatingSystem.IsMacCatalystVersionAtLeast(26))
+ {
+ foreach (var subview in progressbar.Subviews)
+ {
+ subview.SemanticContentAttribute = contentAttribute;
+ }
+ }
+ }
+
+ static UISemanticContentAttribute GetSemanticContentAttribute(IProgress progress)
+ {
+ return progress.FlowDirection switch
+ {
+ FlowDirection.RightToLeft => UISemanticContentAttribute.ForceRightToLeft,
+ FlowDirection.LeftToRight => UISemanticContentAttribute.ForceLeftToRight,
+ _ => GetParentSemanticContentAttribute(progress)
+ };
+ }
+
+ static UISemanticContentAttribute GetParentSemanticContentAttribute(IProgress progress)
+ {
+ var parentView = (progress as IView)?.Parent as IView;
+ if (parentView is null)
+ {
+ return UISemanticContentAttribute.Unspecified;
+ }
+
+ return parentView.FlowDirection switch
+ {
+ FlowDirection.LeftToRight => UISemanticContentAttribute.ForceLeftToRight,
+ FlowDirection.RightToLeft => UISemanticContentAttribute.ForceRightToLeft,
+ _ => UISemanticContentAttribute.Unspecified,
+ };
+ }
}
}
\ No newline at end of file
diff --git a/src/Core/src/Handlers/ProgressBar/ProgressBarHandler2.Android.cs b/src/Core/src/Handlers/ProgressBar/ProgressBarHandler2.Android.cs
new file mode 100644
index 000000000000..5cbe7e7f7c5f
--- /dev/null
+++ b/src/Core/src/Handlers/ProgressBar/ProgressBarHandler2.Android.cs
@@ -0,0 +1,16 @@
+using Google.Android.Material.ProgressIndicator;
+
+namespace Microsoft.Maui.Handlers;
+
+// TODO: Material3 - make it public in .net 11
+internal class ProgressBarHandler2 : ProgressBarHandler
+{
+ protected override LinearProgressIndicator CreatePlatformView()
+ {
+ return new LinearProgressIndicator(MauiMaterialContextThemeWrapper.Create(Context))
+ {
+ Indeterminate = false,
+ Max = ProgressBarExtensions.Maximum
+ };
+ }
+}
diff --git a/src/Core/src/Handlers/RadioButton/RadioButtonHandler.Android.cs b/src/Core/src/Handlers/RadioButton/RadioButtonHandler.Android.cs
index 77c3e7a059f7..4bef4f881775 100644
--- a/src/Core/src/Handlers/RadioButton/RadioButtonHandler.Android.cs
+++ b/src/Core/src/Handlers/RadioButton/RadioButtonHandler.Android.cs
@@ -6,7 +6,7 @@ namespace Microsoft.Maui.Handlers
{
public partial class RadioButtonHandler : ViewHandler
{
- static AppCompatRadioButton? GetPlatformRadioButton(IRadioButtonHandler handler) => handler.PlatformView as AppCompatRadioButton;
+ internal static AppCompatRadioButton? GetPlatformRadioButton(IRadioButtonHandler handler) => handler.PlatformView as AppCompatRadioButton;
public override void PlatformArrange(Graphics.Rect frame)
{
diff --git a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cs b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cs
index 6b836a33ac5a..f10875ee339f 100644
--- a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cs
+++ b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cs
@@ -215,6 +215,8 @@ static void UpdateInsetView(IScrollView scrollView, IScrollViewHandler handler,
if (currentPaddingLayer is not null)
{
+ UpdateClipForShadow(currentPaddingLayer, handler.PlatformView, scrollView.PresentedContent);
+
// Only update if content has changed or is missing
if (currentPaddingLayer.ChildCount == 0 || currentPaddingLayer.GetChildAt(0) != nativeContent)
{
@@ -241,11 +243,21 @@ static void InsertInsetView(IScrollViewHandler handler, IScrollView scrollView,
Tag = InsetPanelTag
};
+ UpdateClipForShadow(paddingShim, handler.PlatformView, scrollView.PresentedContent);
+
handler.PlatformView.RemoveAllViews();
paddingShim.AddView(nativeContent);
handler.PlatformView.SetContent(paddingShim);
}
+ static void UpdateClipForShadow(ContentViewGroup paddingShim, MauiScrollView scrollView, IView? content)
+ {
+ bool hasShadow = content?.Shadow is not null;
+ paddingShim.SetClipChildren(!hasShadow);
+ paddingShim.SetClipToPadding(!hasShadow);
+ scrollView.SetClipChildren(!hasShadow);
+ }
+
Size ICrossPlatformLayout.CrossPlatformMeasure(double widthConstraint, double heightConstraint)
{
if (VirtualView is not { } scrollView)
diff --git a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Windows.cs b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Windows.cs
index 8a3214733212..61d173bf8e9e 100644
--- a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Windows.cs
+++ b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Windows.cs
@@ -78,7 +78,16 @@ public static void MapRequestScrollTo(IScrollViewHandler handler, IScrollView sc
{
if (args is ScrollToRequest request)
{
- handler.PlatformView.ChangeView(request.HorizontalOffset, request.VerticalOffset, null, request.Instant);
+ var targetHorizontalOffset = Math.Clamp(request.HorizontalOffset, 0, handler.PlatformView.ScrollableWidth);
+ var targetVerticalOffset = Math.Clamp(request.VerticalOffset, 0, handler.PlatformView.ScrollableHeight);
+
+ if (targetVerticalOffset == handler.PlatformView.VerticalOffset && targetHorizontalOffset == handler.PlatformView.HorizontalOffset)
+ {
+ handler.VirtualView.ScrollFinished();
+ return;
+ }
+
+ handler.PlatformView.ChangeView(targetHorizontalOffset, targetVerticalOffset, null, request.Instant);
}
}
diff --git a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs
index 56baabdf0fc5..290576e299c9 100644
--- a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs
+++ b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs
@@ -112,9 +112,15 @@ public static void MapRequestScrollTo(IScrollViewHandler handler, IScrollView sc
var availableScrollWidth = Math.Max(uiScrollView.ContentSize.Width - uiScrollView.Frame.Width, 0);
var minScrollHorizontal = Math.Clamp(request.HorizontalOffset, 0, availableScrollWidth);
var minScrollVertical = Math.Clamp(request.VerticalOffset, 0, availableScrollHeight);
- uiScrollView.SetContentOffset(new CGPoint(minScrollHorizontal, minScrollVertical), !request.Instant);
+
+ bool alreadyAtTarget = uiScrollView.ContentOffset.Y == minScrollVertical && uiScrollView.ContentOffset.X == minScrollHorizontal;
+
+ if (!alreadyAtTarget)
+ {
+ uiScrollView.SetContentOffset(new CGPoint(minScrollHorizontal, minScrollVertical), !request.Instant);
+ }
- if (request.Instant)
+ if (request.Instant || alreadyAtTarget)
{
scrollView.ScrollFinished();
}
diff --git a/src/Core/src/Handlers/SearchBar/SearchBarHandler.cs b/src/Core/src/Handlers/SearchBar/SearchBarHandler.cs
index 2fc4011bb310..f852f89f67d2 100644
--- a/src/Core/src/Handlers/SearchBar/SearchBarHandler.cs
+++ b/src/Core/src/Handlers/SearchBar/SearchBarHandler.cs
@@ -17,7 +17,7 @@ public partial class SearchBarHandler : ISearchBarHandler
{
public static IPropertyMapper Mapper = new PropertyMapper(ViewHandler.ViewMapper)
{
-#if __IOS__
+#if __IOS__ || ANDROID
[nameof(ISearchBar.IsEnabled)] = MapIsEnabled,
#endif
[nameof(ISearchBar.Background)] = MapBackground,
diff --git a/src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs b/src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs
index 3b0f1416f7c3..3594483ca8c7 100644
--- a/src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs
+++ b/src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs
@@ -41,7 +41,10 @@ public override Size GetDesiredSize(double widthConstraint, double heightConstra
if (double.IsInfinity(widthConstraint) || double.IsInfinity(heightConstraint))
{
PlatformView.SizeToFit();
- return new Size(PlatformView.Frame.Width, PlatformView.Frame.Height);
+
+ double constrainedWidth = ViewHandlerExtensions.ResolveConstraints(PlatformView.Frame.Width, VirtualView.Width, VirtualView.MinimumWidth, VirtualView.MaximumWidth);
+ double constrainedHeight = ViewHandlerExtensions.ResolveConstraints(PlatformView.Frame.Height, VirtualView.Height, VirtualView.MinimumHeight, VirtualView.MaximumHeight);
+ return new Size(constrainedWidth, constrainedHeight);
}
return base.GetDesiredSize(widthConstraint, heightConstraint);
diff --git a/src/Core/src/Handlers/SearchBar/SearchBarHandler2.Android.cs b/src/Core/src/Handlers/SearchBar/SearchBarHandler2.Android.cs
new file mode 100644
index 000000000000..ec1ae3644122
--- /dev/null
+++ b/src/Core/src/Handlers/SearchBar/SearchBarHandler2.Android.cs
@@ -0,0 +1,317 @@
+using System;
+using Android.Content.Res;
+using Android.Text;
+using Android.Views;
+using Android.Views.InputMethods;
+using Android.Widget;
+using Google.Android.Material.TextField;
+using AView = Android.Views.View;
+
+namespace Microsoft.Maui.Handlers;
+
+// TODO: material3 - make it public in .net 11
+internal class SearchBarHandler2 : ViewHandler
+{
+ public static PropertyMapper Mapper =
+ new(ViewMapper)
+ {
+ [nameof(ISearchBar.Background)] = MapBackground,
+ [nameof(ISearchBar.CharacterSpacing)] = MapCharacterSpacing,
+ [nameof(ISearchBar.Font)] = MapFont,
+ [nameof(ITextAlignment.HorizontalTextAlignment)] = MapHorizontalTextAlignment,
+ [nameof(ITextAlignment.VerticalTextAlignment)] = MapVerticalTextAlignment,
+ [nameof(ISearchBar.IsReadOnly)] = MapIsReadOnly,
+ [nameof(ISearchBar.IsTextPredictionEnabled)] = MapIsTextPredictionEnabled,
+ [nameof(ISearchBar.IsSpellCheckEnabled)] = MapIsSpellCheckEnabled,
+ [nameof(ISearchBar.MaxLength)] = MapMaxLength,
+ [nameof(ISearchBar.Placeholder)] = MapPlaceholder,
+ [nameof(ISearchBar.PlaceholderColor)] = MapPlaceholderColor,
+ [nameof(ISearchBar.Text)] = MapText,
+ [nameof(ISearchBar.TextColor)] = MapTextColor,
+ [nameof(ISearchBar.CancelButtonColor)] = MapCancelButtonColor,
+ [nameof(ISearchBar.SearchIconColor)] = MapSearchIconColor,
+ [nameof(ISearchBar.Keyboard)] = MapKeyboard,
+ [nameof(ISearchBar.ReturnType)] = MapReturnType,
+ [nameof(ISearchBar.FlowDirection)] = MapFlowDirection,
+ [nameof(ISearchBar.IsEnabled)] = MapIsEnabled,
+ [nameof(ISearchBar.CursorPosition)] = MapCursorPosition,
+ [nameof(ISearchBar.SelectionLength)] = MapSelectionLength,
+ };
+
+ public static CommandMapper CommandMapper =
+ new(ViewCommandMapper)
+ {
+ [nameof(ISearchBar.Focus)] = MapFocus
+ };
+
+ public EditText? QueryEditor => PlatformView?.EditText;
+
+ public SearchBarHandler2() : base(Mapper, CommandMapper)
+ {
+ }
+
+ protected override MauiMaterialSearchBarTextInputLayout CreatePlatformView()
+ {
+ var layout = new MauiMaterialSearchBarTextInputLayout(Context);
+ layout.BoxBackgroundMode = TextInputLayout.BoxBackgroundFilled;
+ layout.AddView(new MauiMaterialSearchBarTextInputEditText(layout.Context!));
+ return layout;
+ }
+
+ protected override void ConnectHandler(MauiMaterialSearchBarTextInputLayout platformView)
+ {
+ base.ConnectHandler(platformView);
+ if (platformView.EditText is not null)
+ {
+ platformView.EditText.TextChanged += OnTextChanged;
+ platformView.EditText.EditorAction += OnEditorAction;
+ platformView.EditText.FocusChange += OnFocusChange;
+ if (platformView.EditText is MauiMaterialSearchBarTextInputEditText editText)
+ {
+ editText.SelectionChanged += OnSelectionChanged;
+ }
+ }
+ }
+
+ protected override void DisconnectHandler(MauiMaterialSearchBarTextInputLayout platformView)
+ {
+ if (platformView.EditText is not null)
+ {
+ platformView.EditText.TextChanged -= OnTextChanged;
+ platformView.EditText.EditorAction -= OnEditorAction;
+ platformView.EditText.FocusChange -= OnFocusChange;
+
+ if (platformView.EditText is MauiMaterialSearchBarTextInputEditText editText)
+ {
+ editText.SelectionChanged -= OnSelectionChanged;
+ }
+ }
+
+ platformView.SetEndIconOnClickListener(null);
+
+ base.DisconnectHandler(platformView);
+ }
+
+ public static void MapBackground(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.UpdateBackground(searchBar);
+ }
+
+ public static void MapCharacterSpacing(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.EditText?.UpdateCharacterSpacing(searchBar);
+ }
+
+ public static void MapFont(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ var fontManager = handler.GetRequiredService();
+ handler.PlatformView?.EditText?.UpdateFont(searchBar, fontManager);
+ }
+
+ public static void MapHorizontalTextAlignment(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.EditText?.UpdateHorizontalTextAlignment(searchBar);
+ }
+
+ public static void MapVerticalTextAlignment(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.EditText?.UpdateVerticalTextAlignment(searchBar);
+ }
+
+ public static void MapIsReadOnly(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.EditText?.UpdateIsReadOnly(searchBar);
+ }
+
+ public static void MapIsTextPredictionEnabled(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.EditText?.UpdateIsTextPredictionEnabled(searchBar);
+ }
+
+ public static void MapIsSpellCheckEnabled(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.EditText?.UpdateIsSpellCheckEnabled(searchBar);
+ }
+
+ public static void MapMaxLength(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.EditText?.UpdateMaxLength(searchBar.MaxLength);
+ }
+
+ public static void MapPlaceholder(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.EditText?.UpdatePlaceholder(searchBar);
+ }
+
+ public static void MapPlaceholderColor(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.EditText?.UpdatePlaceholderColor(searchBar);
+ }
+
+ public static void MapText(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.EditText?.UpdateText(searchBar);
+ handler.PlatformView?.UpdateCloseButtonVisibility(!string.IsNullOrEmpty(searchBar.Text));
+ }
+
+ public static void MapTextColor(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.EditText?.UpdateTextColor(searchBar.TextColor);
+ }
+
+ public static void MapCancelButtonColor(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.UpdateCancelButtonColor(searchBar);
+ }
+
+ public static void MapSearchIconColor(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.UpdateSearchIconColor(searchBar);
+ }
+
+ public static void MapKeyboard(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.UpdateValue(nameof(ISearchBar.Text));
+ handler.PlatformView?.EditText?.SetInputType(searchBar);
+ }
+
+ public static void MapReturnType(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.EditText?.UpdateReturnType(searchBar);
+ }
+
+ public static void MapFlowDirection(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ if (searchBar.FlowDirection == FlowDirection.MatchParent && searchBar.Parent is not null && searchBar.Parent is IView parentView)
+ {
+ // When FlowDirection is MatchParent, respect the parent's FlowDirection
+ if (handler.PlatformView is AView platformView)
+ {
+ Microsoft.Maui.Platform.ViewExtensions.UpdateFlowDirection(platformView, parentView);
+ }
+
+ if (handler.PlatformView?.EditText is TextView textView)
+ {
+ Microsoft.Maui.Platform.TextViewExtensions.UpdateFlowDirection(textView, parentView);
+ }
+ }
+ else
+ {
+ // Otherwise, use the SearchBar's own FlowDirection
+ handler.PlatformView?.UpdateFlowDirection(searchBar);
+ handler.PlatformView?.EditText?.UpdateFlowDirection(searchBar);
+ }
+ }
+
+ public static void MapIsEnabled(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.UpdateIsEnabled(searchBar);
+ }
+
+ public static void MapFocus(SearchBarHandler2 handler, ISearchBar searchBar, object? args)
+ {
+ if (args is FocusRequest request)
+ {
+ handler.PlatformView?.EditText?.Focus(request);
+ }
+ }
+
+ public static void MapCursorPosition(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.EditText?.UpdateCursorPosition(searchBar);
+ }
+
+ public static void MapSelectionLength(SearchBarHandler2 handler, ISearchBar searchBar)
+ {
+ handler.PlatformView?.EditText?.UpdateSelectionLength(searchBar);
+ }
+
+ void OnTextChanged(object? sender, TextChangedEventArgs e)
+ {
+ if (VirtualView is null || sender is not EditText editText)
+ {
+ return;
+ }
+
+ var newText = editText.Text ?? string.Empty;
+ VirtualView.UpdateText(newText);
+
+ // Update clear button visibility based on text content
+ PlatformView?.UpdateCloseButtonVisibility(!string.IsNullOrEmpty(newText));
+ }
+
+ void OnEditorAction(object? sender, TextView.EditorActionEventArgs e)
+ {
+ var returnType = VirtualView?.ReturnType;
+
+ // Inside android implementations that map events to listeners, the default return value for "Handled" is always true.
+ // Setting handled to false here maintains default behavior.
+ bool handled = false;
+
+ if (returnType is not null && PlatformView?.EditText is not null)
+ {
+ var actionId = e.ActionId;
+ var evt = e.Event;
+ ImeAction currentInputImeFlag = PlatformView.EditText.ImeOptions;
+
+ // On API 34 the issue where actionId is ImeAction.ImeNull when using a hardware keyboard was fixed.
+ // Normalize it here so the rest of the logic is consistent across API levels.
+ if (actionId == ImeAction.ImeNull && evt?.KeyCode == Keycode.Enter)
+ {
+ actionId = currentInputImeFlag;
+ }
+
+ // Hardware keyboard path: consume Down event, fire on Up event.
+ if (evt?.KeyCode == Keycode.Enter && evt?.Action == KeyEventActions.Down)
+ {
+ handled = true;
+ }
+ else if (evt?.KeyCode == Keycode.Enter && evt?.Action == KeyEventActions.Up)
+ {
+ VirtualView?.SearchButtonPressed();
+ }
+ // Input pane path: fire when the action matches either the default Search action
+ // or the ImeAction configured via ReturnType (Go, Send, Done, etc.).
+ else if (evt?.KeyCode is null && (actionId == ImeAction.Search || actionId == currentInputImeFlag))
+ {
+ VirtualView?.SearchButtonPressed();
+ // For Search, Go, Send the EditorAction is also invoked for KeyEventActions,
+ // which would cause SearchButtonPressed to fire twice — so consume the event.
+ if (actionId == ImeAction.Search ||
+ actionId == ImeAction.Go ||
+ actionId == ImeAction.Send)
+ {
+ handled = true;
+ }
+ }
+ }
+
+ e.Handled = handled;
+ }
+
+ void OnSelectionChanged(object? sender, EventArgs e)
+ {
+ if (PlatformView.EditText is null || VirtualView is null)
+ {
+ return;
+ }
+
+ var cursorPosition = PlatformView.EditText.GetCursorPosition();
+ var selectionLength = PlatformView.EditText.GetSelectedTextLength();
+
+ if (VirtualView.CursorPosition != cursorPosition)
+ {
+ VirtualView.CursorPosition = cursorPosition;
+ }
+
+ if (VirtualView.SelectionLength != selectionLength)
+ {
+ VirtualView.SelectionLength = selectionLength;
+ }
+ }
+
+ void OnFocusChange(object? sender, View.FocusChangeEventArgs e)
+ {
+ VirtualView?.IsFocused = e.HasFocus;
+ }
+}
diff --git a/src/Core/src/Handlers/Slider/SliderHandler.Windows.cs b/src/Core/src/Handlers/Slider/SliderHandler.Windows.cs
index ba2111a38d16..d82162a8972c 100644
--- a/src/Core/src/Handlers/Slider/SliderHandler.Windows.cs
+++ b/src/Core/src/Handlers/Slider/SliderHandler.Windows.cs
@@ -91,6 +91,14 @@ public static void MapThumbImageSource(ISliderHandler handler, ISlider slider)
}
}
+ internal static void MapBackgroundColor(ISliderHandler handler, ISlider slider)
+ {
+ if (handler.PlatformView is MauiSlider mauiSlider)
+ {
+ mauiSlider.UpdateBackgroundColor(slider);
+ }
+ }
+
void OnPlatformViewLoaded(object sender, RoutedEventArgs e)
{
var platformView = sender as Slider;
diff --git a/src/Core/src/Handlers/Slider/SliderHandler.cs b/src/Core/src/Handlers/Slider/SliderHandler.cs
index 096dd3f95f34..e9465b5232f5 100644
--- a/src/Core/src/Handlers/Slider/SliderHandler.cs
+++ b/src/Core/src/Handlers/Slider/SliderHandler.cs
@@ -24,6 +24,9 @@ public partial class SliderHandler : ISliderHandler
[nameof(ISlider.ThumbColor)] = MapThumbColor,
[nameof(ISlider.ThumbImageSource)] = MapThumbImageSource,
[nameof(ISlider.Value)] = MapValue,
+#if WINDOWS
+ [nameof(ISlider.Background)] = MapBackgroundColor
+#endif
};
public static CommandMapper CommandMapper = new(ViewCommandMapper)
diff --git a/src/Core/src/Handlers/Slider/SliderHandler2.Android.cs b/src/Core/src/Handlers/Slider/SliderHandler2.Android.cs
new file mode 100644
index 000000000000..254c53f31dbe
--- /dev/null
+++ b/src/Core/src/Handlers/Slider/SliderHandler2.Android.cs
@@ -0,0 +1,141 @@
+using Android.Views;
+using Google.Android.Material.Slider;
+
+namespace Microsoft.Maui.Handlers;
+
+// TODO: Material3: Make it public in .NET 11
+internal class SliderHandler2 : ViewHandler
+{
+ public static PropertyMapper Mapper =
+ new(ViewMapper)
+ {
+ [nameof(ISlider.Value)] = MapValue,
+ [nameof(ISlider.Minimum)] = MapMinimum,
+ [nameof(ISlider.Maximum)] = MapMaximum,
+ [nameof(ISlider.MinimumTrackColor)] = MapMinimumTrackColor,
+ [nameof(ISlider.MaximumTrackColor)] = MapMaximumTrackColor,
+ [nameof(ISlider.ThumbColor)] = MapThumbColor,
+ [nameof(ISlider.ThumbImageSource)] = MapThumbImageSource,
+ };
+
+ public static CommandMapper CommandMapper =
+ new(ViewCommandMapper);
+
+ public SliderHandler2() : base(Mapper, CommandMapper)
+ {
+ }
+
+ protected override Slider CreatePlatformView()
+ {
+ return new Slider(MauiMaterialContextThemeWrapper.Create(Context))
+ {
+ DuplicateParentStateEnabled = false,
+ };
+ }
+
+ protected override void ConnectHandler(Slider platformView)
+ {
+ // TODO: Material3: Add listeners when https://github.com/dotnet/android-libraries/issues/230 is resolved
+ // Using Touch event as a workaround for missing addOnChangeListener binding
+ // See: https://github.com/dotnet/android-libraries/issues/230#issuecomment-891341936
+ platformView.Touch += Slider_Touch;
+ }
+
+ void Slider_Touch(object? sender, View.TouchEventArgs e)
+ {
+ if (sender is not Slider slider)
+ {
+ return;
+ }
+
+ switch (e.Event?.Action)
+ {
+ case MotionEventActions.Down:
+ {
+ OnStartTrackingTouch(slider);
+ break;
+ }
+ case MotionEventActions.Move:
+ {
+ OnValueChanged(slider, slider.Value);
+ break;
+ }
+ case MotionEventActions.Up:
+ {
+ OnValueChanged(slider, slider.Value);
+ OnStopTrackingTouch(slider);
+ break;
+ }
+ case MotionEventActions.Cancel:
+ {
+ OnStopTrackingTouch(slider);
+ break;
+ }
+ }
+ // Pass through to Material3 Slider so it can update its own visual state
+ e.Handled = false;
+ }
+
+ protected override void DisconnectHandler(Slider platformView)
+ {
+ // TODO: Material3: Cleanup listeners when implemented
+ platformView.Touch -= Slider_Touch;
+ }
+
+ public static void MapValue(SliderHandler2 handler, ISlider slider)
+ {
+ handler.PlatformView?.UpdateValue(slider);
+ }
+
+ public static void MapMinimum(SliderHandler2 handler, ISlider slider)
+ {
+ handler.PlatformView?.UpdateMinimum(slider);
+ }
+
+ public static void MapMaximum(SliderHandler2 handler, ISlider slider)
+ {
+ handler.PlatformView?.UpdateMaximum(slider);
+ }
+
+ public static void MapMinimumTrackColor(SliderHandler2 handler, ISlider slider)
+ {
+ handler.PlatformView?.UpdateMinimumTrackColor(slider);
+ }
+
+ public static void MapMaximumTrackColor(SliderHandler2 handler, ISlider slider)
+ {
+ handler.PlatformView?.UpdateMaximumTrackColor(slider);
+ }
+
+ public static void MapThumbColor(SliderHandler2 handler, ISlider slider)
+ {
+ handler.PlatformView?.UpdateThumbColor(slider);
+ }
+
+ public static void MapThumbImageSource(SliderHandler2 handler, ISlider slider)
+ {
+ var provider = handler.GetRequiredService();
+
+ handler.PlatformView?.UpdateThumbImageSourceAsync(slider, provider)
+ .FireAndForget(handler);
+ }
+
+ void OnStartTrackingTouch(Slider slider) =>
+ VirtualView?.DragStarted();
+
+ void OnStopTrackingTouch(Slider slider) =>
+ VirtualView?.DragCompleted();
+
+ void OnValueChanged(Slider slider, float value)
+ {
+ if (VirtualView is null)
+ {
+ return;
+ }
+
+ if ((float)VirtualView.Value != value)
+ {
+ VirtualView.Value = value;
+ }
+ }
+}
diff --git a/src/Core/src/Handlers/Stepper/StepperHandler.cs b/src/Core/src/Handlers/Stepper/StepperHandler.cs
index 828d2ed2e386..741af54a0c7e 100644
--- a/src/Core/src/Handlers/Stepper/StepperHandler.cs
+++ b/src/Core/src/Handlers/Stepper/StepperHandler.cs
@@ -24,6 +24,8 @@ public partial class StepperHandler : IStepperHandler
[nameof(IStepper.IsEnabled)] = MapIsEnabled,
#elif WINDOWS
[nameof(IStepper.Background)] = MapBackground,
+#elif __IOS__ || MACCATALYST
+ [nameof(IStepper.FlowDirection)] = MapFlowDirection,
#endif
};
diff --git a/src/Core/src/Handlers/Stepper/StepperHandler.iOS.cs b/src/Core/src/Handlers/Stepper/StepperHandler.iOS.cs
index 2dfbebd2e385..ba1adebea5cb 100644
--- a/src/Core/src/Handlers/Stepper/StepperHandler.iOS.cs
+++ b/src/Core/src/Handlers/Stepper/StepperHandler.iOS.cs
@@ -1,5 +1,5 @@
using System;
-using System.Drawing;
+using Microsoft.Maui.Graphics;
using ObjCRuntime;
using UIKit;
@@ -7,11 +7,47 @@ namespace Microsoft.Maui.Handlers
{
public partial class StepperHandler : ViewHandler
{
+ // Trailing glass pill overflow (pts) added to UIStepper width in landscape on iOS 26+.
+ // No UIKit API exposes this value; measured empirically on iOS 26.1.
+ // If it changes in a future iOS release, update and re-verify. See: https://github.com/dotnet/maui/issues/34273
+ const double iOSLiquidGlassStepperOverflow = 20;
+
readonly StepperProxy _proxy = new();
protected override UIStepper CreatePlatformView()
{
- return new UIStepper(RectangleF.Empty);
+ return new UIStepper(System.Drawing.RectangleF.Empty);
+ }
+
+ public override Size GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ var result = base.GetDesiredSize(widthConstraint, heightConstraint);
+
+ // iOS 26 Liquid Glass workaround: UIStepper renders its glass pill visually beyond
+ // its logical frame (Layer.MasksToBounds = false). All UIKit size APIs —
+ // IntrinsicContentSize, SizeThatFits, and AlignmentRectInsets — still report the
+ // pre-glass logical size (94×32 pts). Apple does not expose the glass overflow extent
+ // as a measurable value; this compensation was determined empirically.
+ //
+ // In landscape orientation the trailing glass overflow is ~20 pts, causing controls
+ // adjacent in a HorizontalStackLayout to appear inside the visible glass pill.
+ // Portrait orientation has negligible overflow and needs no compensation.
+ //
+ // If this value changes in a future iOS release, update iOSLiquidGlassStepperOverflow
+ // and re-verify on the new OS version. See: https://github.com/dotnet/maui/issues/34273
+ // This workaround targets iOS 26+ only; UIStepper on MacCatalyst has not shown the
+ // same glass pill overflow behavior in testing.
+ if (OperatingSystem.IsIOS() && OperatingSystem.IsIOSVersionAtLeast(26))
+ {
+ var screen = UIKit.UIScreen.MainScreen;
+ bool isLandscape = screen.Bounds.Width > screen.Bounds.Height;
+ if (isLandscape)
+ {
+ result = new Size(result.Width + iOSLiquidGlassStepperOverflow, result.Height);
+ }
+ }
+
+ return result;
}
protected override void ConnectHandler(UIStepper platformView)
@@ -31,21 +67,118 @@ protected override void DisconnectHandler(UIStepper platformView)
public static void MapMinimum(IStepperHandler handler, IStepper stepper)
{
handler.PlatformView?.UpdateMinimum(stepper);
+
+ // iOS 26+ fix: Adjust stepValue for boundary handling when minimum changes
+ if (OperatingSystem.IsIOSVersionAtLeast(26) && handler.PlatformView is UIStepper platformView
+ && NeedsStepValueAdjustment(stepper, platformView))
+ {
+ AdjustStepValueForBoundaries(stepper, platformView);
+ }
}
public static void MapMaximum(IStepperHandler handler, IStepper stepper)
{
handler.PlatformView?.UpdateMaximum(stepper);
+
+ // iOS 26+ fix: Adjust stepValue for boundary handling when maximum changes
+ if (OperatingSystem.IsIOSVersionAtLeast(26) && handler.PlatformView is UIStepper platformView
+ && NeedsStepValueAdjustment(stepper, platformView))
+ {
+ AdjustStepValueForBoundaries(stepper, platformView);
+ }
}
public static void MapIncrement(IStepperHandler handler, IStepper stepper)
{
handler.PlatformView?.UpdateIncrement(stepper);
+
+ // iOS 26+ fix: Adjust stepValue for boundary handling when increment changes
+ if (OperatingSystem.IsIOSVersionAtLeast(26) && handler.PlatformView is UIStepper platformView
+ && NeedsStepValueAdjustment(stepper, platformView))
+ {
+ AdjustStepValueForBoundaries(stepper, platformView);
+ }
}
public static void MapValue(IStepperHandler handler, IStepper stepper)
{
handler.PlatformView?.UpdateValue(stepper);
+
+ // iOS 26+ fix: Adjust stepValue for boundary handling
+ if (OperatingSystem.IsIOSVersionAtLeast(26) && handler.PlatformView is UIStepper platformView
+ && NeedsStepValueAdjustment(stepper, platformView))
+ {
+ AdjustStepValueForBoundaries(stepper, platformView);
+ }
+ }
+
+ // Checks whether the step value needs adjustment due to boundary proximity or a previously modified step value.
+ static bool NeedsStepValueAdjustment(IStepper stepper, UIStepper platformView)
+ {
+ const double epsilon = 1e-10;
+ return stepper.Value + stepper.Interval > stepper.Maximum
+ || stepper.Value - stepper.Interval < stepper.Minimum
+ || Math.Abs(platformView.StepValue - stepper.Interval) > epsilon;
+ }
+
+ // iOS 26+ Workaround: UIStepper behavior changed to prevent button clicks when increment would exceed boundaries
+ // instead of clamping to boundary values like previous iOS versions. This method dynamically adjusts
+ // the stepValue to match available space to boundaries, allowing users to reach exact min/max values.
+ // Reference: https://developer.apple.com/forums/thread/802452
+ static void AdjustStepValueForBoundaries(IStepper virtualView, UIStepper platformView)
+ {
+ var originalIncrement = virtualView.Interval;
+
+ // Only proceed if we have a valid positive increment
+ if (originalIncrement <= 0)
+ {
+ return;
+ }
+
+ var currentValue = virtualView.Value;
+ var minimum = virtualView.Minimum;
+ var maximum = virtualView.Maximum;
+
+ // Validate range configuration - if invalid, fallback to original increment
+ if (maximum <= minimum)
+ {
+ platformView.StepValue = originalIncrement;
+ return;
+ }
+
+ // Clamp current value to valid range if needed
+ currentValue = Math.Max(minimum, Math.Min(maximum, currentValue));
+
+ var spaceToMax = maximum - currentValue;
+ var spaceToMin = currentValue - minimum;
+
+ // Use small epsilon for floating-point comparison precision
+ const double epsilon = 1e-10;
+
+ // Store current stepValue to minimize property access
+ var currentStepValue = platformView.StepValue;
+
+ // If we're close to boundaries and increment would exceed them,
+ // temporarily reduce stepValue to allow reaching exact boundary
+ if (spaceToMax > epsilon && spaceToMax < originalIncrement && Math.Abs(currentStepValue - spaceToMax) > epsilon)
+ {
+ platformView.StepValue = spaceToMax;
+ }
+ else if (spaceToMin > epsilon && spaceToMin < originalIncrement && Math.Abs(currentStepValue - spaceToMin) > epsilon)
+ {
+ platformView.StepValue = spaceToMin;
+ }
+ else if (Math.Abs(currentStepValue - originalIncrement) > epsilon)
+ {
+ // Restore original increment when not near boundaries
+ platformView.StepValue = originalIncrement;
+ }
+ }
+
+ // TODO: Make public for .NET 11.
+ internal static void MapFlowDirection(IStepperHandler handler, IStepper stepper)
+ {
+ handler.PlatformView?.UpdateFlowDirection(stepper);
}
class StepperProxy
@@ -68,7 +201,16 @@ public void Disconnect(UIStepper platformView)
void OnValueChanged(object? sender, EventArgs e)
{
if (VirtualView is IStepper virtualView && sender is UIStepper platformView)
+ {
+ // iOS 26+ fix: Adjust stepValue for boundary handling
+ if (OperatingSystem.IsIOSVersionAtLeast(26)
+ && NeedsStepValueAdjustment(virtualView, platformView))
+ {
+ AdjustStepValueForBoundaries(virtualView, platformView);
+ }
+
virtualView.Value = platformView.Value;
+ }
}
}
}
diff --git a/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.Android.cs b/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.Android.cs
index eabc4383037b..85061bef536e 100644
--- a/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.Android.cs
+++ b/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.Android.cs
@@ -2,6 +2,7 @@
using System.Threading.Tasks;
using Android.Content;
using Android.Graphics.Drawables;
+using Android.Util;
using Android.Widget;
using AButton = AndroidX.AppCompat.Widget.AppCompatButton;
using ATextAlignment = Android.Views.TextAlignment;
@@ -102,8 +103,21 @@ static int GetIconSize(ISwipeItemMenuItemHandler handler)
int contentHeight = mauiSwipeView.MeasuredHeight;
int contentWidth = (int)handler.MauiContext.Context.ToPixels(SwipeViewExtensions.SwipeItemWidth);
+ int maxIconSize = Math.Min(contentHeight, contentWidth) / 2;
- return Math.Min(contentHeight, contentWidth) / 2;
+ if (imageSourcePart.Source is IFontImageSource fontImageSource)
+ {
+ var fontManager = handler.GetRequiredService();
+ var fontSize = fontManager.GetFontSize(fontImageSource.Font);
+ var requestedIconSize = (int)TypedValue.ApplyDimension(
+ fontSize.Unit,
+ fontSize.Value,
+ handler.MauiContext.Context.Resources?.DisplayMetrics);
+
+ return Math.Min(requestedIconSize, maxIconSize);
+ }
+
+ return maxIconSize;
}
void UpdateSize()
@@ -177,4 +191,4 @@ public override void SetImageSource(Drawable? platformImage)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.iOS.cs b/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.iOS.cs
index 36b67d1433dd..647662e5d91b 100644
--- a/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.iOS.cs
+++ b/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.iOS.cs
@@ -118,10 +118,15 @@ public override void SetImageSource(UIImage? platformImage)
try
{
button.SetImage(resizedImage.ImageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate), UIControlState.Normal);
- var tintColor = item.GetTextColor();
- if (tintColor != null)
- button.TintColor = tintColor.ToPlatform();
+ if (item.Source is IFontImageSource fontImageSource && fontImageSource.Color != null)
+ button.TintColor = fontImageSource.Color.ToPlatform();
+ else
+ {
+ var tintColor = item.GetTextColor();
+ if (tintColor != null)
+ button.TintColor = tintColor.ToPlatform();
+ }
}
catch (Exception)
{
diff --git a/src/Core/src/Handlers/Switch/SwitchHandler.Android.cs b/src/Core/src/Handlers/Switch/SwitchHandler.Android.cs
index b08b441e0f4a..718326bcbf26 100644
--- a/src/Core/src/Handlers/Switch/SwitchHandler.Android.cs
+++ b/src/Core/src/Handlers/Switch/SwitchHandler.Android.cs
@@ -1,3 +1,4 @@
+using System;
using Android.Graphics.Drawables;
using Android.Nfc.CardEmulators;
using Android.Widget;
@@ -8,8 +9,7 @@ namespace Microsoft.Maui.Handlers
{
public partial class SwitchHandler : ViewHandler
{
- CheckedChangeListener ChangeListener { get; } = new CheckedChangeListener();
-
+ CheckedChangeListener? _changeListener;
protected override ASwitch CreatePlatformView()
{
return new ASwitch(Context);
@@ -17,16 +17,16 @@ protected override ASwitch CreatePlatformView()
protected override void ConnectHandler(ASwitch platformView)
{
- ChangeListener.Handler = this;
- platformView.SetOnCheckedChangeListener(ChangeListener);
+ _changeListener = new CheckedChangeListener(this);
+ platformView.SetOnCheckedChangeListener(_changeListener);
base.ConnectHandler(platformView);
}
protected override void DisconnectHandler(ASwitch platformView)
{
- ChangeListener.Handler = null;
platformView.SetOnCheckedChangeListener(null);
+ _changeListener = null;
base.DisconnectHandler(platformView);
}
@@ -73,13 +73,21 @@ void OnCheckedChanged(bool isOn)
VirtualView.IsOn = isOn;
}
- class CheckedChangeListener : Java.Lang.Object, CompoundButton.IOnCheckedChangeListener
+ sealed class CheckedChangeListener : Java.Lang.Object, CompoundButton.IOnCheckedChangeListener
{
- public SwitchHandler? Handler { get; set; }
+ readonly WeakReference _handler;
+
+ public CheckedChangeListener(SwitchHandler handler)
+ {
+ _handler = new WeakReference(handler);
+ }
void CompoundButton.IOnCheckedChangeListener.OnCheckedChanged(CompoundButton? buttonView, bool isToggled)
{
- Handler?.OnCheckedChanged(isToggled);
+ if (_handler.TryGetTarget(out var handler))
+ {
+ handler.OnCheckedChanged(isToggled);
+ }
}
}
}
diff --git a/src/Core/src/Handlers/Switch/SwitchHandler.Windows.cs b/src/Core/src/Handlers/Switch/SwitchHandler.Windows.cs
index e3629e73d365..2f6f27249dc5 100644
--- a/src/Core/src/Handlers/Switch/SwitchHandler.Windows.cs
+++ b/src/Core/src/Handlers/Switch/SwitchHandler.Windows.cs
@@ -15,31 +15,71 @@ public static void MapIsOn(ISwitchHandler handler, ISwitch view)
public static void MapTrackColor(ISwitchHandler handler, ISwitch view)
{
if (handler is SwitchHandler)
+ {
handler.PlatformView?.UpdateTrackColor(view);
+ }
}
public static void MapThumbColor(ISwitchHandler handler, ISwitch view)
{
if (handler is SwitchHandler)
+ {
handler.PlatformView?.UpdateThumbColor(view);
+ }
}
protected override void DisconnectHandler(ToggleSwitch platformView)
{
base.DisconnectHandler(platformView);
platformView.Toggled -= OnToggled;
+ platformView.Loaded -= OnLoaded;
}
protected override void ConnectHandler(ToggleSwitch platformView)
{
base.ConnectHandler(platformView);
platformView.Toggled += OnToggled;
+ platformView.Loaded += OnLoaded;
+ }
+
+ void OnLoaded(object sender, UI.Xaml.RoutedEventArgs e)
+ {
+ var toggleSwitch = (ToggleSwitch)sender;
+
+ if (toggleSwitch is null)
+ {
+ return;
+ }
+
+ var rootGrid = toggleSwitch.GetDescendantByName("SwitchAreaGrid")?.Parent as Grid;
+ if (rootGrid is not null && rootGrid.ColumnDefinitions.Count > 0)
+ {
+ // In the default ToggleSwitch template, the second column (index 1) is only used for spacing
+ // between the toggle knob and the On/Off content area (which is defined in the third column).
+ // Since MAUI does not support OnContent/OffContent, this spacing is unnecessary
+ // so we set its width to 0 to reduce unwanted layout padding.
+ rootGrid.ColumnDefinitions[1].Width = new UI.Xaml.GridLength(0);
+ }
+ }
+
+ // TODO: Make it public in .NET 10.0
+ internal static void MapSwitchMinimumWidth(IViewHandler handler, IView view)
+ {
+ // Update the native ToggleSwitch MinWidth to reflect the MAUI view's MinimumWidth,
+ // overriding the default WinUI MinWidth (154) since we're not supporting OnContent and OffContent.
+ // This ensures the control does not reserve unnecessary space for labels.
+ if (view is ISwitch switchView && handler is SwitchHandler switchHandler)
+ {
+ switchHandler.PlatformView?.UpdateMinWidth(switchView);
+ }
}
void OnToggled(object sender, UI.Xaml.RoutedEventArgs e)
{
if (VirtualView is null || PlatformView is null || VirtualView.IsOn == PlatformView.IsOn)
+ {
return;
+ }
VirtualView.IsOn = PlatformView.IsOn;
}
diff --git a/src/Core/src/Handlers/Switch/SwitchHandler.cs b/src/Core/src/Handlers/Switch/SwitchHandler.cs
index 7fa2160b57af..0d5db177f8f2 100644
--- a/src/Core/src/Handlers/Switch/SwitchHandler.cs
+++ b/src/Core/src/Handlers/Switch/SwitchHandler.cs
@@ -20,6 +20,9 @@ public partial class SwitchHandler : ISwitchHandler
[nameof(ISwitch.IsOn)] = MapIsOn,
[nameof(ISwitch.ThumbColor)] = MapThumbColor,
[nameof(ISwitch.TrackColor)] = MapTrackColor,
+#if WINDOWS
+ [nameof(IView.MinimumWidth)] = MapSwitchMinimumWidth,
+#endif
};
public static CommandMapper CommandMapper = new(ViewCommandMapper)
diff --git a/src/Core/src/Handlers/Switch/SwitchHandler2.Android.cs b/src/Core/src/Handlers/Switch/SwitchHandler2.Android.cs
new file mode 100644
index 000000000000..048a8c2b325b
--- /dev/null
+++ b/src/Core/src/Handlers/Switch/SwitchHandler2.Android.cs
@@ -0,0 +1,36 @@
+using Google.Android.Material.MaterialSwitch;
+
+namespace Microsoft.Maui.Handlers;
+
+// TODO: material3 - make it public in .net 11
+internal class SwitchHandler2 : SwitchHandler
+{
+ public static new PropertyMapper Mapper =
+ new(SwitchHandler.Mapper)
+ {
+ [nameof(ISwitch.TrackColor)] = MapTrackColor,
+ [nameof(ISwitch.ThumbColor)] = MapThumbColor,
+ };
+
+ public static new CommandMapper CommandMapper =
+ new(SwitchHandler.CommandMapper);
+
+ public SwitchHandler2() : base(Mapper, CommandMapper)
+ {
+ }
+
+ protected override MaterialSwitch CreatePlatformView()
+ {
+ return new MaterialSwitch(MauiMaterialContextThemeWrapper.Create(Context));
+ }
+
+ public static new void MapTrackColor(ISwitchHandler handler, ISwitch view)
+ {
+ (handler.PlatformView as MaterialSwitch)?.UpdateTrackColor(view);
+ }
+
+ public static new void MapThumbColor(ISwitchHandler handler, ISwitch view)
+ {
+ (handler.PlatformView as MaterialSwitch)?.UpdateThumbColor(view);
+ }
+}
diff --git a/src/Core/src/Handlers/TimePicker/TimePickerHandler.Android.cs b/src/Core/src/Handlers/TimePicker/TimePickerHandler.Android.cs
index 3cbf4126f033..b67518027ae6 100644
--- a/src/Core/src/Handlers/TimePicker/TimePickerHandler.Android.cs
+++ b/src/Core/src/Handlers/TimePicker/TimePickerHandler.Android.cs
@@ -156,7 +156,23 @@ void OnDialogDismiss(object? sender, EventArgs e)
HidePickerDialog();
}
- bool Use24HourView => VirtualView != null && (DateFormat.Is24HourFormat(PlatformView?.Context)
- && VirtualView.Format == "t" || VirtualView.Format == "HH:mm");
+ // "HH" (uppercase) is the .NET 24-hour specifier; "hh" (lowercase) is 12-hour.
+ // Case-sensitive Ordinal comparison is required to distinguish between them.
+ internal static bool IsCustom24HourFormat(string? format) =>
+ !string.IsNullOrEmpty(format) && format.Contains("HH", StringComparison.Ordinal);
+
+ bool Use24HourView
+ {
+ get
+ {
+ if (VirtualView is null || string.IsNullOrEmpty(VirtualView.Format))
+ return false;
+
+ if (VirtualView.Format == "t")
+ return DateFormat.Is24HourFormat(PlatformView?.Context);
+
+ return IsCustom24HourFormat(VirtualView.Format);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Core/src/Handlers/TimePicker/TimePickerHandler.Windows.cs b/src/Core/src/Handlers/TimePicker/TimePickerHandler.Windows.cs
index 689705f768ac..6820908bdfdc 100644
--- a/src/Core/src/Handlers/TimePicker/TimePickerHandler.Windows.cs
+++ b/src/Core/src/Handlers/TimePicker/TimePickerHandler.Windows.cs
@@ -9,12 +9,12 @@ public partial class TimePickerHandler : ViewHandler
protected override void ConnectHandler(TimePicker platformView)
{
- platformView.TimeChanged += OnControlTimeChanged;
+ platformView.SelectedTimeChanged += OnSelectedTimeChanged;
}
protected override void DisconnectHandler(TimePicker platformView)
{
- platformView.TimeChanged -= OnControlTimeChanged;
+ platformView.SelectedTimeChanged -= OnSelectedTimeChanged;
}
public static void MapFormat(ITimePickerHandler handler, ITimePicker timePicker)
@@ -54,7 +54,7 @@ internal static void MapIsOpen(ITimePickerHandler handler, ITimePicker timePicke
handler.PlatformView?.UpdateIsOpen(timePicker);
}
- void OnControlTimeChanged(object? sender, TimePickerValueChangedEventArgs e)
+ void OnSelectedTimeChanged(TimePicker sender, TimePickerSelectedValueChangedEventArgs e)
{
if (VirtualView is not null)
{
diff --git a/src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs b/src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs
index 24dd2695f5ef..64f03f3c8b26 100644
--- a/src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs
+++ b/src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs
@@ -89,6 +89,13 @@ internal static Size GetDesiredSizeFromHandler(this IViewHandler viewHandler, do
sizeThatFits = imageView.SizeThatFitsImage(new CGSize((float)widthConstraint, (float)heightConstraint));
}
+ else if (platformView is LayoutView || platformView is MauiLabel)
+ {
+ widthConstraint = IsExplicitSet(virtualView.Width) ? virtualView.Width : widthConstraint;
+ heightConstraint = IsExplicitSet(virtualView.Height) ? virtualView.Height : heightConstraint;
+
+ sizeThatFits = platformView.SizeThatFits(new CGSize((float)widthConstraint, (float)heightConstraint));
+ }
else if (platformView is WrapperView wrapper)
{
sizeThatFits = wrapper.SizeThatFitsWrapper(new CGSize((float)widthConstraint, (float)heightConstraint), virtualView.Width, virtualView.Height, virtualView);
diff --git a/src/Core/src/Handlers/WebView/WebViewHandler.cs b/src/Core/src/Handlers/WebView/WebViewHandler.cs
index b4dbad5fad5c..7d1fe994ee17 100644
--- a/src/Core/src/Handlers/WebView/WebViewHandler.cs
+++ b/src/Core/src/Handlers/WebView/WebViewHandler.cs
@@ -31,7 +31,8 @@ public partial class WebViewHandler : IWebViewHandler
[nameof(WebViewClient)] = MapWebViewClient,
[nameof(WebChromeClient)] = MapWebChromeClient,
[nameof(WebView.Settings)] = MapWebViewSettings
-#elif __IOS__
+#elif __IOS__ || MACCATALYST
+ [nameof(IWebView.FlowDirection)] = MapFlowDirection,
[nameof(WKUIDelegate)] = MapWKUIDelegate,
[nameof(IWebView.Background)] = MapBackground,
#endif
diff --git a/src/Core/src/Handlers/WebView/WebViewHandler.iOS.cs b/src/Core/src/Handlers/WebView/WebViewHandler.iOS.cs
index 26b20f3bb324..f6abe223b0fa 100644
--- a/src/Core/src/Handlers/WebView/WebViewHandler.iOS.cs
+++ b/src/Core/src/Handlers/WebView/WebViewHandler.iOS.cs
@@ -64,6 +64,15 @@ public static void MapGoForward(IWebViewHandler handler, IWebView webView, objec
handler.PlatformView?.UpdateGoForward(webView);
}
+ internal static void MapFlowDirection(IWebViewHandler handler, IWebView webView)
+ {
+ var scrollView = handler.PlatformView?.ScrollView;
+ if (scrollView == null)
+ return;
+
+ scrollView.UpdateFlowDirectionForScrollView(webView);
+ }
+
public static async void MapReload(IWebViewHandler handler, IWebView webView, object? arg)
{
var platformHandler = handler as WebViewHandler;
diff --git a/src/Core/src/ImageSources/FontImageSourceService/FontImageSourceService.Windows.cs b/src/Core/src/ImageSources/FontImageSourceService/FontImageSourceService.Windows.cs
index 43e818f42102..6af115b579ec 100644
--- a/src/Core/src/ImageSources/FontImageSourceService/FontImageSourceService.Windows.cs
+++ b/src/Core/src/ImageSources/FontImageSourceService/FontImageSourceService.Windows.cs
@@ -77,18 +77,18 @@ internal CanvasImageSource RenderImageSource(IFontImageSource imageSource, float
};
var device = CanvasDevice.GetSharedDevice();
- using var layout = new CanvasTextLayout(device, imageSource.Glyph, textFormat, 0, 0);
+ using var layout = new CanvasTextLayout(device, imageSource.Glyph, textFormat, fontSize, fontSize);
// add a 1px padding all around
- var canvasWidth = (float)layout.DrawBounds.Width + 2;
- var canvasHeight = (float)layout.DrawBounds.Height + 2;
+ var canvasWidth = (float)layout.LayoutBounds.Width + 2;
+ var canvasHeight = (float)layout.LayoutBounds.Height + 2;
var canvasImageSource = new CanvasImageSource(device, canvasWidth, canvasHeight, dpi);
using (var ds = canvasImageSource.CreateDrawingSession(UI.Colors.Transparent))
{
// offset by 1px as we added a 1px padding
- var x = (layout.DrawBounds.X * -1) + 1;
- var y = (layout.DrawBounds.Y * -1) + 1;
+ var x = (layout.LayoutBounds.X * -1) + 1;
+ var y = (layout.LayoutBounds.Y * -1) + 1;
ds.DrawTextLayout(layout, (float)x, (float)y, color);
}
diff --git a/src/Core/src/ImageSources/iOS/ImageSourceExtensions.cs b/src/Core/src/ImageSources/iOS/ImageSourceExtensions.cs
index adc17c4890e7..71dc1af67797 100644
--- a/src/Core/src/ImageSources/iOS/ImageSourceExtensions.cs
+++ b/src/Core/src/ImageSources/iOS/ImageSourceExtensions.cs
@@ -59,8 +59,12 @@ public static partial class ImageSourceExtensions
internal static UIImage? GetPlatformImage(this IFileImageSource imageSource)
{
- var filename = imageSource.File;
- return UIImage.FromBundle(filename) ?? UIImage.FromFile(filename);
+ var file = imageSource.File;
+
+ // On iOS/MacCatalyst, MauiImage assets are flattened into the app bundle root.
+ // UIImage.FromBundle() therefore requires just the base filename without path or extension.
+ var bundleName = Path.GetFileNameWithoutExtension(file);
+ return UIImage.FromBundle(bundleName) ?? UIImage.FromFile(file);
}
internal static CGImageSource? GetPlatformImageSource(this IFileImageSource imageSource, out int scale)
diff --git a/src/Core/src/Layouts/Flex.cs b/src/Core/src/Layouts/Flex.cs
index 2ecadeccf1af..ff5e546ae891 100644
--- a/src/Core/src/Layouts/Flex.cs
+++ b/src/Core/src/Layouts/Flex.cs
@@ -9,6 +9,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Linq;
namespace Microsoft.Maui.Layouts.Flex
{
@@ -782,10 +783,15 @@ static void layout_items(Item item, int child_begin, int child_end, int children
float flex_size = 0;
if (layout.flex_dim > 0)
{
+ // Only the free space is distributed proportionally,
+ // not the total container space. layout.flex_dim was inflated by extra_flex_dim
+ // (the sum of measured sizes of growing items), so we recover the actual free
+ // space by subtracting it back. The item's measured size is preserved and the
+ // proportional share of free space is added on top.
+ float freeSpace = Math.Max(0, layout.flex_dim - layout.extra_flex_dim);
if (child.Grow != 0)
{
- child.Frame[layout.frame_size_i] = 0; // Ignore previous size when growing.
- flex_size = (layout.flex_dim / layout.flex_grows) * child.Grow;
+ flex_size = (freeSpace / layout.flex_grows) * child.Grow;
}
}
else if (layout.flex_dim < 0)
@@ -990,28 +996,12 @@ public void init(Item item, float width, float height)
ordered_indices = null;
if (item.ShouldOrderChildren && item.Count > 0)
{
- var indices = new int[item.Count];
- // Creating a list of item indices sorted using the children's `order'
- // attribute values. We are using a simple insertion sort as we need
- // stability (insertion order must be preserved) and cross-platform
- // support. We should eventually switch to merge sort (or something
- // else) if the number of items becomes significant enough.
- for (int i = 0; i < item.Count; i++)
- {
- indices[i] = i;
- for (int j = i; j > 0; j--)
- {
- int prev = indices[j - 1];
- int curr = indices[j];
- if (item[prev].Order <= item[curr].Order)
- {
- break;
- }
- indices[j - 1] = curr;
- indices[j] = prev;
- }
- }
- ordered_indices = indices;
+ // Sort original indices by each child's Order using a stable sort.
+ // OrderBy is guaranteed stable in .NET, preserving insertion order
+ // for children with equal Order values.
+ ordered_indices = Enumerable.Range(0, item.Count)
+ .OrderBy(i => item[i].Order)
+ .ToArray();
}
flex_dim = 0;
diff --git a/src/Core/src/Layouts/GridLayoutManager.cs b/src/Core/src/Layouts/GridLayoutManager.cs
index 22d9428f6f90..42076d6829ce 100644
--- a/src/Core/src/Layouts/GridLayoutManager.cs
+++ b/src/Core/src/Layouts/GridLayoutManager.cs
@@ -453,6 +453,7 @@ void SecondMeasurePass()
{
height += _rows[n].Size;
}
+ height += _rowSpacing * (cell.RowSpan > 0 ? cell.RowSpan - 1 : 0);
}
if (double.IsInfinity(cell.MeasureWidth))
@@ -465,6 +466,7 @@ void SecondMeasurePass()
{
width += _columns[n].Size;
}
+ width += _columnSpacing * (cell.ColumnSpan > 0 ? cell.ColumnSpan - 1 : 0);
}
if (width == 0 || height == 0)
diff --git a/src/Core/src/Platform/Android/DatePickerExtensions.cs b/src/Core/src/Platform/Android/DatePickerExtensions.cs
index 478167df7576..065443e7c8da 100644
--- a/src/Core/src/Platform/Android/DatePickerExtensions.cs
+++ b/src/Core/src/Platform/Android/DatePickerExtensions.cs
@@ -77,5 +77,34 @@ internal static void SetText(this MauiDatePicker platformDatePicker, IDatePicker
{
platformDatePicker.Text = datePicker.Date?.ToString(datePicker.Format) ?? string.Empty;
}
+
+ // TODO: material3 - make it public in .net 11
+ internal static void UpdateDate(this MauiMaterialDatePicker platformDatePicker, IDatePicker datePicker)
+ {
+ platformDatePicker.SetText(datePicker);
+ }
+
+ internal static void UpdateFormat(this MauiMaterialDatePicker platformDatePicker, IDatePicker datePicker)
+ {
+ platformDatePicker.SetText(datePicker);
+ }
+
+ internal static void UpdateTextColor(this MauiMaterialDatePicker platformDatePicker, IDatePicker datePicker)
+ {
+ var textColor = datePicker.TextColor;
+
+ if (textColor is not null)
+ {
+ if (PlatformInterop.CreateEditTextColorStateList(platformDatePicker.TextColors, textColor.ToPlatform()) is ColorStateList c)
+ {
+ platformDatePicker.SetTextColor(c);
+ }
+ }
+ }
+
+ internal static void SetText(this MauiMaterialDatePicker platformDatePicker, IDatePicker datePicker)
+ {
+ platformDatePicker.Text = datePicker.Date?.ToString(datePicker.Format) ?? string.Empty;
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Core/src/Platform/Android/EditTextExtensions.cs b/src/Core/src/Platform/Android/EditTextExtensions.cs
index 293571ac8043..5c6e7f189833 100644
--- a/src/Core/src/Platform/Android/EditTextExtensions.cs
+++ b/src/Core/src/Platform/Android/EditTextExtensions.cs
@@ -103,7 +103,7 @@ public static void UpdateIsSpellCheckEnabled(this EditText editText, IEditor edi
editText.UpdateIsSpellCheckEnabled(editor as ITextInput);
}
- private static void UpdateIsTextPredictionEnabled(this EditText editText, ITextInput textInput)
+ internal static void UpdateIsTextPredictionEnabled(this EditText editText, ITextInput textInput)
{
var keyboard = textInput.Keyboard;
@@ -116,7 +116,7 @@ private static void UpdateIsTextPredictionEnabled(this EditText editText, ITextI
editText.InputType &= ~InputTypes.TextFlagAutoCorrect;
}
- private static void UpdateIsSpellCheckEnabled(this EditText editText, ITextInput textInput)
+ internal static void UpdateIsSpellCheckEnabled(this EditText editText, ITextInput textInput)
{
// TextFlagNoSuggestions disables spellchecking (the red squiggly lines)
if (!textInput.IsSpellCheckEnabled)
@@ -286,7 +286,20 @@ static void UpdateCursorSelection(EditText editText, ITextInput entry)
int start = GetSelectionStart(editText, entry);
int end = GetSelectionEnd(editText, entry, start);
- editText.SetSelection(start, end);
+ if (editText.IsFocused)
+ {
+ editText.Post(() =>
+ {
+ if (editText.IsAlive())
+ {
+ editText.SetSelection(start, end);
+ }
+ });
+ }
+ else
+ {
+ editText.SetSelection(start, end);
+ }
}
}
@@ -312,10 +325,18 @@ static int GetSelectionStart(EditText editText, ITextInput entry)
static int GetSelectionEnd(EditText editText, ITextInput entry, int start)
{
- int end = start;
int selectionLength = entry.SelectionLength;
- end = System.Math.Max(start, System.Math.Min(editText.Length(), start + selectionLength));
- int newSelectionLength = System.Math.Max(0, end - start);
+ int end = Math.Max(start, Math.Min(editText.Length(), start + selectionLength));
+ int newSelectionLength = Math.Max(0, end - start);
+
+ // If 'start > editText.SelectionEnd', it indicates a reverse selection (right-to-left selection),
+ // where the user selected text starting from the right and moving to the left.
+ if (start > editText.SelectionEnd && selectionLength > 0)
+ {
+ end = editText.SelectionEnd;
+ newSelectionLength = selectionLength;
+ }
+
// Updating this property results in UpdateSelectionLength being called again messing things up
if (newSelectionLength != selectionLength)
entry.SelectionLength = newSelectionLength;
@@ -324,8 +345,7 @@ static int GetSelectionEnd(EditText editText, ITextInput entry, int start)
public static int GetSelectedTextLength(this EditText editText)
{
- var selectedLength = editText.SelectionEnd - editText.SelectionStart;
- return Math.Max(0, selectedLength);
+ return Math.Abs(editText.SelectionEnd - editText.SelectionStart);
}
internal static void SetInputType(this EditText editText, ITextInput textInput)
diff --git a/src/Core/src/Platform/Android/ImageGetter.cs b/src/Core/src/Platform/Android/ImageGetter.cs
new file mode 100644
index 000000000000..48dd629b8904
--- /dev/null
+++ b/src/Core/src/Platform/Android/ImageGetter.cs
@@ -0,0 +1,131 @@
+using System;
+using System.Collections.Concurrent;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using Android.Content.Res;
+using Android.Graphics;
+using Android.Graphics.Drawables;
+using Android.Text;
+using Android.Util;
+
+namespace Microsoft.Maui.Platform
+{
+ internal class ImageGetter : Java.Lang.Object, Html.IImageGetter
+ {
+ const string Tag = "MauiImageGetter";
+ const int MaxCacheEntries = 50;
+ const long MaxImageBytes = 10 * 1024 * 1024;
+
+ static readonly ConcurrentDictionary s_cache = new();
+ static readonly ConcurrentDictionary s_inFlight = new();
+ static readonly HttpClient s_httpClient = new() { Timeout = TimeSpan.FromSeconds(30) };
+
+ readonly Resources _resources;
+ readonly Action? _onImageLoaded;
+ readonly SynchronizationContext? _syncContext;
+
+ public ImageGetter(Resources resources, Action? onImageLoaded = null)
+ {
+ _resources = resources ?? throw new ArgumentNullException(nameof(resources));
+ _onImageLoaded = onImageLoaded;
+ _syncContext = SynchronizationContext.Current;
+ }
+
+ public Drawable? GetDrawable(string? source)
+ {
+ if (string.IsNullOrWhiteSpace(source))
+ return null;
+
+ if (s_cache.TryGetValue(source, out var cached))
+ return cached;
+
+ // Dedupe concurrent fetches for the same URL
+ if (!s_inFlight.ContainsKey(source))
+ {
+ var task = LoadImageAsync(source);
+ s_inFlight.TryAdd(source, task);
+ }
+
+ return new ColorDrawable(Color.Transparent);
+ }
+
+ async Task LoadImageAsync(string source)
+ {
+ try
+ {
+ if (!Uri.TryCreate(source, UriKind.Absolute, out var uri) ||
+ (uri.Scheme != "https" && uri.Scheme != "http"))
+ {
+ Log.Warn(Tag, $"Skipping image with unsupported URI scheme: {source}");
+ return;
+ }
+
+ using var response = await s_httpClient
+ .GetAsync(uri, HttpCompletionOption.ResponseHeadersRead)
+ .ConfigureAwait(false);
+
+ response.EnsureSuccessStatusCode();
+
+ var contentLength = response.Content.Headers.ContentLength;
+ if (contentLength > MaxImageBytes)
+ {
+ Log.Warn(Tag, $"Image exceeds {MaxImageBytes} byte limit ({contentLength} bytes): {source}");
+ return;
+ }
+
+ using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ var bitmap = await BitmapFactory.DecodeStreamAsync(stream).ConfigureAwait(false);
+
+ if (bitmap is null)
+ return;
+
+ var drawable = new BitmapDrawable(_resources, bitmap);
+ drawable.SetBounds(0, 0, bitmap.Width, bitmap.Height);
+
+ if (s_cache.Count >= MaxCacheEntries)
+ EvictOldestEntries();
+
+ s_cache[source] = drawable;
+
+ if (_onImageLoaded is not null)
+ {
+ if (_syncContext is not null)
+ _syncContext.Post(_ => _onImageLoaded.Invoke(), null);
+ else
+ _onImageLoaded.Invoke();
+ }
+ }
+ catch (HttpRequestException ex)
+ {
+ Log.Warn(Tag, $"Failed to download image '{source}': {ex.Message}");
+ }
+ catch (System.IO.IOException ex)
+ {
+ Log.Warn(Tag, $"IO error loading image '{source}': {ex.Message}");
+ }
+ catch (OperationCanceledException)
+ {
+ Log.Debug(Tag, $"Image download timed out or was cancelled: {source}");
+ }
+ finally
+ {
+ s_inFlight.TryRemove(source, out _);
+ }
+ }
+
+ static void EvictOldestEntries()
+ {
+ int toRemove = s_cache.Count / 2;
+ foreach (var key in s_cache.Keys)
+ {
+ if (toRemove-- <= 0)
+ break;
+
+ if (s_cache.TryRemove(key, out var evicted))
+ evicted.Bitmap?.Recycle();
+ }
+ }
+ }
+}
+
diff --git a/src/Core/src/Platform/Android/ImageViewExtensions.cs b/src/Core/src/Platform/Android/ImageViewExtensions.cs
index 52f8b28f2fcb..2d46f3fae4fd 100644
--- a/src/Core/src/Platform/Android/ImageViewExtensions.cs
+++ b/src/Core/src/Platform/Android/ImageViewExtensions.cs
@@ -18,7 +18,12 @@ public static void Clear(this ImageView imageView)
public static void UpdateAspect(this ImageView imageView, IImage image)
{
- if (image.Aspect == Aspect.AspectFill)
+ // Apply bounds adjustment for Image control, but not for ImageButton control
+ // ShapeableImageView is used by both ImageButton and ImageHandler2 (Image control)
+ // Skip bounds adjustment only if it's a ShapeableImageView AND it's an IImageButton
+ bool shouldSkipBoundsAdjustment = imageView is ShapeableImageView && image is IImageButton;
+
+ if (!shouldSkipBoundsAdjustment)
{
imageView.SetAdjustViewBounds(false);
}
diff --git a/src/Core/src/Platform/Android/Material3Controls/MauiMaterialDatePicker.cs b/src/Core/src/Platform/Android/Material3Controls/MauiMaterialDatePicker.cs
new file mode 100644
index 000000000000..d330cfa574e1
--- /dev/null
+++ b/src/Core/src/Platform/Android/Material3Controls/MauiMaterialDatePicker.cs
@@ -0,0 +1,59 @@
+using System;
+using Android.Content;
+using Android.Runtime;
+using Android.Text.Method;
+using Android.Util;
+using Android.Views;
+using AndroidX.AppCompat.Widget;
+using AndroidX.Core.Graphics.Drawable;
+using Google.Android.Material.TextField;
+using static Android.Views.View;
+
+namespace Microsoft.Maui.Platform;
+
+// TODO: material3 - make it public in .net 11
+internal class MauiMaterialDatePicker : TextInputEditText, IOnClickListener
+{
+ public MauiMaterialDatePicker(Context context) : base(MauiMaterialContextThemeWrapper.Create(context))
+ {
+ Initialize();
+ }
+
+ public MauiMaterialDatePicker(Context context, IAttributeSet? attrs) : base(MauiMaterialContextThemeWrapper.Create(context), attrs)
+ {
+ Initialize();
+ }
+
+ public MauiMaterialDatePicker(Context context, IAttributeSet? attrs, int defStyleAttr) : base(MauiMaterialContextThemeWrapper.Create(context), attrs, defStyleAttr)
+ {
+ Initialize();
+ }
+
+ protected MauiMaterialDatePicker(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
+ {
+ }
+
+ // MovementMethod handles cursor positioning, scrolling, and text selection (per Android docs).
+ // Since text is readonly, we disable it to avoid unnecessary cursor navigation during keyboard input.
+ protected override IMovementMethod? DefaultMovementMethod => null;
+
+ public Action? ShowPicker { get; set; }
+ public Action? HidePicker { get; set; }
+
+ public void OnClick(View? v)
+ {
+ ShowPicker?.Invoke();
+ }
+
+ void Initialize()
+ {
+ if (Background is not null)
+ {
+ DrawableCompat.Wrap(Background);
+ }
+
+ PickerManager.Init(this);
+
+ SetOnClickListener(this);
+ }
+}
diff --git a/src/Core/src/Platform/Android/Material3Controls/MauiMaterialEditText.cs b/src/Core/src/Platform/Android/Material3Controls/MauiMaterialEditText.cs
index 6adf5a48d10b..daf70978fb6c 100644
--- a/src/Core/src/Platform/Android/Material3Controls/MauiMaterialEditText.cs
+++ b/src/Core/src/Platform/Android/Material3Controls/MauiMaterialEditText.cs
@@ -2,6 +2,7 @@
using Android.Content;
using Android.Runtime;
using Android.Util;
+using Android.Views;
using Google.Android.Material.TextField;
namespace Microsoft.Maui.Platform;
@@ -11,7 +12,7 @@ internal class MauiMaterialEditText : TextInputEditText
{
public event EventHandler? SelectionChanged;
- public MauiMaterialEditText(Context context) : base(MauiMaterialContextThemeWrapper.Create(context))
+ public MauiMaterialEditText(Context context) : base(context)
{
}
@@ -19,11 +20,11 @@ protected MauiMaterialEditText(nint javaReference, JniHandleOwnership transfer)
{
}
- public MauiMaterialEditText(Context context, IAttributeSet? attrs) : base(MauiMaterialContextThemeWrapper.Create(context), attrs)
+ public MauiMaterialEditText(Context context, IAttributeSet? attrs) : base(context, attrs)
{
}
- public MauiMaterialEditText(Context context, IAttributeSet? attrs, int defStyleAttr) : base(MauiMaterialContextThemeWrapper.Create(context), attrs, defStyleAttr)
+ public MauiMaterialEditText(Context context, IAttributeSet? attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
{
}
@@ -33,4 +34,56 @@ protected override void OnSelectionChanged(int selStart, int selEnd)
SelectionChanged?.Invoke(this, EventArgs.Empty);
}
+
+ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
+ {
+ // Get the measure spec mode and size
+ var heightMode = MeasureSpec.GetMode(heightMeasureSpec);
+ var heightSize = MeasureSpec.GetSize(heightMeasureSpec);
+
+ if (heightMode == MeasureSpecMode.AtMost && heightSize > 0)
+ {
+ heightMeasureSpec = MeasureSpec.MakeMeasureSpec(heightSize, MeasureSpecMode.Exactly);
+ }
+
+ base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+}
+
+// TODO: Material3: Make it public in .NET 11
+internal class MauiMaterialTextInputLayout : TextInputLayout
+{
+ public MauiMaterialTextInputLayout(Context context) : base(context)
+ {
+ }
+
+ protected MauiMaterialTextInputLayout(nint javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
+ {
+ }
+
+ public MauiMaterialTextInputLayout(Context context, IAttributeSet? attrs) : base(context, attrs)
+ {
+ }
+
+ public MauiMaterialTextInputLayout(Context context, IAttributeSet? attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
+ {
+ }
+
+ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
+ {
+ // Get the measure spec mode and size
+ var widthMode = MeasureSpec.GetMode(widthMeasureSpec);
+ var widthSize = MeasureSpec.GetSize(widthMeasureSpec);
+
+ // If we have an AtMost constraint with a specific size, treat it as Exactly
+ // to force the TextInputLayout to expand to the available width
+ // This ensures the Material TextInputLayout fills the available space
+ // instead of wrapping its content
+ if (widthMode == MeasureSpecMode.AtMost && widthSize > 0)
+ {
+ widthMeasureSpec = MeasureSpec.MakeMeasureSpec(widthSize, MeasureSpecMode.Exactly);
+ }
+
+ base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
}
diff --git a/src/Core/src/Platform/Android/MaterialActivityIndicator.cs b/src/Core/src/Platform/Android/MaterialActivityIndicator.cs
new file mode 100644
index 000000000000..d60bb781bcd0
--- /dev/null
+++ b/src/Core/src/Platform/Android/MaterialActivityIndicator.cs
@@ -0,0 +1,45 @@
+using System;
+using Android.Content;
+using Android.Runtime;
+using Android.Util;
+using Google.Android.Material.ProgressIndicator;
+
+namespace Microsoft.Maui.Platform;
+
+// TODO: material3 - make it public in .net 11
+internal class MaterialActivityIndicator : CircularProgressIndicator
+{
+ public MaterialActivityIndicator(Context context)
+ : base(MauiMaterialContextThemeWrapper.Create(context))
+ {
+ }
+
+ protected MaterialActivityIndicator(nint javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
+ {
+ }
+
+ public MaterialActivityIndicator(Context context, IAttributeSet? attrs) : base(MauiMaterialContextThemeWrapper.Create(context), attrs)
+ {
+ }
+
+ public MaterialActivityIndicator(Context context, IAttributeSet? attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
+ {
+
+ }
+
+ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
+ {
+ // Calculate desired size including indicator size and padding
+ var desiredSize =
+ IndicatorSize +
+ Math.Max(PaddingLeft + PaddingRight, PaddingTop + PaddingBottom);
+
+ var width = ResolveSize(desiredSize, widthMeasureSpec);
+ var height = ResolveSize(desiredSize, heightMeasureSpec);
+
+ // ActivityIndicator must always be square
+ var finalSize = Math.Min(width, height);
+
+ SetMeasuredDimension(finalSize, finalSize);
+ }
+}
diff --git a/src/Core/src/Platform/Android/MauiMaterialSearchBarTextInputLayout.cs b/src/Core/src/Platform/Android/MauiMaterialSearchBarTextInputLayout.cs
new file mode 100644
index 000000000000..3d5b0b82a08d
--- /dev/null
+++ b/src/Core/src/Platform/Android/MauiMaterialSearchBarTextInputLayout.cs
@@ -0,0 +1,95 @@
+using System;
+using Android.Content;
+using Android.Views;
+using Google.Android.Material.TextField;
+
+namespace Microsoft.Maui.Platform;
+
+// TODO: material3 - make it public in .net 11
+internal class MauiMaterialSearchBarTextInputLayout : TextInputLayout
+{
+ public MauiMaterialSearchBarTextInputLayout(Context context) : base(context)
+ {
+ // Set the search icon as the start/leading icon
+ SetStartIconDrawable(Resource.Drawable.abc_ic_search_api_material);
+
+ // Set the clear/cancel button as the end icon with custom behavior
+ // Custom mode allows showing icon regardless of focus state (matching Material2)
+ EndIconMode = EndIconCustom;
+ SetEndIconDrawable(Resource.Drawable.abc_ic_clear_material);
+
+ // Set up click listener for clear button
+ SetEndIconOnClickListener(new ClearButtonClickListener(this));
+ }
+
+ public void UpdateCloseButtonVisibility(bool hasText)
+ {
+ // Show/hide the end icon based on whether text exists (regardless of focus)
+ EndIconVisible = hasText;
+ }
+
+ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
+ {
+ // Get the measure spec mode and size
+ var widthMode = MeasureSpec.GetMode(widthMeasureSpec);
+ var widthSize = MeasureSpec.GetSize(widthMeasureSpec);
+
+ // If we have an AtMost constraint with a specific size, treat it as Exactly
+ // to force the TextInputLayout to expand to the available width
+ // This ensures the Material TextInputLayout fills the available space
+ // instead of wrapping its content
+ if (widthMode == MeasureSpecMode.AtMost && widthSize > 0)
+ {
+ widthMeasureSpec = MeasureSpec.MakeMeasureSpec(widthSize, MeasureSpecMode.Exactly);
+ }
+
+ base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+}
+
+// TODO: material3 - make it public in .net 11
+internal class MauiMaterialSearchBarTextInputEditText : TextInputEditText
+{
+ public event EventHandler? SelectionChanged;
+ public MauiMaterialSearchBarTextInputEditText(Context context) : base(context)
+ {
+ }
+
+ protected override void OnSelectionChanged(int selStart, int selEnd)
+ {
+ base.OnSelectionChanged(selStart, selEnd);
+
+ SelectionChanged?.Invoke(this, EventArgs.Empty);
+ }
+
+ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
+ {
+ // Get the measure spec mode and size
+ var heightMode = MeasureSpec.GetMode(heightMeasureSpec);
+ var heightSize = MeasureSpec.GetSize(heightMeasureSpec);
+
+ if (heightMode == MeasureSpecMode.AtMost && heightSize > 0)
+ {
+ heightMeasureSpec = MeasureSpec.MakeMeasureSpec(heightSize, MeasureSpecMode.Exactly);
+ }
+
+ base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+}
+
+class ClearButtonClickListener : Java.Lang.Object, View.IOnClickListener
+{
+ readonly MauiMaterialSearchBarTextInputLayout _layout;
+
+ public ClearButtonClickListener(MauiMaterialSearchBarTextInputLayout layout)
+ {
+ _layout = layout;
+ }
+
+ void View.IOnClickListener.OnClick(View? v)
+ {
+ // Clear the text in the EditText
+ var editText = _layout.GetFirstChildOfType();
+ editText?.Text = string.Empty;
+ }
+}
diff --git a/src/Core/src/Platform/Android/MauiScrollView.cs b/src/Core/src/Platform/Android/MauiScrollView.cs
index 61e9af6a0a24..bc92251800b6 100644
--- a/src/Core/src/Platform/Android/MauiScrollView.cs
+++ b/src/Core/src/Platform/Android/MauiScrollView.cs
@@ -128,7 +128,7 @@ public void SetHorizontalScrollBarVisibility(ScrollBarVisibility scrollBarVisibi
_hScrollView.HorizontalScrollBarEnabled = scrollBarVisibility == ScrollBarVisibility.Always;
_hScrollView.ScrollbarFadingEnabled = _horizontalScrollVisibility != ScrollBarVisibility.Always;
- PlatformInterop.RequestLayoutIfNeeded(this);
+ PlatformInterop.RequestLayoutIfNeeded(_hScrollView);
}
public void SetVerticalScrollBarVisibility(ScrollBarVisibility scrollBarVisibility)
diff --git a/src/Core/src/Platform/Android/MauiShapeableImageView.cs b/src/Core/src/Platform/Android/MauiShapeableImageView.cs
index 63e0c7358aa1..b85989b74705 100644
--- a/src/Core/src/Platform/Android/MauiShapeableImageView.cs
+++ b/src/Core/src/Platform/Android/MauiShapeableImageView.cs
@@ -36,4 +36,37 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
SetPadding(0, 0, 0, 0);
}
}
+
+ // TODO: net11 - Remove this class and update MauiShapeableImageView to public API with the same changes.
+ internal class MaterialShapeableImageView : ShapeableImageView
+ {
+ public MaterialShapeableImageView(Context context) : base(MauiMaterialContextThemeWrapper.Create(context))
+ {
+ }
+
+ public MaterialShapeableImageView(Context context, IAttributeSet? attrs) : base(MauiMaterialContextThemeWrapper.Create(context), attrs)
+ {
+ }
+
+ public MaterialShapeableImageView(Context context, IAttributeSet? attrs, int defStyle) : base(MauiMaterialContextThemeWrapper.Create(context), attrs, defStyle)
+ {
+ }
+
+ protected MaterialShapeableImageView(nint javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
+ {
+ }
+
+ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
+ {
+ // The padding has a few issues. This is a workaround for the following issue:
+ // https://github.com/material-components/material-components-android/issues/2063
+
+ // ShapeableImageView combines ContentPadding with Padding and updates
+ // Padding with the result.
+ base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ // We need to reset the padding to 0 to avoid a double padding.
+ SetPadding(0, 0, 0, 0);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Core/src/Platform/Android/MauiSwipeRefreshLayout.cs b/src/Core/src/Platform/Android/MauiSwipeRefreshLayout.cs
index 3f9734bb143a..1d2553dde335 100644
--- a/src/Core/src/Platform/Android/MauiSwipeRefreshLayout.cs
+++ b/src/Core/src/Platform/Android/MauiSwipeRefreshLayout.cs
@@ -39,6 +39,9 @@ public ICrossPlatformLayout? CrossPlatformLayout
public override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
+ // Always call base.OnMeasure — unlike ContentViewGroup/LayoutViewGroup,
+ // SwipeRefreshLayout.onMeasure internally measures its spinner indicator
+ // (mCircleView). Skipping this leaves the spinner at 0x0, making it invisible.
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
if (CrossPlatformLayout is null)
@@ -49,11 +52,33 @@ public override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
var deviceIndependentWidth = widthMeasureSpec.ToDouble(_context);
var deviceIndependentHeight = heightMeasureSpec.ToDouble(_context);
- CrossPlatformLayout?.CrossPlatformMeasure(deviceIndependentWidth, deviceIndependentHeight);
+ var widthMode = MeasureSpec.GetMode(widthMeasureSpec);
+ var heightMode = MeasureSpec.GetMode(heightMeasureSpec);
+
+ var measure = CrossPlatformLayout.CrossPlatformMeasure(deviceIndependentWidth, deviceIndependentHeight);
+
+ // Unlike ContentViewGroup/LayoutViewGroup, SwipeRefreshLayout internally positions
+ // its spinner at getMeasuredWidth()/2. We must use the full spec size for both
+ // Exactly and AtMost modes (matching View.getDefaultSize behavior) so the spinner
+ // is centered correctly. Only for Unspecified do we use the cross-platform measure.
+ var width = widthMode == MeasureSpecMode.Unspecified ? measure.Width : deviceIndependentWidth;
+ var height = heightMode == MeasureSpecMode.Unspecified ? measure.Height : deviceIndependentHeight;
+
+ var platformWidth = _context.ToPixels(width);
+ var platformHeight = _context.ToPixels(height);
+
+ // Minimum values win over everything
+ platformWidth = Math.Max(MinimumWidth, platformWidth);
+ platformHeight = Math.Max(MinimumHeight, platformHeight);
+
+ SetMeasuredDimension((int)platformWidth, (int)platformHeight);
}
protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
{
+ // Always call base.OnLayout — SwipeRefreshLayout.onLayout positions the
+ // spinner indicator (mCircleView) centered horizontally. Without this,
+ // the spinner won't appear or will be mispositioned.
base.OnLayout(changed, left, top, right, bottom);
if (CrossPlatformLayout is null)
{
diff --git a/src/Core/src/Platform/Android/MauiWindowInsetListener.cs b/src/Core/src/Platform/Android/MauiWindowInsetListener.cs
index 9763ec85ed32..b9b3a1328ab6 100644
--- a/src/Core/src/Platform/Android/MauiWindowInsetListener.cs
+++ b/src/Core/src/Platform/Android/MauiWindowInsetListener.cs
@@ -101,8 +101,10 @@ internal void RegisterView(AView view)
// Skip setting listener on views inside nested scroll containers or AppBarLayout (except MaterialToolbar)
// We want the layout listener logic to get applied to the MaterialToolbar itself
// But we don't want any layout listeners to get applied to the children of MaterialToolbar (like the TitleView)
+ // CollectionView/CarouselView items are not excluded to enable per-item SafeAreaEdges control.
+ // Performance overhead is negligible due to early pass-through for items without insets.
if (view is not MaterialToolbar &&
- (parent is AppBarLayout || parent is MauiScrollView || parent is IMauiRecyclerView))
+ (parent is AppBarLayout || parent is MauiScrollView))
{
return null;
}
@@ -264,37 +266,30 @@ public MauiWindowInsetListener() : base(DispatchModeStop)
}
}
- // Handle bottom navigation
- var hasBottomNav = v.FindViewById(Resource.Id.navigationlayout_bottomtabs)?.MeasuredHeight > 0;
+ var bottomTabContainer = v.FindViewById(Resource.Id.navigationlayout_bottomtabs);
+ var hasBottomNav = bottomTabContainer?.MeasuredHeight > 0;
+ var contentView = v.FindViewById(Resource.Id.navigationlayout_content);
+
if (hasBottomNav)
{
var bottomInset = Math.Max(systemBars?.Bottom ?? 0, displayCutout?.Bottom ?? 0);
- v.SetPadding(0, 0, 0, bottomInset);
+
+ // Only pad the bottom of contentView to prevent content from sliding under the
+ // BottomNavigationView + system navigation bar. Left/right are intentionally
+ // excluded: landscape cutout padding on the content area is handled by
+ // SafeAreaExtensions which applies per-view overlap logic.
+ contentView?.SetPadding(0, 0, 0, bottomInset);
}
else
{
- v.SetPadding(0, 0, 0, 0);
+ // Reset contentView padding when bottom navigation is removed dynamically
+ contentView?.SetPadding(0, 0, 0, 0);
}
- // Create new insets with consumed values
- var newSystemBars = Insets.Of(
- systemBars?.Left ?? 0,
- appBarHasContent ? 0 : systemBars?.Top ?? 0,
- systemBars?.Right ?? 0,
- hasBottomNav ? 0 : systemBars?.Bottom ?? 0
- ) ?? Insets.None;
-
- var newDisplayCutout = Insets.Of(
- displayCutout?.Left ?? 0,
- appBarHasContent ? 0 : displayCutout?.Top ?? 0,
- displayCutout?.Right ?? 0,
- hasBottomNav ? 0 : displayCutout?.Bottom ?? 0
- ) ?? Insets.None;
-
- return new WindowInsetsCompat.Builder(insets)
- ?.SetInsets(WindowInsetsCompat.Type.SystemBars(), newSystemBars)
- ?.SetInsets(WindowInsetsCompat.Type.DisplayCutout(), newDisplayCutout)
- ?.Build() ?? insets;
+ // Pass insets through unconsumed so child views receive them intact.
+ // Bottom: BottomNavigationView needs the nav bar inset to extend its background in Edge-to-Edge mode (Issue #33344).
+ // Top: SafeAreaExtensions handles per-view overlap, avoiding double-padding.
+ return insets;
}
public void TrackView(AView view)
diff --git a/src/Core/src/Platform/Android/PlatformTouchGraphicsView.cs b/src/Core/src/Platform/Android/PlatformTouchGraphicsView.cs
index 16744f1a48d6..5abdcf408067 100644
--- a/src/Core/src/Platform/Android/PlatformTouchGraphicsView.cs
+++ b/src/Core/src/Platform/Android/PlatformTouchGraphicsView.cs
@@ -19,14 +19,19 @@ public class PlatformTouchGraphicsView : PlatformGraphicsView
public PlatformTouchGraphicsView(Context context) : base(context)
{
- _scale = (context ?? global::Android.App.Application.Context)?.Resources?.DisplayMetrics?.Density ?? 1;
}
+ // Override to use MAUI's cached density (Context.GetDisplayDensity) so that
+ // dirtyRect, touch coords and _bounds stay consistent with GraphicsView.Width/Height.
+ internal override float GetDisplayDensity() => Context!.GetDisplayDensity();
+
protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
{
base.OnLayout(changed, left, top, right, bottom);
if (changed)
{
+ // Cache density once per layout; reused by touch/hover events until next layout.
+ _scale = GetDisplayDensity();
var width = right - left;
var height = bottom - top;
_bounds = new RectF(0, 0, width / _scale, height / _scale);
diff --git a/src/Core/src/Platform/Android/ProgressBarExtensions.cs b/src/Core/src/Platform/Android/ProgressBarExtensions.cs
index 14934bc981d6..c110c893b5c5 100644
--- a/src/Core/src/Platform/Android/ProgressBarExtensions.cs
+++ b/src/Core/src/Platform/Android/ProgressBarExtensions.cs
@@ -1,4 +1,5 @@
using Android.Content.Res;
+using Google.Android.Material.ProgressIndicator;
using Microsoft.Maui.Graphics;
using AProgressBar = Android.Widget.ProgressBar;
@@ -14,6 +15,18 @@ public static void UpdateProgress(this AProgressBar platformProgressBar, IProgre
}
public static void UpdateProgressColor(this AProgressBar platformProgressBar, IProgress progress)
+ {
+ if (platformProgressBar is LinearProgressIndicator materialProgressBar)
+ {
+ materialProgressBar.UpdateLinearProgressIndicatorColor(progress);
+ }
+ else
+ {
+ platformProgressBar.UpdateProgressBarColor(progress);
+ }
+ }
+
+ static void UpdateProgressBarColor(this AProgressBar platformProgressBar, IProgress progress)
{
Color color = progress.ProgressColor;
@@ -27,9 +40,30 @@ public static void UpdateProgressColor(this AProgressBar platformProgressBar, IP
var tintList = ColorStateList.ValueOf(color.ToPlatform());
if (platformProgressBar.Indeterminate)
+ {
platformProgressBar.IndeterminateTintList = tintList;
+ }
else
+ {
platformProgressBar.ProgressTintList = tintList;
+ }
+ }
+ }
+
+ static void UpdateLinearProgressIndicatorColor(this LinearProgressIndicator materialProgressBar, IProgress progress)
+ {
+ Color color = progress.ProgressColor;
+
+ if (color is null)
+ {
+ // Reset to theme default by passing empty array - Material3's setIndicatorColor()
+ // automatically resolves this to theme's colorPrimary when length == 0
+ materialProgressBar.SetIndicatorColor([]);
+ }
+ else
+ {
+ var colorArray = new int[] { color.ToPlatform() };
+ materialProgressBar.SetIndicatorColor(colorArray);
}
}
}
diff --git a/src/Core/src/Platform/Android/Resources/values/styles-material3.xml b/src/Core/src/Platform/Android/Resources/values/styles-material3.xml
index c5e6cfecd56a..169d12b88745 100644
--- a/src/Core/src/Platform/Android/Resources/values/styles-material3.xml
+++ b/src/Core/src/Platform/Android/Resources/values/styles-material3.xml
@@ -5,6 +5,7 @@
- true
- false
+ - @style/Widget.Material3.BottomNavigationView
- @style/MauiMaterialButton
diff --git a/src/Core/src/Platform/Android/SearchViewExtensions.cs b/src/Core/src/Platform/Android/SearchViewExtensions.cs
index 64b5669ae454..735e536413a5 100644
--- a/src/Core/src/Platform/Android/SearchViewExtensions.cs
+++ b/src/Core/src/Platform/Android/SearchViewExtensions.cs
@@ -7,6 +7,7 @@
using Android.Util;
using Android.Views.InputMethods;
using Android.Widget;
+using Google.Android.Material.TextField;
using static Android.Content.Res.Resources;
using AAttribute = Android.Resource.Attribute;
using SearchView = AndroidX.AppCompat.Widget.SearchView;
@@ -15,6 +16,8 @@ namespace Microsoft.Maui.Platform
{
public static class SearchViewExtensions
{
+ static readonly int[] s_DisabledState = [-AAttribute.StateEnabled];
+ static readonly int[] s_EnabledState = [AAttribute.StateEnabled];
public static void UpdateText(this SearchView searchView, ISearchBar searchBar)
{
searchView.SetQuery(searchBar.Text, false);
@@ -228,15 +231,12 @@ static bool TryGetDefaultStateColor(SearchView searchView, int attribute, out Co
if (searchView.Context?.Theme is not Theme theme)
return false;
- int[] s_disabledState = [-AAttribute.StateEnabled];
- int[] s_enabledState = [AAttribute.StateEnabled];
-
using var ta = theme.ObtainStyledAttributes([attribute]);
var cs = ta.GetColorStateList(0);
if (cs is null)
return false;
- var state = searchView.Enabled ? s_enabledState : s_disabledState;
+ var state = searchView.Enabled ? s_EnabledState : s_DisabledState;
color = new Color(cs.GetColorForState(state, Color.Black));
return true;
}
@@ -259,5 +259,115 @@ internal static void SafeSetTint(ImageView? imageView, Color color)
safe.SetTint(color);
imageView?.SetImageDrawable(safe);
}
+
+ // material3 searchbar extension methods
+ // TODO: material3 - make it public in .net 11
+ internal static void UpdateText(this EditText editText, ISearchBar searchBar)
+ {
+ // Check if text is already the same to prevent unnecessary updates and TextWatcher loops
+ var currentText = editText.Text ?? string.Empty;
+ var newText = searchBar.Text ?? string.Empty;
+
+ if (currentText == newText)
+ {
+ return;
+ }
+
+ editText.Text = newText;
+ }
+
+ internal static void UpdateBackground(this TextInputLayout textInputLayout, ISearchBar searchBar)
+ {
+ var background = searchBar.Background;
+
+ if (background is Microsoft.Maui.Graphics.SolidPaint solidPaint)
+ {
+ // For Material 3 filled TextInputLayout, use ColorStateList to maintain
+ // the same background color across all states (enabled, disabled, focused)
+ // This prevents Material Design from applying its default disabled state styling
+ var platformColor = solidPaint.Color.ToPlatform();
+ var colorInt = (int)platformColor;
+
+ // Use the same color for both enabled and disabled states
+ var colorStateList = ColorStateListExtensions.CreateEditText(colorInt, colorInt);
+
+ textInputLayout.SetBoxBackgroundColorStateList(colorStateList);
+ }
+ else if (background is null)
+ {
+ // Clear the custom background and restore Material 3 default
+ if (TryGetDefaultStateColor(textInputLayout, AAttribute.ColorBackground, out var defaultColor))
+ {
+ var colorInt = (int)defaultColor;
+ var colorStateList = ColorStateListExtensions.CreateEditText(colorInt, colorInt);
+ textInputLayout.SetBoxBackgroundColorStateList(colorStateList);
+ }
+ }
+ }
+
+ internal static void UpdateSearchIconColor(this TextInputLayout textInputLayout, ISearchBar searchBar)
+ {
+ // For TextInputLayout, the search icon is the start icon
+ if (searchBar.SearchIconColor is not null)
+ {
+ var color = searchBar.SearchIconColor.ToPlatform();
+ textInputLayout.SetStartIconTintList(ColorStateList.ValueOf(color));
+ }
+ else if (TryGetDefaultStateColor(textInputLayout, AAttribute.TextColorPrimary, out var defaultColor))
+ {
+ // Restore default theme color
+ textInputLayout.SetStartIconTintList(ColorStateList.ValueOf(defaultColor));
+ }
+ }
+
+ internal static void UpdateCancelButtonColor(this TextInputLayout textInputLayout, ISearchBar searchBar)
+ {
+ // For TextInputLayout, the cancel/clear button is the end icon
+ if (searchBar.CancelButtonColor is not null)
+ {
+ var color = searchBar.CancelButtonColor.ToPlatform();
+ textInputLayout.SetEndIconTintList(ColorStateList.ValueOf(color));
+ }
+ else if (TryGetDefaultStateColor(textInputLayout, AAttribute.TextColorPrimary, out var defaultColor))
+ {
+ // Restore default theme color
+ textInputLayout.SetEndIconTintList(ColorStateList.ValueOf(defaultColor));
+ }
+ }
+
+ static bool TryGetDefaultStateColor(TextInputLayout textInputLayout, int attribute, out Color color)
+ {
+ color = default;
+
+ if (!OperatingSystem.IsAndroidVersionAtLeast(23))
+ {
+ return false;
+ }
+
+ if (textInputLayout.Context?.Theme is not Theme theme)
+ {
+ return false;
+ }
+
+ using var ta = theme.ObtainStyledAttributes([attribute]);
+ var cs = ta.GetColorStateList(0);
+ if (cs is null)
+ {
+ return false;
+ }
+
+ var state = textInputLayout.Enabled ? s_EnabledState : s_DisabledState;
+ color = new Color(cs.GetColorForState(state, Color.Black));
+ return true;
+ }
+
+ internal static void UpdateReturnType(this EditText editText, ISearchBar searchBar)
+ {
+ editText.ImeOptions = searchBar.ReturnType.ToPlatform();
+
+ // Restart the input on the current focused EditText
+ InputMethodManager? imm = (InputMethodManager?)editText.Context?.GetSystemService(Context.InputMethodService);
+ imm?.RestartInput(editText);
+ }
}
}
diff --git a/src/Core/src/Platform/Android/SliderExtensions.cs b/src/Core/src/Platform/Android/SliderExtensions.cs
index 720d3eb0e3b5..9366726ad937 100644
--- a/src/Core/src/Platform/Android/SliderExtensions.cs
+++ b/src/Core/src/Platform/Android/SliderExtensions.cs
@@ -1,8 +1,11 @@
using System.Threading.Tasks;
+using Android.Content;
using Android.Content.Res;
using Android.Graphics;
+using Android.Graphics.Drawables;
using Android.Util;
using Android.Widget;
+using Google.Android.Material.Slider;
namespace Microsoft.Maui.Platform
{
@@ -10,10 +13,28 @@ public static class SliderExtensions
{
public const double PlatformMaxValue = int.MaxValue;
+ //Material 2 design spec - https://m2.material.io/components/sliders/android#discrete-slider
+ //Additional info - https://github.com/material-components/material-components-android/blob/60b0325b39741784fca4d7aba079b65453bc7c66/lib/java/com/google/android/material/slider/res/values/dimens.xml#L27
+ // Thumb diameter per Material Design spec: https://m2.material.io/components/sliders
+ const int ThumbDiameterDp = 20;
+
public static void UpdateMinimum(this SeekBar seekBar, ISlider slider) => UpdateValue(seekBar, slider);
+ // TODO: Make this public in NET 11.
+ internal static void UpdateMinimum(this Slider mSlider, ISlider slider)
+ {
+
+ mSlider.ValueFrom = (float)slider.Minimum;
+ }
+
public static void UpdateMaximum(this SeekBar seekBar, ISlider slider) => UpdateValue(seekBar, slider);
+ // TODO: Make this public in NET 11.
+ internal static void UpdateMaximum(this Slider mSlider, ISlider slider)
+ {
+ mSlider.ValueTo = (float)slider.Maximum;
+ }
+
public static void UpdateValue(this SeekBar seekBar, ISlider slider)
{
var min = slider.Minimum;
@@ -23,61 +44,172 @@ public static void UpdateValue(this SeekBar seekBar, ISlider slider)
seekBar.Progress = (int)((value - min) / (max - min) * PlatformMaxValue);
}
+ // TODO: Make this public in NET 11.
+ internal static void UpdateValue(this Slider mSlider, ISlider slider)
+ {
+ if ((float)slider.Value != mSlider.Value)
+ {
+ mSlider.Value = (float)slider.Value;
+ }
+ }
+
public static void UpdateMinimumTrackColor(this SeekBar seekBar, ISlider slider)
{
- if (slider.MinimumTrackColor != null)
+ if (slider.MinimumTrackColor is not null)
{
seekBar.ProgressTintList = ColorStateList.ValueOf(slider.MinimumTrackColor.ToPlatform());
seekBar.ProgressTintMode = PorterDuff.Mode.SrcIn;
}
}
+ // TODO: Make this public in NET 11.
+ internal static void UpdateMinimumTrackColor(this Slider mSlider, ISlider slider)
+ {
+ if (slider.MinimumTrackColor is not null)
+ {
+ mSlider.TrackActiveTintList = ColorStateList.ValueOf(slider.MinimumTrackColor.ToPlatform());
+ }
+ }
+
public static void UpdateMaximumTrackColor(this SeekBar seekBar, ISlider slider)
{
- if (slider.MaximumTrackColor != null)
+ if (slider.MaximumTrackColor is not null)
{
seekBar.ProgressBackgroundTintList = ColorStateList.ValueOf(slider.MaximumTrackColor.ToPlatform());
seekBar.ProgressBackgroundTintMode = PorterDuff.Mode.SrcIn;
}
}
+ // TODO: Make this public in NET 11.
+ internal static void UpdateMaximumTrackColor(this Slider mSlider, ISlider slider)
+ {
+ if (slider.MaximumTrackColor is not null)
+ {
+ mSlider.TrackInactiveTintList = ColorStateList.ValueOf(slider.MaximumTrackColor.ToPlatform());
+ }
+ }
+
public static void UpdateThumbColor(this SeekBar seekBar, ISlider slider) =>
seekBar.Thumb?.SetColorFilter(slider.ThumbColor, FilterMode.SrcIn);
+ // TODO: Make this public in NET 11.
+ internal static void UpdateThumbColor(this Slider mSlider, ISlider slider)
+ {
+ if (slider.ThumbImageSource is not null && slider.Handler is not null)
+ {
+ var provider = slider.Handler.GetRequiredService();
+ mSlider.UpdateThumbImageSourceAsync(slider, provider)
+ .FireAndForget();
+ }
+ else if (slider.ThumbColor is not null)
+ {
+ mSlider.ThumbTintList = ColorStateList.ValueOf(slider.ThumbColor.ToPlatform());
+ }
+ }
+
public static async Task UpdateThumbImageSourceAsync(this SeekBar seekBar, ISlider slider, IImageSourceServiceProvider provider)
{
var context = seekBar.Context;
-
- if (context == null)
+ if (context is null)
+ {
return;
+ }
var thumbImageSource = slider.ThumbImageSource;
-
- if (thumbImageSource != null)
+ if (thumbImageSource is not null)
{
var service = provider.GetRequiredImageSourceService(thumbImageSource);
var result = await service.GetDrawableAsync(thumbImageSource, context);
-
var thumbDrawable = result?.Value;
- if (seekBar.IsAlive() && thumbDrawable != null)
- seekBar.SetThumb(thumbDrawable);
+ if (seekBar.IsAlive())
+ {
+ if (thumbDrawable is not null)
+ {
+ SetThumbDrawable(seekBar, context, thumbDrawable);
+ }
+ else
+ {
+ SetDefaultThumb(seekBar, slider, context);
+ }
+ }
}
else
{
- seekBar.SetThumb(context.GetDrawable(Resource.Drawable.abc_seekbar_thumb_material));
- if (slider.ThumbColor is null && context.Theme is not null)
+ SetDefaultThumb(seekBar, slider, context);
+ }
+ }
+
+ static void SetThumbDrawable(SeekBar seekBar, Context context, Drawable thumbDrawable)
+ {
+ int thumbSize = (int)context.ToPixels(ThumbDiameterDp);
+
+ if (thumbSize <= 0)
+ {
+ return;
+ }
+
+ using (Bitmap bitmap = Bitmap.CreateBitmap(thumbSize, thumbSize, Bitmap.Config.Argb8888!))
+ using (Canvas canvas = new Canvas(bitmap))
+ {
+ thumbDrawable.SetBounds(0, 0, thumbSize, thumbSize);
+ thumbDrawable.Draw(canvas);
+
+ using (BitmapDrawable finalDrawable = new BitmapDrawable(context.Resources, bitmap))
{
- using var value = new TypedValue();
- context.Theme.ResolveAttribute(global::Android.Resource.Attribute.ColorAccent, value, true);
- var color = new Color(value.Data);
- seekBar.Thumb?.SetColorFilter(color, FilterMode.SrcIn);
+ seekBar.SetThumb(finalDrawable);
}
- else
+ }
+ }
+
+ static void SetDefaultThumb(SeekBar seekBar, ISlider slider, Context context)
+ {
+ seekBar.SetThumb(context.GetDrawable(Resource.Drawable.abc_seekbar_thumb_material));
+
+ if (slider.ThumbColor is null && context.Theme is not null)
+ {
+ using var value = new TypedValue();
+ if (context.Theme.ResolveAttribute(global::Android.Resource.Attribute.ColorAccent, value, true))
+ {
+ seekBar.Thumb?.SetColorFilter(new Color(value.Data), FilterMode.SrcIn);
+ }
+ }
+ else
+ {
+ seekBar.UpdateThumbColor(slider);
+ }
+ }
+
+ // TODO: Make this public in NET 11.
+ internal static async Task UpdateThumbImageSourceAsync(this Slider mSlider, ISlider slider, IImageSourceServiceProvider provider)
+ {
+ var context = mSlider.Context;
+
+ if (context is null)
+ {
+ return;
+ }
+
+ var thumbImageSource = slider.ThumbImageSource;
+
+ if (thumbImageSource is not null)
+ {
+ var service = provider.GetRequiredImageSourceService(thumbImageSource);
+ var result = await service.GetDrawableAsync(thumbImageSource, context);
+
+ var thumbDrawable = result?.Value;
+
+ if (mSlider.IsAlive() && thumbDrawable is not null)
{
- seekBar.UpdateThumbColor(slider);
+ if (slider.ThumbColor is not null)
+ {
+ // Mutate the drawable to avoid affecting other instances
+ thumbDrawable = thumbDrawable.Mutate();
+ thumbDrawable.SetColorFilter(slider.ThumbColor.ToPlatform(), FilterMode.SrcIn);
+ }
+ mSlider.SetCustomThumbDrawable(thumbDrawable);
}
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Core/src/Platform/Android/SwitchExtensions.cs b/src/Core/src/Platform/Android/SwitchExtensions.cs
index e551b53aaac6..48cf9357a3b7 100644
--- a/src/Core/src/Platform/Android/SwitchExtensions.cs
+++ b/src/Core/src/Platform/Android/SwitchExtensions.cs
@@ -1,12 +1,24 @@
+using System.Runtime.CompilerServices;
+using Android.Content.Res;
using Android.Graphics.Drawables;
using ASwitch = AndroidX.AppCompat.Widget.SwitchCompat;
+using MSwitch = Google.Android.Material.MaterialSwitch.MaterialSwitch;
namespace Microsoft.Maui.Platform
{
public static class SwitchExtensions
{
- public static void UpdateIsOn(this ASwitch aSwitch, ISwitch view) =>
+ // Store the original theme tint per MaterialSwitch instance to support:
+ // Per-Activity theming (different Activities can have different themes)
+ // Theme switching at runtime (dark mode toggle)
+ // Thread safety (no shared mutable state)
+ static readonly ConditionalWeakTable _defaultTrackTintCache = new();
+ static readonly ConditionalWeakTable _defaultThumbTintCache = new();
+
+ public static void UpdateIsOn(this ASwitch aSwitch, ISwitch view)
+ {
aSwitch.Checked = view.IsOn;
+ }
public static void UpdateTrackColor(this ASwitch aSwitch, ISwitch view)
{
@@ -22,13 +34,71 @@ public static void UpdateTrackColor(this ASwitch aSwitch, ISwitch view)
}
}
+ // TODO: material3 - make it public in .net 11
+ internal static void UpdateTrackColor(this MSwitch materialSwitch, ISwitch view)
+ {
+ // Cache the original theme tint before first modification
+ // so it can be restored when TrackColor is cleared.
+ if (!_defaultTrackTintCache.TryGetValue(materialSwitch, out var defaultTrackTintList))
+ {
+ var currentTint = materialSwitch.TrackTintList;
+ if (currentTint is not null)
+ {
+ _defaultTrackTintCache.Add(materialSwitch, currentTint);
+ defaultTrackTintList = currentTint;
+ }
+ }
+
+ var trackColor = view.TrackColor;
+
+ if (trackColor is not null)
+ {
+ materialSwitch.TrackTintList = ColorStateList.ValueOf(trackColor.ToPlatform());
+ }
+ else
+ {
+ materialSwitch.TrackTintList = defaultTrackTintList;
+ }
+ }
+
+ internal static void UpdateThumbColor(this MSwitch materialSwitch, ISwitch view)
+ {
+ // Cache the original theme tint before first modification
+ // so it can be restored when ThumbColor is cleared.
+ if (!_defaultThumbTintCache.TryGetValue(materialSwitch, out var defaultThumbTintList))
+ {
+ var currentTint = materialSwitch.ThumbTintList;
+ if (currentTint is not null)
+ {
+ _defaultThumbTintCache.Add(materialSwitch, currentTint);
+ defaultThumbTintList = currentTint;
+ }
+ }
+
+ var thumbColor = view.ThumbColor;
+ if (thumbColor is not null)
+ {
+ materialSwitch.ThumbTintList = ColorStateList.ValueOf(thumbColor.ToPlatform());
+ }
+ else
+ {
+ materialSwitch.ThumbTintList = defaultThumbTintList;
+ }
+ }
+
public static void UpdateThumbColor(this ASwitch aSwitch, ISwitch view)
{
var thumbColor = view.ThumbColor;
if (thumbColor is not null)
{
- aSwitch.ThumbDrawable?.SetColorFilter(thumbColor, FilterMode.SrcAtop);
+ // Use ThumbTintList instead of SetColorFilter to preserve the thumb shadow
+ // SetColorFilter flattens the drawable and removes the shadow effect
+ aSwitch.ThumbTintList = ColorStateListExtensions.CreateDefault(thumbColor.ToPlatform());
+ }
+ else
+ {
+ aSwitch.ThumbTintList = null;
}
}
diff --git a/src/Core/src/Platform/Android/TextViewExtensions.cs b/src/Core/src/Platform/Android/TextViewExtensions.cs
index fd3fe5cbde17..a8d24b04f08a 100644
--- a/src/Core/src/Platform/Android/TextViewExtensions.cs
+++ b/src/Core/src/Platform/Android/TextViewExtensions.cs
@@ -1,5 +1,6 @@
using System;
using System.Net;
+using System.Runtime.CompilerServices;
using Android.Graphics;
using Android.Text;
using Android.Views;
@@ -12,6 +13,8 @@ namespace Microsoft.Maui.Platform
{
public static class TextViewExtensions
{
+ static readonly ConditionalWeakTable> s_htmlGenerations = new();
+
public static void UpdateTextPlainText(this TextView textView, IText label)
{
textView.Text = label.Text;
@@ -27,12 +30,28 @@ internal static void UpdateTextHtml(this TextView textView, string text)
{
var htmlText = WebUtility.HtmlDecode(text);
- if (OperatingSystem.IsAndroidVersionAtLeast(24))
- textView.SetText(Html.FromHtml(htmlText, FromHtmlOptions.ModeCompact), BufferType.Spannable);
- else
+ // Track generation to prevent stale image-load callbacks from overwriting newer text
+ var generation = s_htmlGenerations.GetOrCreateValue(textView);
+ int currentGen = ++generation.Value;
+
+ ImageGetter? imageGetter = null;
+
+ void SetTextHtml()
+ {
+ if (generation.Value != currentGen)
+ return;
+
+ imageGetter ??= new ImageGetter(textView.Resources!, SetTextHtml);
+
+ if (OperatingSystem.IsAndroidVersionAtLeast(24))
+ textView.SetText(Html.FromHtml(htmlText, FromHtmlOptions.ModeCompact, imageGetter, null), BufferType.Spannable);
+ else
#pragma warning disable CS0618 // Type or member is obsolete
- textView.SetText(Html.FromHtml(htmlText), BufferType.Spannable);
+ textView.SetText(Html.FromHtml(htmlText, imageGetter, null), BufferType.Spannable);
#pragma warning restore CS0618 // Type or member is obsolete
+ }
+
+ SetTextHtml();
}
public static void UpdateTextColor(this TextView textView, ITextStyle textStyle)
@@ -87,7 +106,7 @@ public static void UpdateVerticalTextAlignment(this TextView textView, ITextAlig
public static void UpdatePadding(this TextView textView, ILabel label)
{
- textView.SetPadding(
+ textView.SetPaddingRelative(
(int)textView.ToPixels(label.Padding.Left),
(int)textView.ToPixels(label.Padding.Top),
(int)textView.ToPixels(label.Padding.Right),
diff --git a/src/Core/src/Platform/Android/ViewExtensions.cs b/src/Core/src/Platform/Android/ViewExtensions.cs
index 8bbb4fe6a141..5cccd5bc9068 100644
--- a/src/Core/src/Platform/Android/ViewExtensions.cs
+++ b/src/Core/src/Platform/Android/ViewExtensions.cs
@@ -345,7 +345,7 @@ internal static void UpdateBackground(this AView platformView, Paint? background
platformView.Background = drawable;
}
}
- else if (platformView is LayoutViewGroup)
+ else if (platformView is LayoutViewGroup or ContentViewGroup)
{
platformView.Background = null;
}
@@ -353,7 +353,16 @@ internal static void UpdateBackground(this AView platformView, Paint? background
public static void UpdateOpacity(this AView platformView, IView view) => platformView.UpdateOpacity(view.Opacity);
- internal static void UpdateOpacity(this AView platformView, double opacity) => platformView.Alpha = (float)opacity;
+ internal static void UpdateOpacity(this AView platformView, double opacity)
+ {
+ platformView.Alpha = (float)opacity;
+
+ if (platformView is WrapperView wrapperView && wrapperView.Shadow != null && wrapperView.IsLoaded())
+ {
+ // Post invalidation to ensure shadow redraws correctly after opacity changes.
+ wrapperView.ScheduleInvalidate();
+ }
+ }
public static void UpdateFlowDirection(this AView platformView, IView view)
{
@@ -640,9 +649,17 @@ internal static IDisposable OnLoaded(this View view, Action action)
return;
}
- disposable?.Dispose();
+ // Store local reference to allow cancellation inside the Post callback
+ var localDisposable = disposable;
disposable = null;
- action();
+ view.Post(() =>
+ {
+ if (view.IsAttachedToWindow && localDisposable is not null)
+ {
+ action();
+ localDisposable.Dispose();
+ }
+ });
};
view.ViewAttachedToWindow += routedEventHandler;
diff --git a/src/Core/src/Platform/Android/WrapperView.cs b/src/Core/src/Platform/Android/WrapperView.cs
index 8f56c13c8961..4ea62dfba644 100644
--- a/src/Core/src/Platform/Android/WrapperView.cs
+++ b/src/Core/src/Platform/Android/WrapperView.cs
@@ -201,5 +201,10 @@ void CleanupContainerView(AView containerView, Action clearWrapperView)
clearWrapperView.Invoke();
}
}
+
+ internal void ScheduleInvalidate()
+ {
+ Post(() => Invalidate());
+ }
}
}
diff --git a/src/Core/src/Platform/Windows/MauiPasswordTextBox.cs b/src/Core/src/Platform/Windows/MauiPasswordTextBox.cs
index b68052c54995..ca9b594b3b17 100644
--- a/src/Core/src/Platform/Windows/MauiPasswordTextBox.cs
+++ b/src/Core/src/Platform/Windows/MauiPasswordTextBox.cs
@@ -10,6 +10,7 @@
using Microsoft.UI.Xaml.Input;
using Windows.System;
using Windows.UI.Core;
+using Microsoft.UI.Xaml.Automation.Peers;
namespace Microsoft.Maui.Platform
{
@@ -151,6 +152,11 @@ protected override void OnKeyDown(KeyRoutedEventArgs e)
base.OnKeyDown(e);
}
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new MauiPasswordTextBoxAutomationPeer(this);
+ }
+
private void OnNativeTextChanging(TextBox sender, TextBoxTextChangingEventArgs args)
{
// We are handling the CursorPosition issue at Platform.TextBoxExtensions.UpdateText method
@@ -348,4 +354,17 @@ static InputScope CreateInputScope(InputScopeNameValue value) =>
}
};
}
+
+ //TODO: Make it public in NET 11
+ internal partial class MauiPasswordTextBoxAutomationPeer : TextBoxAutomationPeer
+ {
+ public MauiPasswordTextBoxAutomationPeer(MauiPasswordTextBox owner) : base(owner)
+ {
+ }
+
+ protected override bool IsPasswordCore()
+ {
+ return ((MauiPasswordTextBox)Owner).IsPassword;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Core/src/Platform/Windows/SliderExtensions.cs b/src/Core/src/Platform/Windows/SliderExtensions.cs
index 5c0f0e29e0a6..1e005424ffd2 100644
--- a/src/Core/src/Platform/Windows/SliderExtensions.cs
+++ b/src/Core/src/Platform/Windows/SliderExtensions.cs
@@ -6,6 +6,7 @@
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Media.Imaging;
+using Microsoft.UI.Xaml.Media;
namespace Microsoft.Maui.Platform
{
@@ -19,7 +20,9 @@ static void UpdateIncrement(this Slider nativeSlider, ISlider slider)
// Setting the Slider SmallChange property to 0 would throw an System.ArgumentException.
if (difference != 0)
+ {
stepping = Math.Min((difference) / 1000, 1);
+ }
nativeSlider.StepFrequency = stepping;
}
@@ -39,19 +42,14 @@ public static void UpdateMaximum(this Slider nativeSlider, ISlider slider)
public static void UpdateValue(this Slider nativeSlider, ISlider slider)
{
if (nativeSlider.Value != slider.Value)
+ {
nativeSlider.Value = slider.Value;
+ }
}
public static void UpdateMinimumTrackColor(this Slider platformSlider, ISlider slider)
{
- var brush = slider.MinimumTrackColor?.ToPlatform();
-
- if (brush is null)
- platformSlider.Resources.RemoveKeys(MinimumTrackColorResourceKeys);
- else
- platformSlider.Resources.SetValueForAllKey(MinimumTrackColorResourceKeys, brush);
-
- platformSlider.RefreshThemeResources();
+ UpdateColor(platformSlider, MinimumTrackColorResourceKeys, slider.MinimumTrackColor?.ToPlatform());
}
static readonly string[] MinimumTrackColorResourceKeys =
@@ -64,14 +62,7 @@ public static void UpdateMinimumTrackColor(this Slider platformSlider, ISlider s
public static void UpdateMaximumTrackColor(this Slider platformSlider, ISlider slider)
{
- var brush = slider.MaximumTrackColor?.ToPlatform();
-
- if (brush == null)
- platformSlider.Resources.RemoveKeys(MaximumTrackColorResourceKeys);
- else
- platformSlider.Resources.SetValueForAllKey(MaximumTrackColorResourceKeys, brush);
-
- platformSlider.RefreshThemeResources();
+ UpdateColor(platformSlider, MaximumTrackColorResourceKeys, slider.MaximumTrackColor?.ToPlatform());
}
static readonly string[] MaximumTrackColorResourceKeys =
@@ -84,14 +75,7 @@ public static void UpdateMaximumTrackColor(this Slider platformSlider, ISlider s
public static void UpdateThumbColor(this Slider platformSlider, ISlider slider)
{
- var brush = slider.ThumbColor?.ToPlatform();
-
- if (brush is null)
- platformSlider.Resources.RemoveKeys(ThumbColorResourceKeys);
- else
- platformSlider.Resources.SetValueForAllKey(ThumbColorResourceKeys, brush);
-
- platformSlider.RefreshThemeResources();
+ UpdateColor(platformSlider, ThumbColorResourceKeys, slider.ThumbColor?.ToPlatform());
}
static readonly string[] ThumbColorResourceKeys =
@@ -144,13 +128,43 @@ void OnImageOpened(object sender, RoutedEventArgs e)
}
if (nativeSlider.Parent is FrameworkElement frameworkElement)
+ {
frameworkElement.InvalidateMeasure();
+ }
}
- ;
}
nativeSlider.ThumbImageSource = nativeThumbImageSource?.Value;
}
}
+
+ static readonly string[] BackgroundColorResourceKeys =
+ {
+ "SliderContainerBackground",
+ "SliderContainerBackgroundPointerOver",
+ "SliderContainerBackgroundPressed",
+ "SliderContainerBackgroundDisabled",
+ };
+
+ internal static void UpdateBackgroundColor(this MauiSlider platformSlider, ISlider slider)
+ {
+ UpdateColor(platformSlider, BackgroundColorResourceKeys, slider.Background?.ToPlatform());
+ }
+
+ static void UpdateColor(Slider platformSlider, string[] keys, Brush? brush)
+ {
+ ResourceDictionary resource = platformSlider.Resources;
+
+ if (brush is null)
+ {
+ resource.RemoveKeys(keys);
+ }
+ else
+ {
+ resource.SetValueForAllKey(keys, brush);
+ }
+
+ platformSlider.RefreshThemeResources();
+ }
}
}
\ No newline at end of file
diff --git a/src/Core/src/Platform/Windows/StackNavigationManager.cs b/src/Core/src/Platform/Windows/StackNavigationManager.cs
index 8e805d1bc245..a86588ceafd3 100644
--- a/src/Core/src/Platform/Windows/StackNavigationManager.cs
+++ b/src/Core/src/Platform/Windows/StackNavigationManager.cs
@@ -10,6 +10,8 @@ namespace Microsoft.Maui.Platform
{
public class StackNavigationManager
{
+ static readonly SuppressNavigationTransitionInfo s_suppressTransition = new();
+
IView? _currentPage;
IMauiContext _mauiContext;
Frame? _navigationFrame;
@@ -120,7 +122,9 @@ protected virtual Type GetDestinationPageType() =>
protected virtual NavigationTransitionInfo? GetNavigationTransition(NavigationRequest args)
{
if (!args.Animated)
- return null;
+ {
+ return s_suppressTransition;
+ }
// GoBack just plays the animation in reverse so we always just return the same animation
return new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight };
diff --git a/src/Core/src/Platform/Windows/SwitchExtensions.cs b/src/Core/src/Platform/Windows/SwitchExtensions.cs
index ca36152c1c06..8f25c1525c1f 100644
--- a/src/Core/src/Platform/Windows/SwitchExtensions.cs
+++ b/src/Core/src/Platform/Windows/SwitchExtensions.cs
@@ -91,5 +91,18 @@ public static void UpdateThumbColor(this ToggleSwitch toggleSwitch, ISwitch view
"ToggleSwitchKnobFillOffDisabled");
}
}
+
+ internal static void UpdateMinWidth(this ToggleSwitch toggleSwitch, ISwitch view)
+ {
+ double minWidth = view.MinimumWidth;
+ if (double.IsNaN(minWidth))
+ {
+ toggleSwitch.MinWidth = 0;
+ }
+ else
+ {
+ toggleSwitch.MinWidth = minWidth;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Core/src/Platform/Windows/TimePickerExtensions.cs b/src/Core/src/Platform/Windows/TimePickerExtensions.cs
index 0fc4d6dfa813..c9fe3a01c695 100644
--- a/src/Core/src/Platform/Windows/TimePickerExtensions.cs
+++ b/src/Core/src/Platform/Windows/TimePickerExtensions.cs
@@ -13,7 +13,7 @@ public static class TimePickerExtensions
{
public static void UpdateTime(this TimePicker nativeTimePicker, ITimePicker timePicker)
{
- nativeTimePicker.Time = timePicker.Time ?? TimeSpan.Zero;
+ nativeTimePicker.SelectedTime = timePicker.Time;
if (timePicker.Format?.Contains('H', StringComparison.Ordinal) == true)
{
diff --git a/src/Core/src/Platform/Windows/WindowRootView.cs b/src/Core/src/Platform/Windows/WindowRootView.cs
index c7e9a7886efc..f89b51a3d550 100644
--- a/src/Core/src/Platform/Windows/WindowRootView.cs
+++ b/src/Core/src/Platform/Windows/WindowRootView.cs
@@ -383,6 +383,9 @@ void OnNavigationViewControlOnApplyTemplateFinished(object? sender, EventArgs e)
{
NavigationViewControl.OnApplyTemplateFinished -= OnNavigationViewControlOnApplyTemplateFinished;
NavigationViewControl.ButtonHolderGrid!.SizeChanged += OnButtonHolderGridSizeChanged;
+
+ // Need to update the background color for the buttons after the template is applied
+ UpdateBackgroundColorForButtons();
}
ContentChanged?.Invoke(this, EventArgs.Empty);
diff --git a/src/Core/src/Platform/Windows/WindowRootViewContainer.cs b/src/Core/src/Platform/Windows/WindowRootViewContainer.cs
index dae4bcf97c50..6abef173dc98 100644
--- a/src/Core/src/Platform/Windows/WindowRootViewContainer.cs
+++ b/src/Core/src/Platform/Windows/WindowRootViewContainer.cs
@@ -1,6 +1,9 @@
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Input;
+using Microsoft.UI.Xaml.Media;
using Windows.Foundation;
namespace Microsoft.Maui.Platform
@@ -9,6 +12,10 @@ internal partial class WindowRootViewContainer : Panel
{
FrameworkElement? _topPage;
UIElementCollection? _cachedChildren;
+ bool _modalFocusTrapActive;
+ TypedEventHandler? _gettingFocusHandler;
+ readonly Dictionary _originalTabNavigation = new();
+ readonly Dictionary _originalIsHitTestVisible = new();
[SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "Panel.Children property is banned to enforce use of this CachedChildren property.")]
internal UIElementCollection CachedChildren
@@ -56,27 +63,187 @@ internal void AddPage(FrameworkElement pageView)
{
if (!CachedChildren.Contains(pageView))
{
+ if (_topPage is not null)
+ {
+ // Block pointer/touch input on the page being covered
+ _originalIsHitTestVisible[_topPage] = _topPage.IsHitTestVisible;
+ _topPage.IsHitTestVisible = false;
+ }
+
int indexOFTopPage = 0;
- if (_topPage != null)
+ if (_topPage is not null)
indexOFTopPage = CachedChildren.IndexOf(_topPage) + 1;
CachedChildren.Insert(indexOFTopPage, pageView);
_topPage = pageView;
+
+ // When covering another page, activate keyboard focus trapping
+ // so Tab cannot escape the modal — mirroring how WinUI ContentDialog works.
+ if (indexOFTopPage > 0)
+ {
+ // Cycle Tab within the modal so it never escapes to underlying pages
+ if (pageView is Control modalControl)
+ {
+ _originalTabNavigation[pageView] = modalControl.TabFocusNavigation;
+ modalControl.TabFocusNavigation = KeyboardNavigationMode.Cycle;
+ }
+
+ EnableModalFocusTrap();
+ }
+
+ TryMoveFocusToPage(_topPage);
}
}
internal void RemovePage(FrameworkElement pageView)
{
- int indexOFTopPage = -1;
- if (_topPage != null)
- indexOFTopPage = CachedChildren.IndexOf(_topPage) - 1;
+ // Clean up any pending Loaded handler to prevent memory leaks
+ pageView.Loaded -= OnPageLoadedForFocus;
+
+ // Restore the original TabFocusNavigation value that was overridden in AddPage
+ if (_originalTabNavigation.Remove(pageView, out var originalMode) && pageView is Control control)
+ {
+ control.TabFocusNavigation = originalMode;
+ }
+
+ // Find the new top page by scanning backwards through children.
+ // CachedChildren may contain non-page elements (e.g., W2DGraphicsView for visual diagnostics),
+ // so we cannot rely on simple index arithmetic. We look for the topmost page that isn't
+ // the one being removed.
+ FrameworkElement? newTopPage = null;
+ int pageCount = 0;
+ for (int i = CachedChildren.Count - 1; i >= 0; i--)
+ {
+ var child = CachedChildren[i] as FrameworkElement;
+ if (child is null || child == pageView)
+ continue;
+
+ // Only count actual page views (WindowRootView), not overlays
+ if (child is not WindowRootView)
+ continue;
+
+ pageCount++;
+ newTopPage ??= child;
+ }
+
+ // Update _topPage BEFORE removing from the collection.
+ // WinUI3 fires GettingFocus synchronously when a focused element is removed from the tree.
+ _topPage = newTopPage;
+
+ // Disable the focus trap if we're back to a single page (no more modals)
+ if (pageCount <= 1)
+ {
+ DisableModalFocusTrap();
+ }
CachedChildren.Remove(pageView);
- if (indexOFTopPage >= 0)
- _topPage = (FrameworkElement)CachedChildren[indexOFTopPage];
+ if (_topPage is not null)
+ {
+ // Re-enable pointer/touch on the revealed page, restoring its original value
+ _topPage.IsHitTestVisible = _originalIsHitTestVisible.Remove(_topPage, out var originalHitTest)
+ ? originalHitTest
+ : true;
+ TryMoveFocusToPage(_topPage);
+ }
+ }
+
+ void EnableModalFocusTrap()
+ {
+ if (!_modalFocusTrapActive)
+ {
+ _gettingFocusHandler ??= new TypedEventHandler(OnContainerGettingFocus);
+ AddHandler(GettingFocusEvent, _gettingFocusHandler, true);
+ _modalFocusTrapActive = true;
+ }
+ }
+
+ void DisableModalFocusTrap()
+ {
+ if (_modalFocusTrapActive && _gettingFocusHandler is not null)
+ {
+ RemoveHandler(GettingFocusEvent, _gettingFocusHandler);
+ _modalFocusTrapActive = false;
+ }
+ }
+
+ void OnContainerGettingFocus(UIElement sender, GettingFocusEventArgs args)
+ {
+ // Guard: only act when trap is explicitly active
+ if (!_modalFocusTrapActive)
+ return;
+
+ if (_topPage is null || args.NewFocusedElement is not DependencyObject newElement)
+ return;
+
+ // Allow focus changes within the current top (modal) page
+ if (newElement == _topPage || IsDescendantOf(newElement, _topPage))
+ return;
+
+ // Focus is trying to leave the modal — redirect it back
+ if (FocusManager.FindFirstFocusableElement(_topPage) is DependencyObject firstFocusable)
+ {
+ args.TrySetNewFocusedElement(firstFocusable);
+ }
+ else
+ {
+ args.TryCancel();
+ }
+ }
+
+ static bool IsDescendantOf(DependencyObject element, DependencyObject ancestor)
+ {
+ var current = VisualTreeHelper.GetParent(element);
+ while (current is not null)
+ {
+ if (current == ancestor)
+ return true;
+ current = VisualTreeHelper.GetParent(current);
+ }
+ return false;
+ }
+
+ static void TryMoveFocusToPage(FrameworkElement page)
+ {
+ if (page.IsLoaded)
+ {
+ SetFocusToFirstElement(page);
+ }
else
- _topPage = null;
+ {
+ page.Loaded -= OnPageLoadedForFocus;
+ page.Loaded += OnPageLoadedForFocus;
+ }
+ }
+
+ static void OnPageLoadedForFocus(object sender, RoutedEventArgs e)
+ {
+ if (sender is FrameworkElement page)
+ {
+ page.Loaded -= OnPageLoadedForFocus;
+ SetFocusToFirstElement(page);
+ }
+ }
+
+ static void SetFocusToFirstElement(FrameworkElement page)
+ {
+ if (FocusManager.FindFirstFocusableElement(page) is UIElement focusableElement)
+ {
+ if (focusableElement.Focus(FocusState.Programmatic))
+ return;
+ }
+
+ if (page.Focus(FocusState.Programmatic))
+ return;
+
+ // If immediate focus failed (visual tree not ready yet), defer until after layout
+ page.DispatcherQueue?.TryEnqueue(() =>
+ {
+ if (FocusManager.FindFirstFocusableElement(page) is UIElement el)
+ el.Focus(FocusState.Programmatic);
+ else
+ page.Focus(FocusState.Programmatic);
+ });
}
internal void AddOverlay(FrameworkElement overlayView)
@@ -90,4 +257,4 @@ internal void RemoveOverlay(FrameworkElement overlayView)
CachedChildren.Remove(overlayView);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Core/src/Platform/iOS/ActivityIndicatorExtensions.cs b/src/Core/src/Platform/iOS/ActivityIndicatorExtensions.cs
index 9e8936dad16d..b00902d937a9 100644
--- a/src/Core/src/Platform/iOS/ActivityIndicatorExtensions.cs
+++ b/src/Core/src/Platform/iOS/ActivityIndicatorExtensions.cs
@@ -6,13 +6,22 @@ public static class ActivityIndicatorExtensions
{
public static void UpdateIsRunning(this UIActivityIndicatorView activityIndicatorView, IActivityIndicator activityIndicator)
{
- if (activityIndicator.IsRunning)
+ // Only show and animate if both IsRunning AND Visibility == Visible
+ if (activityIndicator.IsRunning && activityIndicator.Visibility == Visibility.Visible)
+ {
+ activityIndicatorView.Hidden = false;
activityIndicatorView.StartAnimating();
+ }
else
- activityIndicatorView.StopAnimating();
+ {
+ if (activityIndicatorView.IsAnimating)
+ activityIndicatorView.StopAnimating();
+
+ activityIndicatorView.Hidden = activityIndicator.Visibility != Visibility.Visible;
+ }
}
public static void UpdateColor(this UIActivityIndicatorView activityIndicatorView, IActivityIndicator activityIndicator)
=> activityIndicatorView.Color = activityIndicator.Color?.ToPlatform();
}
-}
\ No newline at end of file
+}
diff --git a/src/Core/src/Platform/iOS/CheckBoxExtensions.cs b/src/Core/src/Platform/iOS/CheckBoxExtensions.cs
index 03b64104326a..300fecc30f04 100644
--- a/src/Core/src/Platform/iOS/CheckBoxExtensions.cs
+++ b/src/Core/src/Platform/iOS/CheckBoxExtensions.cs
@@ -16,6 +16,11 @@ public static void UpdateForeground(this MauiCheckBox platformCheckBox, ICheckBo
{
platformCheckBox.CheckBoxTintColor = solid.Color;
}
+ else if (check.Foreground is null)
+ {
+ // Color was cleared; reset to null so the view inherits the default tint color
+ platformCheckBox.CheckBoxTintColor = null;
+ }
}
}
}
\ No newline at end of file
diff --git a/src/Core/src/Platform/iOS/CollectionViewExtensions.cs b/src/Core/src/Platform/iOS/CollectionViewExtensions.cs
index 3bfefae7b65e..fd57f241bde0 100644
--- a/src/Core/src/Platform/iOS/CollectionViewExtensions.cs
+++ b/src/Core/src/Platform/iOS/CollectionViewExtensions.cs
@@ -5,6 +5,14 @@ namespace Microsoft.Maui.Platform
{
public static class CollectionViewExtensions
{
+ // TODO: Change the modifier to public in .NET 11.
+ internal static void UpdateIsEnabled(this UICollectionView collectionView, IView view)
+ {
+ // UICollectionView inherits from UIScrollView (not UIControl), so we set UserInteractionEnabled
+ // to properly disable user interactions like scrolling and swiping based on IsEnabled
+ collectionView.UserInteractionEnabled = view.IsEnabled;
+ }
+
public static void UpdateVerticalScrollBarVisibility(this UICollectionView collectionView, ScrollBarVisibility scrollBarVisibility)
{
collectionView.ShowsVerticalScrollIndicator = scrollBarVisibility == ScrollBarVisibility.Always || scrollBarVisibility == ScrollBarVisibility.Default;
diff --git a/src/Core/src/Platform/iOS/KeyboardAutoManagerScroll.cs b/src/Core/src/Platform/iOS/KeyboardAutoManagerScroll.cs
index 4fe675055aa6..0850fcbba2dc 100644
--- a/src/Core/src/Platform/iOS/KeyboardAutoManagerScroll.cs
+++ b/src/Core/src/Platform/iOS/KeyboardAutoManagerScroll.cs
@@ -27,6 +27,7 @@ public static class KeyboardAutoManagerScroll
internal static CGRect KeyboardFrame = CGRect.Empty;
static CGPoint TopViewBeginOrigin = new(nfloat.MaxValue, nfloat.MaxValue);
static readonly CGPoint InvalidPoint = new(nfloat.MaxValue, nfloat.MaxValue);
+ static CGSize TopViewBeginContainerSize = CGSize.Empty;
static double AnimationDuration = 0.25;
static UIView? View;
static UIView? ContainerView;
@@ -348,6 +349,7 @@ internal static void AdjustPosition()
if (TopViewBeginOrigin == InvalidPoint)
{
TopViewBeginOrigin = new CGPoint(ContainerView.Frame.X, ContainerView.Frame.Y);
+ TopViewBeginContainerSize = ContainerView.Frame.Size;
}
var rootViewOrigin = new CGPoint(ContainerView.Frame.GetMinX(), ContainerView.Frame.GetMinY());
@@ -933,11 +935,25 @@ static void RestorePosition()
&& (ContainerView.Frame.X != TopViewBeginOrigin.X || ContainerView.Frame.Y != TopViewBeginOrigin.Y)
&& TopViewBeginOrigin != InvalidPoint)
{
- var rect = ContainerView.Frame;
- rect.X = TopViewBeginOrigin.X;
- rect.Y = TopViewBeginOrigin.Y;
+ // if the container size changed since the keyboard appeared, the device was rotated while
+ // the keyboard was visible. the stored origin belongs to the previous orientation, so skip
+ // the restore and let the view settle naturally in the new orientation.
+ var currentSize = ContainerView.Frame.Size;
+ // use a 1pt tolerance to guard against sub-pixel floating-point drift in CGSize;
+ // a real orientation change produces a delta of hundreds of points
+ const float SizeChangeTolerance = 1.0f;
+ var sizeChanged = TopViewBeginContainerSize != CGSize.Empty
+ && (Math.Abs(currentSize.Width - TopViewBeginContainerSize.Width) > SizeChangeTolerance
+ || Math.Abs(currentSize.Height - TopViewBeginContainerSize.Height) > SizeChangeTolerance);
+
+ if (!sizeChanged)
+ {
+ var rect = ContainerView.Frame;
+ rect.X = TopViewBeginOrigin.X;
+ rect.Y = TopViewBeginOrigin.Y;
- UIView.Animate(AnimationDuration, 0, UIViewAnimationOptions.CurveEaseOut, () => AnimateRootView(rect), () => { });
+ UIView.Animate(AnimationDuration, 0, UIViewAnimationOptions.CurveEaseOut, () => AnimateRootView(rect), () => { });
+ }
}
if (ScrolledView is not null && ScrolledView.ContentInset != UIEdgeInsets.Zero)
@@ -954,6 +970,7 @@ static void RestorePosition()
View = null;
ContainerView = null;
TopViewBeginOrigin = InvalidPoint;
+ TopViewBeginContainerSize = CGSize.Empty;
CursorRect = null;
ShouldIgnoreSafeAreaAdjustment = false;
ShouldScrollAgain = false;
diff --git a/src/Core/src/Platform/iOS/MauiActivityIndicator.cs b/src/Core/src/Platform/iOS/MauiActivityIndicator.cs
index 7fc1d8cd1c0c..14aa212e7fa1 100644
--- a/src/Core/src/Platform/iOS/MauiActivityIndicator.cs
+++ b/src/Core/src/Platform/iOS/MauiActivityIndicator.cs
@@ -10,7 +10,10 @@ public class MauiActivityIndicator : UIActivityIndicatorView, IUIViewLifeCycleEv
{
readonly WeakReference? _virtualView;
- bool IsRunning => _virtualView is not null && _virtualView.TryGetTarget(out var a) ? a.IsRunning : false;
+ bool IsRunningAndVisible => _virtualView is not null &&
+ _virtualView.TryGetTarget(out var a) &&
+ a.IsRunning &&
+ a.Visibility == Visibility.Visible;
public MauiActivityIndicator(CGRect rect, IActivityIndicator? virtualView) : base(rect)
{
@@ -22,7 +25,7 @@ public override void Draw(CGRect rect)
{
base.Draw(rect);
- if (IsRunning)
+ if (IsRunningAndVisible)
StartAnimating();
else
StopAnimating();
@@ -32,7 +35,7 @@ public override void LayoutSubviews()
{
base.LayoutSubviews();
- if (IsRunning)
+ if (IsRunningAndVisible)
StartAnimating();
else
StopAnimating();
@@ -57,4 +60,4 @@ public override void MovedToWindow()
_movedToWindow?.Invoke(this, EventArgs.Empty);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Core/src/Platform/iOS/MauiLabel.cs b/src/Core/src/Platform/iOS/MauiLabel.cs
index 34784fe763cc..f1237e42e9bb 100644
--- a/src/Core/src/Platform/iOS/MauiLabel.cs
+++ b/src/Core/src/Platform/iOS/MauiLabel.cs
@@ -35,7 +35,20 @@ public MauiLabel()
public override void DrawText(RectangleF rect)
{
- rect = TextInsets.InsetRect(rect);
+ var insets = TextInsets;
+
+ // Respect RTL (flip left/right insets)
+ if (EffectiveUserInterfaceLayoutDirection == UIUserInterfaceLayoutDirection.RightToLeft)
+ {
+ insets = new UIEdgeInsets(
+ insets.Top,
+ insets.Right,
+ insets.Bottom,
+ insets.Left);
+ }
+
+ rect = insets.InsetRect(rect);
+
if (_verticalAlignment != UIControlContentVerticalAlignment.Center
&& _verticalAlignment != UIControlContentVerticalAlignment.Fill)
diff --git a/src/Core/src/Platform/iOS/MauiPageControl.cs b/src/Core/src/Platform/iOS/MauiPageControl.cs
index 66dfa2f8456a..97ba453ebcfc 100644
--- a/src/Core/src/Platform/iOS/MauiPageControl.cs
+++ b/src/Core/src/Platform/iOS/MauiPageControl.cs
@@ -9,9 +9,11 @@ namespace Microsoft.Maui.Platform
public class MauiPageControl : UIPageControl, IUIViewLifeCycleEvents
{
const int DefaultIndicatorSize = 6;
+ const double IndicatorSizeTolerance = 0.001;
WeakReference? _indicatorView;
bool _updatingPosition;
+ double _lastAppliedIndicatorSize = -1;
public MauiPageControl()
{
@@ -66,10 +68,17 @@ public void UpdateIndicatorSize()
if (IndicatorSize == 0 || IndicatorSize == DefaultIndicatorSize)
return;
+ if (Math.Abs(IndicatorSize - _lastAppliedIndicatorSize) < IndicatorSizeTolerance)
+ return;
+
float scale = (float)IndicatorSize / DefaultIndicatorSize;
var newTransform = CGAffineTransform.MakeScale(scale, scale);
+ foreach (var view in Subviews)
+ {
+ view.Transform = newTransform;
+ }
- Transform = newTransform;
+ _lastAppliedIndicatorSize = IndicatorSize;
}
public void UpdatePosition()
diff --git a/src/Core/src/Platform/iOS/MauiTextField.cs b/src/Core/src/Platform/iOS/MauiTextField.cs
index f1bee62410d6..b4f13f8cf601 100644
--- a/src/Core/src/Platform/iOS/MauiTextField.cs
+++ b/src/Core/src/Platform/iOS/MauiTextField.cs
@@ -33,7 +33,7 @@ public override string? Text
base.Text = value;
- if (old != value)
+ if (old != value && !_suppressTextPropertySet)
TextPropertySet?.Invoke(this, EventArgs.Empty);
}
}
@@ -84,5 +84,17 @@ public override void MovedToWindow()
public event EventHandler? TextPropertySet;
[UnconditionalSuppressMessage("Memory", "MEM0001", Justification = "Proven safe in test: MemoryTests.HandlerDoesNotLeak")]
internal event EventHandler? SelectionChanged;
+
+ ///
+ /// When set to true , suppresses the event during
+ /// controlled text updates (e.g., preserving text across IsPassword toggles) to avoid
+ /// triggering unintended binding/handler side effects.
+ ///
+ bool _suppressTextPropertySet;
+
+ internal void SuppressTextPropertySet(bool suppress)
+ {
+ _suppressTextPropertySet = suppress;
+ }
}
}
\ No newline at end of file
diff --git a/src/Core/src/Platform/iOS/MauiView.cs b/src/Core/src/Platform/iOS/MauiView.cs
index 8319ff4246d6..4a2b0972f82e 100644
--- a/src/Core/src/Platform/iOS/MauiView.cs
+++ b/src/Core/src/Platform/iOS/MauiView.cs
@@ -72,6 +72,13 @@ public abstract class MauiView : UIView, ICrossPlatformLayoutBacking, IVisualTre
// Null means not yet determined. Invalidated when view hierarchy changes.
bool? _parentHandlesSafeArea;
+ // Cached UICollectionView parent detection to avoid repeated hierarchy checks.
+ bool? _collectionViewDescendant;
+
+ // Cached Window safe area padding for CollectionView children to detect changes.
+ // Uses SafeAreaPadding with EqualsAtPixelLevel() to absorb sub-pixel animation noise.
+ SafeAreaPadding _lastWindowSafeAreaPadding;
+
// Keyboard tracking
CGRect _keyboardFrame = CGRect.Empty;
bool _isKeyboardShowing;
@@ -125,9 +132,18 @@ bool RespondsToSafeArea()
// To prevent this, we ignore safe area calculations on child views when they are inside a scroll view.
// The scrollview itself is responsible for applying the correct insets, and child views should not apply additional safe area logic.
//
+ // EXCEPTION: CollectionView items must handle their own safe area because UICollectionView (which inherits from UIScrollView)
+ // does not automatically apply safe area insets to individual cells. Without this exception, CollectionView content
+ // would render under the notch and home indicator.
+ //
// For more details and implementation specifics, see MauiScrollView.cs, which contains the logic for safe area management
// within scroll views and explains how this interacts with the overall layout system.
- _scrollViewDescendant = this.GetParentOfType() is not null;
+ var scrollViewParent = this.GetParentOfType();
+ _scrollViewDescendant = scrollViewParent is not null && scrollViewParent is not UICollectionView;
+
+ // Cache whether this view is inside a UICollectionView for use in CrossPlatformArrange()
+ _collectionViewDescendant = scrollViewParent is UICollectionView;
+
return !_scrollViewDescendant.Value;
}
@@ -294,7 +310,12 @@ void OnKeyboardWillHide(NSNotification notification)
SafeAreaPadding GetAdjustedSafeAreaInsets()
{
- var baseSafeArea = SafeAreaInsets.ToSafeAreaInsets();
+ // CollectionView cells don't receive SafeAreaInsetsDidChange notifications, so their SafeAreaInsets
+ // property may be stale during layout (especially after rotation). Use Window.SafeAreaInsets instead,
+ // which always reflects the current device orientation and safe area state.
+ var baseSafeArea = _collectionViewDescendant == true && Window is not null
+ ? Window.SafeAreaInsets.ToSafeAreaInsets()
+ : SafeAreaInsets.ToSafeAreaInsets();
// Check if keyboard-aware safe area adjustments are needed
if (View is ISafeAreaView2 safeAreaPage && _isKeyboardShowing)
@@ -524,6 +545,18 @@ Size CrossPlatformMeasure(double widthConstraint, double heightConstraint)
/// The bounds rectangle to arrange within
void CrossPlatformArrange(CGRect bounds)
{
+ // Force safe area revalidation for CollectionView cells when Window safe area changes.
+ if (View is ISafeAreaView or ISafeAreaView2 && _collectionViewDescendant == true && Window is not null)
+ {
+ var currentWindowPadding = Window.SafeAreaInsets.ToSafeAreaInsets();
+ if (!currentWindowPadding.EqualsAtPixelLevel(_lastWindowSafeAreaPadding))
+ {
+ _lastWindowSafeAreaPadding = currentWindowPadding;
+ _safeAreaInvalidated = true;
+ ValidateSafeArea();
+ }
+ }
+
if (_appliesSafeAreaAdjustments)
{
bounds = AdjustForSafeArea(bounds);
@@ -768,6 +801,8 @@ public override void MovedToWindow()
_scrollViewDescendant = null;
_parentHandlesSafeArea = null;
+ _collectionViewDescendant = null;
+ _lastWindowSafeAreaPadding = SafeAreaPadding.Empty;
// Notify any subscribers that this view has been moved to a window
_movedToWindow?.Invoke(this, EventArgs.Empty);
diff --git a/src/Core/src/Platform/iOS/SearchBarExtensions.cs b/src/Core/src/Platform/iOS/SearchBarExtensions.cs
index ec3a3853df32..558e7dec60f8 100644
--- a/src/Core/src/Platform/iOS/SearchBarExtensions.cs
+++ b/src/Core/src/Platform/iOS/SearchBarExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using CoreGraphics;
using Foundation;
using Microsoft.Maui.Graphics;
using UIKit;
@@ -35,14 +36,29 @@ internal static void UpdateBackground(this UISearchBar uiSearchBar, ISearchBar s
{
var background = searchBar.Background;
- if (background is SolidPaint solidPaint)
- uiSearchBar.BarTintColor = solidPaint.Color.ToPlatform();
-
- if (background is GradientPaint gradientPaint)
- ViewExtensions.UpdateBackground(uiSearchBar, gradientPaint);
-
- if (background == null)
- uiSearchBar.BarTintColor = UISearchBar.Appearance.BarTintColor;
+ switch (background)
+ {
+ case null:
+ uiSearchBar.BarTintColor = UISearchBar.Appearance.BarTintColor;
+ break;
+
+ case SolidPaint solid:
+ if (solid.Color == Colors.Transparent)
+ {
+ uiSearchBar.BackgroundImage = new UIImage();
+ uiSearchBar.BarTintColor = UIColor.Clear;
+ }
+ else
+ {
+ uiSearchBar.BackgroundImage = null;
+ uiSearchBar.BarTintColor = solid.Color.ToPlatform();
+ }
+ break;
+
+ case GradientPaint gradientPaint:
+ ViewExtensions.UpdateBackground(uiSearchBar, gradientPaint);
+ break;
+ }
}
public static void UpdateIsEnabled(this UISearchBar uiSearchBar, ISearchBar searchBar)
@@ -119,26 +135,193 @@ public static void UpdateIsReadOnly(this UISearchBar uiSearchBar, ISearchBar sea
internal static bool ShouldShowCancelButton(this ISearchBar searchBar) =>
!string.IsNullOrEmpty(searchBar.Text);
+ // Tag used to identify the cancel button color overlay view added on iOS 26+.
+ // Value 0x53424343 encodes "SBCC" (SearchBarCancelColor) to avoid collisions with system-assigned tags.
+ const nint CancelButtonColorOverlayTag = unchecked((nint)0x53424343);
+
public static void UpdateCancelButton(this UISearchBar uiSearchBar, ISearchBar searchBar)
{
uiSearchBar.ShowsCancelButton = searchBar.ShouldShowCancelButton();
// We can't cache the cancel button reference because iOS drops it when it's not displayed
- // and creates a brand new one when necessary, so we have to look for it each time
- var cancelButton = uiSearchBar.FindDescendantView();
+ // and creates a brand new one when necessary, so we have to look for it each time.
+ // Exclude UIButton instances that are descendants of UITextField — those are the
+ // text-clear button inside the search field, not the cancel button outside it.
+ var cancelButton = uiSearchBar.FindDescendantView(
+ btn => btn.FindParent(v => v is UITextField) == null);
if (cancelButton == null)
+ {
+ // Cancel button is hidden — remove any overlay we previously added
+ if (OperatingSystem.IsIOSVersionAtLeast(26))
+ RemoveCancelButtonOverlay(uiSearchBar);
return;
+ }
if (searchBar.CancelButtonColor != null)
{
- cancelButton.SetTitleColor(searchBar.CancelButtonColor.ToPlatform(), UIControlState.Normal);
- cancelButton.SetTitleColor(searchBar.CancelButtonColor.ToPlatform(), UIControlState.Highlighted);
- cancelButton.SetTitleColor(searchBar.CancelButtonColor.ToPlatform(), UIControlState.Disabled);
+ var platformColor = searchBar.CancelButtonColor.ToPlatform();
+
+ cancelButton.SetTitleColor(platformColor, UIControlState.Normal);
+ cancelButton.SetTitleColor(platformColor, UIControlState.Highlighted);
+ cancelButton.SetTitleColor(platformColor, UIControlState.Disabled);
+ // On Mac, the cancel button is rendered as an icon (X mark) rather than text,
+ // so TintColor must be used to apply the color to the icon.
if (cancelButton.TraitCollection.UserInterfaceIdiom == UIUserInterfaceIdiom.Mac)
- cancelButton.TintColor = searchBar.CancelButtonColor.ToPlatform();
+ {
+ cancelButton.TintColor = platformColor;
+ }
+
+ // On iOS 26+, UIKit overrides TintColor/UIButtonConfiguration on every layout
+ // pass, making standard color APIs ineffective for the cancel button icon.
+ // Place a colored UIImageView sibling on top of the cancel button to apply
+ // the color outside UIButton's rendering pipeline.
+ // Defer via DispatchAsync so the cancel button frame is valid after layout.
+ // Capture the search bar (not the button) to always look up the current
+ // cancel button instance — iOS 26 may recreate it during theme transitions.
+ if (OperatingSystem.IsIOSVersionAtLeast(26))
+ {
+ var weakSearchBar = new WeakReference(uiSearchBar);
+ var weakVirtualView = new WeakReference(searchBar);
+ CoreFoundation.DispatchQueue.MainQueue.DispatchAsync(() =>
+ {
+ if (!weakSearchBar.TryGetTarget(out var sb))
+ {
+ return;
+ }
+
+ if (!weakVirtualView.TryGetTarget(out var virtualSearchBar))
+ {
+ return;
+ }
+
+ // Re-evaluate the desired cancel button color; it may have been
+ // changed or cleared since this callback was queued.
+ var currentColor = virtualSearchBar.CancelButtonColor;
+ if (currentColor is null)
+ {
+ RemoveCancelButtonOverlay(sb);
+ return;
+ }
+
+ var currentButton = sb.FindDescendantView(
+ btn => btn.FindParent(v => v is UITextField) == null);
+ if (currentButton is not null)
+ {
+ ApplyCancelButtonOverlay(sb, currentButton, currentColor.ToPlatform());
+ }
+ });
+ }
+ }
+ else if (OperatingSystem.IsIOSVersionAtLeast(26))
+ {
+ // CancelButtonColor was cleared — remove any overlay we previously added
+ RemoveCancelButtonOverlay(uiSearchBar);
+ }
+ }
+
+ // Schedules a deferred retry of ApplyCancelButtonOverlay on the main queue.
+ // Used when the cancel button is not ready for layout (detached or zero-frame).
+ static void ScheduleOverlayRetry(UISearchBar uiSearchBar, UIColor color, int retryCount)
+ {
+ var weakSB = new WeakReference(uiSearchBar);
+ CoreFoundation.DispatchQueue.MainQueue.DispatchAsync(() =>
+ {
+ if (!weakSB.TryGetTarget(out var sb)) return;
+ var btn = sb.FindDescendantView(
+ b => b.FindParent(v => v is UITextField) == null);
+ if (btn != null)
+ ApplyCancelButtonOverlay(sb, btn, color, retryCount + 1);
+ });
+ }
+
+ static void ApplyCancelButtonOverlay(UISearchBar uiSearchBar, UIButton cancelButton, UIColor color, int retryCount = 0)
+ {
+ var parentView = cancelButton.Superview;
+ if (parentView == null)
+ {
+ // Button was detached by UIKit (e.g. mid-transition during a theme change).
+ // Retry with a fresh lookup so we always work with the current button instance.
+ if (retryCount < 2)
+ ScheduleOverlayRetry(uiSearchBar, color, retryCount);
+ return;
+ }
+
+ // Remove any overlay from a previous call (e.g. color change or re-focus)
+ uiSearchBar.ViewWithTag(CancelButtonColorOverlayTag)?.RemoveFromSuperview();
+
+ // Find the UIImageView that UIButton uses to render the X icon.
+ // We need its frame to determine the rendered image size.
+ var iv = cancelButton.FindDescendantView();
+ if (iv == null)
+ return;
+
+ // Convert the icon's frame from its local coordinate space to the parent view.
+ var iconFrameInParent = parentView.ConvertRectFromView(iv.Frame, iv.Superview);
+ if (iconFrameInParent.Width <= 0 || iconFrameInParent.Height <= 0)
+ {
+ // The cancel button hasn't been laid out yet (e.g. on initial load when
+ // CancelButtonColor is set via AppThemeBinding before the view appears).
+ // Retry after the next layout pass (up to two times) so we get a valid frame.
+ if (retryCount < 2)
+ ScheduleOverlayRetry(uiSearchBar, color, retryCount);
+ return;
}
+
+ // Render the xmark icon in the requested color using CoreGraphics.
+ // AlwaysOriginal prevents UIKit from re-tinting the baked image.
+ var xmarkImage = UIImage.GetSystemImage("xmark");
+ if (xmarkImage == null)
+ return;
+
+ var imageSize = iconFrameInParent.Size;
+ var renderer = new UIGraphicsImageRenderer(imageSize, new UIGraphicsImageRendererFormat
+ {
+ Opaque = false,
+ Scale = 0,
+ });
+
+ var coloredImage = renderer.CreateImage(_ =>
+ {
+ xmarkImage.Draw(new CGRect(CGPoint.Empty, imageSize));
+ var ctx = UIGraphics.GetCurrentContext();
+ if (ctx != null)
+ {
+ ctx.SetBlendMode(CGBlendMode.SourceIn);
+ ctx.SetFillColor(color.CGColor);
+ ctx.FillRect(new CGRect(CGPoint.Empty, imageSize));
+ }
+ }).ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
+
+ // Add the overlay as the last (topmost) sibling of the cancel button.
+ // Use Auto Layout constraints so the overlay stays centered over the X icon
+ // on rotation, multitasking split view, or dynamic type changes.
+ var overlay = new UIImageView
+ {
+ Image = coloredImage,
+ ContentMode = UIViewContentMode.ScaleAspectFit,
+ Tag = CancelButtonColorOverlayTag,
+ UserInteractionEnabled = false,
+ TranslatesAutoresizingMaskIntoConstraints = false,
+ };
+
+ parentView.AddSubview(overlay);
+
+ NSLayoutConstraint.ActivateConstraints(new NSLayoutConstraint[]
+ {
+ overlay.CenterXAnchor.ConstraintEqualTo(cancelButton.CenterXAnchor),
+ overlay.CenterYAnchor.ConstraintEqualTo(cancelButton.CenterYAnchor),
+ overlay.WidthAnchor.ConstraintEqualTo(imageSize.Width),
+ overlay.HeightAnchor.ConstraintEqualTo(imageSize.Height),
+ });
+ }
+
+ static void RemoveCancelButtonOverlay(UISearchBar uiSearchBar)
+ {
+ // UIView.ViewWithTag searches the entire subtree recursively, so it finds the overlay
+ // regardless of where it was placed in the search bar's view hierarchy.
+ uiSearchBar.ViewWithTag(CancelButtonColorOverlayTag)?.RemoveFromSuperview();
}
internal static void UpdateSearchIcon(this UISearchBar uiSearchBar, ISearchBar searchBar)
diff --git a/src/Core/src/Platform/iOS/SliderExtensions.cs b/src/Core/src/Platform/iOS/SliderExtensions.cs
index e2be1be23f73..b8cf380bc406 100644
--- a/src/Core/src/Platform/iOS/SliderExtensions.cs
+++ b/src/Core/src/Platform/iOS/SliderExtensions.cs
@@ -1,4 +1,5 @@
-using System.Threading.Tasks;
+using System;
+using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using ObjCRuntime;
using UIKit;
@@ -73,6 +74,13 @@ public static async Task UpdateThumbImageSourceAsync(this UISlider uiSlider, ISl
uiSlider.SetThumbImage(null, UIControlState.Normal);
uiSlider.UpdateThumbColor(slider);
}
+
+ // On iOS 26+, SetThumbImage() no longer triggers a layout pass that recalculates
+ // the thumb position at runtime. Explicitly call SetNeedsLayout() to restore this.
+ if (OperatingSystem.IsIOSVersionAtLeast(26))
+ {
+ uiSlider.SetNeedsLayout();
+ }
}
}
}
\ No newline at end of file
diff --git a/src/Core/src/Platform/iOS/StepperExtensions.cs b/src/Core/src/Platform/iOS/StepperExtensions.cs
index fb3662ecc09c..1089d1cf774b 100644
--- a/src/Core/src/Platform/iOS/StepperExtensions.cs
+++ b/src/Core/src/Platform/iOS/StepperExtensions.cs
@@ -1,5 +1,7 @@
-using ObjCRuntime;
+using System;
+using ObjCRuntime;
using UIKit;
+using CoreGraphics;
namespace Microsoft.Maui.Platform
{
@@ -38,5 +40,85 @@ public static void UpdateValue(this UIStepper platformStepper, IStepper stepper)
}
}
+
+ // Applies the semantic content attribute and visual transform
+ // to the UIStepper and its subviews based on the Stepper's FlowDirection
+ // and its parent's layout direction.
+ internal static void UpdateFlowDirection(this UIStepper platformStepper, IStepper stepper)
+ {
+ UISemanticContentAttribute contentAttribute = GetSemanticContentAttribute(stepper);
+ // iOS 26 changed UIStepper internal rendering so that SemanticContentAttribute alone
+ // is no longer sufficient to mirror the control for RTL. A horizontal transform is required.
+ bool isIOS26 = OperatingSystem.IsIOSVersionAtLeast(26) || OperatingSystem.IsMacCatalystVersionAtLeast(26);
+ platformStepper.SemanticContentAttribute = contentAttribute;
+
+ // Apply transform to stepper subviews on iOS 26+.
+ if (isIOS26)
+ {
+ CGAffineTransform transform = GetCGAffineTransform(stepper);
+ platformStepper.Transform = transform;
+
+ foreach (var subview in platformStepper.Subviews)
+ {
+ subview.SemanticContentAttribute = contentAttribute;
+ subview.Transform = transform;
+ }
+ }
+ else
+ {
+ foreach (var subview in platformStepper.Subviews)
+ {
+ subview.SemanticContentAttribute = contentAttribute;
+ }
+ }
+ }
+
+ static UISemanticContentAttribute GetSemanticContentAttribute(IStepper stepper)
+ {
+ return stepper.FlowDirection switch
+ {
+ FlowDirection.LeftToRight => UISemanticContentAttribute.ForceLeftToRight,
+ FlowDirection.RightToLeft => UISemanticContentAttribute.ForceRightToLeft,
+ _ => GetParentSemanticContentAttribute(stepper),
+ };
+ }
+
+ static UISemanticContentAttribute GetParentSemanticContentAttribute(IStepper stepper)
+ {
+ var parentView = (stepper as IView)?.Parent as IView;
+ if (parentView is null)
+ {
+ return UISemanticContentAttribute.Unspecified;
+ }
+
+ return parentView.FlowDirection switch
+ {
+ FlowDirection.LeftToRight => UISemanticContentAttribute.ForceLeftToRight,
+ FlowDirection.RightToLeft => UISemanticContentAttribute.ForceRightToLeft,
+ _ => UISemanticContentAttribute.Unspecified,
+ };
+ }
+
+ static CGAffineTransform GetCGAffineTransform(IStepper stepper)
+ {
+ return stepper.FlowDirection switch
+ {
+ FlowDirection.LeftToRight => CGAffineTransform.MakeIdentity(),
+ FlowDirection.RightToLeft => CGAffineTransform.MakeScale(-1, 1),
+ _ => GetParentTransform(stepper), // Default to parent's direction if MatchParent
+ };
+ }
+
+ static CGAffineTransform GetParentTransform(IStepper stepper)
+ {
+ var parentSemanticAttribute = GetParentSemanticContentAttribute(stepper);
+ if (parentSemanticAttribute == UISemanticContentAttribute.ForceRightToLeft)
+ {
+ // Flip horizontally for RTL
+ return CGAffineTransform.MakeScale(-1, 1);
+ }
+ // Identity transform for LTR
+ return CGAffineTransform.MakeIdentity();
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Core/src/Platform/iOS/TextFieldExtensions.cs b/src/Core/src/Platform/iOS/TextFieldExtensions.cs
index 880bbdf886c1..20e3ca01250e 100644
--- a/src/Core/src/Platform/iOS/TextFieldExtensions.cs
+++ b/src/Core/src/Platform/iOS/TextFieldExtensions.cs
@@ -32,13 +32,29 @@ public static void UpdateIsPassword(this UITextField textField, IEntry entry)
{
if (entry.IsPassword && textField.IsFirstResponder)
{
+ var currentText = textField.Text;
textField.Enabled = false;
textField.SecureTextEntry = true;
textField.Enabled = entry.IsEnabled;
textField.BecomeFirstResponder();
+ if (!string.IsNullOrEmpty(currentText) && textField is MauiTextField mauiTextField)
+ {
+ mauiTextField.SuppressTextPropertySet(true);
+ try
+ {
+ textField.Text = string.Empty;
+ textField.InsertText(currentText);
+ }
+ finally
+ {
+ mauiTextField.SuppressTextPropertySet(false);
+ }
+ }
}
else
+ {
textField.SecureTextEntry = entry.IsPassword;
+ }
#if MACCATALYST
textField.TextContentType = UITextContentType.OneTimeCode;
#endif
@@ -159,12 +175,28 @@ public static void UpdateSelectionLength(this UITextField textField, IEntry entr
/* Updates both the IEntry.CursorPosition and IEntry.SelectionLength properties. */
static void UpdateCursorSelection(this UITextField textField, IEntry entry)
{
- if (!entry.IsReadOnly)
+ if (entry.IsReadOnly)
{
- UITextPosition start = GetSelectionStart(textField, entry, out int startOffset);
- UITextPosition end = GetSelectionEnd(textField, entry, start, startOffset);
+ return;
+ }
- textField.SelectedTextRange = textField.GetTextRange(start, end);
+ void UpdateSelection()
+ {
+ if (textField is not null && textField.Handle != IntPtr.Zero)
+ {
+ UITextPosition start = GetSelectionStart(textField, entry, out int startOffset);
+ UITextPosition end = GetSelectionEnd(textField, entry, start, startOffset);
+ textField.SelectedTextRange = textField.GetTextRange(start, end);
+ }
+ }
+
+ if (entry.IsFocused)
+ {
+ CoreFoundation.DispatchQueue.MainQueue.DispatchAsync(UpdateSelection);
+ }
+ else
+ {
+ UpdateSelection();
}
}
diff --git a/src/Core/src/Platform/iOS/TimePickerExtensions.cs b/src/Core/src/Platform/iOS/TimePickerExtensions.cs
index 1d6119139d9f..4784340feb38 100644
--- a/src/Core/src/Platform/iOS/TimePickerExtensions.cs
+++ b/src/Core/src/Platform/iOS/TimePickerExtensions.cs
@@ -55,30 +55,35 @@ public static void UpdateTime(this MauiTimePicker mauiTimePicker, ITimePicker ti
var time = timePicker.Time;
var format = timePicker.Format;
- mauiTimePicker.Text = time?.ToFormattedString(format, cultureInfo);
-
- if (format is not null)
+ // Determine which culture to use for consistent formatting
+ CultureInfo formattingCulture;
+ if (format != null)
{
- if (format.Contains('H', StringComparison.Ordinal))
+ if (format.Contains('t', StringComparison.Ordinal) || format.Contains('h', StringComparison.Ordinal))
{
- var ci = new CultureInfo("de-DE");
- NSLocale locale = new NSLocale(ci.TwoLetterISOLanguageName);
-
- if (picker is not null)
- {
- picker.Locale = locale;
- }
+ formattingCulture = new CultureInfo("en-US");
+ }
+ else if (format.Contains('H', StringComparison.Ordinal))
+ {
+ formattingCulture = new CultureInfo("de-DE");
}
- else if (format.Contains('h', StringComparison.Ordinal))
+ else
{
- var ci = new CultureInfo("en-US");
- NSLocale locale = new NSLocale(ci.TwoLetterISOLanguageName);
-
- if (picker is not null)
- {
- picker.Locale = locale;
- }
+ formattingCulture = cultureInfo;
}
+
+ }
+ else
+ {
+ formattingCulture = cultureInfo;
+ }
+
+ // Apply the same culture to both the text display and the picker
+ mauiTimePicker.Text = time?.ToFormattedString(format ?? string.Empty, formattingCulture);
+
+ if (picker != null && format != null)
+ {
+ picker.Locale = new NSLocale(formattingCulture.TwoLetterISOLanguageName);
}
mauiTimePicker.UpdateCharacterSpacing(timePicker);
diff --git a/src/Core/src/Platform/iOS/ViewExtensions.cs b/src/Core/src/Platform/iOS/ViewExtensions.cs
index b5d13b1ddb26..2e57201320be 100644
--- a/src/Core/src/Platform/iOS/ViewExtensions.cs
+++ b/src/Core/src/Platform/iOS/ViewExtensions.cs
@@ -83,13 +83,12 @@ public static void UpdateBackground(this UIView platformView, Paint? paint, IBut
if (paint.IsNullOrEmpty())
{
- if (platformView is LayoutView)
+ if (platformView is LayoutView or ContentView)
platformView.BackgroundColor = null;
else
return;
}
-
if (paint is SolidPaint solidPaint)
{
Color backgroundColor = solidPaint.Color;
diff --git a/src/Core/src/Platform/iOS/WebViewExtensions.cs b/src/Core/src/Platform/iOS/WebViewExtensions.cs
index 7f4a5c748bb1..09b0e3c2bf2f 100644
--- a/src/Core/src/Platform/iOS/WebViewExtensions.cs
+++ b/src/Core/src/Platform/iOS/WebViewExtensions.cs
@@ -65,6 +65,29 @@ internal static void UpdateCanGoBackForward(this WKWebView platformWebView, IWeb
webView.CanGoForward = platformWebView.CanGoForward;
}
+ internal static void UpdateFlowDirectionForScrollView(this UIKit.UIScrollView scrollView, IView view)
+ {
+ scrollView.UpdateFlowDirection(view);
+
+ // On macOS, we need to refresh the scroll indicators when flow direction changes
+ // But only for runtime changes, not during initial load
+ // The view.IsLoadedOnPlatform() check ensures that this code is executed
+ // only after the view has been loaded on the platform. During the initial load,
+ // the scroll indicators do not need to be refreshed as they are set up correctly
+ // by default. This avoids unnecessary operations during the initial load phase.
+ if (OperatingSystem.IsMacCatalyst() && view.IsLoadedOnPlatform())
+ {
+ bool showsVertical = scrollView.ShowsVerticalScrollIndicator;
+ bool showsHorizontal = scrollView.ShowsHorizontalScrollIndicator;
+
+ scrollView.ShowsVerticalScrollIndicator = false;
+ scrollView.ShowsHorizontalScrollIndicator = false;
+
+ scrollView.ShowsVerticalScrollIndicator = showsVertical;
+ scrollView.ShowsHorizontalScrollIndicator = showsHorizontal;
+ }
+ }
+
public static void Eval(this WKWebView platformWebView, IWebView webView, string script)
{
platformWebView.EvaluateJavaScriptAsync(script);
diff --git a/src/Core/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt b/src/Core/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt
index ebd197b91951..a68fec2e18fa 100644
--- a/src/Core/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt
+++ b/src/Core/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt
@@ -1,2 +1,3 @@
-#nullable enable
+#nullable enable
+override Microsoft.Maui.Handlers.StepperHandler.GetDesiredSize(double widthConstraint, double heightConstraint) -> Microsoft.Maui.Graphics.Size
override Microsoft.Maui.Platform.MauiView.DidUpdateFocus(UIKit.UIFocusUpdateContext! context, UIKit.UIFocusAnimationCoordinator! coordinator) -> void
diff --git a/src/Core/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt b/src/Core/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
index ebd197b91951..a68fec2e18fa 100644
--- a/src/Core/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
+++ b/src/Core/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
@@ -1,2 +1,3 @@
-#nullable enable
+#nullable enable
+override Microsoft.Maui.Handlers.StepperHandler.GetDesiredSize(double widthConstraint, double heightConstraint) -> Microsoft.Maui.Graphics.Size
override Microsoft.Maui.Platform.MauiView.DidUpdateFocus(UIKit.UIFocusUpdateContext! context, UIKit.UIFocusAnimationCoordinator! coordinator) -> void
diff --git a/src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt b/src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt
index 7dc5c58110bf..d63f63f741bb 100644
--- a/src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt
+++ b/src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt
@@ -1 +1,3 @@
#nullable enable
+override Microsoft.Maui.Platform.MauiPasswordTextBox.OnCreateAutomationPeer() -> Microsoft.UI.Xaml.Automation.Peers.AutomationPeer!
+override Microsoft.Maui.Handlers.GraphicsViewHandler.NeedsContainer.get -> bool
diff --git a/src/Core/tests/DeviceTests/Handlers/ActivityIndicator/ActivityIndicatorHandlerTests.cs b/src/Core/tests/DeviceTests/Handlers/ActivityIndicator/ActivityIndicatorHandlerTests.cs
index 13f73d557398..5579857dc74d 100644
--- a/src/Core/tests/DeviceTests/Handlers/ActivityIndicator/ActivityIndicatorHandlerTests.cs
+++ b/src/Core/tests/DeviceTests/Handlers/ActivityIndicator/ActivityIndicatorHandlerTests.cs
@@ -7,6 +7,85 @@ namespace Microsoft.Maui.DeviceTests
[Category(TestCategory.ActivityIndicator)]
public partial class ActivityIndicatorHandlerTests : CoreHandlerTestBase
{
+#if !WINDOWS // On Windows, the platform control will return IsActive as true even when the control is not visible.
+ [Theory(DisplayName = "IsRunning Should Respect Visibility At Init")]
+ [InlineData(true, true)]
+ [InlineData(true, false)]
+ [InlineData(false, true)]
+ [InlineData(false, false)]
+ public async Task IsRunningShouldRespectVisibilityAtInit(bool isRunning, bool isVisible)
+ {
+ var activityIndicator = new ActivityIndicatorStub
+ {
+ IsRunning = isRunning,
+ Visibility = isVisible ? Visibility.Visible : Visibility.Hidden
+ };
+
+ bool isAnimating = false;
+
+ await InvokeOnMainThreadAsync(() =>
+ {
+ var handler = CreateHandler(activityIndicator);
+ isAnimating = GetNativeIsRunning(handler);
+ });
+
+ if (isVisible && isRunning)
+ Assert.True(isAnimating);
+ else
+ Assert.False(isAnimating);
+ }
+
+ [Fact(DisplayName = "Setting IsRunning After Init Should Respect Hidden Visibility")]
+ public async Task SettingIsRunningAfterInitShouldRespectHiddenVisibility()
+ {
+ var activityIndicator = new ActivityIndicatorStub
+ {
+ IsRunning = false,
+ Visibility = Visibility.Hidden
+ };
+
+ bool isAnimating = false;
+
+ await InvokeOnMainThreadAsync(() =>
+ {
+ var handler = CreateHandler(activityIndicator);
+
+ // Simulate runtime: set IsRunning=true while Visibility=Hidden
+ activityIndicator.IsRunning = true;
+ handler.UpdateValue(nameof(IActivityIndicator.IsRunning));
+
+ isAnimating = GetNativeIsRunning(handler);
+ });
+
+ Assert.False(isAnimating, "ActivityIndicator should not animate when Visibility is Hidden");
+ }
+
+ [Fact(DisplayName = "Setting IsRunning After Init Should Respect Collapsed Visibility")]
+ public async Task SettingIsRunningAfterInitShouldRespectCollapsedVisibility()
+ {
+ var activityIndicator = new ActivityIndicatorStub
+ {
+ IsRunning = false,
+ Visibility = Visibility.Collapsed
+ };
+
+ bool isAnimating = false;
+
+ await InvokeOnMainThreadAsync(() =>
+ {
+ var handler = CreateHandler(activityIndicator);
+
+ // Simulate runtime: set IsRunning=true while Visibility=Collapsed
+ activityIndicator.IsRunning = true;
+ handler.UpdateValue(nameof(IActivityIndicator.IsRunning));
+
+ isAnimating = GetNativeIsRunning(handler);
+ });
+
+ Assert.False(isAnimating, "ActivityIndicator should not animate when Visibility is Collapsed");
+ }
+#endif
+
[Theory(DisplayName = "IsRunning Initializes Correctly")]
[InlineData(true)]
[InlineData(false)]
diff --git a/src/Core/tests/DeviceTests/Handlers/CheckBox/CheckBoxHandlerTests.iOS.cs b/src/Core/tests/DeviceTests/Handlers/CheckBox/CheckBoxHandlerTests.iOS.cs
index be4b94d70cb0..b872e8ac4be0 100644
--- a/src/Core/tests/DeviceTests/Handlers/CheckBox/CheckBoxHandlerTests.iOS.cs
+++ b/src/Core/tests/DeviceTests/Handlers/CheckBox/CheckBoxHandlerTests.iOS.cs
@@ -64,5 +64,31 @@ async Task ValidateColor(ICheckBox checkBoxStub, Color color, Action action = nu
});
Assert.Equal(expected, color);
}
+
+ [Fact(DisplayName = "Foreground Resets to Default When Set to Null")]
+ public async Task ForegroundResetsToDefaultWhenSetToNull()
+ {
+ var checkBoxStub = new CheckBoxStub
+ {
+ Foreground = new SolidPaint(Colors.Red),
+ IsChecked = true
+ };
+
+ await InvokeOnMainThreadAsync(() =>
+ {
+ var handler = CreateHandler(checkBoxStub);
+ var native = GetNativeCheckBox(handler);
+
+ // Confirm that the red tint was applied initially
+ Assert.NotNull(native.CheckBoxTintColor);
+
+ // Simulate a dynamic Color = null (Foreground reset to default)
+ checkBoxStub.Foreground = null;
+ handler.UpdateValue(nameof(ICheckBox.Foreground));
+
+ // After reset, the native tint must be null so iOS reverts to its inherited default
+ Assert.Null(native.CheckBoxTintColor);
+ });
+ }
}
}
\ No newline at end of file
diff --git a/src/Core/tests/DeviceTests/Handlers/GraphicsView/GraphicsViewHandlerTests.iOS.cs b/src/Core/tests/DeviceTests/Handlers/GraphicsView/GraphicsViewHandlerTests.iOS.cs
index bf7190781ff2..e21504e485c8 100644
--- a/src/Core/tests/DeviceTests/Handlers/GraphicsView/GraphicsViewHandlerTests.iOS.cs
+++ b/src/Core/tests/DeviceTests/Handlers/GraphicsView/GraphicsViewHandlerTests.iOS.cs
@@ -1,8 +1,12 @@
using System;
using System.Threading.Tasks;
+using Microsoft.Maui.DeviceTests.Stubs;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Graphics.Platform;
using Microsoft.Maui.Handlers;
+using Microsoft.Maui.Platform;
+using UIKit;
+using Xunit;
namespace Microsoft.Maui.DeviceTests
{
@@ -10,5 +14,69 @@ public partial class GraphicsViewHandlerTests
{
PlatformGraphicsView GetPlatformGraphicsView(GraphicsViewHandler graphicsViewHandler) =>
graphicsViewHandler.PlatformView;
+
+ // Regression test for https://github.com/dotnet/maui/issues/31239
+ // Verifies that setting Background on GraphicsView applies the color to the platform layer.
+ [Fact(DisplayName = "GraphicsView applies Background color to platform layer")]
+ public async Task GraphicsViewAppliesBackgroundToLayer()
+ {
+ var expectedColor = Colors.Red;
+
+ var graphicsView = new GraphicsViewStub
+ {
+ Drawable = new TestDrawable(Colors.Blue),
+ Background = new SolidPaintStub(expectedColor),
+ Width = 100,
+ Height = 100,
+ };
+
+ var platformBackgroundColor = await GetValueAsync(graphicsView, (GraphicsViewHandler handler) =>
+ {
+ var nativeView = GetPlatformGraphicsView(handler);
+ return nativeView.BackgroundColor;
+ });
+
+ Assert.NotNull(platformBackgroundColor);
+
+ platformBackgroundColor.GetRGBA(out var r, out var g, out var b, out var a);
+
+ Assert.Equal(expectedColor.Red, (float)r, 2);
+ Assert.Equal(expectedColor.Green, (float)g, 2);
+ Assert.Equal(expectedColor.Blue, (float)b, 2);
+ }
+
+ // Regression test for https://github.com/dotnet/maui/issues/25502
+ // A GraphicsView with decimal WidthRequest/HeightRequest must have pixel-aligned Bounds
+ // to prevent the CALayer background from rendering a gray hairline at the sub-pixel edge.
+ [Fact(DisplayName = "GraphicsView Bounds are pixel-aligned when decimal dimensions are used")]
+ public async Task GraphicsViewBoundsArePixelAlignedWithDecimalDimensions()
+ {
+ var graphicsView = new GraphicsViewStub
+ {
+ Drawable = new TestDrawable(Colors.White),
+ Background = new SolidPaintStub(Colors.White),
+ Width = 248.25,
+ Height = 200.25,
+ };
+
+ var (bounds, scale) = await GetValueAsync(graphicsView, (GraphicsViewHandler handler) =>
+ {
+ var nativeView = GetPlatformGraphicsView(handler);
+ return (nativeView.Bounds, (double)UIScreen.MainScreen.Scale);
+ }, attachAndRun: true);
+
+ var widthInPixels = bounds.Width * scale;
+ var heightInPixels = bounds.Height * scale;
+
+ Assert.True(
+ Math.Abs(widthInPixels - Math.Round(widthInPixels)) < 0.01,
+ $"GraphicsView Bounds.Width ({bounds.Width}pt) is not pixel-aligned on a {scale}x display. " +
+ $"Physical width {widthInPixels}px must be an integer to prevent the gray hairline artifact.");
+
+ Assert.True(
+ Math.Abs(heightInPixels - Math.Round(heightInPixels)) < 0.01,
+ $"GraphicsView Bounds.Height ({bounds.Height}pt) is not pixel-aligned on a {scale}x display. " +
+ $"Physical height {heightInPixels}px must be an integer to prevent the gray hairline artifact.");
+ }
}
}
\ No newline at end of file
diff --git a/src/Core/tests/DeviceTests/Handlers/TimePicker/TimePickerHandlerTests.Android.cs b/src/Core/tests/DeviceTests/Handlers/TimePicker/TimePickerHandlerTests.Android.cs
index f08bf9b2a9bf..46e2fea7994b 100644
--- a/src/Core/tests/DeviceTests/Handlers/TimePicker/TimePickerHandlerTests.Android.cs
+++ b/src/Core/tests/DeviceTests/Handlers/TimePicker/TimePickerHandlerTests.Android.cs
@@ -11,6 +11,24 @@ namespace Microsoft.Maui.DeviceTests
{
public partial class TimePickerHandlerTests
{
+ [Theory(DisplayName = "IsCustom24HourFormat detects HH patterns correctly")]
+ [InlineData("HH:mm", true)]
+ [InlineData("HH:mm:ss", true)]
+ [InlineData("HH.mm", true)]
+ [InlineData("HH-mm-ss", true)]
+ [InlineData("hh:mm", false)]
+ [InlineData("hh:mm tt", false)]
+ [InlineData("h:mm", false)]
+ [InlineData("H:mm", false)]
+ [InlineData("t", false)]
+ [InlineData("T", false)]
+ [InlineData("", false)]
+ [InlineData(null, false)]
+ public void IsCustom24HourFormatDetectsCorrectly(string format, bool expected)
+ {
+ Assert.Equal(expected, TimePickerHandler.IsCustom24HourFormat(format));
+ }
+
[Fact(DisplayName = "CharacterSpacing Initializes Correctly")]
public async Task CharacterSpacingInitializesCorrectly()
{
diff --git a/src/Core/tests/UnitTests/Layouts/GridLayoutManagerTests.cs b/src/Core/tests/UnitTests/Layouts/GridLayoutManagerTests.cs
index 9bc738a3f43d..d034c06798ca 100644
--- a/src/Core/tests/UnitTests/Layouts/GridLayoutManagerTests.cs
+++ b/src/Core/tests/UnitTests/Layouts/GridLayoutManagerTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
@@ -3235,5 +3235,48 @@ public void StarsAdjustWhenArrangeAndMeasureWidthDiffer(double widthConstraint,
AssertArranged(smallerView, new Rect(0, 0, expectedWidth, heightConstraint));
AssertArranged(largerView, new Rect(expectedWidth, 0, expectedWidth, heightConstraint));
}
- }
+[Fact]
+public void SpannedCellMeasurementIncludesColumnSpacingIssue26633()
+{
+// Issue: https://github.com/dotnet/maui/issues/26633
+// Grid cells spanning multiple columns with ColumnSpacing > 0 have incorrect measurement
+
+var grid = CreateGridLayout(columns: "auto, *, auto", colSpacing: 6);
+var view = CreateTestView(new Size(50, 50));
+
+SubstituteExtensions.ReturnsForAnyArgs(view.DesiredSize, new Size(50, 50));
+
+SubstituteChildren(grid, view);
+SetLocation(grid, view, row: 0, col: 0, colSpan: 3);
+
+var manager = new GridLayoutManager(grid);
+var measure = manager.Measure(200, double.PositiveInfinity);
+
+// Cell spans all 3 columns: should get full grid width of 200px
+// Without fix: gets 188px (missing spacing in calculation)
+view.Received().Measure(Arg.Is(width => width == 200), Arg.Any());
+}
+
+[Fact]
+public void SpannedCellMeasurementIncludesRowSpacingIssue26633()
+{
+// Issue: https://github.com/dotnet/maui/issues/26633
+// Grid cells spanning multiple rows with RowSpacing > 0 have incorrect measurement
+
+var grid = CreateGridLayout(rows: "auto, *, auto", rowSpacing: 6);
+var view = CreateTestView(new Size(50, 50));
+
+SubstituteExtensions.ReturnsForAnyArgs(view.DesiredSize, new Size(50, 50));
+
+SubstituteChildren(grid, view);
+SetLocation(grid, view, row: 0, col: 0, rowSpan: 3);
+
+var manager = new GridLayoutManager(grid);
+var measure = manager.Measure(double.PositiveInfinity, 200);
+
+// Cell spans all 3 rows: should get full grid width of 200px
+// Without fix: gets 188px (missing spacing in calculation)
+view.Received().Measure(Arg.Any(), Arg.Is(height => height == 200));
+}
}
+}
\ No newline at end of file
diff --git a/src/Essentials/src/Connectivity/Connectivity.android.cs b/src/Essentials/src/Connectivity/Connectivity.android.cs
index b2e936bda75b..6112f7770fef 100644
--- a/src/Essentials/src/Connectivity/Connectivity.android.cs
+++ b/src/Essentials/src/Connectivity/Connectivity.android.cs
@@ -151,13 +151,9 @@ public NetworkAccess NetworkAccess
var currentAccess = NetworkAccess.None;
var manager = ConnectivityManager;
-#pragma warning disable CS0618 // Type or member is obsolete
-#pragma warning disable CA1416 // Validate platform compatibility
#pragma warning disable CA1422 // Validate platform compatibility
var networks = manager.GetAllNetworks();
#pragma warning restore CA1422 // Validate platform compatibility
-#pragma warning restore CA1416 // Validate platform compatibility
-#pragma warning restore CS0618 // Type or member is obsolete
// some devices running 21 and 22 only use the older api.
if (networks.Length == 0 && !OperatingSystem.IsAndroidVersionAtLeast(23))
@@ -177,17 +173,6 @@ public NetworkAccess NetworkAccess
continue;
}
-#pragma warning disable CS0618 // Type or member is obsolete
-#pragma warning disable CA1416 // Validate platform compatibility
-#pragma warning disable CA1422 // Validate platform compatibility
- var info = manager.GetNetworkInfo(network);
-
- if (info == null || !info.IsAvailable)
- {
- continue;
- }
-#pragma warning restore CS0618 // Type or member is obsolete
-
// Check to see if it has the internet capability
if (!capabilities.HasCapability(NetCapability.Internet))
{
@@ -196,21 +181,38 @@ public NetworkAccess NetworkAccess
continue;
}
- ProcessNetworkInfo(info);
+ // Use modern NetworkCapabilities instead of obsolete NetworkInfo
+ ProcessNetworkCapabilities(capabilities);
}
- catch
+ catch (Exception ex)
{
- // there is a possibility, but don't worry
+ Debug.WriteLine("Failed to get network capabilities: {0}", ex);
}
}
void ProcessAllNetworkInfo()
{
+ // Fallback for API 21-22 devices where GetAllNetworks() returns empty.
+ // BEHAVIORAL CHANGE: The original code used GetAllNetworkInfo() which
+ // enumerated all network interfaces and picked the best access level.
+ // ActiveNetworkInfo returns only the current default network, so a
+ // better-connected secondary interface will no longer be considered.
+ // On these older API levels multi-network support is limited,
+ // so this is an acceptable trade-off.
+ try
+ {
#pragma warning disable CS0618 // Type or member is obsolete
- foreach (var info in manager.GetAllNetworkInfo())
+ var activeInfo = manager.ActiveNetworkInfo;
+ if (activeInfo != null)
+ {
+ ProcessNetworkInfo(activeInfo);
+ }
#pragma warning restore CS0618 // Type or member is obsolete
+ }
+ catch (Exception ex)
{
- ProcessNetworkInfo(info);
+ Debug.WriteLine("Failed to query active network info: {0}", ex);
+ currentAccess = NetworkAccess.None;
}
}
@@ -230,9 +232,33 @@ void ProcessNetworkInfo(NetworkInfo info)
{
currentAccess = IsBetterAccess(currentAccess, NetworkAccess.ConstrainedInternet);
}
-#pragma warning restore CA1422 // Validate platform compatibility
-#pragma warning restore CA1416 // Validate platform compatibility
+ }
#pragma warning restore CS0618 // Type or member is obsolete
+
+ // Caller guarantees capabilities is non-null and has NetCapability.Internet.
+ void ProcessNetworkCapabilities(NetworkCapabilities capabilities)
+ {
+ // NetCapability.Validated (API 23+) means the system has verified the
+ // network can actually reach the internet (passes Google's connectivity check).
+ // Without it, the network may be behind a captive portal or otherwise unusable.
+ if (OperatingSystem.IsAndroidVersionAtLeast(23))
+ {
+ if (capabilities.HasCapability(NetCapability.Validated))
+ {
+ currentAccess = IsBetterAccess(currentAccess, NetworkAccess.Internet);
+ }
+ else
+ {
+ // Has internet capability but not validated (e.g. captive portal)
+ currentAccess = IsBetterAccess(currentAccess, NetworkAccess.ConstrainedInternet);
+ }
+ }
+ else
+ {
+ // NetCapability.Validated is unavailable on API 21-22;
+ // treat Internet capability alone as a connected state.
+ currentAccess = IsBetterAccess(currentAccess, NetworkAccess.Internet);
+ }
}
return currentAccess;
@@ -253,50 +279,79 @@ public IEnumerable ConnectionProfiles
Permissions.EnsureDeclared();
var manager = ConnectivityManager;
-#pragma warning disable CS0618 // Type or member is obsolete
-#pragma warning disable CA1416 // Validate platform compatibility
#pragma warning disable CA1422 // Validate platform compatibility
var networks = manager.GetAllNetworks();
-#pragma warning restore CS0618 // Type or member is obsolete
+#pragma warning restore CA1422 // Validate platform compatibility
foreach (var network in networks)
{
-#pragma warning disable CS0618 // Type or member is obsolete
- NetworkInfo info = null;
+ NetworkCapabilities capabilities = null;
try
{
- info = manager.GetNetworkInfo(network);
+ capabilities = manager.GetNetworkCapabilities(network);
}
- catch
+ catch (Exception ex)
{
- // there is a possibility, but don't worry about it
+ Debug.WriteLine("Failed to get network capabilities for profile: {0}", ex);
}
-#pragma warning restore CS0618 // Type or member is obsolete
- var p = ProcessNetworkInfo(info);
+ var p = ProcessNetworkCapabilities(capabilities);
if (p.HasValue)
{
yield return p.Value;
}
}
-#pragma warning disable CS0618 // Type or member is obsolete
- static ConnectionProfile? ProcessNetworkInfo(NetworkInfo info)
+ static ConnectionProfile? ProcessNetworkCapabilities(NetworkCapabilities capabilities)
{
-
- if (info == null || !info.IsAvailable || !info.IsConnectedOrConnecting)
+ if (capabilities == null)
{
return null;
}
+ // Only include networks that declare internet capability
+ if (!capabilities.HasCapability(NetCapability.Internet))
+ {
+ return null;
+ }
- return GetConnectionType(info.Type, info.TypeName);
+ return GetConnectionTypeFromCapabilities(capabilities);
}
-#pragma warning restore CA1422 // Validate platform compatibility
-#pragma warning restore CA1416 // Validate platform compatibility
-#pragma warning restore CS0618 // Type or member is obsolete
}
}
+ internal static ConnectionProfile GetConnectionTypeFromCapabilities(NetworkCapabilities capabilities)
+ {
+ if (capabilities == null)
+ {
+ return ConnectionProfile.Unknown;
+ }
+
+ // A network may advertise multiple transports (e.g. WiFi + VPN).
+ // We return the first "underlying" transport match, prioritising the
+ // physical link type over overlay transports like VPN.
+ if (capabilities.HasTransport(TransportType.Wifi))
+ {
+ return ConnectionProfile.WiFi;
+ }
+
+ if (capabilities.HasTransport(TransportType.Cellular))
+ {
+ return ConnectionProfile.Cellular;
+ }
+
+ if (capabilities.HasTransport(TransportType.Ethernet))
+ {
+ return ConnectionProfile.Ethernet;
+ }
+
+ if (capabilities.HasTransport(TransportType.Bluetooth))
+ {
+ return ConnectionProfile.Bluetooth;
+ }
+
+ return ConnectionProfile.Unknown;
+ }
+
internal static ConnectionProfile GetConnectionType(ConnectivityType connectivityType, string typeName)
{
switch (connectivityType)
diff --git a/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.cs b/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.cs
index d87759446959..b3a70c3ed7f1 100644
--- a/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.cs
+++ b/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.cs
@@ -9,7 +9,6 @@ namespace Microsoft.Maui.Networking
partial class ConnectivityImplementation : IConnectivity
{
#if !(MACCATALYST || MACOS)
- // TODO: Use NWPathMonitor on > iOS 12
#pragma warning disable BI1234, CA1416 // Analyzer bug https://github.com/dotnet/roslyn-analyzers/issues/5938
static readonly Lazy cellularData = new Lazy(() => new CTCellularData());
@@ -41,7 +40,6 @@ public NetworkAccess NetworkAccess
{
var restricted = false;
#if !(MACCATALYST || MACOS)
- // TODO: Use NWPathMonitor on > iOS 12
#pragma warning disable BI1234, CA1416 // Analyzer bug https://github.com/dotnet/roslyn-analyzers/issues/5938
restricted = CellularData.RestrictedState == CTCellularDataRestrictedState.Restricted;
#pragma warning restore BI1234, CA1416
diff --git a/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs b/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs
index a95367b74c72..376fcc6f5d49 100644
--- a/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs
+++ b/src/Essentials/src/Connectivity/Connectivity.ios.tvos.macos.reachability.cs
@@ -1,12 +1,12 @@
using System;
using System.Collections.Generic;
-using System.Net;
+using System.Threading;
using System.Threading.Tasks;
#if !(MACCATALYST || MACOS)
using CoreTelephony;
#endif
using CoreFoundation;
-using SystemConfiguration;
+using Network;
namespace Microsoft.Maui.Networking
{
@@ -19,144 +19,102 @@ enum NetworkStatus
static class Reachability
{
- internal const string HostName = "www.microsoft.com";
+ static NWPathMonitor sharedMonitor;
+ static readonly object monitorLock = new object();
+ static readonly ManualResetEventSlim initEvent = new ManualResetEventSlim(false);
- internal static NetworkStatus RemoteHostStatus()
+ static NWPathMonitor SharedMonitor
{
- using (var remoteHostReachability = new NetworkReachability(HostName))
+ get
{
- var reachable = remoteHostReachability.TryGetFlags(out var flags);
-
- if (!reachable)
- return NetworkStatus.NotReachable;
-
- if (!IsReachableWithoutRequiringConnection(flags))
- return NetworkStatus.NotReachable;
-
-#if __IOS__
- if ((flags & NetworkReachabilityFlags.IsWWAN) != 0)
- return NetworkStatus.ReachableViaCarrierDataNetwork;
-#endif
-
- return NetworkStatus.ReachableViaWiFiNetwork;
+ lock (monitorLock)
+ {
+ if (sharedMonitor == null)
+ {
+ sharedMonitor = new NWPathMonitor();
+ sharedMonitor.SnapshotHandler = _ => initEvent.Set();
+ sharedMonitor.SetQueue(DispatchQueue.DefaultGlobalQueue);
+ sharedMonitor.Start();
+ }
+ }
+ // Wait for the first path update to ensure CurrentPath is available.
+ // ManualResetEventSlim stays signaled once Set(), so subsequent calls return immediately.
+ initEvent.Wait(TimeSpan.FromSeconds(5));
+ return sharedMonitor;
}
}
- internal static NetworkStatus InternetConnectionStatus()
+ static NWPath GetCurrentPath()
{
- var status = NetworkStatus.NotReachable;
+ var monitor = SharedMonitor;
+ return monitor?.CurrentPath;
+ }
- var defaultNetworkAvailable = IsNetworkAvailable(out var flags);
+ static NetworkStatus GetNetworkStatus()
+ {
+ var path = GetCurrentPath();
+ if (path == null || path.Status != NWPathStatus.Satisfied)
+ return NetworkStatus.NotReachable;
#if __IOS__
- // If it's a WWAN connection..
- if ((flags & NetworkReachabilityFlags.IsWWAN) != 0)
- status = NetworkStatus.ReachableViaCarrierDataNetwork;
+ if (path.UsesInterfaceType(NWInterfaceType.Cellular))
+ return NetworkStatus.ReachableViaCarrierDataNetwork;
#endif
- // If the connection is reachable and no connection is required, then assume it's WiFi
- if (defaultNetworkAvailable)
- {
- status = NetworkStatus.ReachableViaWiFiNetwork;
- }
+ return NetworkStatus.ReachableViaWiFiNetwork;
+ }
- // If the connection is on-demand or on-traffic and no user intervention
- // is required, then assume WiFi.
- if (((flags & NetworkReachabilityFlags.ConnectionOnDemand) != 0 || (flags & NetworkReachabilityFlags.ConnectionOnTraffic) != 0) &&
- (flags & NetworkReachabilityFlags.InterventionRequired) == 0)
- {
- status = NetworkStatus.ReachableViaWiFiNetwork;
- }
+ // RemoteHostStatus and InternetConnectionStatus previously had different
+ // implementations (DNS probe to www.microsoft.com vs default route check).
+ // With NWPathMonitor they share the same underlying path status.
+ internal static NetworkStatus RemoteHostStatus() => GetNetworkStatus();
- return status;
- }
+ internal static NetworkStatus InternetConnectionStatus() => GetNetworkStatus();
internal static IEnumerable GetActiveConnectionType()
{
var status = new List();
+ var path = GetCurrentPath();
- var defaultNetworkAvailable = IsNetworkAvailable(out var flags);
+ if (path == null || path.Status != NWPathStatus.Satisfied)
+ return status;
#if __IOS__
- // If it's a WWAN connection.
- if ((flags & NetworkReachabilityFlags.IsWWAN) != 0)
+ if (path.UsesInterfaceType(NWInterfaceType.Cellular))
{
status.Add(NetworkStatus.ReachableViaCarrierDataNetwork);
}
- else if (defaultNetworkAvailable)
+ else if (path.UsesInterfaceType(NWInterfaceType.Wifi) || path.UsesInterfaceType(NWInterfaceType.Wired))
#else
- // If the connection is reachable and no connection is required, then assume it's WiFi
- if (defaultNetworkAvailable)
+ if (path.UsesInterfaceType(NWInterfaceType.Wifi) || path.UsesInterfaceType(NWInterfaceType.Wired))
#endif
{
status.Add(NetworkStatus.ReachableViaWiFiNetwork);
}
- else if (((flags & NetworkReachabilityFlags.ConnectionOnDemand) != 0 || (flags & NetworkReachabilityFlags.ConnectionOnTraffic) != 0) &&
- (flags & NetworkReachabilityFlags.InterventionRequired) == 0)
- {
- // If the connection is on-demand or on-traffic and no user intervention
- // is required, then assume WiFi.
- status.Add(NetworkStatus.ReachableViaWiFiNetwork);
- }
return status;
}
- internal static bool IsNetworkAvailable(out NetworkReachabilityFlags flags)
+ internal static bool IsNetworkAvailable()
{
- var ip = new IPAddress(0);
- using (var defaultRouteReachability = new NetworkReachability(ip))
- {
- if (!defaultRouteReachability.TryGetFlags(out flags))
- return false;
-
- return IsReachableWithoutRequiringConnection(flags);
- }
- }
-
- internal static bool IsReachableWithoutRequiringConnection(NetworkReachabilityFlags flags)
- {
- // Is it reachable with the current network configuration?
- var isReachable = (flags & NetworkReachabilityFlags.Reachable) != 0;
-
- // Do we need a connection to reach it?
- var noConnectionRequired = (flags & NetworkReachabilityFlags.ConnectionRequired) == 0;
-
-#if __IOS__
- // Since the network stack will automatically try to get the WAN up,
- // probe that
- if ((flags & NetworkReachabilityFlags.IsWWAN) != 0)
- noConnectionRequired = true;
-#endif
-
- return isReachable && noConnectionRequired;
+ var path = GetCurrentPath();
+ return path != null && path.Status == NWPathStatus.Satisfied;
}
}
class ReachabilityListener : IDisposable
{
- NetworkReachability defaultRouteReachability;
- NetworkReachability remoteHostReachability;
+ // Delay to allow connection status to stabilize before notifying listeners
+ const int ConnectionStatusChangeDelayMs = 100;
+
+ NWPathMonitor pathMonitor;
internal ReachabilityListener()
{
- var ip = new IPAddress(0);
- defaultRouteReachability = new NetworkReachability(ip);
-#pragma warning disable CA1422 // obsolete in MacCatalyst 15, iOS 13
- defaultRouteReachability.SetNotification(OnChange);
- defaultRouteReachability.Schedule(CFRunLoop.Main, CFRunLoop.ModeDefault);
-#pragma warning restore CA1422
-
- remoteHostReachability = new NetworkReachability(Reachability.HostName);
-
- // Need to probe before we queue, or we wont get any meaningful values
- // this only happens when you create NetworkReachability from a hostname
- remoteHostReachability.TryGetFlags(out var flags);
-
-#pragma warning disable CA1422 // obsolete in MacCatalyst 15, iOS 13
- remoteHostReachability.SetNotification(OnChange);
- remoteHostReachability.Schedule(CFRunLoop.Main, CFRunLoop.ModeDefault);
-#pragma warning restore CA1422
+ pathMonitor = new NWPathMonitor();
+ pathMonitor.SnapshotHandler = OnPathUpdate;
+ pathMonitor.SetQueue(DispatchQueue.DefaultGlobalQueue);
+ pathMonitor.Start();
#if !(MACCATALYST || MACOS)
#pragma warning disable BI1234, CA1416 // Analyzer bug https://github.com/dotnet/roslyn-analyzers/issues/5938
@@ -171,10 +129,13 @@ internal ReachabilityListener()
internal void Dispose()
{
- defaultRouteReachability?.Dispose();
- defaultRouteReachability = null;
- remoteHostReachability?.Dispose();
- remoteHostReachability = null;
+ if (pathMonitor != null)
+ {
+ pathMonitor.SnapshotHandler = null;
+ pathMonitor.Cancel();
+ pathMonitor.Dispose();
+ pathMonitor = null;
+ }
#if !(MACCATALYST || MACOS)
#pragma warning disable CA1416 // Analyzer bug https://github.com/dotnet/roslyn-analyzers/issues/5938
@@ -183,6 +144,20 @@ internal void Dispose()
#endif
}
+ async void OnPathUpdate(NWPath path)
+ {
+ try
+ {
+ // Add in artificial delay so the connection status has time to change
+ await Task.Delay(ConnectionStatusChangeDelayMs);
+ ReachabilityChanged?.Invoke();
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"ReachabilityListener handler failed: {ex}");
+ }
+ }
+
#if !(MACCATALYST || MACOS)
#pragma warning disable BI1234
void OnRestrictedStateChanged(CTCellularDataRestrictedState state)
@@ -191,14 +166,5 @@ void OnRestrictedStateChanged(CTCellularDataRestrictedState state)
}
#pragma warning restore BI1234
#endif
-
- async void OnChange(NetworkReachabilityFlags flags)
- {
- // Add in artifical delay so the connection status has time to change
- // else it will return true no matter what.
- await Task.Delay(100);
-
- ReachabilityChanged?.Invoke();
- }
}
}
diff --git a/src/Essentials/src/Contacts/Contacts.ios.macos.cs b/src/Essentials/src/Contacts/Contacts.ios.macos.cs
index 7684d8c14255..696df1a67711 100644
--- a/src/Essentials/src/Contacts/Contacts.ios.macos.cs
+++ b/src/Essentials/src/Contacts/Contacts.ios.macos.cs
@@ -22,25 +22,35 @@ public Task PickContactAsync()
var source = new TaskCompletionSource();
- var picker = new CNContactPickerViewController
+ var contactDelegate = new ContactPickerDelegate(phoneContact =>
{
- Delegate = new ContactPickerDelegate(phoneContact =>
+ try
{
- try
- {
- source?.TrySetResult(ConvertContact(phoneContact));
- }
- catch (Exception ex)
- {
- source?.TrySetException(ex);
- }
- })
+ source?.TrySetResult(ConvertContact(phoneContact));
+ }
+ catch (Exception ex)
+ {
+ source?.TrySetException(ex);
+ }
+ });
+
+ var picker = new CNContactPickerViewController
+ {
+ Delegate = contactDelegate
};
- if (picker.PresentationController != null)
+ if (picker.PresentationController is not null)
{
+ // Only complete with null (swipe-to-dismiss) if the delegate hasn't already
+ // handled an explicit selection or cancellation via its completion callback.
picker.PresentationController.Delegate =
- new UIPresentationControllerDelegate(() => source?.TrySetResult(null));
+ new UIPresentationControllerDelegate(() =>
+ {
+ if (!contactDelegate.IsResultDelivered)
+ {
+ source?.TrySetResult(null);
+ }
+ });
}
vc.PresentViewController(picker, true, null);
@@ -121,25 +131,25 @@ public ContactPickerDelegate(IntPtr handle)
}
public Action DidSelectContactHandler { get; }
-#pragma warning disable CA1416 // picker.DismissModalViewController(bool) has UnsupportedOSPlatform("ios6.0")]. (Deprecated but still works)
-#pragma warning disable CA1422 // Validate platform compatibility
+
+ // Set to true when an explicit selection or cancellation is made, so that
+ // UIPresentationControllerDelegate.DidDismiss does not complete the TCS with null.
+ public bool IsResultDelivered { get; private set; }
+
public override void ContactPickerDidCancel(CNContactPickerViewController picker)
{
- DidSelectContactHandler?.Invoke(default);
- picker.DismissModalViewController(true);
+ IsResultDelivered = true;
+ picker.DismissViewController(true, () => DidSelectContactHandler?.Invoke(default));
}
public override void DidSelectContact(CNContactPickerViewController picker, CNContact contact)
{
- DidSelectContactHandler?.Invoke(contact);
- picker.DismissModalViewController(true);
+ IsResultDelivered = true;
+ picker.DismissViewController(true, () => DidSelectContactHandler?.Invoke(contact));
}
public override void DidSelectContactProperty(CNContactPickerViewController picker, CNContactProperty contactProperty) =>
- picker.DismissModalViewController(true);
-
-#pragma warning restore CA1422 // Validate platform compatibility
-#pragma warning restore CA1416
+ picker.DismissViewController(true, null);
}
#endif
}
diff --git a/src/Essentials/src/FilePicker/FilePicker.shared.cs b/src/Essentials/src/FilePicker/FilePicker.shared.cs
index db43d022fdeb..3bd50798f3b9 100644
--- a/src/Essentials/src/FilePicker/FilePicker.shared.cs
+++ b/src/Essentials/src/FilePicker/FilePicker.shared.cs
@@ -41,7 +41,7 @@ public interface IFilePicker
/// picker, the object that was returned from the first call is cancelled. Be sure to
/// also handle the in this case.
///
- Task> PickMultipleAsync(PickOptions? options = null);
+ Task?> PickMultipleAsync(PickOptions? options = null);
}
///
@@ -77,7 +77,7 @@ public static partial class FilePicker
/// picker, the object that was returned from the first call is cancelled. Be sure to
/// also handle the in this case.
///
- public static Task> PickMultipleAsync(PickOptions? options = null) =>
+ public static Task?> PickMultipleAsync(PickOptions? options = null) =>
Default.PickMultipleAsync(options);
static IFilePicker? defaultImplementation;
@@ -97,7 +97,7 @@ partial class FilePickerImplementation : IFilePicker
public async Task PickAsync(PickOptions? options = null) =>
(await PlatformPickAsync(options))?.FirstOrDefault();
- public Task> PickMultipleAsync(PickOptions? options = null) =>
+ public Task?> PickMultipleAsync(PickOptions? options = null) =>
PlatformPickAsync(options ?? PickOptions.Default, true);
}
diff --git a/src/Essentials/src/MediaPicker/ImageProcessor.android.cs b/src/Essentials/src/MediaPicker/ImageProcessor.android.cs
index c14f034a5f04..42dd5875bcec 100644
--- a/src/Essentials/src/MediaPicker/ImageProcessor.android.cs
+++ b/src/Essentials/src/MediaPicker/ImageProcessor.android.cs
@@ -51,8 +51,8 @@ public static partial async Task RotateImageAsync(Stream inputStream, st
return new MemoryStream(bytes);
}
- // Apply EXIF orientation correction using SetRotate(0) to preserve original EXIF behavior
- Bitmap? rotatedBitmap = ApplyExifOrientation(originalBitmap);
+ // Apply EXIF orientation correction with the actual orientation value
+ Bitmap? rotatedBitmap = ApplyExifOrientation(originalBitmap, orientation);
if (rotatedBitmap is null)
{
return new MemoryStream(bytes);
@@ -113,47 +113,92 @@ public static partial async Task RotateImageAsync(Stream inputStream, st
///
private static int GetExifOrientation(byte[] imageBytes)
{
+ var tempFileName = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.jpg");
try
{
- // Create a temporary file to read EXIF data
- var tempFileName = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.jpg");
using (var fileStream = File.Create(tempFileName))
{
fileStream.Write(imageBytes, 0, imageBytes.Length);
}
- var exif = new ExifInterface(tempFileName);
- int orientation = exif.GetAttributeInt(ExifInterface.TagOrientation, 1);
-
- // Clean up temp file
- try
- {
- File.Delete(tempFileName);
- }
- catch
- {
- // Ignore cleanup failures
- }
-
- return orientation;
+ using var exif = new ExifInterface(tempFileName);
+ return exif.GetAttributeInt(ExifInterface.TagOrientation, 1);
}
catch
{
return 1; // Default to normal orientation
}
+ finally
+ {
+ try { File.Delete(tempFileName); } catch { }
+ }
}
///
- /// Apply EXIF orientation correction by preserving original EXIF behavior
+ /// Apply EXIF orientation correction by rotating the bitmap according to EXIF orientation value
///
- private static Bitmap? ApplyExifOrientation(Bitmap bitmap)
+ private static Bitmap? ApplyExifOrientation(Bitmap bitmap, int orientation = 1)
{
try
{
- // Use SetRotate(0) to preserve original EXIF orientation behavior
- var matrix = new Matrix();
- matrix.SetRotate(0);
- return Bitmap.CreateBitmap(bitmap, 0, 0, bitmap.Width, bitmap.Height, matrix, true);
+ // Convert EXIF orientation to rotation angle and flip
+ // EXIF Orientation values:
+ // 1 = normal
+ // 2 = flip horizontal
+ // 3 = rotate 180°
+ // 4 = flip vertical
+ // 5 = rotate 90° CW + flip horizontal
+ // 6 = rotate 90° CW
+ // 7 = rotate 90° CCW + flip horizontal
+ // 8 = rotate 90° CCW
+
+ bool flipHorizontal = false;
+ bool flipVertical = false;
+ int rotationAngle = orientation switch
+ {
+ 2 => 0, // flip horizontal only
+ 3 => 180,
+ 4 => 0, // flip vertical only
+ 5 => 90, // rotate 90° CW + flip horizontal
+ 6 => 90,
+ 7 => 270, // rotate 270° CW + flip horizontal
+ 8 => 270,
+ _ => 0 // 1 and other values mean no rotation needed
+ };
+
+ // Determine if we need to flip
+ flipHorizontal = orientation is 2 or 5 or 7;
+ flipVertical = orientation is 4;
+
+ // If no rotation and no flip needed, return original bitmap
+ if (rotationAngle == 0 && !flipHorizontal && !flipVertical)
+ {
+ return bitmap;
+ }
+
+ // Apply rotation and/or flip using a transformation matrix
+ using (var matrix = new Matrix())
+ {
+ float centerX = bitmap.Width / 2f;
+ float centerY = bitmap.Height / 2f;
+
+ // Apply rotation around center first, then flip.
+ // For orientations 5 & 7 the EXIF spec requires rotate-then-flip;
+ // reversing the order produces an incorrect result.
+ if (rotationAngle != 0)
+ matrix.PostRotate(rotationAngle, centerX, centerY);
+
+ if (flipHorizontal)
+ matrix.PostScale(-1, 1, centerX, centerY);
+
+ if (flipVertical)
+ matrix.PostScale(1, -1, centerX, centerY);
+
+ // Create transformed bitmap
+ var transformedBitmap = Bitmap.CreateBitmap(bitmap, 0, 0, bitmap.Width, bitmap.Height, matrix, true);
+
+ return transformedBitmap;
+ }
}
catch (Exception ex)
{
@@ -183,61 +228,58 @@ private static int GetExifOrientation(byte[] imageBytes)
// Create temporary file to extract EXIF data
var tempFileName = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.jpg");
- using (var fileStream = File.Create(tempFileName))
+ try
{
- fileStream.Write(bytes, 0, bytes.Length);
- }
-
- // Extract all EXIF attributes
- var exif = new ExifInterface(tempFileName);
- var metadataList = new List();
+ using (var fileStream = File.Create(tempFileName))
+ {
+ fileStream.Write(bytes, 0, bytes.Length);
+ }
- // Extract common EXIF tags
- var tags = new string[]
- {
- ExifInterface.TagArtist,
- ExifInterface.TagCopyright,
- ExifInterface.TagDatetime,
- ExifInterface.TagImageDescription,
- ExifInterface.TagMake,
- ExifInterface.TagModel,
- ExifInterface.TagOrientation,
- ExifInterface.TagSoftware,
- ExifInterface.TagGpsLatitude,
- ExifInterface.TagGpsLongitude,
- ExifInterface.TagGpsAltitude,
- ExifInterface.TagExposureTime,
- ExifInterface.TagFNumber,
- ExifInterface.TagIso,
- ExifInterface.TagWhiteBalance,
- ExifInterface.TagFlash,
- ExifInterface.TagFocalLength
- };
+ // Extract all EXIF attributes
+ using var exif = new ExifInterface(tempFileName);
+ var metadataList = new List();
- foreach (var tag in tags)
- {
- var value = exif.GetAttribute(tag);
- if (!string.IsNullOrEmpty(value))
+ // Extract common EXIF tags
+ var tags = new string[]
+ {
+ ExifInterface.TagArtist,
+ ExifInterface.TagCopyright,
+ ExifInterface.TagDatetime,
+ ExifInterface.TagImageDescription,
+ ExifInterface.TagMake,
+ ExifInterface.TagModel,
+ ExifInterface.TagOrientation,
+ ExifInterface.TagSoftware,
+ ExifInterface.TagGpsLatitude,
+ ExifInterface.TagGpsLongitude,
+ ExifInterface.TagGpsAltitude,
+ ExifInterface.TagExposureTime,
+ ExifInterface.TagFNumber,
+ ExifInterface.TagIso,
+ ExifInterface.TagWhiteBalance,
+ ExifInterface.TagFlash,
+ ExifInterface.TagFocalLength
+ };
+
+ foreach (var tag in tags)
{
- metadataList.Add($"{tag}={value}");
+ var value = exif.GetAttribute(tag);
+ if (!string.IsNullOrEmpty(value))
+ {
+ metadataList.Add($"{tag}={value}");
+ }
}
- }
- // Serialize metadata to simple string format
- var metadataString = string.Join("\n", metadataList);
- var metadataBytes = System.Text.Encoding.UTF8.GetBytes(metadataString);
+ // Serialize metadata to simple string format
+ var metadataString = string.Join("\n", metadataList);
+ var metadataBytes = System.Text.Encoding.UTF8.GetBytes(metadataString);
- // Clean up temp file
- try
- {
- File.Delete(tempFileName);
+ return metadataBytes;
}
- catch
+ finally
{
- // Ignore cleanup failures
+ try { File.Delete(tempFileName); } catch { }
}
-
- return metadataBytes;
}
catch
{
@@ -283,40 +325,36 @@ public static partial async Task ApplyMetadataAsync(Stream processedStre
// Create temporary file to apply EXIF data
var tempFileName = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.jpg");
- using (var fileStream = File.Create(tempFileName))
- {
- fileStream.Write(bytes, 0, bytes.Length);
- }
-
- // Apply EXIF data
- var exif = new ExifInterface(tempFileName);
- foreach (var kvp in metadataDict)
+ try
{
- try
+ using (var fileStream = File.Create(tempFileName))
{
- exif.SetAttribute(kvp.Key, kvp.Value);
+ fileStream.Write(bytes, 0, bytes.Length);
}
- catch
+
+ // Apply EXIF data
+ using var exif = new ExifInterface(tempFileName);
+ foreach (var kvp in metadataDict)
{
- // Skip attributes that can't be set
+ try
+ {
+ exif.SetAttribute(kvp.Key, kvp.Value);
+ }
+ catch
+ {
+ // Skip attributes that can't be set
+ }
}
- }
- exif.SaveAttributes();
-
- // Read back the file with applied metadata
- var resultBytes = File.ReadAllBytes(tempFileName);
+ exif.SaveAttributes();
- // Clean up temp file
- try
- {
- File.Delete(tempFileName);
+ // Read back the file with applied metadata
+ var resultBytes = File.ReadAllBytes(tempFileName);
+ return new MemoryStream(resultBytes);
}
- catch
+ finally
{
- // Ignore cleanup failures
+ try { File.Delete(tempFileName); } catch { }
}
-
- return new MemoryStream(resultBytes);
}
catch
{
diff --git a/src/Essentials/src/MediaPicker/ImageProcessor.ios.cs b/src/Essentials/src/MediaPicker/ImageProcessor.ios.cs
index 077d6226ff46..d555465e0f1b 100644
--- a/src/Essentials/src/MediaPicker/ImageProcessor.ios.cs
+++ b/src/Essentials/src/MediaPicker/ImageProcessor.ios.cs
@@ -32,38 +32,60 @@ public static partial async Task RotateImageAsync(Stream inputStream, st
return inputStream;
}
- // Create a new image with corrected orientation metadata (no pixel manipulation)
- // This preserves the original image data while fixing the display orientation
- var correctedImage = UIImage.FromImage(image.CGImage, image.CurrentScale, UIImageOrientation.Up);
-
- // Write the corrected image back to a stream, preserving original quality
+ // image.Size on iOS is already orientation-corrected.
+ // Draw into a new context to normalize pixel orientation.
+ // End the context before any await to avoid thread-affinity issues with
+ // UIGraphics' thread-local context stack.
+ UIGraphics.BeginImageContextWithOptions(image.Size, false, image.CurrentScale);
+ UIImage? rotatedImage;
+ try
+ {
+ image.Draw(new CGRect(CGPoint.Empty, image.Size));
+ rotatedImage = UIGraphics.GetImageFromCurrentImageContext();
+ }
+ finally
+ {
+ UIGraphics.EndImageContext();
+ }
+
+ if (rotatedImage is null)
+ {
+ return inputStream;
+ }
+
+ // Write the rotated image back to a stream, preserving original quality
var outputStream = new MemoryStream();
-
+
// Determine output format based on original file
- NSData? imageData = null;
+ NSData? imageData;
if (!string.IsNullOrEmpty(originalFileName))
{
var extension = Path.GetExtension(originalFileName).ToLowerInvariant();
if (extension == ".png")
{
- imageData = correctedImage.AsPNG();
+ imageData = rotatedImage.AsPNG();
}
else
{
// For JPEG and other formats, use maximum quality (1.0)
- // People can downscale themselves through the MediaPickerOptions
- imageData = correctedImage.AsJPEG(1f);
+ imageData = rotatedImage.AsJPEG(1f);
}
}
else
{
// Default to JPEG with maximum quality (1.0)
- imageData = correctedImage.AsJPEG(1f);
+ imageData = rotatedImage.AsJPEG(1f);
}
-
+
+ rotatedImage.Dispose();
+
if (imageData is not null)
{
- await imageData.AsStream().CopyToAsync(outputStream);
+ using (imageData)
+ using (var encodedStream = imageData.AsStream())
+ {
+ await encodedStream.CopyToAsync(outputStream);
+ }
outputStream.Position = 0;
return outputStream;
}
@@ -129,7 +151,7 @@ public static partial Task ApplyMetadataAsync(Stream processedStream, by
return Task.FromResult(processedStream);
// Create mutable data for output
- var outputData = NSMutableData.FromCapacity(0);
+ using var outputData = NSMutableData.FromCapacity(0);
if (outputData == null)
return Task.FromResult(processedStream);
@@ -154,7 +176,8 @@ public static partial Task ApplyMetadataAsync(Stream processedStream, by
destination.AddImage(image, restoredMetadata);
destination.Close();
- return Task.FromResult(outputData.AsStream());
+ // Copy bytes before outputData is disposed at end of using scope
+ return Task.FromResult(new MemoryStream(outputData.ToArray()));
}
return Task.FromResult(processedStream);
diff --git a/src/Essentials/src/MediaPicker/MediaPicker.android.cs b/src/Essentials/src/MediaPicker/MediaPicker.android.cs
index 5632c51ad6f3..08a3de2da51b 100644
--- a/src/Essentials/src/MediaPicker/MediaPicker.android.cs
+++ b/src/Essentials/src/MediaPicker/MediaPicker.android.cs
@@ -23,6 +23,20 @@ partial class MediaPickerImplementation : IMediaPicker
public bool IsCaptureSupported
=> Application.Context?.PackageManager?.HasSystemFeature(PackageManager.FeatureCameraAny) ?? false;
+ static async Task RotateImageInPlace(string filePath, MediaPickerOptions options)
+ {
+ using var inputStream = File.OpenRead(filePath);
+ var fileName = System.IO.Path.GetFileName(filePath);
+ using var rotatedStream = await ImageProcessor.RotateImageAsync(inputStream, fileName);
+ rotatedStream.Position = 0;
+ inputStream.Dispose(); // explicit close before delete
+ try
+ { File.Delete(filePath); }
+ catch { }
+ using var outputStream = File.Create(filePath);
+ await rotatedStream.CopyToAsync(outputStream);
+ }
+
internal static bool IsPhotoPickerAvailable
=> PickVisualMedia.InvokeIsPhotoPickerAvailable(Platform.AppContext);
@@ -85,30 +99,10 @@ public async Task CaptureAsync(MediaPickerOptions options, bool phot
if (photo)
{
captureResult = await CapturePhotoAsync(captureIntent);
- // Apply rotation if needed for photos
- if (captureResult is not null && ImageProcessor.IsRotationNeeded(options))
- {
- using var inputStream = File.OpenRead(captureResult);
- var fileName = System.IO.Path.GetFileName(captureResult);
- using var rotatedStream = await ImageProcessor.RotateImageAsync(inputStream, fileName);
-
- var rotatedPath = System.IO.Path.Combine(
- System.IO.Path.GetDirectoryName(captureResult),
- System.IO.Path.GetFileNameWithoutExtension(captureResult) + "_rotated" + System.IO.Path.GetExtension(captureResult));
-
- using var outputStream = File.Create(rotatedPath);
- rotatedStream.Position = 0;
- await rotatedStream.CopyToAsync(outputStream);
-
- // Use the rotated image and delete the original
- try
- {
- File.Delete(captureResult);
- }
- catch { }
- captureResult = rotatedPath;
- }
-
+ // Apply rotation if needed for photos
+ if (captureResult is not null && ImageProcessor.IsRotationNeeded(options))
+ await RotateImageInPlace(captureResult, options);
+
// Apply compression/resizing if needed for photos
if (captureResult is not null && ImageProcessor.IsProcessingNeeded(options?.MaximumWidth, options?.MaximumHeight, options?.CompressionQuality ?? 100))
{
@@ -161,22 +155,7 @@ void OnResult(Intent intent)
{
// Apply rotation if needed
if (ImageProcessor.IsRotationNeeded(options))
- {
- using var inputStream = File.OpenRead(path);
- var fileName = System.IO.Path.GetFileName(path);
- using var rotatedStream = await ImageProcessor.RotateImageAsync(inputStream, fileName);
-
- var rotatedPath = System.IO.Path.Combine(
- System.IO.Path.GetDirectoryName(path),
- System.IO.Path.GetFileNameWithoutExtension(path) + "_rotated" + System.IO.Path.GetExtension(path));
-
- using var outputStream = File.Create(rotatedPath);
- rotatedStream.Position = 0;
- await rotatedStream.CopyToAsync(outputStream);
-
- // Use the rotated image
- path = rotatedPath;
- }
+ await RotateImageInPlace(path, options);
// Apply compression/resizing if needed
if (ImageProcessor.IsProcessingNeeded(options?.MaximumWidth, options?.MaximumHeight, options?.CompressionQuality ?? 100))
@@ -184,7 +163,7 @@ void OnResult(Intent intent)
path = await CompressImageIfNeeded(path, options);
}
}
-
+
return new FileResult(path);
}
@@ -215,22 +194,7 @@ async Task PickUsingPhotoPicker(MediaPickerOptions options, bool pho
{
// Apply rotation if needed
if (ImageProcessor.IsRotationNeeded(options))
- {
- using var inputStream = File.OpenRead(path);
- var fileName = System.IO.Path.GetFileName(path);
- using var rotatedStream = await ImageProcessor.RotateImageAsync(inputStream, fileName);
-
- var rotatedPath = System.IO.Path.Combine(
- System.IO.Path.GetDirectoryName(path),
- System.IO.Path.GetFileNameWithoutExtension(path) + "_rotated" + System.IO.Path.GetExtension(path));
-
- using var outputStream = File.Create(rotatedPath);
- rotatedStream.Position = 0;
- await rotatedStream.CopyToAsync(outputStream);
-
- // Use the rotated image
- path = rotatedPath;
- }
+ await RotateImageInPlace(path, options);
// Apply compression/resizing if needed
if (ImageProcessor.IsProcessingNeeded(options?.MaximumWidth, options?.MaximumHeight, options?.CompressionQuality ?? 100))
@@ -281,27 +245,12 @@ async Task> PickMultipleUsingPhotoPicker(MediaPickerOptions opt
if (!uri?.Equals(AndroidUri.Empty) ?? false)
{
var path = FileSystemUtils.EnsurePhysicalPath(uri);
-
+
if (photo)
{
// Apply rotation if needed
if (ImageProcessor.IsRotationNeeded(options))
- {
- using var inputStream = File.OpenRead(path);
- var fileName = System.IO.Path.GetFileName(path);
- using var rotatedStream = await ImageProcessor.RotateImageAsync(inputStream, fileName);
-
- var rotatedPath = System.IO.Path.Combine(
- System.IO.Path.GetDirectoryName(path),
- System.IO.Path.GetFileNameWithoutExtension(path) + "_rotated" + System.IO.Path.GetExtension(path));
-
- using var outputStream = File.Create(rotatedPath);
- rotatedStream.Position = 0;
- await rotatedStream.CopyToAsync(outputStream);
-
- // Use the rotated image
- path = rotatedPath;
- }
+ await RotateImageInPlace(path, options);
// Apply compression/resizing if needed
if (ImageProcessor.IsProcessingNeeded(options?.MaximumWidth, options?.MaximumHeight, options?.CompressionQuality ?? 100))
@@ -368,21 +317,29 @@ static async Task CompressImageIfNeeded(string imagePath, MediaPickerOpt
if (processedStream != null)
{
- // Determine output extension based on processed data and original filename
+ // Determine the correct output extension based on the processed format
+ processedStream.Position = 0;
var outputExtension = ImageProcessor.DetermineOutputExtension(processedStream, options?.CompressionQuality ?? 100, inputFileName);
- var processedFileName = System.IO.Path.GetFileNameWithoutExtension(imagePath) + "_processed" + outputExtension;
- var processedPath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(imagePath), processedFileName);
+ var originalExtension = System.IO.Path.GetExtension(imagePath);
- // Write processed image to file
- using var outputStream = File.Create(processedPath);
- processedStream.Position = 0;
- await processedStream.CopyToAsync(outputStream);
+ // If format changed (e.g., PNG -> JPEG), use new extension
+ string outputPath = imagePath;
+ if (!string.Equals(outputExtension, originalExtension, StringComparison.OrdinalIgnoreCase))
+ {
+ outputPath = System.IO.Path.ChangeExtension(imagePath, outputExtension);
+ }
- // Delete original file
+ // Delete original file first
try
{ originalFile.Delete(); }
catch { }
- return processedPath;
+
+ // Write processed image to output path with correct extension
+ using var outputStream = File.Create(outputPath);
+ processedStream.Position = 0;
+ await processedStream.CopyToAsync(outputStream);
+
+ return outputPath;
}
// If ImageProcessor returns null (e.g., on .NET Standard), ImageProcessor.IsProcessingNeeded would have returned false,
@@ -481,32 +438,17 @@ void OnResult(Intent resultIntent)
foreach (var path in tempResultList)
{
string processedPath = path;
-
+
// Apply rotation if needed
if (ImageProcessor.IsRotationNeeded(options))
- {
- using var inputStream = File.OpenRead(processedPath);
- var fileName = System.IO.Path.GetFileName(processedPath);
- using var rotatedStream = await ImageProcessor.RotateImageAsync(inputStream, fileName);
-
- var rotatedPath = System.IO.Path.Combine(
- System.IO.Path.GetDirectoryName(processedPath),
- System.IO.Path.GetFileNameWithoutExtension(processedPath) + "_rotated" + System.IO.Path.GetExtension(processedPath));
-
- using var outputStream = File.Create(rotatedPath);
- rotatedStream.Position = 0;
- await rotatedStream.CopyToAsync(outputStream);
-
- // Use the rotated image
- processedPath = rotatedPath;
- }
+ await RotateImageInPlace(processedPath, options);
// Apply compression/resizing if needed
if (ImageProcessor.IsProcessingNeeded(options?.MaximumWidth, options?.MaximumHeight, options?.CompressionQuality ?? 100))
{
processedPath = await CompressImageIfNeeded(processedPath, options);
}
-
+
resultList.Add(new FileResult(processedPath));
}
}
diff --git a/src/Essentials/src/MediaPicker/MediaPicker.ios.cs b/src/Essentials/src/MediaPicker/MediaPicker.ios.cs
index 5e7ca0389f65..4b7296208f0e 100644
--- a/src/Essentials/src/MediaPicker/MediaPicker.ios.cs
+++ b/src/Essentials/src/MediaPicker/MediaPicker.ios.cs
@@ -98,11 +98,6 @@ public async Task PhotoAsync(MediaPickerOptions options, bool photo,
}
else
{
- if (!pickExisting)
- {
- await Permissions.EnsureGrantedAsync();
- }
-
var sourceType = pickExisting
? UIImagePickerControllerSourceType.PhotoLibrary
: UIImagePickerControllerSourceType.Camera;
@@ -282,17 +277,17 @@ static async Task> PickerResultsToMediaFiles(PHPickerResult[] r
{
using var originalStream = await result.OpenReadAsync();
using var rotatedStream = await ImageProcessor.RotateImageAsync(originalStream, result.FileName);
-
+
// Create a temp file for the rotated image
var tempFileName = $"{Guid.NewGuid()}{Path.GetExtension(result.FileName)}";
var tempFilePath = Path.Combine(Path.GetTempPath(), tempFileName);
-
+
using (var fileStream = File.Create(tempFilePath))
{
rotatedStream.Position = 0;
await rotatedStream.CopyToAsync(fileStream);
}
-
+
var rotatedResult = new FileResult(tempFilePath)
{
FileName = result.FileName,
@@ -315,7 +310,7 @@ static async Task