Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ void SearchHandlerPropertyChanged(object sender, System.ComponentModel.PropertyC
{
UpdateSearchBarVerticalTextAlignment(_uiSearchBar.FindDescendantView<UITextField>());
}
else if (e.Is(SearchHandler.ShowsCancelButtonProperty))
{
UpdateShowsCancelButton();
}
}

void GetDefaultSearchBarColors(UISearchBar searchBar)
Expand Down Expand Up @@ -319,13 +323,29 @@ void UpdateKeyboard()
_uiSearchBar.ReloadInputViews();
}

void UpdateShowsCancelButton()
{
if (_searchHandler.IsFocused)
{
_uiSearchBar.SetShowsCancelButton(_searchHandler.ShowsCancelButton, true);
}
Comment on lines +328 to +331
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UpdateShowsCancelButton() method only updates the cancel button visibility if the SearchHandler is currently focused. However, when the property changes while unfocused, the cancel button state won't be updated until the next focus change event.

This could lead to unexpected behavior where changing ShowsCancelButton from true to false while unfocused won't take effect until the user focuses the search bar again. Consider updating the method to handle both focused and unfocused states, or at a minimum, document this behavior limitation.

Suggested change
if (_searchHandler.IsFocused)
{
_uiSearchBar.SetShowsCancelButton(_searchHandler.ShowsCancelButton, true);
}
_uiSearchBar.SetShowsCancelButton(_searchHandler.ShowsCancelButton, true);

Copilot uses AI. Check for mistakes.
}

void OnEditingEnded(object sender, EventArgs e)
{
if (_searchHandler.ShowsCancelButton)
{
_uiSearchBar.SetShowsCancelButton(false, true);
}
_searchHandler.SetIsFocused(false);
}

