xamarin – CollectionView SelectionChange event not working properly in Android (.Net MAUI)

I’m trying to create custom tabs control in .Net MAUI, for that, I had first tried it with ScrollView and BindableStackLayout control but in that, I’m facing a problem.

Reported here Custom tabs with ScrollView bug

So, as an alternative approach or work-around, I have tried to develop the same Tabs control using CollectionView.

This alternative approach is working fine in iOS but not working properly in Android.

There is one problem that is common in both Android and iOS. I have taken BoxView control as an Indicator for the selected tab. That I’m going to show only for the Selected tab but this just shows in the first tab, when I click on other tabs the tabs get changed but it does not get hidden from the first tab and get visible in the other selected tab.

I have used the visual state manager with white color for the selected state because it gives looks like an indicator which I’,m trying to create using BoxView. But this also shows Selected item for Android only when that view gets loads for iOS I have to select the tab first then only it shows the selected color there.

Here is what I have done:

MainPage.xaml

<ContentPage.Content>

    <Grid RowDefinitions="50, *" RowSpacing="0">

        <CollectionView x:Name="TabsView"
                        Grid.Row="0"
                        ItemsSource="{Binding Tabs,Mode=TwoWay}"
                        ItemsUpdatingScrollMode="KeepItemsInView"
                        ItemSizingStrategy="MeasureAllItems"
                        SelectedItem="{Binding SelectedTab}"
                        SelectionChangedCommand="{Binding Path=BindingContext.TabChangedCommand,Source={x:Reference TabsView}}"
                        SelectionChangedCommandParameter="{Binding SelectedTab}"
                        SelectionMode="Single">

            <CollectionView.ItemsLayout>
                <LinearItemsLayout Orientation="Horizontal"/>
            </CollectionView.ItemsLayout>

            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <Grid RowSpacing="0" RowDefinitions="*, 3">
                        
                        <Label Grid.Row="0"
                                    Text="{Binding TabTitle}"
                                    TextColor="White"
                                    BackgroundColor="navy"
                                    Padding="20,0"
                                    VerticalTextAlignment="Center"
                                    HorizontalTextAlignment="Center"
                                    FontSize="12" />
                        <BoxView Grid.Row="1" Color="{Binding BoxColor}"
                                    IsVisible="{Binding IsSelected}"/>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup Name="CommonStates">
                                <VisualState Name="Normal"/>
                                <VisualState Name="Selected">
                                    <VisualState.Setters>
                                        <Setter Property="BackgroundColor" Value="White" />
                                    </VisualState.Setters>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                    </Grid>
                </DataTemplate>

            </CollectionView.ItemTemplate>
        </CollectionView>

        
        <tabs:ParentRecordTabView Grid.Row="1"
                                    IsVisible="{Binding IsParentRecordTabVisible}"
                                    VerticalOptions="FillAndExpand"/>

        <tabs:AdditionalInfoTabView Grid.Row="1"
                                    IsVisible="{Binding IsAdditionalInfoTabVisible}"
                                    VerticalOptions="FillAndExpand" />
        


    </Grid>

</ContentPage.Content>

MainPageViewModel.cs

public class MainPageViewModel : BaseViewModel
{
    public MainPageViewModel()
    {
        GetTabs();
    }

    private bool _isParentRecordTabVisible = true;
    private bool _isAdditionalInfoTabVisible;

    private ObservableCollection<TabViewModel> _tabs { get; set; }
    private TabViewModel _selectedTab { get; set; }

    public bool IsParentRecordTabVisible
    {
        get => _isParentRecordTabVisible;
        set { _isParentRecordTabVisible = value; OnPropertyChanged(nameof(IsParentRecordTabVisible)); }
    }

    public bool IsAdditionalInfoTabVisible
    {
        get => _isAdditionalInfoTabVisible;
        set { _isAdditionalInfoTabVisible = value; OnPropertyChanged(nameof(IsAdditionalInfoTabVisible)); }
    }

    public ObservableCollection<TabViewModel> Tabs
    {
        get => _tabs;
        set { _tabs = value; OnPropertyChanged(nameof(Tabs)); }
    }

    public TabViewModel SelectedTab
    {
        get => _selectedTab;
        set
        {
            _selectedTab = value;
            OnPropertyChanged(nameof(SelectedTab));
        }
    }

    public ICommand TabChangedCommand { get { return new Command<TabViewModel>(ChangeTabClick); } }

    private void GetTabs()
    {
        Tabs = new ObservableCollection<TabViewModel>();
        Tabs.Add(new TabViewModel { TabId = 1, IsSelected = true, TabTitle = "Parent record" });
        Tabs.Add(new TabViewModel { TabId = 2, TabTitle = "Additional Info" });
        Tabs.Add(new TabViewModel { TabId = 3, TabTitle = "Contacts" });
        Tabs.Add(new TabViewModel { TabId = 4, TabTitle = "Previous inspections" });
        Tabs.Add(new TabViewModel { TabId = 5, TabTitle = "Attachments" });

        SelectedTab = Tabs.FirstOrDefault();
    }

    public void ChangeTabClick(TabViewModel tab)
    {
        Tabs.All((arg) =>
        {
            if (arg.TabId == tab.TabId)
            {
                arg.IsSelected = true;
            }
            else
            {
                arg.IsSelected = false;
            }
            return true;
        });
        SelectedTab = Tabs.Where(t => t.IsSelected == true).FirstOrDefault();

        switch (SelectedTab.TabId)
        {
            case 1:
                IsParentRecordTabVisible = true;
                IsAdditionalInfoTabVisible = false;
                break;
            case 2:
                IsParentRecordTabVisible = false;
                IsAdditionalInfoTabVisible = true;
                break;
        }
    }
}

TabViewModel.cs

public class TabViewModel : BaseViewModel
{
    private bool _IsSelected;
    public bool IsSelected
    {
        get { return _IsSelected; }
        set
        {
            _IsSelected = value;
            OnPropertyChanged(nameof(IsSelected));
        }
    }

    private int _TabId;
    public int TabId
    {
        get { return _TabId; }
        set
        {
            _TabId = value;
            OnPropertyChanged(nameof(TabId));
        }
    }

    private string _TabTitle;
    public string TabTitle
    {
        get { return _TabTitle; }
        set
        {
            _TabTitle = value;
            OnPropertyChanged(nameof(TabTitle));
        }
    }
}

Note: This same approach again works fine in Xamarin.Forms (Visual Studio 2019), this just not working in MAUI, so does anyone notice something like this?

How to Reproduce error: check github

Leave a Comment