Prism: [XF] IActiveAware not working when ContentPage (as child of a TabbedPage) is wrapped into a navigationpage

Created on 6 Jun 2017  路  20Comments  路  Source: PrismLibrary/Prism

  • Platform: iOS
  • Prism version: 6.3.0
  • Xamarin Forms version (if applicable): 2.3.4.247

Repro steps

  • Add multiple ContentPages wrapped in a NavigationPage as a child of a TabbedPage
  • Switch between tabs, and the IsActive property isn't called
XF

Most helpful comment

One more note about the MyNavigationPage code:
It seems to set IsActive when the TabbedPage is first loaded, even if the MyNavigationPage isn't the initial page selected.

As a result, IsActive doesn't change when the tab is selected for the first time.

This causes an unexpected behavior, at least in my specific case, because I'm trying to ensure that a Page displaying settings is up-to-date.

All 20 comments

Thanks for reporting this. I'll check it out.

At current it's not designed to. I believe to make this work we'll need to support IActiveAware across all pages.

You could easily make it work for the initial child page, but it becomes a bit more complex how to manage IActiveAware as you add children to the NavigationPage. If you implement a NavigationPage like the following you should get the behavior that you're looking for in the mean time.

public class MyNavigationPage : NavigationPage, IActiveAware
{
    public bool IsActive { get; set; }

    public event EventHandler IsActiveChanged;

    private void OnIsActiveChanged()
    {
        PageUtilities.InvokeViewAndViewModelAction<IActiveAware>(CurrentPage, (obj) => obj.IsActive = IsActive);
    }

    protected override void OnPropertyChanging(string propertyName = null)
    {
        base.OnPropertyChanging(propertyName);

        if(propertyName == nameof(CurrentPage))
        {
            PageUtilities.InvokeViewAndViewModelAction<IActiveAware>(CurrentPage, (obj) => obj.IsActive = false);
        }
    }

    protected override void OnPropertyChanged(string propertyName = null)
    {
        base.OnPropertyChanged(propertyName);

        if(propertyName == nameof(CurrentPage))
        {
            PageUtilities.InvokeViewAndViewModelAction<IActiveAware>(CurrentPage, (obj) => obj.IsActive = true);
        }
    }
}

I'm not sure this is very hard. We just modify the current IActiveAware behavior to check to see if the page is a NavigationPage, and if it is set the IsActive on the NavigainPage.CurrentPage. At least off the top of my head it seems like a simple fix. It may not be the case when I actually start looking into it.

Actually, now I see what you are saying. If you are on a tab, and then navigate within the tab's navigationPage, the new current page has to update the active property on both the previous and current. Or should it? Is this a scenario we want to handle? Maybe this falls into the "custom" scenario and you must manually handle the more complex scenario? Hmmmm....

@brianlagunas if you don't manage the children of the NavigationPage then you might set the initial child page (say ViewA) to IsActive = true, then you push ViewB and the NavigationPage's CurrentPage is ViewB... ViewB doesn't know that it's the active page. Now say that you select a different tab... The Active Aware NavigationPage would have IsActive = false, ViewB was never active anyway, and ViewA still thinks it's active...

So three possible options I see here:

  1. We implement an ActiveAwareNavigationPage like the one shown above and we can provide it as the registered NavigationPage in the templates.
  2. We implement a Behavior that could be added to any NavigationPage that implements IActiveAware
  3. We combine the two strategies so that we have a NavigationPage that only implements IActiveAware. If someone wants to implement it differently they could still bring their own custom NavigationPage and the Behavior would still work.

Keep in mind, the primary use of this feature is going to be in a TabbedPage scenario. So this means that the tabs will most likely be defined in XAML. I'm curious if we should take the behavior approach, and automatically apply it to the NavigationPage tab. I am looking the approach with the least amount of effort on the developers part, and the highest amount of maintainability on ours.