void OnEditingStarted(object sender, EventArgs e)
{
if (_searchHandler.ShowsCancelButton)
{
_uiSearchBar.SetShowsCancelButton(true, true);
}
Comment on lines +345 to +348
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When ShowsCancelButton is false, the cancel button is never shown, which means users have no way to dismiss the keyboard on iOS if this property is set to false. The PR description states this fixes a "keyboard trap," but setting ShowsCancelButton="False" would create the exact same keyboard trap.

The iOS implementation should respect the property value while still providing a way to dismiss the keyboard. Consider either:

  1. Documenting this limitation clearly in the XML documentation
  2. Providing alternative keyboard dismissal when ShowsCancelButton is false (e.g., tap-outside-to-dismiss)
  3. Reconsidering whether allowing false is appropriate for iOS

Copilot uses AI. Check for mistakes.
UpdateCancelButtonColor(_uiSearchBar.FindDescendantView<UIButton>());
_searchHandler.SetIsFocused(true);
//ElementController?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
#nullable enable
Microsoft.Maui.Controls.SearchBar.ShowsCancelButton.get -> bool
Microsoft.Maui.Controls.SearchBar.ShowsCancelButton.set -> void
~static readonly Microsoft.Maui.Controls.SearchBar.ShowsCancelButtonProperty -> Microsoft.Maui.Controls.BindableProperty
Microsoft.Maui.Controls.SearchHandler.ShowsCancelButton.get -> bool
Microsoft.Maui.Controls.SearchHandler.ShowsCancelButton.set -> void
~static readonly Microsoft.Maui.Controls.SearchHandler.ShowsCancelButtonProperty -> Microsoft.Maui.Controls.BindableProperty
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
#nullable enable
~override Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer.ViewWillTransitionToSize(CoreGraphics.CGSize toSize, UIKit.IUIViewControllerTransitionCoordinator coordinator) -> void
Microsoft.Maui.Controls.SearchBar.ShowsCancelButton.get -> bool
Microsoft.Maui.Controls.SearchBar.ShowsCancelButton.set -> void
~static readonly Microsoft.Maui.Controls.SearchBar.ShowsCancelButtonProperty -> Microsoft.Maui.Controls.BindableProperty
Microsoft.Maui.Controls.SearchHandler.ShowsCancelButton.get -> bool
Microsoft.Maui.Controls.SearchHandler.ShowsCancelButton.set -> void
~static readonly Microsoft.Maui.Controls.SearchHandler.ShowsCancelButtonProperty -> Microsoft.Maui.Controls.BindableProperty
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
#nullable enable
~override Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer.ViewWillTransitionToSize(CoreGraphics.CGSize toSize, UIKit.IUIViewControllerTransitionCoordinator coordinator) -> void
Microsoft.Maui.Controls.SearchBar.ShowsCancelButton.get -> bool
Microsoft.Maui.Controls.SearchBar.ShowsCancelButton.set -> void
~static readonly Microsoft.Maui.Controls.SearchBar.ShowsCancelButtonProperty -> Microsoft.Maui.Controls.BindableProperty
Microsoft.Maui.Controls.SearchHandler.ShowsCancelButton.get -> bool
Microsoft.Maui.Controls.SearchHandler.ShowsCancelButton.set -> void
~static readonly Microsoft.Maui.Controls.SearchHandler.ShowsCancelButtonProperty -> Microsoft.Maui.Controls.BindableProperty
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
#nullable enable
Microsoft.Maui.Controls.SearchBar.ShowsCancelButton.get -> bool
Microsoft.Maui.Controls.SearchBar.ShowsCancelButton.set -> void
~static readonly Microsoft.Maui.Controls.SearchBar.ShowsCancelButtonProperty -> Microsoft.Maui.Controls.BindableProperty
Microsoft.Maui.Controls.SearchHandler.ShowsCancelButton.get -> bool
Microsoft.Maui.Controls.SearchHandler.ShowsCancelButton.set -> void
~static readonly Microsoft.Maui.Controls.SearchHandler.ShowsCancelButtonProperty -> Microsoft.Maui.Controls.BindableProperty
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
#nullable enable
Microsoft.Maui.Controls.SearchBar.ShowsCancelButton.get -> bool
Microsoft.Maui.Controls.SearchBar.ShowsCancelButton.set -> void
~static readonly Microsoft.Maui.Controls.SearchBar.ShowsCancelButtonProperty -> Microsoft.Maui.Controls.BindableProperty
Microsoft.Maui.Controls.SearchHandler.ShowsCancelButton.get -> bool
Microsoft.Maui.Controls.SearchHandler.ShowsCancelButton.set -> void
~static readonly Microsoft.Maui.Controls.SearchHandler.ShowsCancelButtonProperty -> Microsoft.Maui.Controls.BindableProperty
6 changes: 6 additions & 0 deletions src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
#nullable enable
Microsoft.Maui.Controls.SearchBar.ShowsCancelButton.get -> bool
Microsoft.Maui.Controls.SearchBar.ShowsCancelButton.set -> void
~static readonly Microsoft.Maui.Controls.SearchBar.ShowsCancelButtonProperty -> Microsoft.Maui.Controls.BindableProperty
Microsoft.Maui.Controls.SearchHandler.ShowsCancelButton.get -> bool
Microsoft.Maui.Controls.SearchHandler.ShowsCancelButton.set -> void
~static readonly Microsoft.Maui.Controls.SearchHandler.ShowsCancelButtonProperty -> Microsoft.Maui.Controls.BindableProperty
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
#nullable enable
Microsoft.Maui.Controls.SearchBar.ShowsCancelButton.get -> bool
Microsoft.Maui.Controls.SearchBar.ShowsCancelButton.set -> void
~static readonly Microsoft.Maui.Controls.SearchBar.ShowsCancelButtonProperty -> Microsoft.Maui.Controls.BindableProperty
Microsoft.Maui.Controls.SearchHandler.ShowsCancelButton.get -> bool
Microsoft.Maui.Controls.SearchHandler.ShowsCancelButton.set -> void
~static readonly Microsoft.Maui.Controls.SearchHandler.ShowsCancelButtonProperty -> Microsoft.Maui.Controls.BindableProperty
17 changes: 17 additions & 0 deletions src/Controls/src/Core/SearchBar/SearchBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public partial class SearchBar : InputView, ITextAlignmentElement, ISearchBarCon
/// <summary>Bindable property for <see cref="CancelButtonColor"/>.</summary>
public static readonly BindableProperty CancelButtonColorProperty = BindableProperty.Create(nameof(CancelButtonColor), typeof(Color), typeof(SearchBar), default(Color));

/// <summary>Bindable property for <see cref="ShowsCancelButton"/>.</summary>
public static readonly BindableProperty ShowsCancelButtonProperty = BindableProperty.Create(nameof(ShowsCancelButton), typeof(bool), typeof(SearchBar), true);

/// <summary>Bindable property for <see cref="SearchIconColor"/>.</summary>
public static readonly BindableProperty SearchIconColorProperty = BindableProperty.Create(nameof(SearchIconColor), typeof(Color), typeof(SearchBar), default(Color));

Expand Down Expand Up @@ -94,6 +97,20 @@ public Color CancelButtonColor
get { return (Color)GetValue(CancelButtonColorProperty); }
set { SetValue(CancelButtonColorProperty, value); }
}

