Prism: [XF] Navigating between Tabs

Created on 20 Jun 2016  ·  101Comments  ·  Source: PrismLibrary/Prism

When working with a Tabbed Page, and navigating between tabs there is currently no implementation that allows you to properly handle the navigation events as INavigationAware is only called by the INavigationService, not by native navigation, like in the event of tapping a different Tab.

As discussed we could possibly add a new interface and tie this in via a custom behavior.

XF enhancement

Most helpful comment

Personally, I would prefer it be added by default with a way to disable it. I think the more common use case is going to be that developers want it to have that behavior out of the box. In my case, the user selects an item from a list and it navigates to a TabbedPage where each page displays different data that has to be loaded. All of the pages need the parameters, but I can only give them to the first one.

I'll give the gist a try and let you know if any pain points jump out at me.

All 101 comments

I'm curious if we could create a new interface that the NavigationService can use to control the TabbedPage navigation behavior. Something like

public interface ITabbbedPageNavigationOptions
{
    bool UseTabbedBasedNavigation {get;}
}

Then the TabbedPage could implement the interface and switch tabs instead of calling PushModelAsync/PushAsync.

But... that would require calling the navigation action off of the TabbedPageViewModel itself, not a page within the TabbedPaged. SO how would that work?

I'm thinking an approach similar to the ViewModelLocator's AutoWireViewModel property might work particularly well so that we can easily attach an event handler to the TabbedPage CurrentPageChanged and PagesChanged events.

The only issue I can think about with the behavior is preventing the INavAware from invoking multiple times; once when navigated to, then again when responding the the tab changed event.

I would say that you want to fire when you load the initially selected tab, and again any time you change tabs. You may have an OnNavigatedFrom/To that needs to be handled at least if we're sticking with the current INavAware. Certainly how to handle things may get tricky, but that's on the developer to handle in their implementation not so much on the framework.

Maybe a new interface would be better as to not to cause confusion between a navigation operation and a tab switching operation. I'm curious if passing parameters between tab changes would be needed, and if so how we could solve that.

I can certainly think of a few times where you may want to react to a change on one tab and update other tabs. I suppose you could use events, but I think there's a better way.

I solved this particular issue with a Behavior that can be associated with a TabPage. It listens to the page change event, and calls TabNaviagatedFrom and TabNavigatedTo from the inner page ViewModels that implement ITabNavigationAware (a lot like the INavigationAware that already exist). I use a NavigationParameters object to pass parameters from one tab to the other.

@polepage would you mind sharing your approach?

Navigated from prompt the last view model to set the data to give to the next, Navigated to gives that data to the newly selected page. I used a different interface so a Page can also be INavigationAware. The thing works with any MultiPage,l not just TabPage.

public interface IMultiPageNavigationAware
{
  void OnInternalNavigatedFrom(NavigationParameters navParams);
  void OnInternalNavigatedTo(NavigationParameters navParams);
}

public class MultipageNavigationBehavior : Xamarin.Behaviors.Behavior<MultiPage<Page>>
{
  private Page _lastSelectedPage;

  protected override void OnAttach()
  {
    AssociatedObject.CurrentPageChanged += CurrentPageChangedHandler;
  }

  protected override void OnDetach()
  {
    AssociatedObject.CurrentPageChanged -= CurrentPageChangedHandler;
  }

  private void CurrentPageChangedHandler(object sender, EventArgs e)
  {
    NavigationParameters navParams = new NavigationParameters();
    if (_lastSelectedPage != null)
    {
      IMultiPageNavigationAware lastPageAware = _lastSelectedPage.BindingContext as IMultiPageNavigationAware;
      if (lastPageAware != null)
      {
        lastPageAware.OnInternalNavigatedFrom(navParams);
      }

      IMultiPageNavigationAware newPageAware = AssociatedObject.CurrentPage.BindingContext as IMultiPageNavigationAware;
      if (newPageAware != null)
      {
        newPageAware.OnInternalNavigatedTo(navParams);
      }
    }

    _lastSelectedPage = AssociatedObject.CurrentPage;
  }
}

I use Xamarin.Behavior.Behavior(T) instead of the built in Xamarin.Forms.Behavior(T) because I had issues with the Xamarin Forms one some times ago and I never checked if my issues were fixed.

