Shell - Disable navigation when busy

marcmidmarcmid Member ✭✭

Hello,
I have a shell with different tabs, each tab with each content page and ViewModel. In some pages I do a request to an API to obtain some data, the thing is I want to disable the tab navigation while I wait for the response.

I have the BaseViewModel class from which I inherit in every ViewModel, and before using shell I used the IsBusy property to control the navigation and the user interaction.

I know there is the OnNavigating(object sender, ShellNavigatingEventArgs e) but I don't know how to get If the application IsBusy without using and static object which I would like to avoid.

Thanks

Tagged:

Answers

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭
    edited June 12

    In my non-shell apps I cheat.
    When I do a long-running operation like you describe... Where I consider the app to be 'busy' in a way that I don't want the user tapping on stuff... I push a rg.plugins.popup popup with an ActivityIndicator and some text like "Getting your records..." or some such.

    The popup tells the user there is a reason for the delay and that its not just frozen.
    It also covers the page with the message in the middle and semi-transparant background so they can't tap stuff and hurt themselves.

    Also we disable the [BACK] button on the device, so their own way to navigate is with the controls the app provides. But it's a corporate enterprise app so we have the right to do that - unlike an app to the public.

  • marcmidmarcmid Member ✭✭
    edited June 12

    Hi @ClintStLaurent I do this with my own popup, but the problem is that its specific to each ContentPage and ViewModel and only disables its content but not the tab navigation of the shell.

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    but the problem is that Is specific to each ContentPage and ViewModel

    Well... You've identified the problem. So there's no problem.

    Our popups are not page specific. They can be called from any place. And they are full screen, so they cover the navigation area.
    Scanner starts scanning - popup
    Alert there is a problem with the engine - popup
    etc.

    The popups are part of a system the app uses. Not part of a page.

  • marcmidmarcmid Member ✭✭

    So, how I can control the tab navigation inside the ViewModel, or I shouldn't do it?

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    I'm not experienced with the new Shell.
    But based on past experience within Xamarin I'm going to bet you can't do much with it.
    This is an example where using out-of-the-box mechanisms is fast to get an app stubbed, but limiting to what it provides.

    Even in our non-shell app we don't use a simple page-called navigation. We created our own NavigationManager where we can check for permissions... is there a priority alert on screen... is there a condition to be met... Before the request for navigation is even handled. Then on top of that all the navigation requests are queued up.

    Under normal conditions navigation happens as fast as they are requested. But in some cases we can clean up the request queue. For example: User rapid fire bangs on a button. Well, you don't want 15 navigation to the same page, right? Or they hit [BACK] but haven't finished all the requirements of that page... So throw up an "Are you sure?" popup first.

    The more you rely on the built-in, the less actual control you have.

  • marcmidmarcmid Member ✭✭

    Is there a way to control the navigation tabs? Because when doing the following:

    void OnNavigating(object sender, ShellNavigatingEventArgs e)
    {
        // If it's the first time go to config
        if (Settings.Current.FirstRun)
            Shell.Current.GoToAsync("config");
    }
    

    The application crashes, and if I do this on the method OnAppearing() of the other ContentPages it works but the tab of configuration is not selected.

  • LucasZhangLucasZhang Member, Xamarin Team Xamurai
    edited June 17

    Can you provide a sample which contains the issue so that I can test it on my side :)

  • marcmidmarcmid Member ✭✭

    Hi @LucasZhang,
    I already solved this issue, I was stupid and In the appShell I had:

    <TabBar >
        <Tab Title="Configuració">
            <ShellContent Route="config"
                                    Style="{StaticResource GeneralShell}"
                                    Title="Configuració"
                                    ContentTemplate="{DataTemplate views:ConfigPage}" />
            </Tab>        
        </TabBar>
    

    And in the navigation I was doing the following:

    ShellNavigationState target = new ShellNavigationState("//config");
    await Shell.Current.GoToAsync(target.Location, animate);
    

    Obviously it wasn't working because "//config" wasn't a correct route. After setting the Route property in the TabBar and Tab and putting the complete route it worked:

    <TabBar  Route="tabBar" >
            <Tab Route="tabConfig" Title="Configuració">
                <ShellContent Route="config"
                              Style="{StaticResource GeneralShell}"
                              Title="Configuració"
                              ContentTemplate="{DataTemplate views:ConfigPage}" />
            </Tab>        
        </TabBar>
    
    ShellNavigationState target = new ShellNavigationState("//tabBar/tabConfig/config");
    await Shell.Current.GoToAsync(target.Location, animate);`
    
  • marcmidmarcmid Member ✭✭

    I ended up doing something similar to what @ClintStLaurent told me to manage navigation, I will post it when I have time, but I run into another problem. When the app boots I want to start an specific tab if a certain condition is met. The problem Is that when I do the:

    await Shell.Current.GoToAsync(target.Location, animate);
    

    Shell.Current is null and the app crashes. Is there a way to change the default starting tab or to redirect(navigate) to another page when the app is starting?

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    Where are you calling that? SOunds like you just need to move the call later in the startup process

  • marcmidmarcmid Member ✭✭

    I made a manager for the navigation of my app, I try to do the redirection in the AppShell OnNavigating:

            async void OnNavigating(object sender, ShellNavigatingEventArgs e)
            {
                NavigationBase nav = NavigationBase.ParseNavigation(e);
                if (nav != null)
                    await nav.Navigate();
            }
    

    The important part of NavigationBase:

        public static NavigationBase ParseNavigation(ShellNavigatingEventArgs e)
            {
                try
                {
                    NavigationBase navObj = null;
    
                    if (e == null)
                        return null;
    
                    string targetLocation = e.Target.Location.ToString();
    
                    switch (targetLocation)
                    {
                        case ConfigRoute:
                            navObj = new NavigationConfig(e);
                            break;
                        case MarcatgeRoute:
                            navObj = new NavigationMarcatge(e);
                            break;
                        case GestioRoute:
                            navObj = new NavigationGestio(e);
                            break;
                        default:
                            break;
                    }
                    return navObj;
                }
                catch (Exception ex)
                {
                    return null;
                }
            }
    
            public NavigationBase(ShellNavigatingEventArgs e)
            {
                NavigationEvents = e;
                IsStartingNavigation = e?.Current == null;
            }
    

    And in the NavigationGestio:

            public override async Task Navigate()
            {
                if (!BeginNavigation())
                    return;
    
                ShellNavigationState shNavState = null;
    
                // IsStartingNavigation és true vol dir que és al obrir l'app, si no és el primer run hem d'anar 
                // a marcatges
                if (IsStartingNavigation && !Settings.Current.FirstRun)
                    shNavState = new ShellNavigationState(MarcatgeRoute);
    
                await EndNavigation(shNavState);
            }
    

    Where the BeginNavigation checks that the application is not busy and is not already navigating, also it checks that the targe route where I'm trying to navigate is no the current route. The EndNavigation only sets to false the IsNavigating property and redirects, if not null, to the route specified by shNavState, by executing:

                await Shell.Current.GoToAsync(target.Location, animate);
    

    I could solve the issue redirecting on the OnAppearing method of The ConfigPage, but then I won't have all the navigation managed on the OnNavigating of the AppShell to have it centralized.

    PS: I'm sure my implementation is not the best and there may be other ways to do it better so I'm open to suggestions.

    Thanks!

Sign In or Register to comment.