/// <summary>
/// Gets or sets a value that indicates whether the cancel button is shown.
/// </summary>
/// <remarks>
/// On iOS, the cancel button appears when the search bar is focused and allows users to dismiss the keyboard.
/// Default value is <c>true</c>.
/// </remarks>
public bool ShowsCancelButton
{
get { return (bool)GetValue(ShowsCancelButtonProperty); }
set { SetValue(ShowsCancelButtonProperty, value); }
}
Comment on lines +101 to +112
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The XML documentation states "On iOS, the cancel button appears when the search bar is focused and allows users to dismiss the keyboard." However, based on the implementation in SearchBarExtensions.ShouldShowCancelButton(), the cancel button only appears when BOTH conditions are true: the property is true AND there is text in the search bar.

The documentation should be updated to accurately reflect this behavior: "On iOS, the cancel button appears when the search bar is focused and contains text, allowing users to dismiss the keyboard. Default value is true."

Copilot uses AI. Check for mistakes.

/// <summary>
/// Gets or sets the color of the search icon in the <see cref="SearchBar"/>.
/// </summary>
Expand Down
16 changes: 16 additions & 0 deletions src/Controls/src/Core/Shell/SearchHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ public Color TextColor
/// <summary>Bindable property for <see cref="CancelButtonColor"/>.</summary>
public static readonly BindableProperty CancelButtonColorProperty = BindableProperty.Create(nameof(CancelButtonColor), typeof(Color), typeof(SearchHandler), default(Color));

/// <summary>Bindable property for <see cref="ShowsCancelButton"/>.</summary>
public static readonly BindableProperty ShowsCancelButtonProperty = BindableProperty.Create(nameof(ShowsCancelButton), typeof(bool), typeof(SearchHandler), true);

/// <summary>Bindable property for <see cref="FontFamily"/>.</summary>
public static readonly BindableProperty FontFamilyProperty = FontElement.FontFamilyProperty;

Expand Down Expand Up @@ -200,6 +203,19 @@ public Color CancelButtonColor
set { SetValue(CancelButtonColorProperty, value); }
}

/// <summary>
/// Gets or sets a value that indicates whether the cancel button is shown.
/// </summary>
/// <remarks>
/// On iOS, the cancel button appears when the search bar is focused and allows users to dismiss the keyboard.
/// Default value is <c>true</c>.
/// </remarks>
public bool ShowsCancelButton
{
get { return (bool)GetValue(ShowsCancelButtonProperty); }
set { SetValue(ShowsCancelButtonProperty, value); }
}
Comment on lines +206 to +217
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The XML documentation states "On iOS, the cancel button appears when the search bar is focused and allows users to dismiss the keyboard." However, based on the implementation in SearchBarExtensions.ShouldShowCancelButton(), the cancel button only appears when BOTH conditions are true: the property is true AND there is text in the search bar.

The documentation should be updated to accurately reflect this behavior: "On iOS, the cancel button appears when the search bar is focused and contains text, allowing users to dismiss the keyboard. Default value is true."

