Kevin Dodd
Kevin Dodd

Reputation: 63

How can I populate multiple Tabs with the same page content but a different binding source in .Net MAUI Shell and MVVM

I am developing an App that will display data for each day of the week , Monday to Sunday I would like each day to be a separate tab so you can swipe through the days, Currently i am using a Tab that adds 7 different shellContent pages each one is exactly the same and is bound to a different observable collection in the viewmodel. This does work but there are 2 main issues I am having. 1) as I have created 7 different content pages if i change the layout in one i have to then manually change the layout for all of them, which is tedious and error prone. 2) every time i open a tab it is creating a new instance of the viewmodel and these need to get loaded into the tab page on each swipe which is jarring for the user.

I looked at TabbedPage and this seems to be what i want, I can add one Datatemplate and bind the tabs using the item source however i cannont use TabbedBar with Shell.

How can i resuse the same content for each tab and ideally have all the pages loaded with their data before the user has to swipe. I am ok with the initial loading being a little slower as i can but a loading indicator on it.

currently I am adding them like this

<FlyoutItem Title="Weekly Rota" Icon="team.png">
        <Tab>
            <ShellContent Title="Monday" Shell.TabBarIsVisible="False" ContentTemplate="{DataTemplate startupPages:WeeklyRotaMonday}"/>
            <ShellContent Title="Tuesday" Shell.TabBarIsVisible="False" ContentTemplate="{DataTemplate startupPages:WeeklyRotaTuesday}" />
            <ShellContent Title="Wednesday" Shell.TabBarIsVisible="False" ContentTemplate="{DataTemplate startupPages:WeeklyRotaWednesday}" />
            <ShellContent Title="Thursday" Shell.TabBarIsVisible="False" ContentTemplate="{DataTemplate startupPages:WeeklyRotaThursday}" />
            <ShellContent Title="Friday" Shell.TabBarIsVisible="False" ContentTemplate="{DataTemplate startupPages:WeeklyRotaFriday}" />
            <ShellContent Title="Saturday" Shell.TabBarIsVisible="False" ContentTemplate="{DataTemplate startupPages:WeeklyRotaSaturday}" />
            <ShellContent Title="Sunday" Shell.TabBarIsVisible="False" ContentTemplate="{DataTemplate startupPages:WeeklyRotaSunday}"  />
        </Tab>
    </FlyoutItem>

the ViewModel has the following Code

        public ObservableCollection<DailyData> MondayList { get; set; } = new();
        public ObservableCollection<DailyData> TuesdayList { get; } = new();
        public ObservableCollection<DailyData> WedensdayList { get; } = new();

The data is then bound to each page via the codebehind of the page

    public WeeklyRotaFriday(WeeklyRotaViewModel viewModel)
    {
        InitializeComponent();
        this.BindingContext = viewModel;
        viewModel.GetDailyDataCommand.Execute(DayOfWeek.Friday);
    }

I understand this approach is far from ideal but I am very new to App Development and and have not been able to find any examples that do exactly what i am trying to do. It does function but there must be a better way to do it.

Upvotes: 3

Views: 1183

Answers (1)

Jianwei Sun - MSFT
Jianwei Sun - MSFT

Reputation: 4302

You can use only one page and achieve to change content for different day by override OnParentChanging method.

Add Route to every ShellContent:

<FlyoutItem>
    <Tab>
        <ShellContent Title="Monday" Route="Monday" Shell.TabBarIsVisible="False" ContentTemplate="{DataTemplate local:MainPage}"/>
        <ShellContent Title="Tuesday" Route="Tuesday" Shell.TabBarIsVisible="False" ContentTemplate="{DataTemplate local:MainPage}" />
        <ShellContent Title="Wednesday" Route="Wednesday" Shell.TabBarIsVisible="False" ContentTemplate="{DataTemplate local:MainPage}" />
        <ShellContent Title="Thursday" Route="Thursday" Shell.TabBarIsVisible="False" ContentTemplate="{DataTemplate local:MainPage}" />
        <ShellContent Title="Friday" Route="Friday" Shell.TabBarIsVisible="False" ContentTemplate="{DataTemplate local:MainPage}" />
        <ShellContent Title="Saturday" Route="Saturday" Shell.TabBarIsVisible="False" ContentTemplate="{DataTemplate local:MainPage}" />
        <ShellContent Title="Sunday" Route="Sunday" Shell.TabBarIsVisible="False" ContentTemplate="{DataTemplate local:MainPage}" />
    </Tab>
</FlyoutItem>

Page.xaml:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="MauiApp2.MainPage">
    <ScrollView>
        <VerticalStackLayout>
            <Label Text="{Binding Selection}"/>
            <CollectionView ItemsSource="{Binding WeekList}">
                <CollectionView.ItemTemplate>
                    <DataTemplate>
                        <Image Source="{Binding Name}"/>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>

Override OnParentChanging in Page.xaml.cs:

public partial class MainPage : ContentPage
{
    VM VM = new VM();
    public MainPage()
    {
        InitializeComponent();
        BindingContext = VM;

    }
    protected override void
    OnParentChanging(ParentChangingEventArgs args)
    {
        base.OnParentChanging(args);
        var shellContent = args.NewParent as ShellContent;
        var route = shellContent.Route;
        switch (route)
        {
            case "Monday":
                VM.Selection = "Wednesday";
                break;
            case "Tuesday":
                VM.Selection = "Tuesday";
                VM.WeekList = VM.NameIdList1;
                break;
            case "Wednesday":
                VM.Selection = "Wednesday";
                VM.WeekList = VM.NameIdList2;
                break;
            case "Thursday":
                VM.Selection = "Thursday";
                break;
            case "Friday":
                VM.Selection = "Friday";
                break;
            case "Saturday":
                VM.Selection = "Saturday";
                break;
            case "Sunday":
                VM.Selection = "Sunday";
                break;
            default: break;
        }
    }
}

ViewModel:

public class VM : INotifyPropertyChanged
{
    public ObservableCollection<NameId> WeekList { get; set; } = new();
    public ObservableCollection<NameId> NameIdList1 { get; set; } 
    = new ObservableCollection<NameId> 
    { 
        new NameId { Id = "Id A", Name = "Name A" }, 
        new NameId { Id = "Id B", Name = "Name B" }, 
        new NameId { Id = "Id C", Name = "Name C" } 
    };
    public ObservableCollection<NameId> NameIdList2 { get; set; } 
    = new ObservableCollection<NameId> 
    { 
        new NameId { Id = "Id A", Name = "Name 2" }, 
        new NameId { Id = "Id B", Name = "Name 2" }, 
        new NameId { Id = "Id C", Name = "Name 2" } 
    };
    
    public string selection;
    public string Selection
    {
        get => selection;
        set
        {
            selection = value;
            OnPropertyChanged(nameof(Selection));
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Upvotes: 1

Related Questions