That is a valid point, though one of he goals for v7 was the creation of tabs through the Navigation Parameters

Sure, but this approach will work in all scenarios since we would be adding the behavior to a NavigationPage tab regardless of how it was added.

True... there are benefits and drawbacks in my opinion to all three methods.

  1. Having a page we can maintain the functionality of is a huge benefit. Plus it would be easier to use as you just need to use that special NavigationPage.
  2. Explicitly adding the behavior in XAML is more verbose and less magic...
  3. Providing flexibility is always a nice thing IMO

Just to add for anyone looking for an interim solution:
If using @dansiegel's MyNavigationPage in XAML, then you'll need to add the following constructor to set a Page through XAML Arguments:
c# public MyNavigationPage(Page root) : base(root) { }

One more note about the MyNavigationPage code:
It seems to set IsActive when the TabbedPage is first loaded, even if the MyNavigationPage isn't the initial page selected.

As a result, IsActive doesn't change when the tab is selected for the first time.

This causes an unexpected behavior, at least in my specific case, because I'm trying to ensure that a Page displaying settings is up-to-date.

@cpboyd after implementing the custom navigation page suggested by @dansiegel, the navigation service won't navigate the child view and I'm seeing this in the ouput:

"pushViewController:animated: called on while an existing transition or presentation is occurring; the navigation stack will not be updated."

any thoughts?

@jgtaveras What is the child view that you're referring to?

A child of the MyNavigationPage or the MyNavigationPage itself (which is, presumably, a child of a TabbedPage or some other MultiPage)?

How has was the MyNavigationPage instantiated? Through XAML? If so, does it have a root page defined?

Lastly, how are you performing the navigation itself? Have you registered the page for navigation (assuming it's not the root page declared through XAML)?

I'm able to use Prism's NavigationService (as described here) to navigate from my XAML-declared root page to other pages that have been registered for navigation. (And IsActive changes appropriately on the root page's view model.)

This is what I'm referring:

  • TabbedPage (defined in Xaml)

    • CustomNavigationPage



      • Page A (Tap on list item to go to View B)


      • Page B (this Page it's never shown)



The Root TabbedPage is defined in Xaml as follows

<TabbedPage>
   <utils:CustomNavigationPage>
        <x:Arguments>
            <local:ApartmentsListPage/>
        </x:Arguments>
    </utils:CustomNavigationPage>
    <local:PaymentsPage/>
    <local:SettingsPage/>
</TabbedPage>

*Note, namespaces, titles and icons removed for simplicity

Navigation to View B is being done through a command in the view model using the navigation service

private async Task OnGoToDetailsCommand(object obj) 
{ 
await _navigationService.NavigateAsync("ApartmentDetailPage"); 
}

And yes all page had been registered correctly

     Container.RegisterTypeForNavigation<SettingsPage>();
     Container.RegisterTypeForNavigation<ApartmentDetailPage>();        
     Container.RegisterTypeForNavigation<ApartmentsListPage>();
     Container.RegisterTypeForNavigation<HomePage>();
     Container.RegisterTypeForNavigation<PaymentsPage>();

It looks like you've basically done what I've done, so I have doubts that this is a Prism issue.

A quick Google returns the following in the top 4 results:
On Xamarin forums
On StackOverflow

Both of those seem to indicate issues with UIAlertControllers.
Are you using modal windows or a "dialog" library (like Acr.UserDialogs)?

thx for the support @cpboyd indeed Prism wasn't the problem, Acr.UserDialogs was the one to blame in this case

@cpboyd Did you find a way around the page being set as Active even when its first loaded? I'm getting the same issue.

This has been fixed in the latest CI build. Please test it out on MyGet.

Follow the instructions here: #1164

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

brianlagunas picture brianlagunas  路  5Comments

joacar picture joacar  路  6Comments

kant2002 picture kant2002  路  3Comments

BrianBeck44 picture BrianBeck44  路  3Comments