Copilot uses AI. Check for mistakes.


/// <include file="../../../docs/Microsoft.Maui.Controls/SearchHandler.xml" path="//Member[@MemberName='FontAttributes']/Docs/*" />
public FontAttributes FontAttributes
Expand Down
64 changes: 64 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue33008.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue33008"
Title="Issue 33008 - ShowsCancelButton">

<Shell.SearchHandler>
<SearchHandler Placeholder="Search..."
AutomationId="SearchHandler"
x:Name="SearchHandler"
ShowsCancelButton="True"/>
</Shell.SearchHandler>

<ScrollView>
<VerticalStackLayout Padding="20"
Spacing="15">
<Label Text="ShowsCancelButton Property Tests"
FontSize="18"
FontAttributes="Bold"
AutomationId="TitleLabel"/>

<!-- SearchBar Tests -->
<Label Text="SearchBar Tests"
FontSize="16"
FontAttributes="Bold"
Margin="0,10,0,0"/>

<Label Text="1. Default (true):"
FontAttributes="Bold"/>
<SearchBar x:Name="SearchBarDefault"
Placeholder="Default - shows cancel when typing"
AutomationId="SearchBarDefault"/>

<Label Text="2. ShowsCancelButton=True:"
FontAttributes="Bold"/>
<SearchBar x:Name="SearchBarTrue"
Placeholder="True - shows cancel when typing"
ShowsCancelButton="True"
AutomationId="SearchBarTrue"/>

<Label Text="3. ShowsCancelButton=False:"
FontAttributes="Bold"/>
<SearchBar x:Name="SearchBarFalse"
Placeholder="False - NEVER shows cancel"
ShowsCancelButton="False"
AutomationId="SearchBarFalse"/>

<Button Text="Set Text"
Clicked="OnSetText"
AutomationId="SetTextButton"/>

<!-- Status -->
<BoxView HeightRequest="2"
Color="Gray"
Margin="0,10,0,0"/>
<Label Text="Status:"
FontAttributes="Bold"/>
<Label x:Name="StatusLabel"
Text="Ready"
AutomationId="StatusLabel"/>

</VerticalStackLayout>
</ScrollView>
</ContentPage>
27 changes: 27 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue33008.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 33008, "SearchBar and SearchHandler ShowsCancelButton property", PlatformAffected.iOS)]
public class Issue33008Shell : Shell
{
public Issue33008Shell()
{
Items.Add(new Issue33008());
}
}
Comment on lines +4 to +10
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Issue33008Shell creates a Shell with SearchHandler, but the ContentPage also defines a Shell.SearchHandler in XAML. This creates ambiguity about which SearchHandler is actually being tested.

The typical pattern for Shell-based issue tests is either:

  1. Use a Shell subclass (Issue33008Shell) and set the SearchHandler in the Shell's constructor
  2. OR use a ContentPage and define Shell.SearchHandler in XAML

Mixing both approaches is confusing. Consider removing the Issue33008Shell class and just using the ContentPage with the Shell.SearchHandler defined in XAML, or move all SearchHandler configuration to the Shell constructor.

Suggested change
public class Issue33008Shell : Shell
{
public Issue33008Shell()
{
Items.Add(new Issue33008());
}
}

Copilot uses AI. Check for mistakes.

public partial class Issue33008 : ContentPage
{
public Issue33008()
{
InitializeComponent();
}

private void OnSetText(object sender, EventArgs e)
{
SearchBarDefault.Text = "Test text";
SearchBarTrue.Text = "Test text";
SearchBarFalse.Text = "Test text";
StatusLabel.Text = "Text set on all SearchBars";
SearchHandler.Query = "Test text";
}
Comment on lines +25 to +26
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OnSetText method sets SearchHandler.Query = "Test text" but there's no SearchHandler instance accessible in the ContentPage scope. The SearchHandler is defined in the XAML as Shell.SearchHandler, not as a field accessible via this.SearchHandler.

This code will result in a null reference exception at runtime. To fix this, either:

