英文:
How can I populate multiple Tabs with the same page content but a different binding source in .Net MAUI Shell and MVVM
问题
我正在开发一个应用程序,该应用程序将显示每周每天的数据,从星期一到星期日。我希望每天都是一个单独的选项卡,以便您可以在不同天之间轻松切换。目前,我正在使用一个选项卡,其中包含7个不同的ShellContent页面,每个页面完全相同,与ViewModel中的不同可观察集合绑定。
这种方法确实可以工作,但我遇到了两个主要问题:
1)由于我创建了7个不同的内容页面,如果我更改其中一个的布局,那么我必须手动更改所有页面的布局,这既繁琐又容易出错。
2)每次打开一个选项卡时,都会创建ViewModel的新实例,这些实例需要在用户切换选项卡时加载,这对用户来说可能不太流畅。
我查看了TabbedPage,看起来这是我想要的东西,我可以添加一个DataTemplate并使用项源绑定选项卡,但是无法与Shell一起使用TabbedBar。
我应该如何重复使用相同的内容以及在用户切换选项卡之前加载所有页面的数据?我可以接受初始加载速度较慢,因为我可以在上面放置加载指示器。
目前,我是这样添加它们的:
<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>
ViewModel中的代码如下:
public ObservableCollection<DailyData> MondayList { get; set; } = new();
public ObservableCollection<DailyData> TuesdayList { get; } = new();
public ObservableCollection<DailyData> WednesdayList { get; } = new();
然后,通过页面的代码后台,将数据绑定到每个页面:
public WeeklyRotaFriday(WeeklyRotaViewModel viewModel)
{
    InitializeComponent();
    this.BindingContext = viewModel;
    viewModel.GetDailyDataCommand.Execute(DayOfWeek.Friday);
}
我理解这种方法远非理想,但我对应用程序开发非常陌生,并且找不到完全符合我尝试实现的示例。它确实可以运行,但应该有更好的方法。
英文:
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.
答案1
得分: 1
以下是您要翻译的内容:
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));
    }
}
英文:
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));
    }
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论