Ch1h0
Ch1h0

Reputation: 43

.Net Maui, Constructor and Page Navigation Without Using Shell

I am developing a mobile app with .Net MAUI without using shell and came up with a question.

When using shell to navigate pages, AppShell.xaml would look like this:

<ShellContent
        Title="Home"
        ContentTemplate="{DataTemplate local:MainPage}"
        Route="MainPage" />

and you would be able to move aournd pages.

When you don't use shell, App.xaml.cs would look like this:

public App(){
     MainPage = new MainPage();
     //or
     MainPage = new NavigationPage(new MainPage());
}

Then, if you have MainPageViewModel.cs to enable data binding, the constructor in MainPage.xaml.cs would need to take this in the parameter in order to set it to BindingContext.

public partial class MainPage : Flyout
{
    public MainPage(MainPageViewModel viewModel){
       InitializeComponent();
       BindingContext = viewModel;
    }
}

Here, I can think of two cases where

1. you pass the parameter when you call the page in App.xaml.cs:

public App(){
     MainPage = new MainPage(new ViewModels.MainPageViewModel());

     //If MainPageViewModel() needs a parameter, it would be longer and longer depending on the structure
     MainPage = new MainPage(new ViewModels.MainPageViewModel(new DatabaseContext()));

}

2. you don't pass anything, make a constructor with no parameter in MainPage.xaml.cs, instantiate MainPageViewModel there:

App.xaml.cs:

public App(){
     MainPage = new MainPage();
}

MainPage.xaml.cs:

public MainPage(){
   InitializeComponent();
   BindingContext = new MainPageViewModel();
}

I was wondering how those two differ essentially and what are downsides of doing those.

I watched tutorial videos of building .Net MAUI apps, but most of them used shell to navigate pages and didn't have a lot of resources for this case.

Upvotes: 0

Views: 2299

Answers (1)

Nick Peppers
Nick Peppers

Reputation: 3251

Typically, I don't like to register and inject or pass ViewModels into pages and instead use DI to pull the services needed by the ViewModel and let the page create a new ViewModel each time, which is set in XAML.

<ContentPage.BindingContext>
    <vm:MainViewModel/>
</ContentPage.BindingContext>

This way XAML will give you autocomplete based on the ViewModel properties and you don't have to worry about the Page being a potential memory leak as much. For example, when there's a single registered instance of the ViewModel the page may get held onto because it never cleared the BindingContext after being popped, which still has a reference to that registered instance of the ViewModel.

I use a helper class to pull any services the ViewModel would need.

public static class ServiceHelper
{
    public static IServiceProvider Services { get; private set; }

    public static void Initialize(IServiceProvider serviceProvider) =>
        Services = serviceProvider;

    public static T GetService<T>() => Services.GetService<T>();
}

Then in the ViewModel use it to get the service

 private readonly INavigationService _navigationService = ServiceHelper.GetService<INavigationService>()

Just don't forget to register everything in your MauiProgram.cs

    var builder = MauiApp.CreateBuilder();
    builder
        .UseMauiApp<App>()
        .UseMauiCommunityToolkit()
        .Services.AddSingleton<INavigationService, NavigationService>()

    var app = builder.Build();
    ServiceHelper.Initialize(app.Services);
    return app;

Upvotes: 2

Related Questions