Reputation: 17432
I am using Commnunity.Toolkit.Mvvm
to use MVVM in my project. I have seen many article, YouTube videos almost everyone at the end link View to Viewmodel using BindingContext. I want to connect it without BindingContext.
How can I avoid this code
public partial class ItemEntryPage : ContentPage
{
public ItemEntryPage()
{
InitializeComponent();
BindingContext = new ItemEntryPageModel();
}
}
Where do I need to make that connection in MauiProgram.cs
or in App.xaml.cs
file or do I need to use different Mvvm nuget package ?
There are few approaches where to not need to worry about BindingContext
just follow the naming convention of View and Viewmodel, it works, in few cases I have seen in Xamarin.Forms
we need to keep View and Viewmodel name in dependency injection
in App.xaml.cs
file and it works.
I am following this thread mvvm in .net maui.
Upvotes: 1
Views: 1346
Reputation: 26169
If you have numerous pages, but, they all have the same view model, the recommendation is to use dependency injection and configure that view model as a singleton in your MauiProgram.cs
:
// MauiProgram.cs ...
builder.Services.AddSingleton<GlobalViewModel>();
builder.Services.AddTransient<MainPage>();
builder.Services.AddTransient<SecondPage>();
// ...
// MainPage.xaml.cs
public class MainPage : ContentPage
{
public MainPage(GlobalViewModel VM)
{
InitializeComponent();
BindingContext = VM;
}
}
// SecondPage.xaml.cs
public class SecondPage : ContentPage
{
public SecondPage(GlobalViewModel VM)
{
InitializeComponent();
BindingContext = VM;
}
}
Alternatively, if you want to give each page its own transient ViewModel, but, also, within each ViewModel, have access to the global singleton ViewModel, you can do it like this:
// MauiProgram.cs ...
builder.Services.AddSingleton<GlobalViewModel>();
builder.Services.AddTransient<MainPage>();
builder.Services.AddTransient<MainViewModel>();
builder.Services.AddTransient<SecondPage>();
builder.Services.AddTransient<SecondViewModel>();
// ...
// MainPage.xaml.cs
public class MainPage : ContentPage
{
public MainPage(MainViewModel VM)
{
InitializeComponent();
BindingContext = VM;
}
}
// MainViewModel.cs
public class MainViewModel
{
public GlobalViewModel Global { get; }
public MainViewModel(GlobalViewModel Global)
{
this.Global = Global;
}
}
// SecondPage.xaml.cs
public class SecondPage : ContentPage
{
public SecondPage(SecondViewModel VM)
{
InitializeComponent();
BindingContext = VM;
}
}
// SecondViewModel.cs
public class SecondViewModel
{
public GlobalViewModel Global { get; }
public SecondViewModel(GlobalViewModel Global)
{
this.Global = Global;
}
}
If your question is not able global or singleton, but, about avoiding setting BindingContext
in the c# code behind, you can actually set BindingContext
in XAML instead, e.g.
<!-- MainPage.xaml -->
<ContentPage BindingContext="{local:MainViewModel}"/>
</ContentPage>
<!-- SecondPage.xaml -->
<ContentPage BindingContext="{local:SecondViewModel}"/>
</ContentPage>
If you want to declare a singleton global GlobalViewModel
but just in XAML, one way you could place it in a ResourceDictionary
in your App.xaml
:
<!-- App.xaml ... -->
<Application>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
<ResourceDictionary>
<local:GlobalViewModel x:Key="globalViewModel"/>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
<!-- MainPage.xaml -->
<ContentPage BindingContext="{x:StaticResource globalViewModel}">
</ContentPage>
<!-- SecondPage.xaml -->
<ContentPage BindingContext="{x:StaticResource globalViewModel}">
</ContentPage>
Or, even, just declare it in the BindingContext
in App.xaml
:
<!-- App.xaml ... -->
<Application>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
<Application.BindingContext>
<local:GlobalViewModel/>
</Application.BindingContext>
</Application>
Upvotes: 3
Reputation: 3907
First we follow the official documentation: https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
Instead of random links, that may be not-available (or turn out to contain banana shake recipe) after a month or so.
Second, we do not do this:
BindingContext = new ItemEntryPageModel();
But we use the Dependency Injection, we have in MAUI by:
In your case:
public ItemEntryPage(ItemEntryViewModel vm)
{
InitializeComponent();
BindingContext = vm;
}
That is the right way to do it. Nothing else is required.
Anyway, if you want to save some lines of codes using reflection and feel better, this is what I do:
Extension:
public static void AddTransientNamespace(this MauiAppBuilder builder, string nameSpace)
{
foreach (Type t in Utils.GetTypesInNamespace(Assembly.GetExecutingAssembly(), nameSpace))
{
builder.Services.AddTransient(t);
}
}
How to use:
builder.AddTransientNamespace("MyProject.ViewModelsNamespace");
Upvotes: 1