@polepage thanks for sharing

@polepage this is a great approach!
@brianlagunas I would say we should add something like the ViewModelLocator so we can simply add the property to the TabbedPage and it can attach the behavior. I can look into this more later tonight.

@brianlagunas any thoughts as to whether this behavior should:

  • Be added automagically by the NavigationService for CarouselPages and TabbedPages
  • Be a Bindable Property that adds the behavior similar to the ViewModelLocator
  • Force developers to explicitly add the behavior? (Keep in mind with the other two, a developer always has the ability to do this if they want)
<TabbedPage xmlns:b="clr-namespace:Prism.Behaviors;assembly=Prism.Forms"
            xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
            prism:ViewModelLocator.AutowireViewModel="True"
            prism:PrismBehaviors.AddMultiPageNavigation="True">
    <TabbedPage.Behaviors>
        <!-- Explicitly Added -->
        <b:MultiPageNavigationBehavior />
    </TabbedPage.Behaviors>
</TabbedPage>

My gut has me leaning towards a behavior

Well it has to be a behavior. The question is how do you think that behavior should be added. Should it be automatically by the NavigationService, by a BindableProperty making it an opt-in feature or explicitly added like any other behavior?

I am leaning towards explicitly

I would really like to see this, is there anything I can do to help?

@toadzky I have it all working. There are just some implementation details that have to be figured out. If this is holding you back I've created a Gist you can borrow from to get you through until it gets added. For now you'll need to add the behavior to your MultiPage (TabbedPage/CarouselPage).

That said any feedback people have on the expected behavior would be great. Specifically would the community prefer to have this behavior automatically added by the NavigationService or should it be on the Developer to add to each page? Or is there another way all together that people would like to see it work?

Personally, I would prefer it be added by default with a way to disable it. I think the more common use case is going to be that developers want it to have that behavior out of the box. In my case, the user selects an item from a list and it navigates to a TabbedPage where each page displays different data that has to be loaded. All of the pages need the parameters, but I can only give them to the first one.

I'll give the gist a try and let you know if any pain points jump out at me.

I very much agree with @toadzky, this is behavior that I was expecting so it seems like it should be the default.

It can't be default because there is no easy way to remove it if it's not wanted. It will have to be an opt-in.

I would make two points here:

1) If your VM does not implement the IMultiPageNavigationAware you shouldn't notice a difference.
2) If someone prefers to use their own behavior or something... We can easily incorporate a bindable property that they can attach similar to the VML.AutoWire property. We could use either a nullable bool or just name it something like DisableBehaviorInjection.

I am not a fan of adding a separate attached property to turn off a behavior. That is not a common or expected approach in XAML development.

So after playing around a little bit, I do kind of like that it is done automatically :)

The dev experience is much nicer. We just have to think of a good way to opt-out. Any other ideas besides an attached property? Maybe an interface IMultiPageNavigationOptions just like we have for NavigationPage and MasterDetail? It follows the same pattern anyways.

If we were to do something like that, I would image we would want something that goes on the MultiPage (Tabbed/Carousel).

public interface IMultiPageNavigationOptions
{
    bool InjectNavigationBehavior { get; }
}

public class PageNavigationService
{
   async Task ProcessNavigationForTabbedPage( ... )
   {
       var options = currentPage as IMultiPageNavigationOptions;

       if( options == null || options.InjectNavigationBehavior )
           currentPage.Behaviors.Add( new MultiPageNavigationBehavior() );
   }
}

Would it be possible to have more general interfaces IDeactivateAware and IActivateAware that could also be called when activating and deactivating views through calls to Region.Activate etc. I wouldn't call switching tabs a navigation per say, it is more a user interaction to activate and deactivate a views.

I disagree. It's definitely navigation, it's just sideways navigation
instead of up and down.

On Mon, Sep 5, 2016 at 9:52 AM Sune Frederiksen [email protected]
wrote:

Would it be possible to have more general interfaces IDeactivateAware and
IActivateAware that could also be called when manually activating and
deactivating views. I wouldn't call switching tabs a navigation per say, it
is more a user interaction to activate and deactivate a views.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/PrismLibrary/Prism/issues/650#issuecomment-244776149,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ACLkWiJqMuuaAcoC6XEsAifrkX3_xxPBks5qnDqigaJpZM4I6Iai
.

