.NET MAUI - Dependency Injection - Constructor is never called

1 day ago 4
ARTICLE AD BOX

I'm trying to make a generic popup which allows me to reuse it for multiple different occasions. For some reason tho the constructor of my view model is never called even though I register it and call it correctly.

This is how I register it in my MauiProgram.cs:

builder.Services.AddTransientPopup<PickerBottomSheet, BaitPickerViewModel>();

This is my generic caller:

public async Task<TItem?> DisplayBottomPickerAsync<TViewModel, TItem>(CancellationToken token = default) where TViewModel : PickerBottomSheetViewModel<TItem> where TItem : IPickableItem { var result = await _popup.ShowPopupAsync<TViewModel, TItem>(Shell.Current, GetOptions(true), token); return result.WasDismissedByTappingOutsideOfPopup ? default : result.Result; }

This is the code I use to call it:

var result = await _popup.DisplayBottomPickerAsync<BaitPickerViewModel, Bait>(token);

This is the essential part of the code, sharing the whole classes would be way too much (ordered by inheritance):

public abstract partial class PickerBottomSheetViewModel : ViewModelBase public abstract partial class PickerBottomSheetViewModel<T> : PickerBottomSheetViewModel where T : IPickableItem public sealed partial class BaitPickerViewModel : PickerBottomSheetViewModel<Bait> { private readonly SqliteStorage _sql; private readonly ILocalizationResourceManager _lang; public BaitPickerViewModel(PopupHost popup, SqliteStorage sql, ILocalizationResourceManager lang) : base(popup) { _sql = sql; //Never called _lang = lang; Title = _lang["SelectBaitTitle"]; AddNewText = _lang["AddNewBait"]; CanEditOrDelete = true; } protected override IAsyncEnumerable<Bait> LoadTypedItemsAsync(CancellationToken token) => _sql.EnumerateBaitsAsync(token); protected override Task<Bait?> AddTypedItemAsync(CancellationToken token) => base.AddTypedItemAsync(token); protected override Task<Bait?> EditTypedItemAsync(Bait item, CancellationToken token) => base.EditTypedItemAsync(item, token); protected override Task DeleteTypedItemAsync(Bait item, CancellationToken token) => base.DeleteTypedItemAsync(item, token); }

The Popup itself:

public sealed partial class PickerBottomSheet : Popup<IPickableItem> { public PickerBottomSheet() { InitializeComponent(); } } <mct:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:mct="http://schemas.microsoft.com/dotnet/2022/maui/toolkit" xmlns:icon="http://www.aathifmahir.com/dotnet/2022/maui/icons" xmlns:lang="clr-namespace:LocalizationResourceManager.Maui;assembly=LocalizationResourceManager.Maui" xmlns:common="clr-namespace:CatchLog.Common" xmlns:models="clr-namespace:CatchLog.Models" xmlns:viewmodels="clr-namespace:CatchLog.ViewModels.Popups" x:Class="CatchLog.Popups.PickerBottomSheet" x:DataType="viewmodels:PickerBottomSheetViewModel" x:TypeArguments="models:IPickableItem" Background="{mct:AppThemeResource Surface}" HorizontalOptions="Fill" VerticalOptions="End">

I really have no idea why this isn't working. I don't get an exception and the popup even shows. Without the values I assign though, because the constructor isn't even called.

Edit

If I pass the BaitPickerViewModel directly into PickerBottomSheet, it works. But this isn't a viable solution as I cannot use other view models then, which is the whole point of this construction.

Also passing PickerBottomSheetViewModel is not possible because it's abstract.

Read Entire Article