Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

[Enhancement] Shell Navigation and Structural Management #5166

Open
dansiegel opened this issue Feb 8, 2019 · 20 comments
Open

[Enhancement] Shell Navigation and Structural Management #5166

dansiegel opened this issue Feb 8, 2019 · 20 comments
Labels
a/shell 🐚 API-change Heads-up to reviewers that this PR may contain an API change e/6 🕕 6 in-progress This issue has an associated pull request that may resolve it! m/high impact ⬛ partner Issue submitted by partner or related open source project proposal-accepted t/enhancement ➕

Comments

@dansiegel
Copy link
Contributor

dansiegel commented Feb 8, 2019

Nuget to play with

#6764

Shell Navigation and Structural Management

The core idea of this API is to expose the active structural state of the Shell as a model class that can be manipulated and applied to the shell.

  • intent of this setup is to empower MVVM frameworks to effectively influence every aspect of shell processing a Uri. ShellRouteState represents the data object that Shell understands but MVVM frameworks can effectively implement there own uri scheme with the above set of interfaces. People can also just tap into parts of the process if they only want to influence one or two things.
  • Lots of interfaces to minimize ABI breaks and also allow people to plug into single aspects of the navigation service opposed to having to go whole hog
  • Everything listed here will have an Event based version that's on the Shell. I'm not super clear yet if those will support async somehow via the args. Quite possibly you can set something on the args like a Task?

ShellRouteState

Used to indicate the entire routing state of the shell. This contains all the branched stacks, the different paths that represent each different page, the paramteres to apply to those pages, how to transition to and away from those. The idea here is that you can setup an entire route state based on this data. All of this could be serialized during tombstoning as well to fully recreate the application. The renderer level can also take this in to see where the user is going and then at that point make decisions about what it wants to present and how

public class ShellRouteState
{
    public RoutePath[] Routes { get; }

    public RoutePath CurrentRoute { get;  }

}

Properties

API Description
Routes Flyout items effectively cause you to swap stacks so this is a way to represent those different stacks.
ActiveRoute this represents the visible stack.

RoutePath

Signifies an individual stack path

public class RoutePath
{
    public RoutePath(IList<PathPart> pathParts)
    {
        PathParts = new ReadOnlyCollection<PathPart>(pathParts);
    }

    public IReadOnlyList<PathPart> PathParts { get; }
    public Dictionary<string, string> NavigationParameters { get; }
}

Properties

API Description
PathParts List of path parts representing different pages on the stack.
NavigationParameters The parameters to apply at the shell level.

PathPart

Signifies a specific part of the route that applies to a given shell element being navigated to. This also contains the parameters that will be applied to the particular shell part

public class PathPart
{
    public string Path { get; }

    // in theory you could store a bindingcontext here for tombstoning        
    public IEnumerable<KeyValuePair<string, object>> Parameters { get; }
    public Element ShellPart { get; }

    // This describes how you will transition to and away from this Path Part
    ITransitionPlan Transition { get; }

    // how am I presented? Modally? as a page?W
    PresentationHint Presentation { get;  }
}

Properties

API Description
Path The path associated with this Shell Part.
Parameters List of parameters to apply to the realized content.
Transition How to transition to this page when it's added and when it's removed.
Presentation Am I modal? A dialog box? other?
ShellPart Element associated with this path of the Uri

Interfaces

IUriProjectionParser

  • Allows a developer to implement a custom uri scheme. The uri could completely demolish the current state and just return a new setup completely
public interface IUriProjectionParser
{
    Task<ShellRouteState> ParseAsync(ShellUriParserArgs args);
}

IShellNavigationRequest

  • The args for Navigating will have the current shell and the route being traveled to. Based on where the user is going the developer can modify anything they want to with the route being traveled to. If they want to they can return a completely different one. If you want to cancel navigation then just return null or current state. The shell is part of the args so the user can also modify the shell visibility here if they want to as well before navigation is submitted
public interface IShellNavigationRequest
{
    Task<ShellRouteState> NavigatingToAsync(ShellNavigationArgs args);
}

IShellContentCreator

  • Customize how a content page is created
public interface IShellContentCreator
{		
    ContentPage Create(ShellContentCreateArgs content);
}

IShellApplyParameters

  • Apply routing parameters to the destination point. This will be called prior to navigating to any page. So if the user
    hits the back button this will get called
public interface IShellApplyParameters
{
    void ApplyParameters(ShellLifecycleArgs args);
}

IShellPartAppearing

  • Do things before the Shell is appearing but the intention for it to appear is definite and nothing can stop it. This is called for ShellItem, Section, Content.
public interface IShellPartAppearing
{
    Task AppearingAsync(ShellLifecycleArgs args);
}

IShellPartAppeared

  • Page is visible on the screen
public interface IShellPartAppeared
{
    Task Appeared(ShellLifecycleArgs args);
}

IShellPartDisappeared

  • Shell part has been navigated away from and is no longer visible
