dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.26k stars 1.76k forks source link

TabbedPage with ToolbarPlacement = Bottom: SelectedTabColor does not move highlight to correct tab if the tab is changed programmatically #18558

Open mcmcelro opened 1 year ago

mcmcelro commented 1 year ago

Description

In a .NET 7 Maui app on Android, if you have a TabbedPage with the tab bar located at the bottom of the page, and you change the current page during a Handler-mapped page changing event method or an OnCurrentPageChanged event handler, the SelectedTabColor highlight will not move to the new CurrentPage, but will instead stay on the actual tab selected (tapped) by the user.

In my application, we have user-level permissions which can block certain tabs from being accessed. We check these permissions in a TabbedViewHandler-derived class:

public MainPageHandler()
{
    Mapper.AppendToMapping(nameof(TabbedPage.CurrentPage), this.BlockIfNeeded);
}

In the BlockIfNeeded method, we check the user's permissions, and if they're denied access, we send them back to their most recent tab. We've tried several different methods of changing the active tab (setting SelectedItem, setting CurrentPage, going into the underlying platform view and calling SetCurrentItem, etc.); in each of these cases, the actual selected tab is updated correctly, but the bottom tab bar continues to highlight the previous (inaccessible) tab.

Steps to Reproduce

  1. Create a new Maui app targeting Android
  2. In App.xaml.cs, instead of creating a Shell page, set the MainPage to a view based on TabbedPage (call it e.g. MainPage)
  3. In MainPage.xaml, add to the opening tag:
xmlns:android="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;assembly=Microsoft.Maui.Controls"
android:TabbedPage.ToolbarPlacement="Bottom"
SelectedTabColor="[anything]"
UnselectedTabColor="[anything]"
  1. Add 3 child NavigationPage pages to MainPage. Set their titles to "Page 1", "Page 2", and "Page 3", with whatever page types embedded that you like.
  2. Add the following handler in the Android platform folder:

    internal class MainPageHandler : TabbedViewHandler
    {
    public MainPageHandler()
    {
        Mapper.AppendToMapping(nameof(TabbedPage.CurrentPage), this.BlockIfNeeded);
    }
    
    private void BlockIfNeeded(ITabbedViewHandler handler, ITabbedView view)
    {
        if (handler.PlatformView == null || handler.VirtualView == null)
        {
            return;
        }
        var virtualView = view as TabbedPage;
        // redirect taps on the second tab to the first tab
        if (((NavigationPage)virtualView.CurrentPage).Title == "Page 2")
        {
            // this should set the selected tab highlight on the first tab but it does not
            var platformView = (handler.PlatformView) as ViewPager2;
            virtualView.CurrentPage = virtualView.Children[0];
            virtualView.SelectedItem = virtualView.Children[0];
            platformView.CurrentItem = 0;
            platformView.SetCurrentItem(0, false);
            platformView.PerformClick();
        }
    }
    }
  3. In MauiProgram.cs, add this handler for the MyTabbedPage class.
  4. Run the application.
  5. Tap the second tab ("Page 2").

Expected result: Page 1 is activated, and the tab for Page 1 gets the SelectedTabColor highlight. All other tabs have the UnselectedTabColor highlight.

Actual result: Page 1 is activated, and the tab for Page 2 gets the SelectedTabColor highlight. All other tabs have the UnselectedTabColor highlight.

Link to public reproduction project repository

https://github.com/mcmcelro/SelectedTabColorBug

Version with bug

7.0.100

Is this a regression from previous behavior?

Yes, this used to work in Xamarin.Forms

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

All compatible Android versions

Did you find any workaround?

No.

In Xamarin.Forms, this was handled using a Renderer which derived from TabbedPageRenderer and NavigationBarView.IOnItemSelectedListener. The method bool NavigationBarView.IOnItemSelectedListener.OnNavigationItemSelected(IMenuItem item) would prevent the highlight from moving to the new tab if false was returned. We have yet to find a way to implement this in Maui.

Relevant log output

N/A
ghost commented 1 year ago

We've added this issue to our backlog, and we will work to address it as time and resources allow. If you have any additional information or questions about this issue, please leave a comment. For additional info about issue management, please read our Triage Process.

PureWeen commented 1 year ago

@mcmcelro can you try this on NET8 and see if it's any better for you? We reworked a little bit of this code in NET8

mcmcelro commented 1 year ago

@PureWeen unfortunately there's no change. I upgraded my sample project to use the latest updates for .NET 8, in the latest version of VS 2022 Professional Preview, and the behavior is the same.

Screen recording: https://github.com/dotnet/maui/assets/44167199/551d65d4-0a18-4b3e-a833-89875eb724c0

In the video, notice that tapping on the tab for Page 2 loads Page 1 - that's intended, since we're blocking the user from going to Page 2. But it's still highlighting the tab for Page 2.

XamlTest commented 1 year ago

Verified this on Visual Studio Enterprise 17.8.0 Preview 7.0(8.0.0-rc.2.9530). Repro on Android 14.0-API34 with below Project: SelectedTabColorBug.zip

image

mcmcelro commented 1 week ago

FYI, this issue still exists in the newly released .NET 9: https://github.com/mcmcelro/SelectedTabColorBug/tree/dotnet9