I agree with @sunefred. This is not navigation. You are just in a tabbed control changing/selecting a different tab.

@toadzky Let me elaborate a little bit then, and we will see if you agree with me.

There are a multitude of ways to activate and deactivate content in a TabControl.

  1. View injection
  2. View discovery
  3. Navigation
  4. Region.Activate/Deactivate
  5. Manual tab selection

All of these will already affect the Region.ActiveViews and if you implement the IActiveAware interface you will get notification when your tab is deactivated. To me it is odd that manual tab selection should get special treatment when it is just one of many methods to select the active tab. And by the way IActiveAware also works on ContentControl, ListBox etc.

I see a complication if you use navigation in parts of your application to switch tabs and if you use INavigationAware to confirm switching tabs. In that case it is counter intuitive that manually clicking a tab does not require the same confirmation. Is this the use case you want to cover?

So I'm using Prism in Xamarin.Forms, and it sounds like basically everything you just said applies to WPF. Unless you add swiping (which isn't enabled by default IIRC), the only way to change tabs in XF is to tap the tab header.

@brianlagunas if the tabs themselves aren't part of navigation, why are their names part of the navigation urls? they are part of navigation if you are building a deep link, but not for siblings. it's kind of a weird way to separate it IMO.

@sunefred There is a distinct difference between activation and navigation whether it is done programatically such as NavigationService.NavigateAsync( "MyTabbedPage/TabA" ) or by "Manual tab selection". Given your line of thinking INavigationAware shouldn't even exist because really a page is Activating/Deactivating when you Navigate To it or away From it.

Given an activation scenario you are saying that we have created SomePage and the corresponding ViewModel, now go do something without any context to what is going on in the app. A Navigation scenario differs because there may be context that we want to share without explicitly knowing where it is coming from.

Besides that the implementation of TabbedPage isn't comparable to a Tab Control that you may be familiar with on the web, where it is a child element of a page (such as the one you see when writing a comment on this thread). The TabbedPage is really closer to a Control Template that you are applying to a selection of Pages which presents a common view wrapper with a control on the bottom for abstracting the navigation between a specified set of pages.

@toadzky Let's say you have a nav uri like this:

NavigatAsync("MyTabbedPage/NextPage")

The first argument would be the actual TabbedPage. The next argument represents the next page in the navigation request, which may or may not be a page representing a tab in the MyTabbedPage. If it is a tab, then it will select that tab and continue processing the navigation request. This becomes more important when the tab is a navigation page, and navigation must continue within a different navigation stack. If it is not, it will create an instance of the NextPage, add it to the navigaton stack, and continue processing the navigation request. Just to clarify, the tabs don't have to be part of the navigation URI.

There is a distinct difference between simply changing tab selection, and processing a complex navigation request.

That makes sense. I agree that it shouldn't trigger the normal INavigationAware methods, but changing the name of the interface to something more generic feels weird. There is a concept of activation, but there is also the concept of explicit navigation. I may want to do something different when a user explicitly changes tabs then when a view is injected/discovered, etc. Activated doesn't mean navigated.

Hey Guys, I have just started with Prism and love it, well done Brian.
I have the same issue, I have a tabbed page with three content pages, it would be really great if the OnNavigatedTo or similar was available. I wish I had enough experience to contribute.

So I am finally getting around to looking at this. First off, in WPF, responding to tab changes in a tab control is handled by using the IActiveAware interface, with no options for passing parameters between tabs. Essentially, you are responding to a tab that is becoming active. Couldn't we take the same approach and leverage the IActiveAware interface for this?

I am also curious how common it is to pass parameters between tabs of a TabbedPage? I have a hard time seeing the need for that. Thoughts?

If IActiveAware wasn't already a thing I would say that could be a good name. However I think a solution that provides a way to pass information in the way of NavigationParameters would be better.

Take the case where we navigate from PageA => PageB. Now say PageA is a search page that incorporates the lookup logic for the object we want to work with. When we navigate to PageB it only makes sense that PageB would know about our object. If PageB is a TabbedPage we have no way of telling its children what object we're working with unless we allow the parent to share its navigation parameters and have a way to pass it to the children and/or their view models.

