How to implement LazyLoad on TabbedPage MAUI - c#

I have a MAUI project that uses TabbedPage with Shell, it is working fine, but since my app has 4 tabs and all of them are making api calls, the app takes up to 25 seconds to load completely. Is there a way to only load the pagges until they are selected?
Here is my code (It only has 2 tabs but the other 2 are the same:
<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NewScholarApp.Views.HomePage"
xmlns:pages="clr-namespace:NewScholarApp.Views"
xmlns:vm="clr-namespace:NewScholarApp.ViewModels"
Shell.NavBarIsVisible="False"
Shell.TabBarBackgroundColor="#00928A"
BackgroundColor="#00928A"
Title="HomePage">
<TabBar Route="Home">
<Tab Title="Mensajes" Icon="messages_icon.png">
<ShellContent ContentTemplate="{DataTemplate pages:MessagesPage}"/>
</Tab>
<Tab Title="Documentos" Icon="documents_icon.png">
<ShellContent ContentTemplate="{DataTemplate pages:DocumentsPage}"/>
</Tab>
</TabBar>
With Xamarin.Forms, I used Prism, it has a built-in interface to detect when a tab is selected. On MAUI, I am using Community Toolkit MVVM nuget so I'm looking for an alternative to achieve this.

This is a feature request tracked in Enhancement Shell TabBar Action on Current Tab Tapped #6544. There is something like a command property OnCurrentTabTapped for each tab to detect when a tab is selected. You can follow up there or create a new one on Github.
As an alternative workaround, if you are only using TabbedPage without Shell, you can manage to override the method OnCurrentPageChanged that call on tab change to detect which tab is selected and then loaded the corresponding page.
XAML:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiAppTabPage.MyTabbedPage"
xmlns:pages="clr-namespace:MauiAppTabPage.Views"
Title="MyTabbedPage">
<pages:MessagePage x:Name="Message"></pages:MessagePage>
<pages:DocumentsPage x:Name="Document"></pages:DocumentsPage>
</TabbedPage>
Code-behind:
protected override void OnCurrentPageChanged()
{
base.OnCurrentPageChanged();
if(CurrentPage is MessagePage)
{
System.Diagnostics.Debug.WriteLine("MessagePage");
//detect the tab is selected and load the page
}
else if (CurrentPage is DocumentsPage)
{
System.Diagnostics.Debug.WriteLine("DocumentsPage");
//detect the tab is selected and load the page
}
}

Related

NET MAUI, how to set the first page to load depending on do I have a JWT or not in the Shell

For example, if I had a Shell, where I add FlyoutItem(s) in code, I need to set some Content on the app start (for example, depending on that I do have a JWT or not), how can I do that.
public partial class AppShell : Shell
{
public AppShell ()
{
InitializeComponent ();
FlyoutItem flyoutItem = new FlyoutItem ();
flyoutItem.FlyoutDisplayOptions = FlyoutDisplayOptions.AsMultipleItems;
flyoutItem.Items.Add (new ShellContent () { Title = "NewPage1", Content = new NewPage1 () });
flyoutItem.Items.Add (new ShellContent () { Title = "home", Content = new MainPage () });
myshell.Items.Add (flyoutItem);
}
}
Or similar in xaml
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="MauiUI.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiUI"
xmlns:pages="clr-namespace:MauiUI.Pages"
FlyoutHeaderBehavior="CollapseOnScroll"
Shell.FlyoutBehavior="Flyout">
<FlyoutItem IsVisible="False">
<ShellContent
Title="Login"
Route="login"
ContentTemplate="{DataTemplate pages:LoginPage}" />
</FlyoutItem>
<FlyoutItem IsVisible="False">
<ShellContent
Title="Register"
Route="register"
ContentTemplate="{DataTemplate pages:RegisterPage}" />
</FlyoutItem>
<FlyoutItem>
<ShellContent
Title="Amazons of Volleyball"
Route="main"
ContentTemplate="{DataTemplate pages:MainPage}" />
</FlyoutItem>
<FlyoutItem>
<ShellContent
Title="Detaiils"
Route="details"
ContentTemplate="{DataTemplate pages:PlayerDetailsPage}" />
</FlyoutItem>
<FlyoutItem>
<ShellContent
Title="Add new Amazon"
Route="add-or-update"
ContentTemplate="{DataTemplate pages:AddOrUpdatePlayer}" />
</FlyoutItem>
</Shell>
Depending on the size of the data you have to load and what you want the user to see in the meantime.
If you don't have much to load you can just do everything in Application.OnStart like this answer said, there is also a bunch of other app lifecycle events that can suit your needs (like OnCreated).
Just be cautious if you use async / await in these methods since the app will continue to load in the mean time and if you are waiting on data that should be displayed you should design your UI with that in mind (using events and dependency injection).
If the data you are loading take some time to load (>few seconds) you can change the start page of your app by a page dedicated to loading data, once the data is loaded you can simply navigate to the good page with Shell.Current.GoToAsync("/home/or/login").
You can also directly load data inside MauiProgram.CreateMauiApp() and pass down information with dependency injection, the splash screen will stay on screen during the loading.

Button above the BottomNavigationBar

I created a TabbedPage which opens the various pages that I created within my project.
<myapp:MainPage Title="Page1" IconImageSource="IC001.png" BackgroundColor="Gainsboro"></myapp:MainPage>
<myapp:DiaryPage Title="Page2" IconImageSource="IC002.png" BackgroundColor="Gainsboro"></myapp:MainPage>
Through this namespace on Android I can see the NavigationBar at the bottom of the screen.
xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core" android:TabbedPage.ToolbarPlacement="Bottom"
I would like to be able to place a Custom Button above the NavigationBar, a button that is within my Page Layout and that covers a portion of the NavigationBar. it's possible?
Example of my page:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:yummy="clr-namespace:Xamarin.Forms.PancakeView;assembly=Xamarin.Forms.PancakeView"
x:Class="MotiVApp.DiaryPage">
<Grid>
<Button/>
</Grid>

Why Toolbar not showing on iOS display using Xamarin

I want to display very simple Toolbar using xamarin on iOS, but I can't see the toolbar on display.
I use this code:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Test.MainPage">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Example Item"
IconImageSource="https://img.icons8.com/cute-clipart/64/000000/menu.png"
Order="Primary"
Priority="0" />
</ContentPage.ToolbarItems>
</ContentPage>
There are no errors in the code and the display is empty..
What needs to change to see the toolbar ?
Are you putting your page in NavigationPage? By default ContentPage does not have Navigation bar (Toolbar), you must put your page in NavigationPage, after that Navigation bar will show up with its functionality.
have a look at here : Xamarin.Forms Navigation
So I assume you should do something like this
public App ()
{
MainPage = new NavigationPage (new MainPage());
}

Xamarin.Forms Shell + ReactiveUI Tab loading performance (on Android)

I've been testing ReactiveUI for a while now (highly recommend it!) but recently came across a performance issue that I'm not sure if it's due to ReactiveUI itself (given that it seems to rely on reflection for some things) OR the Xamarin.Forms App Shell. There's a distinct delay when changing to a Tab whose content has ReactiveUI bindings, this is more noticeable when running on an emulator but can also be experienced on a real device too (tested on Android 6 and Android 9 devices). Is there a strategy to improve App Shell tab performance? The performance issue is only on the tab's first load. Relevant code below (simplified example based on base App Shell visual studio template):
AppShell.Xaml
<?xml version="1.0" encoding="UTF-8"?>
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:local="clr-namespace:ReactiveXam.Views"
Title="ReactiveXam"
x:Class="ReactiveXam.AppShell"
Visual="Material">
<!-- Your Pages -->
<TabBar>
<Tab Title="Browse" Icon="tab_feed.png">
<ShellContent ContentTemplate="{DataTemplate local:ItemsPage}" />
</Tab>
<Tab Title="About" Icon="tab_about.png">
<ShellContent ContentTemplate="{DataTemplate local:AboutPage}" />
</Tab>
<Tab Title="Test" Icon="tab_about.png">
<ShellContent ContentTemplate="{DataTemplate local:TestPage}" /> <!-- The relevant tab -->
</Tab>
</TabBar>
</Shell>
TestPage.Xaml (TestPageViewModel is an "empty" class inheriting from ReactiveObject)
<?xml version="1.0" encoding="utf-8" ?>
<rxui:ReactiveContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:local="clr-namespace:ReactiveXam.Views"
xmlns:vms="clr-namespace:ReactiveXam.ViewModels"
x:TypeArguments="vms:TestPageViewModel"
x:Class="ReactiveXam.Views.TestPage">
<ContentPage.Content>
<StackLayout>
<Label Text="Welcome to Xamarin.Forms!"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
<local:Exposure />
</StackLayout>
</ContentPage.Content>
</rxui:ReactiveContentPage>
Exposure.Xaml
<?xml version="1.0" encoding="UTF-8"?>
<rxui:ReactiveContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:vms="clr-namespace:ReactiveXam.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:TypeArguments="vms:ExposureViewModel"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
x:Class="ReactiveXam.Views.Exposure">
<StackLayout>
<Label x:Name="balance" />
<Label x:Name="exposure" />
</StackLayout>
</rxui:ReactiveContentView>
Exposure.xaml.cs
public partial class Exposure : ReactiveContentView<ExposureViewModel>
{
public Exposure()
{
InitializeComponent();
ViewModel = new ExposureViewModel();
// The code below slows down the loading of the tab (~5x slower).
this.WhenActivated((disposable) =>
{
this.OneWayBind(ViewModel, vm => vm.Exposure, v => v.exposure.Text).DisposeWith(disposable);
this.OneWayBind(ViewModel, vm => vm.Balance, v => v.balance.Text).DisposeWith(disposable);
});
}
}
ExposureViewModel
public class ExposureViewModel : ReactiveObject
{
double balance, exposure;
public double Balance
{
get => balance = 500.00d;
set => this.RaiseAndSetIfChanged(ref balance, value);
}
public double Exposure
{
get => exposure = 25.00d;
set => this.RaiseAndSetIfChanged(ref exposure, value);
}
public ExposureViewModel()
{
}
}
Well, I have been giving a lot of my time to the awesome Xamarin Forms Shell and I think that itself is the reason for the performance delay that you are talking about right now.
When you read the Microsoft documents it clearly says:
In a Shell application, each ContentPage that's a child of a ShellContent object is created during application startup. Adding additional ShellContent objects using this approach will result in additional pages being created during application startup, which can lead to a poor startup experience. However, Shell is also capable of creating pages on demand, in response to navigation. For more information, see Efficient page loading.
The good thing is that you can fix this by loading these pages on Demand as you may see here: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/shell/tabs#efficient-page-loading
Basically, This can be accomplished by using the DataTemplate markup extension to convert each ContentPage into a DataTemplate and then setting the result as the ShellContent.ContentTemplate property value.

Can you put a NavigationPage inside a ContentPage?

I'm trying to create a music player in Xamarin Forms. Now at the bottom of my page there always is a bar with the music player controls. Instead of always adding the tag and creating a new PlayerBar in each view, I thought it would be useful to create a RootPage with the PlayerBar created once inside and above it a NavigationPage that spans the rest of the view.
In XAML it would look a bit like this:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:PodBase.Views"
xmlns:controls="clr-namespace:PodBase.Controls"
x:Class="PodBase.Views.RootPage">
<ContentPage.Content>
<Grid>
<NavigationPage>
<x:Arguments>
<views:MainPage/>
</x:Arguments>
</NavigationPage>
<controls:PlayerControl/>
</Grid>
</ContentPage.Content>
</ContentPage>
However when I try it out I get an error saying NavigationPage cannot be added to IGridList. Are there any other options I can use to work this out?
This is not possible. A Page can only be at the root. Inside you can have one VisualElement and within that VisualElement you can nest as many things as you like.
To achieve this kind of behavior, either inherit from the NavigationPage and add the player control at the bottom. Or, you will have to mimic your own navigation page behavior.

Categories

Resources