EdgeNavigator and MapView "conflict"

The following is a full working stipped-out version of my app that you may copy/paste. Everything works fine but there’s still a issue I’m not able to solve. The app has three pages and a right side sidebar which can be opened by a hamburger icon. The problem is that the map is shown inside the sidebar after the back button is pressed. This happens only when the app is previewed on a Android device and not inside the Designer.

Fuse 1.3.0 (build 14520)
Window 10 Pro

How to reproduce: (Steps 1 to 4 test that the normal behaviour works fine)

  1. Preview the app on a Android device

  2. Open/close the sidebar by clicking the hamburger icon. OK.

  3. Click SecondPage. Open/close the sidebar. Click the back button. OK.

  4. Click Map. Click the back button. OK.

  5. Now open the sidebar. The map is shown inside the sidebar. NOT OK.

  6. Click SecondPage. Open the sidebar. The map is still shown. The app must be closed to remove the map. NOT OK.

  7. Click again the Map. Open the sidebar. The map is NOT shown. OK.

Notice that the map inside the sidebar does not fill it entirely. A space equal to the height of the header is left out.

MAINVIEW.UX

<App>

    <Android.StatusBarConfig Color="#000000" IsVisible="True" />
    <iOS.StatusBarConfig Style="Light" Animation="Slide" IsVisible="True" />

    <JavaScript>
        var Observable      = require("FuseJS/Observable");
        
        var sidebarOpen     = Observable(false);


        module.exports = {
            sidebarOpen:        sidebarOpen,
            setSidebarOpen:     setSidebarOpen,
            setSidebarClosed:   setSidebarClosed,
            closeSidebar:       closeSidebar,
            clickMap:           gotoMap,
            clickSecondPage:    gotoSecondPage,
        }
        
        function setSidebarOpen() {
            sidebarOpen.value = true;
        };

        function setSidebarClosed() {
            sidebarOpen.value = false;            
        };

        function closeSidebar() {
            EdgeNavigator.dismiss();
        };    

        function gotoMap() {
            EdgeNavigator.dismiss();
            router.push("map");
        };

        function gotoSecondPage() {
            EdgeNavigator.dismiss();
            router.push("secondpage");
        };

    </JavaScript>

    <Router ux:Name="router" />

    <ClientPanel>  
        <EdgeNavigator ux:Name="EdgeNavigator">

            <!-- SIDEBAR -->
            <Sidebar Width="180" ux:Name="menu" Edge="Right">
                <ActivatingAnimation>
                    <Change mainAppTranslation.X="-180" />
                    <Callback Handler="{setSidebarOpen}" />
                </ActivatingAnimation>
                <WhileInactive>
                    <Callback Handler="{setSidebarClosed}" />
                </WhileInactive>
            </Sidebar>

            <!-- App content -->
            <DockPanel ux:Name="content" >
                <Translation ux:Name="mainAppTranslation" />

                    <!-- Header -->             
                    <DockPanel Dock="Top" Height="56">
                        <!-- Hambuger icon -->
                        <Panel Height="32" Width="32" HitTestMode="LocalBounds" Alignment="Right">
                            <WhileTrue Value="{sidebarOpen}">
                                <Clicked>
                                    <NavigateTo Target="content" />
                                </Clicked>
                            </WhileTrue>
                            <WhileFalse Value="{sidebarOpen}">
                                <Clicked>
                                    <NavigateTo Target="menu" />
                                </Clicked>
                            </WhileFalse>

                            <!-- Draw Hamburger icon -->                     
                            <Rectangle ux:Name="topRectangle" Height="2" Width="22" Margin="0,0,12,0" Color="#FFFFFF">
                                <Translation Y="-5" ux:Name="topMenuTranslation" />
                                <Rotation ux:Name="topMenuRotation" />
                            </Rectangle>
                            <Rectangle ux:Name="middleRectangle" Height="2" Width="22" Margin="0,0,12,0" Color="#FFFFFF" />
                            <Rectangle ux:Name="bottomRectangle" Height="2" Width="22" Margin="0,0,12,0" Color="#FFFFFF">           
                                <Translation Y="5" ux:Name="bottomMenuTranslation" />
                                <Rotation ux:Name="bottomMenuRotation" />
                            </Rectangle>
                        </Panel>
                        
                         <!-- Draw Header --> 
                        <StackPanel Color="#AC0000" >
                            <Shadow Size="3" Distance="2" />
                            <Text Value="myApp" Color="#ECEFF1" FontSize="13" Alignment="TopCenter" Margin="0,8,0,0" />
                            <Text ux:Name="pageName" Value="" Color="#ECEFF1" FontSize="20" Alignment="Center" Margin="0,0,0,8" />
                        </StackPanel>
                    </DockPanel>


                <!-- Navigator -->
                <Navigator DefaultPath="home">
                    <HomePage ux:Template="home" router="router">
                        <Activated>
                            <Set Target="pageName.Value" Value="HOME"/>
                        </Activated>
                    </HomePage>
                    <SecondPage ux:Template="secondpage" router="router">
                        <Activated>
                            <Set Target="pageName.Value" Value="SECOND PAGE"/>
                        </Activated>
                    </SecondPage>
                    <MapPage ux:Template="map" router="router">
                        <Activated>
                            <Set Target="pageName.Value" Value="MAP"/>
                        </Activated>
                    </MapPage>
                </Navigator>

            </DockPanel>
        </EdgeNavigator>

    </ClientPanel>


    <StackPanel ux:Class="Sidebar" Color="#7092BE">
        <Text Value="Sidebar" Color="#FFFFFF" TextAlignment="Center"/>
    </StackPanel>