public interface IShellPartDisappeared
{
    Task DisappearedAsync(ShellLifecycleArgs args);
}

ShellNavigationArgs

Arguments passed to Navigating process indicating where I'm going to be going

public class ShellNavigationArgs : EventArgs
{
	public Shell Shell { get; }
	public ShellRouteState FutureState { get; }
}

Properties

API Description
Shell The current shell.
FutureState The state being changed to.

ShellContentCreateArgs

Arguments passed to Shell Content creation.

public class ShellContentCreateArgs : EventArgs
{
	public ShellContent Content { get; }
}

Properties

API Description
Content The Shell Content being created.

ShellUriParserArgs

Arguments passed to Method that parses Uri to New Route Path

public class ShellUriParserArgs : EventArgs
{
	public Shell Shell { get; }
	public Uri Uri { get; }
}

Properties

API Description
Shell The Current Shell.
Uri The Uri being navigated to.

ShellLifecycleArgs

Arguments passed to various stages related to life cycle.

public class ShellLifecycleArgs : EventArgs
{
	public Element Element { get; }
	public PathPart PathPart { get; }
	public RoutePath RoutePath { get; }
	public bool IsLast { get; }
}

Properties

API Description
Element Element event is happening to.
PathPart The part of the path that's connected to this Element .
RoutePath The full path.
IsLast Is this the last thing being routed to.

Examples

Custom Navigation Service example

 public class CustomNavigationServices : ShellNavigationService
    {
        ViewModelBase _navigatingViewModel;

        public CustomNavigationServices()
        {

        }

        public async Task GotoAsync(ViewModelBase viewModelBase)
        {
            _navigatingViewModel = viewModelBase;
            await Shell.Current.GoToAsync($"//{viewModelBase.GetType().Name}", true);
        }
        public override Task<ShellRouteState> ParseAsync(ShellUriParserArgs args)
        {
              //provide custom logic here to parse the uri being navigated to
              if(args.Uri.ToString() == "..")
                     return args.Shell.CurrentRouteState().Pop();

              return base.ParseAsync(args);
        }

        public override void ApplyParameters(ShellLifecycleArgs args)
        {
            base.ApplyParameters(args);
        }

        public override Page Create(ShellContentCreateArgs args)
        {
            var content =  base.Create(args);
            if (args.Content.Route == "HomePageViewModel")
                content.BindingContext = _navigatingViewModel as HomePageViewModel ?? new HomePageViewModel();
            else if (args.Content.Route == "LoginPageViewModel")
                content.BindingContext = _navigatingViewModel as LoginPageViewModel ?? new LoginPageViewModel();

            _navigatingViewModel = null;
            return content;
        }
}

Custom Navigation Service example

The ShellRoutePath can be interacted with in the same way as you would any other navigation

public override async Task<ShellRouteState> ParseAsync(ShellUriParserArgs args)
{
    ShellRouteState returnValue = shell.GetCurrentRouteState();

    // Pop current page off stack
    returnValue.Pop();
    returnValue.RemoveRoute("routename");
    returnValue.AddRouteWithParameters("routename", parameterstopassing);
    return returnValue;
}

Existing work being done here please comment!!
#6542

Nuget to play with

debug.zip

Demos

  • really basic example of plugging into the already implemented service to modify the behavior of one part

https://github.com/PureWeen/ReactShell/blob/master/Source/SmartHotel.Clients/SmartHotel.Clients/Services/CustomNavigationServices.cs

Original Ticket

Summary

Currently there is no good way of easily integrating an App's framework into Shell. To make this easier a Handler should be provided so that a Framework can simply provide Forms a custom implementation for the core Navigation functionality.

API Changes

These methods should all be exposed though an Interface so that it could be potentially implemented in a stand alone service or on the Application itself since this is where you would most likely have a direct reference to a DI Container.

  • Create View from Shell Content... the default delegate could simply expand on ContentTemplate, where a custom delegate may utilize Dependency Injection to create the view from the route segment name.
  • Handle Can Navigate From
  • Handle Will Navigate To
  • Handle Did Navigate From/To

Intended Use Case

To allow frameworks like Prism to more easily integrate with Shell.

@puppetSpace
Copy link
Contributor

puppetSpace commented Mar 10, 2019

I'm stuck on the same issue. I have a simple mvvm framework that I use and with the navigationpage I was able to integrate it in my framework so I could navigate from my viewmodel and respond to navigation events.

@dansiegel
Copy link
Contributor Author

@davidortinau as discussed at the MVP Summit this is a major blocker to any possible adoption of Shell by third party frameworks like Prism. Always happy to jump on a call with you and the team if it would help to clarify understanding around the issues.

@glennawatson
Copy link

We are having similar issues to @dansiegel -- on the ReactiveUI MVVM framework we are delayed until the change are made.

