Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
reinierkops opened this issue Jun 6, 2017 · 20 comments
Labels

Comments

@reinierkops
Copy link

  • 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
@brianlagunas brianlagunas added the XF label Jun 6, 2017
@brianlagunas
Copy link
Member

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

@brianlagunas brianlagunas added this to the Prism 7.0 milestone Jun 6, 2017
@dansiegel
Copy link
Member

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);
        }
    }
}

@brianlagunas
Copy link
Member

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.

@brianlagunas
Copy link
Member

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....

@dansiegel
Copy link
Member

@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...

@dansiegel
Copy link
Member

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.

@brianlagunas
Copy link
Member

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.

@dansiegel
Copy link
Member

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

@brianlagunas
Copy link
Member

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.

@dansiegel
Copy link
Member

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

@cpboyd
Copy link

cpboyd commented Jul 15, 2017

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:

        public MyNavigationPage(Page root) : base(root) { }

@cpboyd
Copy link

cpboyd commented Jul 15, 2017

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.

@jgtaveras
Copy link

@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 <Xamarin_Forms_Platform_iOS_NavigationRenderer 0x7fcebf018400> while an existing transition or presentation is occurring; the navigation stack will not be updated."

any thoughts?

@cpboyd
Copy link

cpboyd commented Jul 16, 2017

@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.)

@jgtaveras
Copy link

jgtaveras commented Jul 16, 2017

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>();

@cpboyd
Copy link

cpboyd commented Jul 17, 2017

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)?

@jgtaveras
Copy link

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

@ghost
Copy link

ghost commented Aug 2, 2017

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

@brianlagunas
Copy link
Member

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

Follow the instructions here: #1164

@lock
Copy link

lock bot commented Jan 30, 2020

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.

@lock lock bot locked as resolved and limited conversation to collaborators Jan 30, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

5 participants