@brianlagunas in my particular case, i have multiple tabs that load data independently, but what data they need to load is passed in as navigation parameters from the previous page. since i can't pass navigation parameters to each of the pages in the NavigateAsync call, i pass all the parameters to the first page and then forward them to other pages via IMultiPageNavigationAware. it's not the most elegant solution, i admit, but it's the best one i was able to figure out.

@toadzky Well can't you just propagate those parameters in the TabbedPage to it's children? Implement INavigationAware on the TabbedPage, capture the parameters and then loop through the children and set a property on their ViewModels that is used to load the data?

I'm just trying to think if passing parameters from tab to tab is a common task, or just an exception.

Loop through what children? Where do I get references to the view models
inside a multipage container?

On Fri, Sep 30, 2016, 23:07 Brian Lagunas [email protected] wrote:

@toadzky https://github.com/toadzky Well can't you just propagate those
parameters in the TabbedPage to it's children? Implement INavigationAware
on the TabbedPage, capture the parameters and then loop through the
children and set a property on their ViewModels that is used to load the
data?

I'm just trying to think if passing parameters from tab to tab is a common
task, or just an exception.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/PrismLibrary/Prism/issues/650#issuecomment-250893010,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ACLkWrAk0b8O4sbu_9vzbHiRqCLYALlnks5qveqogaJpZM4I6Iai
.

@dansiegel Well would it be more useful if the TabbedPageNavigationOptions had a property called PropagateParametersToChildren which if set, the navigation service would send the parameters to all child pages via OnNavigatedTo? It would be more of a one time thing, and wouldn't support passing params from tab to tab on selection changed

@toadzky You get the VMs from the child Page's BindingContext. Cast them as a common interface and set the property accordingly.

I'm still not following how I would have that information in the view model
of tabbed page.

To be honest, I actually like the auto propagate flag. It's the behavior I
expected when I started using Prism and wasn't specifying a specific tab in
my navigation url. That should work nicely. Just have an interface (it
could be INavigationAware) that the sub-page implements to receive them
and set the flag.

On Fri, Sep 30, 2016, 23:11 Brian Lagunas [email protected] wrote:

You get the VMs from the child Page's BindingContext. Cast them as a
common interface and set the property accordingly.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/PrismLibrary/Prism/issues/650#issuecomment-250893127,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ACLkWmPzfTMbd1sYrw1hTQGZx02y6rKDks5qveuGgaJpZM4I6Iai
.

If we did add a property to an interface to control this, should it be an enum to control the behavior? Assuming the property was called ForwardNavigationParameters

With the following options:

  • ActivePage
  • AllPages

@brianlagunas while having an option for the NavigationService to pass the parameters to the TabbedPages children would be nice there are cases where I could see it being buggy. For instance I have TabbedPages that dynamically set which children should be added based on values either on my object or parameters passed to the TabbedPage.

Also I can think of some scenarios where you may change a value on the object you're working with, and rather than persisting it to a database and then reloading it on the next tab you may prefer to simply pass the updated object.

Okay, so we may have two issues here:

  1. Need a way to optionally pass parameters to the TabbedPaged children (one or all).
  2. Need a way to pass parameters from tab to tab upon a new tab selection.

So for issue 1 we can utilize a TabbedPageNavigationOptions interface with a property to forward the parameters to the children via the INavigationAware interface. This will be opt-in, and may cause a duplicate call to OnNavigatedTo if they have the property set and are explicitly navigating to a tab; for example NavigateAsync("MyTabbedPage/TabA")

If we implement issue 1, we may be creating a source of bugs and confusion.

For issue 2, we need a behavior that will

  • Call a method from the tab being unselected to signify we are changing tabs
  • Provide a method that can request or gather parameters that will be used in the newly selected tab (maybe this method is the same method in the first bullet)
  • Call a method on the newly selected tab and provide the parameters that were provided from the previously selected tab.

If we properly implement issue 2, we may not need to implement issue 1 as issue 2 would essentially provide a consistent way of getting parameters from tab to tab.

Does this sound right?

