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.
Related
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
}
}
In Xamarin, I frequently used <NavigationPage.TitleView> to customize the navigation title bar in my views. Now that I am working in MAUI, this tag seems to have no effect on a ShellItem view.
Here is AppShell.xaml:
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="MyApp.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:localize="clr-namespace:FleetHDMobile.Resources.Strings"
xmlns:local="clr-namespace:FleetHDMobile.Views"
Shell.FlyoutBehavior="Disabled">
<ShellItem Route="MainPage">
<ShellContent
Title="MainPage"
ContentTemplate="{DataTemplate local:MainPage}"
/>
</ShellItem> . . .
Here is MainPage.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:localize="clr-namespace:FleetHDMobile.Resources.Strings"
Shell.FlyoutBehavior="Flyout"
x:Class="FleetHDMobile.Views.MainPage">
<NavigationPage.TitleView>
<Label
Text="XXX"
HeightRequest="44"
WidthRequest="300" />
</NavigationPage.TitleView>
<ScrollView>
I have tried making MainPage a tab in the tabbar, but that didn't customize the title view either.
The <NavigationPage.TitleView> tag has no effect on the rendered view. I would like to put small logo icons in the title bar, but cannot figure out how to do so.
There are some known issues about this problem.
You can follow up them here:
https://github.com/dotnet/maui/issues/9269
https://github.com/dotnet/maui/issues/9687
Thanks for your support and feedback.
Did you try doing it through the ViewModel or the .cs of your View ?
Such as for MainPage.xaml.cs or MainPageViewModel.cs
public class MainPageViewModel
{
public MainPageViewModel
{
Title = “”; // or this.Title = “”;
}
}
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());
}
I am new to Xamarin (came from C# and WPF)
I build a Xamarin form application using the template provided (VS created it for me)
I add Xamarin.Forms.Map from the nuget and added it to a page 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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:maps="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="XamarinTest.Views.MapPage"
Title="Map"
>
<!--Title="{Binding Title}"-->
<ContentPage.ToolbarItems>
<!--<ToolbarItem Text="Add" Clicked="AddItem_Clicked" />-->
</ContentPage.ToolbarItems>
<StackLayout Spacing="10">
<maps:Map x:Name="map" />
</StackLayout>
</ContentPage>
Now the application doesn't work and I am getting an error which says the code can not be debugged using this debugger. (I am using UWP platform)
When I commented out the map, it would work and there is no problem.
I tested it on Android and I am getting similar errors when reference to map exist.
What is the problem and how I can fix it?
Thanks to comments, I found that I need to initialize the map in each implementation.
For example UWP we should have this line in MainPage.xaml.cs
Xamarin.FormsMaps.Init("INSERT_AUTHENTICATION_TOKEN_HERE");
This is a good document on how to initialise map on different platforms:
Xamarin.Forms Map Initialization and Configuration
I've searched everywhere on google and it seems like this 'Pull To Refresh' feature is only available for ListViews. There are some posts regarding 'Pull To Refresh' for WebView but they are coded in Java and in Android Studio. Is there any easy way to implement this?
You can use #jamesmontemagno's PullToRefreshLayout and include your WebView inside PullToRefreshLayout element:
<?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="RefreshSample.Views.ScrollViewXamlPage"
xmlns:controls="clr-namespace:Refractored.XamForms.PullToRefresh;assembly=Refractored.XamForms.PullToRefresh"
Title="Xaml Scroll">
<controls:PullToRefreshLayout
IsPullToRefreshEnabled="True"
RefreshCommand="{Binding RefreshCommand}"
IsRefreshing="{Binding IsBusy}"
RefreshColor="Blue">
<WebView>
</WebView>
</controls:PullToRefreshLayout>
</ContentPage>