question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Navigation data is retained and also presented to IQueryAttributable on Back navigation.

See original GitHub issue

Description

I’m using Shell with my MAUI app and am passing parameters during navigation with Shell.GoToAsync() using the Dictionary<string, object> and the IQueryAttributable interface as discussed in the MS docs.

var navigationParameter = new Dictionary<string, object>
    {
        { "Bear", animal }
    };
await Shell.Current.GoToAsync($"beardetails", navigationParameter);
public class BearDetailViewModel : IQueryAttributable, INotifyPropertyChanged
{
    public Animal Bear{ get; private set; }

    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        Bear= query["Bear"] as Animal;
        OnPropertyChanged("Bear");
    }
    ...
}

I noted that my bindings and initialization code for the ViewModel kept being re-executed when I would Navigate Back to the parent page. It appears that any data that’s passed as navigation parameters is retained in memory for the life of the page and is subsequently returned to that page during a “Navigate Back” operation. Overriding the back button operation with code that doesn’t pass any parameters didn’t change the result. Such as this:

    <Shell.BackButtonBehavior>
        <BackButtonBehavior
            Command="{Binding BackCommand, Mode=OneTime, Source={x:Reference self}}"/>
    </Shell.BackButtonBehavior>
        }
            this.BackCommand = new Command( this.OnBackPressed );
        }

        private async void OnBackPressed()
        {
            await Shell.Current.GoToAsync( ".." );
        }

Any attempt to clear or supply a different set of data via the method call simply serves to add to the already stored query data. Thus:

Shell.Current.GoToAsync( "..", new Dictionary<string, object> { {"w", new object()}  });

results in the “w” parameter being added to the current store of parameters. And now “w” and “Bear” are retained until the page is removed from the stack.

Maybe I’m used to external navigation paradigms but my presumption was that the data was transient for the life of the navigation operation only. Thus the retention of data was not expected. The docs fail to mention anything about the data being retained for the life for the page as well. Off the cuff this seems like a poor model as it increases memory footprint and possibly has unintended consequences if any system resources were shuttled as parameters.

Steps to Reproduce

Pass parameters to any page as you navigate down a hierarchy. When the back button is pressed the query data that was passed to any particular page on the initial navigation TO that page is returned via the IQueryAttributable interface.

Link to public reproduction project repository

https://github.com/bakerhillpins/Issues/tree/NetMauiIssue10294

Version with bug

6.0.486 (current)

Last version that worked well

Unknown/Other

Affected platforms

iOS, Android, Windows, macOS

Affected platform versions

Android 9.0, API 28

Did you find any workaround?

As the Query parameters are passed via IDictionary<string, object> query the author would need to manually cleanup individual resources with IDictionary<string, object>.Remove() or IDictionary<string, object>.Clear() in an implementation of the IQueryAttributable interface:

public class BearDetailViewModel : IQueryAttributable, INotifyPropertyChanged
{
    public Animal Bear{ get; private set; }

    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        Bear= query["Bear"] as Animal;
        OnPropertyChanged("Bear");

        query.Clear();
    }
    ...
}

If one was using the Attribute:

[QueryProperty(nameof(Name), "name")]
[QueryProperty(nameof(Location), "location")]

they’d be forced to implement the interface to remove the data. Or, I suppose their property setters must qualify any update with a test for != or their bindings would re-execute when they returned to the page.

Relevant log output

No response

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:2
  • Comments:52 (20 by maintainers)

github_iconTop GitHub Comments

4reactions
bilelmnassercommented, Oct 21, 2022

A workaround for me : clear query dictionary after receiving it, I am able to do it in view model class that implement IQueryAttributable :

`public void ApplyQueryAttributes(IDictionary<string, object> query) { if (query.ContainsKey(“querydataKey”)) { //process here query[“querydataKey”]

}

//clear the query to release the data query. Clear(); }`

IQueryAttributable work nicely with ObservableObject ofCommunity toolkit MAUI mvvm hope this helps some that tries to send data througth appShell page navigation !

3reactions
bakerhillpinscommented, Jan 13, 2023

Personally I wasn’t very surprised by this. It makes perfect sense if you’re used to web development, and the MAUI AppShell navigation makes it abundantly clear.

If on the web I:

1. Navigate to https://example.org/page?foo=bar

2. Navigate to https://example.org/otherpage

3. Navigate to the previous page, which is https://example.org/page?foo=bar

It makes perfect sense to me that if I navigate back, I navigate to the exact page I requested, including the query. MAUI AppShell navigation clearly lets you register and navigate to pages as routes, which matches the style of routing used on the web.

I hear you but I don’t agree because I’m not actually “navigating” to a new page as you describe above. The Pop/Back operation doesn’t take in a Uri, It simply removes a View from the z-order and returns us to the same page (same instance) we were viewing previously. Nothing new was created. I’m popping a view off the stack and I didn’t apply any navigation parameters to that action. In your example you show navigation to page twice. There’s 2 specific actions you’ve requested in there.

  • You’re navigating to a new Page and thus new instance of the Page object. In MAUI this would result in a stack of /Page/OtherPage/Page. This issue discusses going from /Page/OtherPage back to /Page, so we’re not increasing the z-order.
  • In both navigation steps you’ve supplied in the URL data to be sent into the page (?foo=bar). In this situation you’ve actually requested a different instance of the Bar object to be supplied to each navigation operation and that’s not what’s happening here. The pop action results in the same instances of data being delivered to the return operation that was applied when the view was initially navigated to.
Read more comments on GitHub >

github_iconTop Results From Across the Web

Backwards Shell navigation does not trigger ...
The problem is that I am not getting the data from the query route when navigating backwards using the two dots. The debugger...
Read more >
NET MAUI Shell navigation
Navigation data can be received by implementing the IQueryAttributable interface on the receiving class. The IQueryAttributable interface ...
Read more >
Process navigation data using IQueryAttributable in Xamarin ...
This method has a query argument, of type IDictionary<string, string>, that contains any data passed during navigation.
Read more >
MAUI: Passing Data Across Pages - Technology Wonders
In this article, we will build the application where we will see how to navigate across pages and share data across pages.
Read more >
.NET MAUI's Navigation for Beginners - Push, Pop, & Pass ...
NET MAUI 02:05 - App code walkthrough 03:08 - Registering routes for pages 05:00 - Simple navigation & back navigation 08:30 - Back...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found