1

Is there anything semantically different in, or inherently wrong with, instantiating a viewmodel explicitly from a page's constructor, opposed to declaring it from within XAML using a property element?

NotePage.xaml

<ContentPage.BindingContext>        
    <viewModels:NoteViewModel/>
</ContentPage.BindingContext>

NotePage.cs

public NotePage()
{
    BindingContext = new ViewModels.NoteViewModel();
    InitializeComponent();
}

From XAML

NotePage.xaml

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"             
             xmlns:viewModels="clr-namespace:Notes.ViewModels"
             x:Class="Notes.Views.NotePage"
             Title="Note">            

    <ContentPage.BindingContext>        
        <viewModels:NoteViewModel />
    </ContentPage.BindingContext>   
    ...
</ContentPage>

NotePage.cs

public partial class NotePage : ContentPage
{
    public NotePage()
    {
        // BindingContext = new ViewModels.NoteViewModel();

        InitializeComponent();
    }
}

From constructor

NotePage.xaml

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Notes.Views.NotePage"
             Title="Note">
            <!--xmlns:viewModels="clr-namespace:Notes.ViewModels"-->

    <!--<ContentPage.BindingContext>        
        <viewModels:NoteViewModel />
    </ContentPage.BindingContext>-->
    ...
</ContentPage>

NotePage.cs

public partial class NotePage : ContentPage
{
    public NotePage()
    {
        BindingContext = new ViewModels.NoteViewModel();

        InitializeComponent();
    }
}
samus
  • 6,102
  • 6
  • 31
  • 69
  • 1
    No. Doing it in code gives you more flexibility, but AFAIK there is no functional difference between the approaches if using the default constructor. – Jason Jul 22 '23 at 19:54
  • 1
    If you want to use Dependency Injection you can only do that in code. XAML only support parameterless constructors. And you should use DI to follow SOLID. – Peter Wessberg Jul 22 '23 at 20:12
  • 2
    @ToolmakerSteve Sorry about that. Dependency Injection is an intricate part of .Net to help us to develop loose coupling code. By this we get maintainable and testable code. In this case we supply viewmodel in the Constructor and by doing this we cannot define Binding Context in xaml that requires no paramter in the Constructor. We pass our ViewModel like this: public NotePage(NotePageViewModel viewModel) { BindingContext = viewModel; InitializeComponent(); } – Peter Wessberg Jul 23 '23 at 08:28
  • 2
    Thanks. `public NotePage(NotePageViewModel viewModel) { BindingContext = viewModel; InitializeComponent(); }`. BTW, personally I always set BIndingContext AFTER the component is initialized: `public NotePage(NotePageViewModel viewModel) { InitializeComponent(); /* any conditional customization of layout here */ BindingContext = viewModel; }` – ToolmakerSteve Jul 23 '23 at 19:16
  • @ToolmakerSteve Good catch. I think I got that from Microsoft pattern. To set Binding context before InitializeComponent, I mean. I double checked and that is how they do it in their eShopOnContainers example app. – Peter Wessberg Jul 24 '23 at 16:21
  • Interesting. Apparently they are not consistent. Thinking about it, order shouldn't matter, since BindingChanged event presumably can't occur until after the constructor returns. Would be a potential problem if that event could happen before component was initialized. I guess that's why I always put it in that order. – ToolmakerSteve Jul 24 '23 at 17:27

1 Answers1

3

There is a third option built into MAUI, that has advantages:

  1. MAUI comes with a Dependency Injection Framework you can easily use to develop loose coupling code.
  2. You can have ViewModels with non-empty constructor (and in most cases you will have such)

To get there you simply change the View code to

public partial class NotePage : ContentPage
{
    public NotePage(ViewModels.NoteViewModel viewModel)
    {
        InitializeComponent();
        BindingContext = viewModel;
    }
}

and register it in the MauiProgram class

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts( fonts =>
            {
                fonts.AddFont( "OpenSans-Regular.ttf", "OpenSansRegular" );
                fonts.AddFont( "OpenSans-Semibold.ttf", "OpenSansSemibold" );
            } );
        // --- ADD THIS ---
        builder.Services
            .AddTransient<NotePage>().AddTransient<NoteViewModel>();
        // --- THATS ALL ---
#if DEBUG
        builder.Logging.AddDebug();
#endif

        return builder.Build();
    }
}
ToolmakerSteve
  • 18,547
  • 14
  • 94
  • 196
Sir Rufo
  • 18,395
  • 2
  • 39
  • 73