it sounds about right to me. personally, i tend to lean more toward issue 1, since i would rather the parent know what all children need than for siblings to have know what each other need. but the IMultipageNavigationAware behavior that was gisted earlier has been working well enough for me to just forward everything that was passed in with whatever additions that page makes.

Hello,

I am new to Prism and Xamarin forms. I am trying to implement the functionality to change the active page in a tabbed page in Xamarin Forms. Is there currently any way to support this?

@scorpio2000 this is probably not something Prism will do. Changing tabs from within a tab is not an orthodox navigation function in XF. You can create a custom behavior to achieve this.

@brianlagunas do you mean that we need to add behavior for event like Appearing for Page and then call navigation method in Prism for transfer to required page

`

<NavigationPage Title="Home" > 
  <x:Arguments>
    <views:MainPage>
    </views:MainPage>
  </x:Arguments>
</NavigationPage>
<NavigationPage Title="Catalog">
  <x:Arguments>
    <views:CatalogPage>
    </views:CatalogPage>
  </x:Arguments>
</NavigationPage>


`

How do I handle tap events during tab click?

could you provide a short sample for this case, pls

@polepage your way is working for you?

Hello,
I have one suggestion.

This is when the contents of the tab are NavigationPage.
In many cases, we would like the IMultiPageNavigationAware event in CurrentPage of NavigationPage.

Now the code looks like this:

public class MultiPageNavigationBehavior<T> : BehaviorBase<MultiPage<T>> where T : Page
{
    ...
    protected async Task HandleNavigationToPage( Page page, bool navigatedTo, NavigationParameters parameters )
    {
        await NavigationAwareHandler( page, parameters, navigatedTo );
        await NavigationAwareHandler( page?.BindingContext, parameters, navigatedTo );
    }

Here, I want to change this way.

protected async Task HandleNavigationToPage( Page page, bool navigatedTo, NavigationParameters parameters )
{
    await NavigationAwareHandler( page, parameters, navigatedTo );
    await NavigationAwareHandler( page?.BindingContext, parameters, navigatedTo );

    var navigationPage = page as NavigationPage;
    if (navigationPage != null)
    {
        await NavigationAwareHandler(navigationPage.CurrentPage, parameters, navigatedTo);
        await NavigationAwareHandler(navigationPage.CurrentPage?.BindingContext, parameters, navigatedTo);
    }
}

Let me hear your opinion.

@nuitsjp Good thinking. It is very common the have a NavigationPage within a TabbedPage. If we are dealing with a navigationPage, should we call the methods on the NavigationPage AND the CurrentPage, or just the CurrentPage?
protected async Task HandleNavigationToPage( Page page, bool navigatedTo, NavigationParameters parameters ) { var navigationPage = page as NavigationPage; if (navigationPage != null) { await NavigationAwareHandler(navigationPage.CurrentPage, parameters, navigatedTo); await NavigationAwareHandler(navigationPage.CurrentPage?.BindingContext, parameters, navigatedTo); } else { await NavigationAwareHandler( page, parameters, navigatedTo ); await NavigationAwareHandler( page?.BindingContext, parameters, navigatedTo ); } }

As everyone can see, I am back from vacation and ready to start implementing more features. I plan on releasing another preview soon after we get the EventToCommand, and maybe some other smaller things fixed. So look for this feature to be implemented in preview 3.

@brianlagunas