</App>

HOMEPAGE.UX

    <Page ux:Class="HomePage">
        <Router ux:Dependency="router" />
        <StackPanel Alignment="Center">
            <Button Text="Second page">
                <Clicked Handler="{clickSecondPage}" />
            </Button>
            <Button Text="Map">
                <Clicked Handler="{clickMap}" />
            </Button>
        </StackPanel>
    </Page>

SECONDPAGE.UX

    <Page ux:Class="SecondPage">
        <Router ux:Dependency="router" />
        <Text Value="Second page" Alignment="Center"/>
    </Page>

MAPPAGE.UX

    <Page ux:Class="MapPage">
        <Router ux:Dependency="router" />
        <DockPanel>
            <NativeViewHost>
                <MapView Latitude="59.911567" Longitude="10.741030" Zoom="15">
                    <MapMarker Latitude="59.911567" Longitude="10.741030" />
                </MapView>
            </NativeViewHost>
        </DockPanel>
    </Page>

The .unoproj file with a reference to Fuse.Maps and a Google Maps key is needed.

Right, so this happens because of a combination of two things: one, a NativeViewHost always is on top of GL components. Two, the spawned instance of MapPage remains active in case you may want to go to that page again. And because of the default Transition (which is “slide in from the right”), it happens to linger on the right side of the screen - exactly where the sidebar lives. So when the Translation kicks in to reveal the sidebar, the NativeViewHost slides in view, too.

The simplest solution then is to get rid of that instance as you navigate away from the MapPage, by adding Remove="Released" to it:

<MapPage ux:Template="map" router="router" Remove="Released">

Hope this helps!

Hi Uldis,

Remove="Released" worked perfectly but there’s an other annoying behaviour:

  1. Map page is open

  2. Open slidebar

  3. Click Back button

  4. The Map page will slide to the right (as all the pages do) but it will slide inside/over the opened sidebar and not under it.

I thought I could solve it by placing…

<OnBackButton>
    <Callback Handler="{closeSidebar}" /> 
</OnBackButton>

…inside <Sidebar> and close the sidebar before the Map page slides to the right, but OnBackButton trigger both the sidebar (that will close) and the page (that will slide to the right) revealing the previous page.

As I noted above:

a NativeViewHost always is on top of GL components

so the only way to Z-order things when using one is to put them all inside of one. In your case this would mean putting all of your app inside of a NativeViewHost, which might cause other issues, since not all GL components / triggers / animations are supported inside of a NativeViewHost.

As for the right solution here, I think that pressing the back button when the sidebar is open should only close the sidebar. You should then wrap the OnBackButton inside of a WhileTrue which checks the state of the sidebar, and either call {closeSidebar}, or perform goBack when it’s closed.

Unfortunately I already tried a number of times to make only the sidebar close using the Back button and I also tried with WhileTrue as you suggested but it still trigger both the sidebar and the page. Here’s <Sidebar> to which I added WhileTrue without success.

 <Sidebar Width="180" ux:Name="menu" Edge="Right">
    <ActivatingAnimation>
        <Change mainAppTranslation.X="-180" />
        <Callback Handler="{setSidebarOpen}" />
    </ActivatingAnimation>
    <WhileInactive>
        <Callback Handler="{setSidebarClosed}" />
    </WhileInactive>
    <WhileTrue Value="{sidebarOpen}">
        <OnBackButton>
            <Callback Handler="{closeSidebar}" /> 
        </OnBackButton>
    </WhileTrue>
</Sidebar>

Thank you for the patience.

Well, you need to sort of intercept the back button click event. There have been similar use cases covered before, so please check if the tip in this post helps you.

Done!! Thank you.

<!-- SIDEBAR -->
<Sidebar Width="180" ux:Name="menu" Edge="Right">
    <ActivatingAnimation>
        <Change mainAppTranslation.X="-180" />
        <Callback Handler="{setSidebarOpen}" />
    </ActivatingAnimation>
    <WhileInactive>
        <Callback Handler="{setSidebarClosed}" />
    </WhileInactive>
    
    <WhileCanGoBack Invert="true">
        <WhileTrue Value="{sidebarOpen}">
            <Change router.BackButtonAction="None" />
        </WhileTrue>
        <OnBackButton>
            <Callback Handler="{closeSidebar}" />
        </OnBackButton>
    </WhileCanGoBack>

</Sidebar>