@PureWeen PureWeen assigned PureWeen and unassigned StephaneDelcroix Apr 8, 2019
@samhouts samhouts added this to the 4.0.0 milestone Apr 10, 2019
@samhouts samhouts removed this from the 4.0.0 milestone Apr 22, 2019
@samhouts samhouts added m/high impact ⬛ and removed blocker Issue blocks next stable release. Prioritize fixing and reviewing this issue. labels Apr 22, 2019
@rid00z
Copy link

rid00z commented May 18, 2019

Thanks @dansiegel. FreshMvvm requires this also. Is this implementation being discussed?

In FreshMvvm we are able to support the shell/url based navigation in Shell. We just need all the information in events when pushing back and forth.

When navigating forward ideally:
-Current/Previous Page Instance
-New Page Type+Instance (The page that's being pushed)
-If pushed as model
-The data that's being pushed

@timahrentlov
Copy link

timahrentlov commented May 18, 2019

It would be nice to have the actual navigation handling decoupled from shell. It could be an interface that comes with a default implementation.

Prism, FreshMvvm could implement their own and assign it to the Shell instance. An INavigator of sorts.

And I would be ecstatic if I could implement something like flutters return values with it. (https://flutter.dev/docs/get-started/flutter-for/xamarin-forms-devs#how-do-i-navigate-between-pages)
Map coordinates = await Navigator.of(context).pushNamed('/location');

With regards to DI and BindingContexts it would be awesome to have it like Blazor: https://docs.microsoft.com/en-us/aspnet/core/blazor/dependency-injection?view=aspnetcore-3.0 and simply control it using attributes.

@samhouts samhouts added the partner Issue submitted by partner or related open source project label Jun 27, 2019
@PureWeen
Copy link
Contributor

PureWeen commented Jul 3, 2019

If anyone wants to start playing with the API for this I will keep the nuget over here updated
#6764

@BenKmann
Copy link

BenKmann commented Aug 2, 2019

@PureWeen great work so far on this Issue.
Is there an Update on the progress of this Issue or could you provide an update to the Nuget #6764 if this Issue doesn't make it into the pre3?
You're Nuget is working great on Android, but on iOS i get an "CurrentItem is null" Exception when the AppShell is getting initialized.

swcomp added a commit to swcomp/FreshMvvm that referenced this issue Oct 6, 2019
NOTE: FreshMvvm ( and others ) XF 4.2 Shell Nav not yet supported  xamarin/Xamarin.Forms#5166
@samhouts samhouts added the in-progress This issue has an associated pull request that may resolve it! label Oct 11, 2019
@HobDev
Copy link

HobDev commented Oct 31, 2019

The developers have to choose either their favourite framework or shell until this issue is resolved. When should we expect this to be completed.

@KevinHu-au
Copy link

Hi XF team. When the features will be available and released? Or any plan. We have a project in which I want to use Prism and Shell. The currently Shell page-to-page navigation and the way parameters are passing during navigation are really a blocker for us. We love Shell. It makes the app structure so simple and graceful.

@samhouts samhouts added proposal-accepted API-change Heads-up to reviewers that this PR may contain an API change and removed proposal-open labels Apr 29, 2020
@timahrentlov
Copy link

timahrentlov commented Jun 3, 2020

How would you go about implementing something akin to Prism's IDestructible interface?
What is the mechanism by which the page instance itself are told that it is no longer used or that all shell references to it has been dropped ?

@PureWeen
Copy link
Contributor

PureWeen commented Jun 4, 2020

@dansiegel or @timahrentlov what are the conditions met that causes Destroy() to get called in Prism?

Just want to make sure I'm answering @timahrentlov 's question correctly :-)

@dansiegel
Copy link
Contributor Author

@PureWeen IDestructible is called when the page is popped and will not be navigated back to. It's one of the things I believe we were starting to look at the last time we streamed.

@PureWeen
Copy link
Contributor

PureWeen commented Jun 4, 2020

@dansiegel that's what I thought

So @timahrentlov basically that :-)

The ShellRouteState data structures contains a references to literally everything.

If you have 12 tabs going each with different navigation stacks that'll be inside that ShellrouteState structure

At the point anything gets removed from that ShellRouteState structure it'll have something like IDestructible that gets called.

@timahrentlov
Copy link

Excellent! Thanks @dansiegel and @PureWeen

@DanGould
Copy link

For the time being this also limits using mobile Blazor bindings with external frameworks as that projects seems to plan support only for Shell.

@PureWeen PureWeen removed their assignment Aug 3, 2022
@soroshsabz
Copy link

ITNOA

Hi,

@PureWeen What is the last state of this issue? is this issue move to MAUI? or done in MAUI? or it is plan to done here?

thanks

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
a/shell 🐚 API-change Heads-up to reviewers that this PR may contain an API change e/6 🕕 6 in-progress This issue has an associated pull request that may resolve it! m/high impact ⬛ partner Issue submitted by partner or related open source project proposal-accepted t/enhancement ➕
Projects
None yet
Development

No branches or pull requests