  • It might be necessary for Customized NavigationPage.
  • No adverse effects on my way (Except for a few performance)

It is unnecessary in the default NavigationPage, but I think that it is better to apply it to both.

@brianlagunas waiting v. 6.3 👍

hi @brianlagunas
do you have an update on when to release this feature? I saw some documentation but not sure when this is going to be released? also is there any example on how to use it?

this hasn't been implemented yet. This will be in the next 6.3 release which currently doesn't have a release date.

ok thanks for the update - i think in the mean time i should create my own behavior according to what's discussed here.

hi Guys,
I implemented the approach of IMultiPageNavigationAware and attached behavior and internal navigation to and from is being raised whenever i change tabs - one question I I have is how to pass navigation parameters between tabs once i click on a tab - since i am not making a direct call to navigate in the VM in I can't pass any parameters from code behind.

any help is appreciated

I haven't really looked at the code, but you could try adding them to the parameters collection in the OnNavigatedFrom. Then they should be passed to the OnNaviatedTo of the target page. Not sure though, as I didn't write the code.

@pasha-o the Gist I created and the code in the PR are designed to give you the same experience as INavigationAware. As previously mentioned you would simply add parameters to the NavigationParameters collection in OnInternalNavigatedFrom and they are automatically passed to OnInternalNavigatedTo the same as INavigationAware. Both OnInternalNavigatedFrom & OnInternalNavigatedTo are called anytime the user taps on a different tab.

Is anyone having problems getting xaml bindings to work when using IActiveAware on children tabs within a tabbedPage?

I have a static ObservableCollection defined in the app.cs class that is being populated after the child page is created. The list view is not populated with the observable collection when I browse the page when the page is accessed via a tab bar click. Note that the OnActive event is firing.

However, the observablecollection correctly binds to the list view when I browse the page via a button click which triggers the INavigationAware interface.

It's like the INavigation call is making a Trigger an binding event, but calls that use the IActiveAware interface calls are not.

I apologize for grammar mistakes. I'm typing from a phone.

I have a static ObservableCollection defined in the app.cs class

This scares me. I would not recommend that.

As of now, INavigationAware does not fire when switching tabs via clicking on tab headers. This is why this issue has been submitted. We need a solution to this, and there is one approach available in this thread, and PR #712 has been submitted which also need to be reviewed. As of now, this has not been implemented.

I wrote INavigationAware when I meant IActiveAware in the first sentence (my bad).

The binding does not trigger binding on an tab press when IActiveAware is used. This is on my on my mac laptop running against the iOS simulator. It works just fine on Android using my PC. I'll sync the code between the machines when I get home and verify.

IActiveAware does nothing special in regards to binding, nor does INavigationAware. The IActiveAware.IsActive property is set after a new Tab selection has been made.

Thanks for the input @dansiegel and @brianlagunas

it worked .

@brianlagunas

We need a solution to this, and there is one approach available in this thread, and PR #712 has been submitted which also need to be reviewed.

it is will be implemented in 6.3 release after your successful review or not?

currently I'm using an approach for switching between tabs based on my custom behavior which is inherited from BehaviorBase<VisualElement> and this behavior was subscribed to TabbedPage on CurrentPageChangedevent.

But this way is not good, cuz, when I want to open a Tab page via NavigationService programmatically, navigation in app is activated twice (1 call by request via NavigationService, 2 call by my custom behavior which is subscribed on CurrentPageChangedevent)

What would be the solution for this issue?
I need to retrieve data when user access a tab page.

I followed the solution below, but it doesn't work for me:
https://github.com/qmatteoq/XamarinForms-Prism/tree/master/DeepNavigation

@riandesign

I followed the solution below, but it doesn't work for me:
https://github.com/qmatteoq/XamarinForms-Prism/tree/master/DeepNavigation

You can found in post by Matteo that him solution is working but it hasn’t been included yet in the current Prism version

@beylkhanovdamir

You can found in post by Matteo that him solution is working but it hasn’t been included yet in the current Prism version

But what would be a solution for now?

@riandesign

I think @brianlagunas must give answer on this question =)
For now I'm temporarily use my custom behavior for it

The plan is to have a solution to tab switching in 6.3 RTM.

@brianlagunas
do you have a planned date for 6.3 RTM?

No. I do not have a date. I work on Prism in my free time in-between my job, family, and other community events. That's why I push previews throughout the process.

@brianlagunas ok, got it. But could you say about your Preview 1 for prism 6.3.0. it's working as well for switching between tabs? cuz according with your release notes, some fixes were marked as Breaking

@beylkhanovdamir no, there is no tab switching solution built into Prism yet. It is still pending.

Where are we on this issue? One of the most important features of Prism Navigation is object construction. Seems like we need a PrismTabbedPage that can handle tab navigation and object construction from the container.

Starting this thread up again. I think the best approach for tab change notifications is to use IActiveAware. Passing parameters between tab selections don't make a lot of sense. If this behavior is desired it can be achieved using either the IEventAggregator, or a class that maintains the state between the tabs. Like a singleton state manager service. When IActiveAware.IsActive is being set, you can set/read the parameters from this service. This also aligns the tab behavior with exactly how Prism for WPF works.