  1. Add x:Name="SearchHandler" to the SearchHandler in XAML (already present) and access it via the name
  2. Use Shell.GetSearchHandler(this) to retrieve the SearchHandler
  3. Store a reference to the SearchHandler in a field during InitializeComponent()
Suggested change
SearchHandler.Query = "Test text";
}
var searchHandler = Shell.GetSearchHandler(this);
if (searchHandler != null)
{
searchHandler.Query = "Test text";
}

Copilot uses AI. Check for mistakes.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue33008 : _IssuesUITest
{
public override string Issue => "SearchBar and SearchHandler ShowsCancelButton property";

public Issue33008(TestDevice device) : base(device) { }

[Test]
[Category(UITestCategories.SearchBar)]
public void SearchBarShowsCancelButtonWorks()
{
App.WaitForElement("TitleLabel");
App.Tap("SetTextButton");
App.EnterText("Search...", "Test text");
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UI test is using App.EnterText("Search...", "Test text") but there's no element with AutomationId "Search...". Looking at the XAML, the SearchBar elements have AutomationIds "SearchBarDefault", "SearchBarTrue", and "SearchBarFalse", not "Search...". The placeholder text "Search..." is not an AutomationId.

This test will fail at runtime because it's trying to interact with a non-existent element. The test should use one of the actual AutomationIds like "SearchBarDefault".

Suggested change
App.EnterText("Search...", "Test text");
App.EnterText("SearchBarDefault", "Test text");

Copilot uses AI. Check for mistakes.

var status = App.FindElement("StatusLabel").GetText();
Assert.That(status, Does.Contain("Text set on all SearchBars"), "Should set text on all SearchBars");

#if IOS || MACCATALYST
// Verify that the Cancel button is visible on SearchBars where ShowsCancelButton is true
VerifyScreenshot();
#endif
}
Comment on lines +13 to +28
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description claims "Device tests added (5 comprehensive tests, all passing)" but the actual test file only contains a single test method SearchBarShowsCancelButtonWorks(). The description mentions specific test methods like ShowsCancelButtonDefaultsToTrue, ShowsCancelButtonTrueShowsCancelButton, ShowsCancelButtonFalseHidesCancelButton, ShowsCancelButtonNoTextNeverShowsCancelButton, and ShowsCancelButtonCanBeToggledDynamically, but none of these tests exist in the code.

Additionally, the test doesn't actually verify that the cancel button behavior is working correctly - it only sets text and checks a status label. There's no assertion that validates the ShowsCancelButton property affects the native control's cancel button visibility.

Copilot uses AI. Check for mistakes.
}
5 changes: 5 additions & 0 deletions src/Core/src/Core/ISearchBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ public interface ISearchBar : IView, ITextInput, ITextAlignment
/// </summary>
Color CancelButtonColor { get; }

/// <summary>
/// Gets a value indicating whether the cancel button should be displayed.
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The XML documentation for the ISearchBar interface should include information about platform-specific behavior, similar to the documentation on the SearchBar and SearchHandler classes. Currently it only states "Gets a value indicating whether the cancel button should be displayed" which doesn't explain the iOS-specific behavior or the text requirement.

Consider adding: "On iOS, the cancel button appears when the search bar contains text. This property has no effect on Android, Windows, or Tizen platforms."

Suggested change
/// Gets a value indicating whether the cancel button should be displayed.
/// Gets a value indicating whether the cancel button should be displayed.
/// On iOS, the cancel button appears when the search bar contains text. This property has no effect on Android, Windows, or Tizen platforms.

Copilot uses AI. Check for mistakes.
/// </summary>
bool ShowsCancelButton { get; }

/// <summary>
/// Gets the color of the Search icon.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions src/Core/src/Handlers/SearchBar/SearchBarHandler.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ public static void MapCancelButtonColor(ISearchBarHandler handler, ISearchBar se
handler.PlatformView?.UpdateCancelButtonColor(searchBar);
}

public static void MapShowsCancelButton(ISearchBarHandler handler, ISearchBar searchBar)
{
// ShowsCancelButton is iOS-specific behavior
// Android SearchView doesn't have an equivalent cancel button
}

