diff --git a/src/Controls/src/Core/Shell/Shell.cs b/src/Controls/src/Core/Shell/Shell.cs index 3e59dde42b40..694298a19950 100644 --- a/src/Controls/src/Core/Shell/Shell.cs +++ b/src/Controls/src/Core/Shell/Shell.cs @@ -1663,6 +1663,17 @@ static void OnCurrentItemChanged(BindableObject bindable, object oldValue, objec shell.ShellController.AppearanceChanged(shell, false); shell.ShellController.UpdateCurrentState(ShellNavigationSource.ShellItemChanged); + // If the new ShellItem is a FlyoutItem or a ShellGroupItem, we need to update the QueryAttributes + var existing = (ShellRouteParameters)shell.CurrentContent?.GetValue(ShellContent.QueryAttributesProperty); + bool isRelevantShellItem = shell.CurrentItem is FlyoutItem || shell.CurrentItem is ShellGroupItem; + bool isNewValueDifferent = oldValue is not null && newValue is not null && existing is null; + + if (isNewValueDifferent && isRelevantShellItem) + { + ShellContent currentShellContent = shell.CurrentItem.CurrentItem?.CurrentItem; + currentShellContent?.SetValue(ShellContent.QueryAttributesProperty, new ShellRouteParameters()); + } + if (shell.CurrentItem?.CurrentItem != null) shell.ShellController.AppearanceChanged(shell.CurrentItem.CurrentItem, false); } diff --git a/src/Controls/src/Core/Shell/ShellItem.cs b/src/Controls/src/Core/Shell/ShellItem.cs index 494bb5c3a7c3..27618634b027 100644 --- a/src/Controls/src/Core/Shell/ShellItem.cs +++ b/src/Controls/src/Core/Shell/ShellItem.cs @@ -300,6 +300,14 @@ static void OnCurrentItemChanged(BindableObject bindable, object oldValue, objec shell.UpdateCurrentState(ShellNavigationSource.ShellSectionChanged); } + //For TabBarItems itemchanged will fall in ShellItem class , we need to set the QueryAttributesProperty here + ShellContent currentShellContent = shellItem.CurrentItem?.CurrentItem; + var existing = (ShellRouteParameters)currentShellContent?.GetValue(ShellContent.QueryAttributesProperty); + if (oldValue is not null && newValue is not null && existing is null) + { + currentShellContent?.SetValue(ShellContent.QueryAttributesProperty, new ShellRouteParameters()); + } + shellItem.SendStructureChanged(); if (shellItem.IsVisibleItem) diff --git a/src/Controls/src/Core/Shell/ShellNavigationManager.cs b/src/Controls/src/Core/Shell/ShellNavigationManager.cs index 9329bf1a5f11..009851a7afa6 100644 --- a/src/Controls/src/Core/Shell/ShellNavigationManager.cs +++ b/src/Controls/src/Core/Shell/ShellNavigationManager.cs @@ -309,7 +309,16 @@ public static void ApplyQueryAttributes(Element element, ShellRouteParameters qu if (baseShellItem is ShellContent) - baseShellItem.ApplyQueryAttributes(MergeData(element, filteredQuery, isPopping)); + { + 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) + { + element.SetValue(ShellContent.QueryAttributesProperty, mergedData); + } + baseShellItem.ApplyQueryAttributes(mergedData); + } else if (isLastItem) element.SetValue(ShellContent.QueryAttributesProperty, MergeData(element, query, isPopping)); diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue13537.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue13537.xaml new file mode 100644 index 000000000000..2e8f5f764fef --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue13537.xaml @@ -0,0 +1,23 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue13537.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue13537.xaml.cs new file mode 100644 index 000000000000..3674f3b4b8b4 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue13537.xaml.cs @@ -0,0 +1,176 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; +using Microsoft.Maui.Controls; +using Microsoft.Maui.Controls.Internals; + +namespace Maui.Controls.Sample.Issues +{ + [Issue(IssueTracker.Github, 13537, "ApplyQueryAttributes should Trigger for all navigations", + PlatformAffected.All)] + public partial class Issue13537 : Shell + { + + public Issue13537() + { + InitializeComponent(); + Routing.RegisterRoute("NewPage", typeof(Issue13537InnerPage)); + Application.Current.Windows[0].Page = new NavigationPage(new Issue13537HomePage()); + } + } + + public class Issue13537HomePage : ContentPage + { + public Issue13537HomePage() + { + Title="Home"; // Setting the title of the page + var viewModel = new Issue13537ViewModel(); + this.BindingContext = viewModel; + + var Label = new Label + { + AutomationId = "HomePageTestLabel", + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center + }; + Label.SetBinding(Label.TextProperty, nameof(viewModel.DisplayText)); + + var button = new Button + { + Text = "Navigate using PushAsync", + AutomationId = "PushAsyncButton", + HorizontalOptions =LayoutOptions.Center, + VerticalOptions=LayoutOptions.Center + }; + button.Clicked += OnNavigationWithPushAsync; + + var stack = new StackLayout(); + stack.Children.Add(Label); + stack.Children.Add(button); + + this.Content = stack; + } + private void OnNavigationWithPushAsync(object sender, EventArgs e) + { + Navigation.PushAsync(new Issue13537InnerPage()); + } + } + public class Issue13537FavoritePage : ContentPage + { + + public Issue13537FavoritePage() + { + Title = "Favorite"; // Setting the title of the page + var viewModel = new Issue13537ViewModel(); + this.BindingContext = viewModel; + + var Label = new Label + { + AutomationId = "FavouritePageTestLabel", + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center + }; + Label.SetBinding(Label.TextProperty, nameof(viewModel.DisplayText)); + var button = new Button + { + Text = "Navigate using GoToAsync", + AutomationId = "GoToAsyncButton", + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center + }; + button.Clicked += OnGoToAsync; + var stack = new StackLayout(); + stack.Children.Add(Label); + stack.Children.Add(button); + this.Content = stack; + } + private void OnGoToAsync(object sender, EventArgs e) + { + + var Parameter = new Dictionary + { + { $"Parameter From {Shell.Current.CurrentPage.Title} Page", $"to New Page" }, + + }; + Shell.Current.GoToAsync("NewPage",parameters:Parameter); + } + } + public class Issue13537InnerPage : ContentPage + { + public Issue13537InnerPage() + { + Title = "NewPage"; // Setting the title of the page + var viewModel = new Issue13537ViewModel(); + this.BindingContext = viewModel; + + var Label = new Label + { + AutomationId = "InnerPageTestLabel", + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center + }; + Label.SetBinding(Label.TextProperty, nameof(viewModel.DisplayText)); + var button = new Button + { + Text = "Navigate using PopAsync back", + AutomationId = "PopAsyncButton", + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center + }; + button.Clicked += OnNavigatingPopButton; + + var stack = new StackLayout(); + stack.Children.Add(Label); + stack.Children.Add(button); + + + stack.AutomationId = "NewInnerPage"; + + this.Content = stack; + } + + private void OnNavigatingPopButton(object sender, EventArgs e) + { + Navigation.PopAsync(); + } + } + public class Issue13537ViewModel : IQueryAttributable, INotifyPropertyChanged where T : Page + { + // Property to hold formatted query attributes as a string + private string _displayText = "Default Query" ; + public string DisplayText + { + get => _displayText; + set + { + _displayText = value; + OnPropertyChanged(nameof(DisplayText)); + } + } + + public void ApplyQueryAttributes(IDictionary query) + { + if(query == null) + { + return; + } + + if( query.Count > 0) + { + DisplayText = $"{string.Join(",\n", query.Select(q => $"{q.Key} {q.Value}"))}"; + } + else + { + DisplayText= $"{typeof(T).Name} QueryAttribute is triggered"; + } + } + + // Implement INotifyPropertyChanged to support binding + public event PropertyChangedEventHandler PropertyChanged; + protected virtual void OnPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } + +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue13537.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue13537.cs new file mode 100644 index 000000000000..733ad2c34fb5 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue13537.cs @@ -0,0 +1,65 @@ +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues +{ + public class Issue13537 : _IssuesUITest + { +#if ANDROID + const string backButtonIdentifier = ""; +#else + const string backButtonIdentifier = "Home"; +#endif + public Issue13537(TestDevice testDevice) : base(testDevice) + { + } + + public override string Issue => "ApplyQueryAttributes should Trigger for all navigations"; + + [Test] + [Category(UITestCategories.Shell)] + public void ApplyQueryAttributeShouldTriggerforPushAndPopButton() + { + App.WaitForElement("HomePageTestLabel"); + App.Tap("PushAsyncButton"); + App.WaitForElement("InnerPageTestLabel"); + App.Tap("PopAsyncButton"); + var result = App.WaitForElement("HomePageTestLabel").GetText(); + Assert.That(result, Is.EqualTo("Issue13537HomePage QueryAttribute is triggered")); + } + +#if TEST_FAILS_ON_CATALYST + //App.TapBackArrow doesnot actually tab the back arrow button instead it taps the first tab element on the screen as both have the same access identifier. + [Test] + [Category(UITestCategories.Shell)] + public void ApplyQueryAttributeShouldTriggerforPushAndBackButton() + { + App.WaitForElement("HomePageTestLabel"); + App.Tap("PushAsyncButton"); + App.WaitForElement("InnerPageTestLabel"); +#if IOS + App.Back(); +#else + App.TapBackArrow(backButtonIdentifier); +#endif + var result = App.WaitForElement("HomePageTestLabel").GetText(); + Assert.That(result, Is.EqualTo("Issue13537HomePage QueryAttribute is triggered")); + } +#endif + + + [Test] + [Category(UITestCategories.Shell)] + public void ApplyQueryAttributeShouldTriggerforTab() + { + App.WaitForElement("Favorite"); + App.Tap("Favorite"); + App.WaitForElement("FavouritePageTestLabel"); + App.Tap("GoToAsyncButton"); + var result = App.WaitForElement("InnerPageTestLabel").GetText(); + Assert.That(result, Is.EqualTo("Parameter From Favorite Page to New Page")); + } + } + +}