However, propagating parameters passed from the TabbedPage to the CurrentPage does make sense. For example, given I navigate to directly to the TabbedPage:

NavigatAsync("MyTabbedPage?name=Brian");

The first child should also be passed the parameter.

Agree.

Karl

From: Brian Lagunas [mailto:[email protected]]
Sent: Sunday, February 26, 2017 9:12 AM
To: PrismLibrary/Prism Prism@noreply.github.com
Cc: Karl Shifflett kdawg1406@gmail.com; Comment comment@noreply.github.com
Subject: Re: [PrismLibrary/Prism] [XF] Navigating between Tabs (#650)

Starting this thread up again. I think the best approach for tab change notifications is to use IActiveAware. Passing parameters between tab selections don't make a lot of sense. If this behavior is desired it can be achieved using either the IEventAggregator, or a class that maintains the state between the tabs. Like a singleton state manager service.

However, propagating parameters passed from the TabbedPage to the CurrentPage does make sense. For example, given I navigate to directly to the TabbedPage:

NavigatAsync("MyTabbedPage?name=Brian");

The first child should also be passed the parameter.


You are receiving this because you commented.
Reply to this email directly, view it on GitHub https://github.com/PrismLibrary/Prism/issues/650#issuecomment-282558552 , or mute the thread https://github.com/notifications/unsubscribe-auth/AFXQTyvsoSM47v4XlH4jb-SQNitsfhfQks5rgYhNgaJpZM4I6Iai . https://github.com/notifications/beacon/AFXQT7-grWi41ffUSJmFBIlPjWEZEaxaks5rgYhNgaJpZM4I6Iai.gif

@brianlagunas I would say INavigationAware isn't what it was when this thread started. Namely the concept of OnNavigatingTo could allow all children to properly initialize so you could have something like: NavigateAsync("MyTabbedPage?tab=TabA&tab=TabB&name=Brian") and both TabA and TabB could initialize with Brian as a parameter even though only TabA will show up.

Okay, so the real issue is being able to pass parameters to either the first tab or all tabs when navigating to the TabbedPage for the first time. This in combination with IActiveAware should address the majority of TabbedPage issues?

This in combination with IActiveAware should address the majority of TabbedPage issues?

IsActiveChanged handler is not firing when I'm switching between tabs in my app

@brianlagunas

my version of prism is 6.3 pre-2

You are responsible for firing that, not the framework. In the IsActive property setter, you raise that event, similar to how you implement INotifyPropertyChanged.

but what profit in this way? according with your way I need anyways detect when tab was changed, but how I'll define it?

Like I said, in the IsActive property setter, you know if the tab is active or not. Raise it there.

I have been investigating this issue and it's much more complex than I thought it would be. I have a branch that kind of works, but not perfectly.

If you navigate to the TabbedPage with no further navigation segments (NavigateAsync("TabbedPage")) all tabs will invoke the INavAware methods just fine.

However, I cannot prevent the INavAware methods from being invoked twice if you provide a deep link such as NavigateAsync("TabbedPage/NavigationPage/SomeTab"). In this case SomeTab will have it's INavAware methods invoked twice.

Now keep in mind that this would only happen if you are trying to deep link into your TabbedPage.

I am really leaning towards requiring having the propagation of parameters to all the other tabs being the responsibility of the developer. Essentially, this is all that would be required:

    public class MyTabbedPage : TabbedPage, INavigatingAware
    {
        public virtual void OnNavigatingTo(NavigationParameters parameters)
        {
            foreach(var child in Children)
            {
                PageUtilities.OnNavigatingTo(child, parameters);

                if(child is NavigationPage navPage)
                {
                    PageUtilities.OnNavigatingTo(navPage.CurrentPage, parameters);
                }
            }
        }
    }

This would give the developer full control over what tabs get what parameters. Although, it is a little more code that has to be written. We could provide some Gists in the docs to help, or maybe even some VS code snippets.

Thoughts?

i have 2 suggestions:

  • move the if check into the PageUtilities.OnNavigatingTo. No reason for developer to know the distinction there.

    • provide a behavior I could add to my code to "enable" this feature. No need for developer to litter their code with this code. That way it could be enhanced later if necessary.

It would not be wise to place the "if check" in the PageUtilties as that method is specific to invoking methods on the target page. Adding that logic may cause issues in other places the method is used (for example during deep linking). If we provided a behavior, it wouldn't solve the problem we have now... the INavAware methods would be invoked twice when you provide a deep link for a tab (NavigateAsync("TabbedPage/NavigationPage/SomeTab")). By handling it in the code-behind, you can prevent this from happening on a case-by-case basis.

Wouldn't it be easier to add the code to a behavior and use the behavior on a case by case basis. That way it is part if the library, allows developers to add the feature if they wanted it and would better than paying a code snippet everywhere. The behavior could do the if check if you don't want to move it into page utilities. I'd think more people would comfortable adding behaviors instead of posting code. Just my $0.02 and it fits better into the paradigm.

You can't use a behavior in this case because we have to respond to the INavAware methods that are called on the page directly. There is nothing for a behavior to listen to. I also don't think you fully undertand the issue with trying to call the INavAware methods on all tabs in general. There is no way to prevent them from being invoked twice.

Okay, I have made great progress on this issue. This is where we are. I had to change the behavior of the TabbedPage.

NavigateAsync("TabbedPage/SomeTab") will no longer select the "SomeTab" tab of the TabbedPage. Instead it will navigate to the SomeTab page. If you wish to select a tab, you simply set a parameter (yet to be named) like this:

NavigateAsync($"TabbedPage?{KnownNavigationParameters.SelectedTab}=SomeTab").

To select tabs that are nested in a NavigationPage you simply provide a special syntax:

NavigateAsync($"TabbedPage?{KnownNavigationParameters.SelectedTab}=NavigationPage|SomeTab").

This is actually more flexible than what we have now.

Now the new problem. How do we indicate that I want to navigate within a Tab that is wrapped in a navigationPage? For example, right now you can use NavigateAsync("TabbedPage/NavigationPage/SomeTab/SomeOtherTab"). This will have the TabbedPage as the root of the app, and navigate the SomeOtherTab within the current tab.

With this new approach this will not work. I need to know how to determine the intent of the navigation.

Should we use another parameter in the target similar to:

NavigateAsync($"TabbedPage?{KnownNavigationParameters.SelectedTab}=NavigationPage|SomeTab/SomeOtherTab?navWithinTab=true").

Should the default behavior always navigate from the TabbedPage.Current page, or do we default to navigating from the TabbedPage itself?

What are your thoughts?

Another option is to have somewhat of a nested navigation which is indicated by the '|' character:

NavigateAsync($"TabbedPage?selectedTab=NavigationPage|ViewA|ViewB/SomeOtherView")

This means that you will not be able to pass individual parameters to any nested pages. Only to the NavigationPage.CurrentPage

How common is it to do a nested navigation in a tab during a deep link?

Would it not be easiest to just register a page as a TabbedPage Tab? That way when that page is in the URL Prism knows it's a tab and selects that tab.

NavigateAsync("TabbedPage/FirstTabNavPage/ViewA")

The only thing that it wouldn't cover is if you want to navigate a tab without selecting it. For this I think it would make sense to have a bool option for SelectTab or something like that.

Knowing that is a tab is irrelevant. The syntax you provided will no longer work in the new API. You will need to provide a selectedTab parameter.

Are there docs for the new API anywhere so I can mess around with it?

No, I am working on it now.

Awesome! On top of things as always. Let me know when you have them put together i'd love to tryout the new version. The hodge podge of a mess I have now getting the tabs to work is having all sorts of issues.

There is now a breaking change in the TabbedPage behavior. Please read PR #1195 for detail ont he new syntax.

The good news is now all tabs will receive parameters in the OnNavigatingTo method. You no longer have t manually forward these.

Feel free to test this out with the latest 7.0.0.124-ci build on MyGet

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

znakeeye picture znakeeye  ·  7Comments

bartlannoeye picture bartlannoeye  ·  3Comments

weitzhandler picture weitzhandler  ·  6Comments

kant2002 picture kant2002  ·  3Comments

topfunet picture topfunet  ·  5Comments