internal static void MapSearchIconColor(ISearchBarHandler handler, ISearchBar searchBar)
{
handler.PlatformView?.UpdateSearchIconColor(searchBar);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public static void MapVerticalTextAlignment(IViewHandler handler, ISearchBar sea
public static void MapCharacterSpacing(IViewHandler handler, ISearchBar searchBar) { }
public static void MapTextColor(IViewHandler handler, ISearchBar searchBar) { }
public static void MapCancelButtonColor(IViewHandler handler, ISearchBar searchBar) { }
public static void MapShowsCancelButton(IViewHandler handler, ISearchBar searchBar) { }
internal static void MapSearchIconColor(IViewHandler handler, ISearchBar searchBar) { }

/// <summary>
Expand Down
5 changes: 5 additions & 0 deletions src/Core/src/Handlers/SearchBar/SearchBarHandler.Windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ public static void MapCancelButtonColor(ISearchBarHandler handler, ISearchBar se
handler.PlatformView?.UpdateCancelButtonColor(searchBar);
}

public static void MapShowsCancelButton(ISearchBarHandler handler, ISearchBar searchBar)
{
// ShowsCancelButton is iOS-specific behavior
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The comment says "ShowsCancelButton is iOS-specific behavior" but this is also applicable to MacCatalyst (which uses UISearchBar). The comment should be updated to reflect that this is iOS/MacCatalyst-specific behavior, or more accurately, that Windows doesn't have an equivalent feature in AutoSuggestBox.

Suggested change
// ShowsCancelButton is iOS-specific behavior
// ShowsCancelButton is an iOS and MacCatalyst-specific behavior (UISearchBar).
// Windows' AutoSuggestBox does not have an equivalent feature.

Copilot uses AI. Check for mistakes.
}

internal static void MapSearchIconColor(ISearchBarHandler handler, ISearchBar searchBar)
{
handler.PlatformView?.UpdateSearchIconColor(searchBar);
Expand Down
1 change: 1 addition & 0 deletions src/Core/src/Handlers/SearchBar/SearchBarHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public partial class SearchBarHandler : ISearchBarHandler
[nameof(ISearchBar.Text)] = MapText,
[nameof(ISearchBar.TextColor)] = MapTextColor,
[nameof(ISearchBar.CancelButtonColor)] = MapCancelButtonColor,
[nameof(ISearchBar.ShowsCancelButton)] = MapShowsCancelButton,
[nameof(ISearchBar.SearchIconColor)] = MapSearchIconColor,
[nameof(ISearchBar.Keyboard)] = MapKeyboard,
[nameof(ISearchBar.ReturnType)] = MapReturnType,
Expand Down
5 changes: 5 additions & 0 deletions src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ public static void MapCancelButtonColor(ISearchBarHandler handler, ISearchBar se
handler.PlatformView?.UpdateCancelButton(searchBar);
}

public static void MapShowsCancelButton(ISearchBarHandler handler, ISearchBar searchBar)
{
handler.PlatformView?.UpdateCancelButton(searchBar);
}

internal static void MapSearchIconColor(ISearchBarHandler handler, ISearchBar searchBar)
{
handler.PlatformView?.UpdateSearchIcon(searchBar);
Expand Down
4 changes: 3 additions & 1 deletion src/Core/src/Platform/iOS/SearchBarExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,12 @@ public static void UpdateIsReadOnly(this UISearchBar uiSearchBar, ISearchBar sea
}

internal static bool ShouldShowCancelButton(this ISearchBar searchBar) =>
!string.IsNullOrEmpty(searchBar.Text);
searchBar.ShowsCancelButton && !string.IsNullOrEmpty(searchBar.Text);

public static void UpdateCancelButton(this UISearchBar uiSearchBar, ISearchBar searchBar)
{
// Respect the ShowsCancelButton property - if false, never show the button
// If true, show it based on whether there's text (iOS standard behavior)
uiSearchBar.ShowsCancelButton = searchBar.ShouldShowCancelButton();
Comment on lines +120 to 126
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment on lines 124-125 explains the logic, but it's actually documenting two different behaviors:

  1. If ShowsCancelButton is false, never show the button
  2. If true, show it based on whether there's text (iOS standard behavior)

However, the actual implementation on line 120 (searchBar.ShowsCancelButton && !string.IsNullOrEmpty(searchBar.Text)) doesn't fully implement "iOS standard behavior." Standard iOS UISearchBar shows the cancel button when focused, regardless of text content.

The current implementation creates a UX inconsistency where the cancel button only appears after typing, which means users can't dismiss the keyboard when the search bar is focused but empty. This could still create a keyboard trap scenario if the user focuses the search bar but hasn't typed anything yet.

Copilot uses AI. Check for mistakes.

// We can't cache the cancel button reference because iOS drops it when it's not displayed
Expand Down
2 changes: 2 additions & 0 deletions src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
Microsoft.Maui.ISearchBar.ShowsCancelButton.get -> bool
static Microsoft.Maui.Handlers.SearchBarHandler.MapShowsCancelButton(Microsoft.Maui.Handlers.ISearchBarHandler! handler, Microsoft.Maui.ISearchBar! searchBar) -> void
2 changes: 2 additions & 0 deletions src/Core/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
#nullable enable
override Microsoft.Maui.Platform.MauiView.DidUpdateFocus(UIKit.UIFocusUpdateContext! context, UIKit.UIFocusAnimationCoordinator! coordinator) -> void
Microsoft.Maui.ISearchBar.ShowsCancelButton.get -> bool
static Microsoft.Maui.Handlers.SearchBarHandler.MapShowsCancelButton(Microsoft.Maui.Handlers.ISearchBarHandler! handler, Microsoft.Maui.ISearchBar! searchBar) -> void
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
#nullable enable
override Microsoft.Maui.Platform.MauiView.DidUpdateFocus(UIKit.UIFocusUpdateContext! context, UIKit.UIFocusAnimationCoordinator! coordinator) -> void
Microsoft.Maui.ISearchBar.ShowsCancelButton.get -> bool
static Microsoft.Maui.Handlers.SearchBarHandler.MapShowsCancelButton(Microsoft.Maui.Handlers.ISearchBarHandler! handler, Microsoft.Maui.ISearchBar! searchBar) -> void
2 changes: 2 additions & 0 deletions src/Core/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
Microsoft.Maui.ISearchBar.ShowsCancelButton.get -> bool
static Microsoft.Maui.Handlers.SearchBarHandler.MapShowsCancelButton(Microsoft.Maui.Handlers.ISearchBarHandler! handler, Microsoft.Maui.ISearchBar! searchBar) -> void
2 changes: 2 additions & 0 deletions src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
Microsoft.Maui.ISearchBar.ShowsCancelButton.get -> bool
static Microsoft.Maui.Handlers.SearchBarHandler.MapShowsCancelButton(Microsoft.Maui.Handlers.ISearchBarHandler! handler, Microsoft.Maui.ISearchBar! searchBar) -> void
2 changes: 2 additions & 0 deletions src/Core/src/PublicAPI/net/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
Microsoft.Maui.ISearchBar.ShowsCancelButton.get -> bool
static Microsoft.Maui.Handlers.SearchBarHandler.MapShowsCancelButton(Microsoft.Maui.IViewHandler! handler, Microsoft.Maui.ISearchBar! searchBar) -> void
2 changes: 2 additions & 0 deletions src/Core/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
Microsoft.Maui.ISearchBar.ShowsCancelButton.get -> bool
static Microsoft.Maui.Handlers.SearchBarHandler.MapShowsCancelButton(Microsoft.Maui.IViewHandler! handler, Microsoft.Maui.ISearchBar! searchBar) -> void
2 changes: 2 additions & 0 deletions src/Core/src/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
Microsoft.Maui.ISearchBar.ShowsCancelButton.get -> bool
static Microsoft.Maui.Handlers.SearchBarHandler.MapShowsCancelButton(Microsoft.Maui.IViewHandler! handler, Microsoft.Maui.ISearchBar! searchBar) -> void
Loading