From d631ab16dd7d8fef7b864260a6c2bfbc604fb373 Mon Sep 17 00:00:00 2001
From: Tamilarasan Paranthaman
<93904422+Tamilarasan-Paranthaman@users.noreply.github.com>
Date: Mon, 2 Mar 2026 17:57:03 +0530
Subject: [PATCH 01/26] Sandbox test sample
---
.../samples/Controls.Sample.Sandbox/App.xaml | 3 +-
.../Controls.Sample.Sandbox/App.xaml.cs | 26 +-
.../FlyoutTestPage.xaml | 149 ++++
.../FlyoutTestPage.xaml.cs | 244 +++++++
.../Controls.Sample.Sandbox/MainPage.xaml | 86 ++-
.../Controls.Sample.Sandbox/MainPage.xaml.cs | 680 +++++++++++++++++-
.../NavigationTestPage.xaml | 72 ++
.../NavigationTestPage.xaml.cs | 121 ++++
.../Resources/Styles/Colors.xaml | 58 ++
.../Resources/Styles/Styles.xaml | 583 +++++++++++++++
.../Controls.Sample.Sandbox/SandboxShell.xaml | 126 +++-
.../Controls.Sample.Sandbox/SettingsPage.xaml | 152 ++++
.../SettingsPage.xaml.cs | 198 +++++
.../TabbedPageTestPage.xaml | 248 +++++++
.../TabbedPageTestPage.xaml.cs | 80 +++
.../TabbedPageTopTabsTestPage.xaml | 245 +++++++
.../TabbedPageTopTabsTestPage.xaml.cs | 86 +++
.../ToolbarTestPage.xaml | 84 +++
.../ToolbarTestPage.xaml.cs | 93 +++
19 files changed, 3311 insertions(+), 23 deletions(-)
create mode 100644 src/Controls/samples/Controls.Sample.Sandbox/FlyoutTestPage.xaml
create mode 100644 src/Controls/samples/Controls.Sample.Sandbox/FlyoutTestPage.xaml.cs
create mode 100644 src/Controls/samples/Controls.Sample.Sandbox/NavigationTestPage.xaml
create mode 100644 src/Controls/samples/Controls.Sample.Sandbox/NavigationTestPage.xaml.cs
create mode 100644 src/Controls/samples/Controls.Sample.Sandbox/Resources/Styles/Colors.xaml
create mode 100644 src/Controls/samples/Controls.Sample.Sandbox/Resources/Styles/Styles.xaml
create mode 100644 src/Controls/samples/Controls.Sample.Sandbox/SettingsPage.xaml
create mode 100644 src/Controls/samples/Controls.Sample.Sandbox/SettingsPage.xaml.cs
create mode 100644 src/Controls/samples/Controls.Sample.Sandbox/TabbedPageTestPage.xaml
create mode 100644 src/Controls/samples/Controls.Sample.Sandbox/TabbedPageTestPage.xaml.cs
create mode 100644 src/Controls/samples/Controls.Sample.Sandbox/TabbedPageTopTabsTestPage.xaml
create mode 100644 src/Controls/samples/Controls.Sample.Sandbox/TabbedPageTopTabsTestPage.xaml.cs
create mode 100644 src/Controls/samples/Controls.Sample.Sandbox/ToolbarTestPage.xaml
create mode 100644 src/Controls/samples/Controls.Sample.Sandbox/ToolbarTestPage.xaml.cs
diff --git a/src/Controls/samples/Controls.Sample.Sandbox/App.xaml b/src/Controls/samples/Controls.Sample.Sandbox/App.xaml
index d5e9a66a4322..c72f57fa1c16 100644
--- a/src/Controls/samples/Controls.Sample.Sandbox/App.xaml
+++ b/src/Controls/samples/Controls.Sample.Sandbox/App.xaml
@@ -6,7 +6,8 @@
-
+
+
diff --git a/src/Controls/samples/Controls.Sample.Sandbox/App.xaml.cs b/src/Controls/samples/Controls.Sample.Sandbox/App.xaml.cs
index 9512dea98e39..9be336a2d319 100644
--- a/src/Controls/samples/Controls.Sample.Sandbox/App.xaml.cs
+++ b/src/Controls/samples/Controls.Sample.Sandbox/App.xaml.cs
@@ -9,16 +9,24 @@ public App()
protected override Window CreateWindow(IActivationState? activationState)
{
- // To test shell scenarios, change this to true
- bool useShell = false;
+ // Test mode selection:
+ // "shell" - Test Shell handler migration
+ // "tabbedpage" - Test TabbedPage with BottomNavigationManager
+ // "navigation" - Test NavigationPage
- if (!useShell)
- {
- return new Window(new NavigationPage(new MainPage()));
- }
- else
+ string testMode = "shell";
+
+ //string testMode = "tabbedpage";
+
+ //string testMode = "navigation";
+
+
+
+ return testMode switch
{
- return new Window(new SandboxShell());
- }
+ "tabbedpage" => new Window(new TabbedPageTestPage()),
+ "navigation" => new Window(new NavigationPage(new MainPage())),
+ _ => new Window(new SandboxShell()) // default: shell
+ };
}
}
diff --git a/src/Controls/samples/Controls.Sample.Sandbox/FlyoutTestPage.xaml b/src/Controls/samples/Controls.Sample.Sandbox/FlyoutTestPage.xaml
new file mode 100644
index 000000000000..e24a94f50db5
--- /dev/null
+++ b/src/Controls/samples/Controls.Sample.Sandbox/FlyoutTestPage.xaml
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/samples/Controls.Sample.Sandbox/FlyoutTestPage.xaml.cs b/src/Controls/samples/Controls.Sample.Sandbox/FlyoutTestPage.xaml.cs
new file mode 100644
index 000000000000..a6498fdf5cc9
--- /dev/null
+++ b/src/Controls/samples/Controls.Sample.Sandbox/FlyoutTestPage.xaml.cs
@@ -0,0 +1,244 @@
+using Microsoft.Maui.Controls;
+#pragma warning disable IDE0031 // Use null propagation
+namespace Maui.Controls.Sample
+{
+ public partial class FlyoutTestPage : ContentPage
+ {
+ public FlyoutTestPage()
+ {
+ InitializeComponent();
+ UpdateFlyoutState();
+ }
+
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+
+ if (Shell.Current is not null)
+ {
+ Shell.Current.PropertyChanged += OnShellPropertyChanged;
+ }
+ }
+
+ protected override void OnDisappearing()
+ {
+ base.OnDisappearing();
+ if (Shell.Current is not null)
+ {
+ Shell.Current.PropertyChanged -= OnShellPropertyChanged;
+ }
+ }
+
+ private void OnShellPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Shell.FlyoutIsPresented))
+ {
+ UpdateFlyoutState();
+ }
+ }
+
+ private void UpdateFlyoutState()
+ {
+ if (Shell.Current != null)
+ {
+ FlyoutStateLabel.Text = $"Flyout State: {(Shell.Current.FlyoutIsPresented ? "Open" : "Closed")}";
+ }
+ }
+
+ private void OnOpenFlyoutClicked(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutIsPresented = true;
+ StatusLabel.Text = "Status: Flyout opened";
+ }
+
+ private void OnCloseFlyoutClicked(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutIsPresented = false;
+ StatusLabel.Text = "Status: Flyout closed";
+ }
+
+ private void OnToggleFlyoutClicked(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutIsPresented = !Shell.Current.FlyoutIsPresented;
+ StatusLabel.Text = $"Status: Flyout toggled to {(Shell.Current.FlyoutIsPresented ? "Open" : "Closed")}";
+ }
+
+ private void OnSetBehaviorFlyout(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutBehavior = FlyoutBehavior.Flyout;
+ StatusLabel.Text = "Status: FlyoutBehavior = Flyout (swipeable)";
+ }
+
+ private void OnSetBehaviorLocked(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutBehavior = FlyoutBehavior.Locked;
+ StatusLabel.Text = "Status: FlyoutBehavior = Locked (always visible)";
+ }
+
+ private void OnSetBehaviorDisabled(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutBehavior = FlyoutBehavior.Disabled;
+ StatusLabel.Text = "Status: FlyoutBehavior = Disabled (hidden)";
+ }
+
+ private void OnChangeFlyoutBackgroundRed(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutBackgroundColor = Colors.Red;
+ StatusLabel.Text = "Status: Flyout background = Red";
+ }
+
+ private void OnChangeFlyoutBackgroundBlue(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutBackgroundColor = Colors.Blue;
+ StatusLabel.Text = "Status: Flyout background = Blue";
+ }
+
+ private void OnResetFlyoutBackground(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutBackgroundColor = null;
+ StatusLabel.Text = "Status: Flyout background reset to default";
+ }
+
+ private void OnSetWidth200(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutWidth = 200;
+ StatusLabel.Text = "Status: Flyout width = 200";
+ }
+
+ private void OnSetWidth300(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutWidth = 300;
+ StatusLabel.Text = "Status: Flyout width = 300";
+ }
+
+ private void OnResetWidth(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutWidth = -1; // Default
+ StatusLabel.Text = "Status: Flyout width reset to default";
+ }
+
+ // Header/Footer Tests
+ private void OnAddHeaderClicked(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutHeader = new Label
+ {
+ Text = "Flyout Header",
+ FontSize = 20,
+ FontAttributes = FontAttributes.Bold,
+ TextColor = Colors.White,
+ BackgroundColor = Colors.Blue,
+ Padding = new Thickness(10),
+ HorizontalOptions = LayoutOptions.Fill
+ };
+ StatusLabel.Text = "Status: Header added (Label)";
+ }
+
+ private void OnChangeHeaderClicked(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutHeader = new Button
+ {
+ Text = "Header Button",
+ BackgroundColor = Colors.Green,
+ TextColor = Colors.White,
+ Margin = new Thickness(10)
+ };
+ StatusLabel.Text = "Status: Header changed (Button)";
+ }
+
+ private void OnRemoveHeaderClicked(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutHeader = null;
+ StatusLabel.Text = "Status: Header removed";
+ }
+
+ private void OnAddFooterClicked(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutFooter = new Label
+ {
+ Text = "Flyout Footer - Version 1.0",
+ FontSize = 12,
+ TextColor = Colors.Gray,
+ BackgroundColor = Colors.LightGray,
+ Padding = new Thickness(10),
+ HorizontalOptions = LayoutOptions.Fill,
+ HorizontalTextAlignment = TextAlignment.Center
+ };
+ StatusLabel.Text = "Status: Footer added";
+ }
+
+ private void OnRemoveFooterClicked(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutFooter = null;
+ StatusLabel.Text = "Status: Footer removed";
+ }
+
+ // Header Behavior Tests
+ private void OnHeaderBehaviorDefault(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutHeaderBehavior = FlyoutHeaderBehavior.Default;
+ StatusLabel.Text = "Status: Header behavior = Default";
+ }
+
+ private void OnHeaderBehaviorScroll(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutHeaderBehavior = FlyoutHeaderBehavior.Scroll;
+ StatusLabel.Text = "Status: Header behavior = Scroll";
+ }
+
+ private void OnHeaderBehaviorCollapse(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutHeaderBehavior = FlyoutHeaderBehavior.CollapseOnScroll;
+ StatusLabel.Text = "Status: Header behavior = CollapseOnScroll";
+ }
+
+ // Flyout Content Tests
+ private void OnSetCustomContentClicked(object? sender, EventArgs e)
+ {
+ var customContent = new CollectionView
+ {
+ ItemsSource = new[] { "Custom Item 1", "Custom Item 2", "Custom Item 3" },
+ BackgroundColor = Colors.LightYellow
+ };
+ Shell.Current.FlyoutContent = customContent;
+ StatusLabel.Text = "Status: Custom content set (ListView)";
+ }
+
+ private void OnResetContentClicked(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutContent = null;
+ StatusLabel.Text = "Status: Content reset to default";
+ }
+
+ // Flyout Backdrop Tests
+ private void OnRedBackdropClicked(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutBackdrop = new SolidColorBrush(Colors.Red);
+ StatusLabel.Text = "Status: Backdrop = Red";
+ }
+
+ private void OnBlackBackdropClicked(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutBackdrop = new SolidColorBrush(Colors.Black.WithAlpha(0.5f));
+ StatusLabel.Text = "Status: Backdrop = Black semi-transparent";
+ }
+
+ private void OnRemoveBackdropClicked(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutBackdrop = Brush.Transparent;
+ StatusLabel.Text = "Status: Backdrop removed (transparent)";
+ }
+
+ // Flow Direction Tests
+ private void OnFlowDirectionLTRClicked(object? sender, EventArgs e)
+ {
+ Shell.Current.FlowDirection = FlowDirection.LeftToRight;
+ StatusLabel.Text = "Status: Flow direction = LeftToRight";
+ }
+
+ private void OnFlowDirectionRTLClicked(object? sender, EventArgs e)
+ {
+ Shell.Current.FlowDirection = FlowDirection.RightToLeft;
+ StatusLabel.Text = "Status: Flow direction = RightToLeft";
+ }
+ }
+}
diff --git a/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml b/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml
index 03475e0227ed..ebde353b7279 100644
--- a/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml
+++ b/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml
@@ -1,3 +1,85 @@
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml.cs b/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml.cs
index b7744fa262ea..146cfbdb995a 100644
--- a/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml.cs
+++ b/src/Controls/samples/Controls.Sample.Sandbox/MainPage.xaml.cs
@@ -1,9 +1,687 @@
-namespace Maui.Controls.Sample;
+using System.Threading.Tasks;
+
+namespace Maui.Controls.Sample;
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
+ Console.WriteLine("SANDBOX: MainPage loaded successfully");
+
+ ToolbarItems.Add(new ToolbarItem
+ {
+ Text = "Info",
+ Order = ToolbarItemOrder.Secondary,
+ Command = new Command(() =>
+ {
+ Console.WriteLine("SANDBOX: Info toolbar item clicked");
+ DisplayAlertAsync("Info", "This is the Info button from toolbar", "OK");
+ })
+ });
+
+ ToolbarItems.Add(new ToolbarItem
+ {
+ Text = "Help",
+ Order = ToolbarItemOrder.Secondary,
+ Command = new Command(() =>
+ {
+ Console.WriteLine("SANDBOX: Help toolbar item clicked");
+ DisplayAlertAsync("Help", "This is the Help button from toolbar", "OK");
+ })
+ });
+ }
+
+ async void Button_Clicked(object? sender, EventArgs e)
+ {
+ Console.WriteLine("SANDBOX: Navigation button clicked");
+ await Navigation.PushAsync(new DetailsPage());
+ Console.WriteLine("SANDBOX: Navigated to DetailsPage");
+ }
+
+ async void ToggleFlyoutButton_Clicked(object? sender, EventArgs e)
+ {
+ Shell.Current.FlyoutIsPresented = !Shell.Current.FlyoutIsPresented;
+ }
+
+ async void ChangeFlyoutBackgroundButton_Clicked(object? sender, EventArgs e)
+ {
+ Console.WriteLine("SANDBOX: Change Flyout Background button clicked");
+ var random = new Random();
+ var color = Color.FromRgb(random.Next(256), random.Next(256), random.Next(256));
+ Shell.Current.FlyoutBackground = new SolidColorBrush(color);
+ Console.WriteLine($"SANDBOX: Flyout background changed to {color}");
+ }
+
+ void OnInfoClicked(object? sender, EventArgs e)
+ {
+ Console.WriteLine("SANDBOX: Info toolbar item clicked");
+ DisplayAlertAsync("Info", "This is the Info button from toolbar", "OK");
+ }
+
+ void OnHelpClicked(object? sender, EventArgs e)
+ {
+ Console.WriteLine("SANDBOX: Help toolbar item clicked");
+ DisplayAlertAsync("Help", "This is the Help button from toolbar", "OK");
+ }
+
+ // GoToAsync tests for single content mode
+ async void GoToSettings_Clicked(object? sender, EventArgs e)
+ {
+ Console.WriteLine("SANDBOX: GoToAsync → Settings");
+ await Shell.Current.GoToAsync("//Settings/SettingsPage");
+ }
+
+ async void GoToMultiTab1_Clicked(object? sender, EventArgs e)
+ {
+ Console.WriteLine("SANDBOX: GoToAsync → MultiTab/Content1");
+ await Shell.Current.GoToAsync("//MultiTab/Content1");
+ }
+
+ async void GoToMultiTab2_Clicked(object? sender, EventArgs e)
+ {
+ Console.WriteLine("SANDBOX: GoToAsync → MultiTab/Content2");
+ await Shell.Current.GoToAsync("//MultiTab/Content2");
+ }
+
+ async void GoToTests_Clicked(object? sender, EventArgs e)
+ {
+ Console.WriteLine("SANDBOX: GoToAsync → Tests/NavigationTest");
+ await Shell.Current.GoToAsync("//Tests/NavigationTest");
+ }
+}
+
+public partial class DetailsPage : ContentPage
+{
+ public DetailsPage()
+ {
+ //Shell.Current.SetValue(Shell.BackgroundColorProperty, Colors.Yellow);
+ Title = "Details Page";
+ ToolbarItems.Add(new ToolbarItem
+ {
+ Text = "Details Info",
+ Command = new Command(() =>
+ {
+ Console.WriteLine("SANDBOX: Details Info toolbar item clicked");
+ })
+ });
+
+ ToolbarItems.Add(new ToolbarItem
+ {
+ Text = "Details Help",
+ Command = new Command(() =>
+ {
+ Console.WriteLine("SANDBOX: Details Help toolbar item clicked");
+ })
+ });
+ Console.WriteLine("SANDBOX: DetailsPage constructor called");
+
+ var stackDepthLabel = new Label
+ {
+ Text = $"Navigation Stack Depth: {Shell.Current?.Navigation?.NavigationStack?.Count ?? 0}",
+ FontSize = 14,
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center,
+ TextColor = Colors.Blue
+ };
+
+ var label = new Label
+ {
+ Text = "This is the Details Page",
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ var instructionLabel = new Label
+ {
+ Text = "Try using the hardware back button!",
+ FontSize = 12,
+ HorizontalOptions = LayoutOptions.Center,
+ TextColor = Colors.Gray,
+ Margin = new Thickness(0, 10, 0, 0)
+ };
+
+ var backButton = new Button
+ {
+ Text = "Go Back",
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ var popToRootButton = new Button
+ {
+ Text = "Pop to Root",
+ AutomationId = "PopToRootButton",
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ BackgroundColor = Colors.Green,
+ TextColor = Colors.White
+ };
+
+ var pushAnotherButton = new Button
+ {
+ Text = "Push Another Page",
+ AutomationId = "PushAnotherButton",
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ var insertBeforeButton = new Button
+ {
+ Text = "Insert Page Before This",
+ AutomationId = "InsertBeforeButton",
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ BackgroundColor = Colors.Orange,
+ TextColor = Colors.White
+ };
+
+ var removePageButton = new Button
+ {
+ Text = "Remove Previous Page",
+ AutomationId = "RemovePageButton",
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ BackgroundColor = Colors.Red,
+ TextColor = Colors.White
+ };
+
+ label.HorizontalOptions = LayoutOptions.Center;
+ label.VerticalOptions = LayoutOptions.Center;
+ backButton.HorizontalOptions = LayoutOptions.Center;
+ backButton.VerticalOptions = LayoutOptions.Center;
+ backButton.Clicked += async (s, e) =>
+ {
+ Console.WriteLine("SANDBOX: Back button clicked");
+ await Navigation.PopAsync();
+ Console.WriteLine("SANDBOX: Navigated back to MainPage");
+ };
+
+ popToRootButton.Clicked += async (s, e) =>
+ {
+ Console.WriteLine("SANDBOX: PopToRoot button clicked");
+ await Navigation.PopToRootAsync();
+ Console.WriteLine("SANDBOX: Popped to root");
+ };
+
+ pushAnotherButton.Clicked += async (s, e) =>
+ {
+ Console.WriteLine("SANDBOX: Push another page button clicked");
+ await Navigation.PushAsync(new DetailsPage());
+
+ //await Navigation.PushAsync(new Content1Page());
+ Console.WriteLine("SANDBOX: Pushed another DetailsPage");
+ };
+
+ insertBeforeButton.Clicked += async (s, e) =>
+ {
+ Console.WriteLine("SANDBOX: Insert before button clicked");
+ try
+ {
+
+ var label = new Label
+ {
+ Text = "This page was inserted!",
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center
+ };
+ // Insert a new page before the current page
+ var insertedPage = new ContentPage
+ {
+ Title = "Inserted Page",
+ Content = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Padding = new Thickness(30, 0),
+ Children = { label }
+ }
+
+
+ };
+ Navigation.InsertPageBefore(insertedPage, this);
+ Console.WriteLine("SANDBOX: Page inserted successfully");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"SANDBOX: Insert failed: {ex.Message}");
+ }
+ };
+
+ removePageButton.Clicked += (s, e) =>
+ {
+ Console.WriteLine("SANDBOX: Remove page button clicked");
+ try
+ {
+ var stack = Navigation.NavigationStack;
+ if (stack.Count > 2)
+ {
+ // Remove the previous page (one before current)
+ var pageToRemove = stack[stack.Count - 2];
+ Navigation.RemovePage(pageToRemove);
+ Console.WriteLine($"SANDBOX: Removed page: {pageToRemove.GetType().Name}");
+ }
+ else
+ {
+ Console.WriteLine("SANDBOX: Not enough pages to remove");
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"SANDBOX: Remove failed: {ex.Message}");
+ }
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Spacing = 25,
+ Padding = new Thickness(30, 0),
+ Children = { stackDepthLabel, label, instructionLabel, backButton, popToRootButton, pushAnotherButton, insertBeforeButton, removePageButton }
+ };
+
+ // Update stack depth label when page appears
+ this.Appearing += (s, e) =>
+ {
+ stackDepthLabel.Text = $"Navigation Stack Depth: {Shell.Current?.Navigation?.NavigationStack?.Count ?? 0}";
+ };
+
+ Console.WriteLine("SANDBOX: DetailsPage loaded successfully");
+ }
+}
+
+// Phase 4 Test Pages: Multiple ShellContent within a Tab
+// APPEARANCE TEST: Each page has different Shell.BackgroundColor
+public partial class Content1Page : ContentPage
+{
+ public Content1Page()
+ {
+ Title = "Content 1";
+
+ // Set per-page Shell appearance - RED toolbar
+ Shell.SetBackgroundColor(this, Colors.DarkRed);
+ Shell.SetForegroundColor(this, Colors.White);
+ Shell.SetTitleColor(this, Colors.White);
+
+ Content = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Padding = new Thickness(30, 0),
+ Children =
+ {
+ new Label
+ {
+ Text = "Content 1 - RED Toolbar",
+ FontSize = 24,
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ TextColor = Colors.DarkRed
+ },
+ new Label
+ {
+ Text = "Swipe left/right to test appearance changes",
+ FontSize = 14,
+ HorizontalOptions = LayoutOptions.Center,
+ TextColor = Colors.Gray
+ },
+ new Label
+ {
+ Text = "✓ Toolbar should be DARK RED",
+ FontSize = 16,
+ HorizontalOptions = LayoutOptions.Center,
+ TextColor = Colors.DarkRed,
+ FontAttributes = FontAttributes.Bold
+ },
+ new Button
+ {
+ Text = "Push Page (Test Navigation Appearance)",
+ HorizontalOptions = LayoutOptions.Center,
+ Command = new Command(async () => await Navigation.PushAsync(new AppearanceDetailPage("Pushed from Content 1", Colors.Purple)))
+ },
+ new Button
+ {
+ Text = "GoToAsync to Content2",
+ HorizontalOptions = LayoutOptions.Center,
+ Command = new Command(async () => await Shell.Current.GoToAsync("//MultiTab/Content2"))
+ }
+ }
+ };
+ Console.WriteLine("SANDBOX: Content1Page loaded - Shell.BackgroundColor = DarkRed");
+ }
+}
+
+public partial class Content2Page : ContentPage
+{
+ public Content2Page()
+ {
+ Title = "Content 2";
+
+ // Set per-page Shell appearance - BLUE toolbar
+ Shell.SetBackgroundColor(this, Colors.DarkBlue);
+ Shell.SetForegroundColor(this, Colors.White);
+ Shell.SetTitleColor(this, Colors.Yellow);
+
+ Content = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Padding = new Thickness(30, 0),
+ Children =
+ {
+ new Label
+ {
+ Text = "Content 2 - BLUE Toolbar",
+ FontSize = 24,
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ TextColor = Colors.DarkBlue
+ },
+ new Label
+ {
+ Text = "Each content maintains its own appearance",
+ FontSize = 14,
+ HorizontalOptions = LayoutOptions.Center,
+ TextColor = Colors.Gray
+ },
+ new Label
+ {
+ Text = "✓ Toolbar should be DARK BLUE with YELLOW title",
+ FontSize = 16,
+ HorizontalOptions = LayoutOptions.Center,
+ TextColor = Colors.DarkBlue,
+ FontAttributes = FontAttributes.Bold
+ },
+ new Entry
+ {
+ Placeholder = "Type something to test state preservation",
+ HorizontalOptions = LayoutOptions.Center,
+ WidthRequest = 300
+ },
+ new Button
+ {
+ Text = "Push Page (Test Navigation Appearance)",
+ HorizontalOptions = LayoutOptions.Center,
+ Command = new Command(async () => await Navigation.PushAsync(new AppearanceDetailPage("Pushed from Content 2", Colors.Orange)))
+ }
+ }
+ };
+ Console.WriteLine("SANDBOX: Content2Page loaded - Shell.BackgroundColor = DarkBlue");
+ }
+}
+
+public partial class Content3Page : ContentPage
+{
+ public Content3Page()
+ {
+ Title = "Content 3";
+
+ // Set per-page Shell appearance - GREEN toolbar
+ Shell.SetBackgroundColor(this, Colors.DarkGreen);
+ Shell.SetForegroundColor(this, Colors.White);
+ Shell.SetTitleColor(this, Colors.LightGreen);
+
+ Content = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Padding = new Thickness(30, 0),
+ Children =
+ {
+ new Label
+ {
+ Text = "Content 3 - GREEN Toolbar",
+ FontSize = 24,
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ TextColor = Colors.DarkGreen
+ },
+ new Label
+ {
+ Text = "Testing ViewPager2 + TabLayout integration",
+ FontSize = 14,
+ HorizontalOptions = LayoutOptions.Center,
+ TextColor = Colors.Gray
+ },
+ new Label
+ {
+ Text = "✓ Toolbar should be DARK GREEN with LIGHT GREEN title",
+ FontSize = 16,
+ HorizontalOptions = LayoutOptions.Center,
+ TextColor = Colors.DarkGreen,
+ FontAttributes = FontAttributes.Bold
+ },
+ new BoxView
+ {
+ Color = Colors.DarkGreen,
+ HeightRequest = 100,
+ WidthRequest = 100,
+ HorizontalOptions = LayoutOptions.Center
+ },
+ new Button
+ {
+ Text = "Push Page (Test Navigation Appearance)",
+ HorizontalOptions = LayoutOptions.Center,
+ Command = new Command(async () => await Navigation.PushAsync(new AppearanceDetailPage("Pushed from Content 3", Colors.Brown)))
+ }
+ }
+ };
+ Console.WriteLine("SANDBOX: Content3Page loaded - Shell.BackgroundColor = DarkGreen");
+ }
+}
+
+// Appearance test detail page - used to test navigation appearance changes
+public partial class AppearanceDetailPage : ContentPage
+{
+ public AppearanceDetailPage(string fromPage, Color toolbarColor)
+ {
+ Title = "Detail Page";
+
+ // Set different appearance for pushed page
+ Shell.SetBackgroundColor(this, toolbarColor);
+ Shell.SetForegroundColor(this, Colors.White);
+ Shell.SetTitleColor(this, Colors.White);
+
+ Content = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Padding = new Thickness(30, 0),
+ VerticalOptions = LayoutOptions.Center,
+ Children =
+ {
+ new Label
+ {
+ Text = $"Detail Page",
+ FontSize = 24,
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center
+ },
+ new Label
+ {
+ Text = fromPage,
+ FontSize = 16,
+ HorizontalOptions = LayoutOptions.Center
+ },
+ new Label
+ {
+ Text = $"✓ Toolbar should be {GetColorName(toolbarColor)}",
+ FontSize = 16,
+ HorizontalOptions = LayoutOptions.Center,
+ TextColor = toolbarColor,
+ FontAttributes = FontAttributes.Bold
+ },
+ new Label
+ {
+ Text = "Press back to return and verify appearance restores",
+ FontSize = 14,
+ HorizontalOptions = LayoutOptions.Center,
+ TextColor = Colors.Gray
+ },
+ new Button
+ {
+ Text = "Push Another Page",
+ HorizontalOptions = LayoutOptions.Center,
+ Command = new Command(async () => await Navigation.PushAsync(new AppearanceDetailPage("Pushed from Detail", Colors.Teal)))
+ },
+ new Button
+ {
+ Text = "Pop Back",
+ HorizontalOptions = LayoutOptions.Center,
+ Command = new Command(async () => await Navigation.PopAsync())
+ }
+ }
+ };
+ Console.WriteLine($"SANDBOX: AppearanceDetailPage loaded - Shell.BackgroundColor = {GetColorName(toolbarColor)}");
+ }
+
+ static string GetColorName(Color color)
+ {
+ if (color == Colors.Purple)
+ return "PURPLE";
+ if (color == Colors.Orange)
+ return "ORANGE";
+ if (color == Colors.Brown)
+ return "BROWN";
+ if (color == Colors.Teal)
+ return "TEAL";
+ return color.ToString();
+ }
+}
+
+// Phase 5 Test Page: SearchHandler
+public partial class SearchTestPage : ContentPage
+{
+ private Label _resultsLabel;
+ private Label _queryLabel;
+
+ public SearchTestPage()
+ {
+ Title = "Search Test";
+
+ _queryLabel = new Label
+ {
+ Text = "Query: (none)",
+ FontSize = 16,
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center,
+ Margin = new Thickness(0, 20, 0, 0)
+ };
+
+ _resultsLabel = new Label
+ {
+ Text = "Selected: (none)",
+ FontSize = 16,
+ HorizontalOptions = LayoutOptions.Center,
+ Margin = new Thickness(0, 10, 0, 0)
+ };
+
+ // Create SearchHandler
+ var searchHandler = new TestSearchHandler(_queryLabel, _resultsLabel)
+ {
+ Placeholder = "Search for fruits...",
+ ShowsResults = true,
+ SearchBoxVisibility = SearchBoxVisibility.Expanded,
+ IsSearchEnabled = true,
+ BackgroundColor = Colors.LightBlue,
+ TextColor = Colors.Black,
+ PlaceholderColor = Colors.Gray
+ };
+
+ Shell.SetSearchHandler(this, searchHandler);
+
+ Content = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Padding = new Thickness(30, 0),
+ Children =
+ {
+ new Label
+ {
+ Text = "Phase 5: SearchHandler Test",
+ FontSize = 24,
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center,
+ Margin = new Thickness(0, 40, 0, 0)
+ },
+ new Label
+ {
+ Text = "Search functionality should appear in the toolbar above",
+ FontSize = 14,
+ HorizontalOptions = LayoutOptions.Center,
+ TextColor = Colors.Gray
+ },
+ _queryLabel,
+ _resultsLabel,
+ new Label
+ {
+ Text = "Try searching for: Apple, Banana, Orange, Grape, Mango",
+ FontSize = 12,
+ HorizontalOptions = LayoutOptions.Center,
+ TextColor = Colors.Blue,
+ Margin = new Thickness(0, 20, 0, 0)
+ }
+ }
+ };
+
+ Console.WriteLine("SANDBOX: SearchTestPage loaded with SearchHandler");
+ }
+}
+
+// Custom SearchHandler for testing
+public class TestSearchHandler : SearchHandler
+{
+ private readonly List _fruits = new List
+ {
+ "Apple", "Apricot", "Avocado",
+ "Banana", "Blackberry", "Blueberry",
+ "Orange", "Olive",
+ "Grape", "Grapefruit", "Guava",
+ "Mango", "Melon"
+ };
+
+ private readonly Label _queryLabel;
+ private readonly Label _resultsLabel;
+
+ public TestSearchHandler(Label queryLabel, Label resultsLabel)
+ {
+ _queryLabel = queryLabel;
+ _resultsLabel = resultsLabel;
+ ClearIconHelpText = "Clear search";
+ ClearIconName = "clear";
+ ClearPlaceholderHelpText = "Clear";
+ }
+
+ protected override void OnQueryChanged(string oldValue, string newValue)
+ {
+ base.OnQueryChanged(oldValue, newValue);
+
+ _queryLabel.Text = $"Query: {newValue ?? "(none)"}";
+ Console.WriteLine($"SANDBOX: SearchHandler Query changed: {newValue}");
+
+ if (string.IsNullOrWhiteSpace(newValue))
+ {
+ ItemsSource = null;
+ }
+ else
+ {
+ ItemsSource = _fruits
+ .Where(f => f.Contains(newValue, StringComparison.OrdinalIgnoreCase))
+ .ToList();
+ }
+ }
+
+ protected override void OnItemSelected(object item)
+ {
+ base.OnItemSelected(item);
+
+ if (item is string fruit)
+ {
+ _resultsLabel.Text = $"Selected: {fruit}";
+ Console.WriteLine($"SANDBOX: SearchHandler Item selected: {fruit}");
+ }
+ }
+
+ protected override void OnQueryConfirmed()
+ {
+ base.OnQueryConfirmed();
+ Console.WriteLine($"SANDBOX: SearchHandler Query confirmed: {Query}");
}
}
\ No newline at end of file
diff --git a/src/Controls/samples/Controls.Sample.Sandbox/NavigationTestPage.xaml b/src/Controls/samples/Controls.Sample.Sandbox/NavigationTestPage.xaml
new file mode 100644
index 000000000000..4823f0fdf69a
--- /dev/null
+++ b/src/Controls/samples/Controls.Sample.Sandbox/NavigationTestPage.xaml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/samples/Controls.Sample.Sandbox/NavigationTestPage.xaml.cs b/src/Controls/samples/Controls.Sample.Sandbox/NavigationTestPage.xaml.cs
new file mode 100644
index 000000000000..d94d758c6648
--- /dev/null
+++ b/src/Controls/samples/Controls.Sample.Sandbox/NavigationTestPage.xaml.cs
@@ -0,0 +1,121 @@
+using System;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Graphics;
+
+namespace Maui.Controls.Sample
+{
+ public partial class NavigationTestPage : ContentPage
+ {
+ public NavigationTestPage()
+ {
+ InitializeComponent();
+ UpdateNavigationStackInfo();
+ }
+
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+ UpdateNavigationStackInfo();
+ }
+
+ private void UpdateNavigationStackInfo()
+ {
+ if (Navigation?.NavigationStack != null)
+ {
+ NavigationStackLabel.Text = $"Navigation Stack: {Navigation.NavigationStack.Count} pages";
+ }
+ }
+
+ private async void OnPushDetailClicked(object? sender, EventArgs e)
+ {
+ await Navigation.PushAsync(new DetailPage());
+ UpdateNavigationStackInfo();
+ }
+
+ private async void OnPushModalClicked(object? sender, EventArgs e)
+ {
+ await Navigation.PushModalAsync(new NavigationPage(new DetailPage()));
+ }
+
+ private async void OnNavigateToMainClicked(object? sender, EventArgs e)
+ {
+ await Shell.Current.GoToAsync("//MainPage");
+ }
+
+ private async void OnNavigateToSettingsClicked(object? sender, EventArgs e)
+ {
+ await Shell.Current.GoToAsync("//Settings");
+ }
+
+ private async void OnGoToMainAsyncClicked(object? sender, EventArgs e)
+ {
+ await Shell.Current.GoToAsync("//MainPage");
+ }
+
+ private async void OnGoToSettingsAsyncClicked(object? sender, EventArgs e)
+ {
+ await Shell.Current.GoToAsync("//Settings");
+ }
+
+ private async void OnGoToWithQueryClicked(object? sender, EventArgs e)
+ {
+ await Shell.Current.GoToAsync($"//MainPage?message=Hello from Navigation Test");
+ }
+
+ private async void OnNavigateDeepClicked(object? sender, EventArgs e)
+ {
+ await Navigation.PushAsync(new DetailPage { Title = "Level 1" });
+ await Navigation.PushAsync(new DetailPage { Title = "Level 2" });
+ await Navigation.PushAsync(new DetailPage { Title = "Level 3" });
+ UpdateNavigationStackInfo();
+ }
+
+ private async void OnPopClicked(object? sender, EventArgs e)
+ {
+ if (Navigation.NavigationStack.Count > 1)
+ {
+ await Navigation.PopAsync();
+ UpdateNavigationStackInfo();
+ }
+ }
+
+ private async void OnPopToRootClicked(object? sender, EventArgs e)
+ {
+ await Navigation.PopToRootAsync();
+ UpdateNavigationStackInfo();
+ }
+ }
+
+ public class DetailPage : ContentPage
+ {
+ public DetailPage()
+ {
+ var layout = new VerticalStackLayout();
+ layout.Padding = new Thickness(20);
+ layout.Spacing = 15;
+
+ var titleLabel = new Label();
+ titleLabel.Text = "Detail Page";
+ titleLabel.FontSize = 24;
+ titleLabel.FontAttributes = FontAttributes.Bold;
+ layout.Children.Add(titleLabel);
+
+ var backButton = new Button();
+ backButton.Text = "Go Back";
+ backButton.Command = new Command(async () => await Navigation.PopAsync());
+ layout.Children.Add(backButton);
+
+ var popModalButton = new Button();
+ popModalButton.Text = "Pop Modal";
+ popModalButton.Command = new Command(async () => await Navigation.PopModalAsync());
+ layout.Children.Add(popModalButton);
+
+ var pushButton = new Button();
+ pushButton.Text = "Push Another";
+ pushButton.Command = new Command(async () => await Navigation.PushAsync(new DetailPage()));
+ layout.Children.Add(pushButton);
+
+ Content = layout;
+ }
+ }
+}
diff --git a/src/Controls/samples/Controls.Sample.Sandbox/Resources/Styles/Colors.xaml b/src/Controls/samples/Controls.Sample.Sandbox/Resources/Styles/Colors.xaml
new file mode 100644
index 000000000000..ed47472ce314
--- /dev/null
+++ b/src/Controls/samples/Controls.Sample.Sandbox/Resources/Styles/Colors.xaml
@@ -0,0 +1,58 @@
+
+
+
+
+
+ #512BD4
+ #ac99ea
+ #242424
+ #DFD8F7
+ #9880e5
+ #2B0B98
+ Red
+
+ White
+ Black
+ #D600AA
+ #190649
+ #1f1f1f
+
+ #E1E1E1
+ #C8C8C8
+ #ACACAC
+ #919191
+ #6E6E6E
+ #404040
+ #212121
+ #141414
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/samples/Controls.Sample.Sandbox/Resources/Styles/Styles.xaml b/src/Controls/samples/Controls.Sample.Sandbox/Resources/Styles/Styles.xaml
new file mode 100644
index 000000000000..0cc5833585bf
--- /dev/null
+++ b/src/Controls/samples/Controls.Sample.Sandbox/Resources/Styles/Styles.xaml
@@ -0,0 +1,583 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/samples/Controls.Sample.Sandbox/SandboxShell.xaml b/src/Controls/samples/Controls.Sample.Sandbox/SandboxShell.xaml
index 358b0b04ef63..a617858a090c 100644
--- a/src/Controls/samples/Controls.Sample.Sandbox/SandboxShell.xaml
+++ b/src/Controls/samples/Controls.Sample.Sandbox/SandboxShell.xaml
@@ -3,20 +3,126 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.SandboxShell"
xmlns:local="clr-namespace:Maui.Controls.Sample"
- x:Name="shell">
+ x:Name="shell"
+ Shell.FlyoutBehavior="Flyout"
+ Title="Shell Handler Tests">
+
+
+
+
-
+ Shell.TabBarUnselectedColor="Red">
+
+
+
+ Title="Home"
+ ContentTemplate="{DataTemplate local:MainPage}"
+ Route="MainPage"/>
-
+
+
+
+ Title="Navigation"
+ ContentTemplate="{DataTemplate local:NavigationTestPage}"
+ Route="NavigationTest"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/samples/Controls.Sample.Sandbox/SettingsPage.xaml b/src/Controls/samples/Controls.Sample.Sandbox/SettingsPage.xaml
new file mode 100644
index 000000000000..76b4bbbc2c5c
--- /dev/null
+++ b/src/Controls/samples/Controls.Sample.Sandbox/SettingsPage.xaml
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Collapsible
+ Expanded
+ Hidden
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/samples/Controls.Sample.Sandbox/SettingsPage.xaml.cs b/src/Controls/samples/Controls.Sample.Sandbox/SettingsPage.xaml.cs
new file mode 100644
index 000000000000..76e3c72b43ce
--- /dev/null
+++ b/src/Controls/samples/Controls.Sample.Sandbox/SettingsPage.xaml.cs
@@ -0,0 +1,198 @@
+using System.Collections.ObjectModel;
+using System.Text;
+
+namespace Maui.Controls.Sample;
+
+public partial class SettingsPage : ContentPage
+{
+ private readonly StringBuilder _logBuilder = new();
+ private readonly Random _random = new();
+
+ // Predefined color themes for SearchHandler
+ private static readonly (string Name, Color Background, Color Text, Color Placeholder, Color CancelButton)[] ColorThemes =
+ [
+ ("Blue", Color.FromArgb("#E3F2FD"), Color.FromArgb("#1565C0"), Color.FromArgb("#64B5F6"), Color.FromArgb("#1976D2")),
+ ("Green", Color.FromArgb("#E8F5E9"), Color.FromArgb("#2E7D32"), Color.FromArgb("#81C784"), Color.FromArgb("#388E3C")),
+ ("Purple", Color.FromArgb("#F3E5F5"), Color.FromArgb("#7B1FA2"), Color.FromArgb("#BA68C8"), Color.FromArgb("#8E24AA")),
+ ("Orange", Color.FromArgb("#FFF3E0"), Color.FromArgb("#E65100"), Color.FromArgb("#FFB74D"), Color.FromArgb("#F57C00")),
+ ("Red", Color.FromArgb("#FFEBEE"), Color.FromArgb("#C62828"), Color.FromArgb("#EF9A9A"), Color.FromArgb("#D32F2F")),
+ ("Teal", Color.FromArgb("#E0F2F1"), Color.FromArgb("#00695C"), Color.FromArgb("#80CBC4"), Color.FromArgb("#00897B")),
+ ("Pink", Color.FromArgb("#FCE4EC"), Color.FromArgb("#AD1457"), Color.FromArgb("#F48FB1"), Color.FromArgb("#C2185B")),
+ ("Amber", Color.FromArgb("#FFF8E1"), Color.FromArgb("#FF6F00"), Color.FromArgb("#FFD54F"), Color.FromArgb("#FFB300")),
+ ];
+
+ public SettingsPage()
+ {
+ InitializeComponent();
+
+ // Set initial picker value to Expanded (index 1)
+ visibilityPicker.SelectedIndex = 1; // Expanded
+
+ // Subscribe to search handler events
+ searchHandler.QueryChangedEvent += OnSearchQueryChanged;
+ searchHandler.ItemSelectedEvent += OnSearchItemSelected;
+ }
+
+ async void Button_Clicked(object? sender, EventArgs e)
+ {
+ Log("PopToRootAsync called");
+ await Navigation.PopToRootAsync();
+ }
+
+ async void OnPushDetailsClicked(object? sender, EventArgs e)
+ {
+ Log("Pushing DetailsPage...");
+ await Navigation.PushAsync(new DetailsPage());
+ }
+
+ void OnSearchQueryChanged(object? sender, string query)
+ {
+ currentQueryLabel.Text = string.IsNullOrEmpty(query) ? "(empty)" : query;
+ Log($"QueryChanged: '{query}'");
+ }
+
+ void OnSearchItemSelected(object? sender, object? item)
+ {
+ if (item is SearchItem searchItem)
+ {
+ Log($"ItemSelected: {searchItem.Name} ({searchItem.Category})");
+ DisplayAlertAsync("Selected", $"You selected: {searchItem.Name}\nCategory: {searchItem.Category}", "OK");
+ }
+ }
+
+ void OnVisibilityChanged(object? sender, EventArgs e)
+ {
+ if (visibilityPicker.SelectedItem is string visibility)
+ {
+ var searchBoxVisibility = visibility switch
+ {
+ "Collapsible" => SearchBoxVisibility.Collapsible,
+ "Expanded" => SearchBoxVisibility.Expanded,
+ "Hidden" => SearchBoxVisibility.Hidden,
+ _ => SearchBoxVisibility.Collapsible
+ };
+
+ searchHandler.SearchBoxVisibility = searchBoxVisibility;
+ Log($"SearchBoxVisibility changed to: {visibility}");
+ }
+ }
+
+ void OnShowsResultsToggled(object? sender, ToggledEventArgs e)
+ {
+ searchHandler.ShowsResults = e.Value;
+ Log($"ShowsResults changed to: {e.Value}");
+ }
+
+ void OnIsEnabledToggled(object? sender, ToggledEventArgs e)
+ {
+ searchHandler.IsSearchEnabled = e.Value;
+ Log($"IsSearchEnabled changed to: {e.Value}");
+ }
+
+ void OnClearLogClicked(object? sender, EventArgs e)
+ {
+ _logBuilder.Clear();
+ logLabel.Text = "Log cleared.";
+ }
+
+ void OnRandomizeColorsClicked(object? sender, EventArgs e)
+ {
+ // Pick a random color theme
+ var theme = ColorThemes[_random.Next(ColorThemes.Length)];
+
+ // Apply to SearchHandler
+ searchHandler.BackgroundColor = theme.Background;
+ searchHandler.TextColor = theme.Text;
+ searchHandler.PlaceholderColor = theme.Placeholder;
+ searchHandler.CancelButtonColor = theme.CancelButton;
+
+ // Update UI
+ currentColorsLabel.Text = $"{theme.Name} theme";
+ currentColorsLabel.TextColor = theme.Text;
+
+ Log($"Colors changed to: {theme.Name} (BG: {theme.Background.ToHex()}, Text: {theme.Text.ToHex()})");
+ }
+
+ void Log(string message)
+ {
+ var timestamp = DateTime.Now.ToString("HH:mm:ss.fff");
+ _logBuilder.Insert(0, $"[{timestamp}] {message}\n");
+ logLabel.Text = _logBuilder.ToString();
+ Console.WriteLine($"SANDBOX SearchHandler: {message}");
+ }
+}
+
+///
+/// Custom SearchHandler that exposes events for query and item selection changes.
+/// SearchHandler uses virtual methods, so we override them and raise events.
+///
+public class SandboxSearchHandler : SearchHandler
+{
+ private static readonly ObservableCollection _allItems = new()
+ {
+ new SearchItem { Name = "Apple", Category = "Fruit" },
+ new SearchItem { Name = "Banana", Category = "Fruit" },
+ new SearchItem { Name = "Cherry", Category = "Fruit" },
+ new SearchItem { Name = "Date", Category = "Fruit" },
+ new SearchItem { Name = "Elderberry", Category = "Fruit" },
+ new SearchItem { Name = "Carrot", Category = "Vegetable" },
+ new SearchItem { Name = "Broccoli", Category = "Vegetable" },
+ new SearchItem { Name = "Spinach", Category = "Vegetable" },
+ new SearchItem { Name = "Settings", Category = "App" },
+ new SearchItem { Name = "Search", Category = "App" },
+ new SearchItem { Name = "Shell", Category = "MAUI" },
+ new SearchItem { Name = "Handler", Category = "MAUI" },
+ new SearchItem { Name = "Navigation", Category = "MAUI" },
+ };
+
+ public event EventHandler? QueryChangedEvent;
+ public event EventHandler