I'm currently developing a .NET MAUI app, everything works fine on DEBUG mode but when publishing the app I'm getting:
System.InvalidCastException
Arg_InvalidCastException
exception on launch making iOS and Android apps unusable.
According to stack trace it's something related to the constructor of my pages,
EDIT [A better stack trace]
2023-02-22 09:27:14.000039-0600 Envivu[94104:520252] Unhandled managed exception: Arg_InvalidCastException (System.InvalidCastException)
at Envivu.Buyer.Controls.Views.OnboardingItemView.InitializeComponent()
at Envivu.Buyer.Controls.Views.OnboardingItemView..ctor()
at Envivu.Buyer.Pages.OnboardingPage.InitializeComponent()
at Envivu.Buyer.Pages.OnboardingPage..ctor(OnboardingVm vm)
at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeResolverContext, Microsoft.Extensions.DependencyInjection, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[System.Object, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].VisitCallSiteMain(ServiceCallSite callSite, RuntimeResolverContext argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeResolverContext, Microsoft.Extensions.DependencyInjection, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[System.Object, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].VisitCallSite(ServiceCallSite callSite, RuntimeResolverContext argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass2_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.Maui.MauiContext.WrappedServiceProvider.GetService(Type serviceType)
at Microsoft.Maui.Controls.ShellContent.<>c__DisplayClass19_0.<Microsoft.Maui.Controls.IShellContentController.GetOrCreateContent>b__0()
at Microsoft.Maui.Controls.ElementTemplate.CreateContent()
at Microsoft.Maui.Controls.Internals.DataTemplateExtensions.CreateContent(DataTemplate self, Object item, BindableObject container)
at Microsoft.Maui.Controls.ShellContent.Microsoft.Maui.Controls.IShellContentController.GetOrCreateContent()
at Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRootRenderer.LoadRenderers()
at Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRootRenderer.ViewDidLoad()
--- End of stack trace from previous location ---
at UIKit.UIApplication.UIApplicationMain(Int32 argc, String[] argv, IntPtr principalClassName, IntPtr delegateClassName)
at UIKit.UIApplication.Main(String[] args, Type principalClass, Type delegateClass)
at Envivu.Buyer.Program.Main(String[] args)
but that View Model is correctly registered on AppShell and Maui App builder.
builder.Services.AddSingleton<AppShell>();
builder.Services.AddPage<SplashScreenPage, SplashScreenVm>(shouldRegisterRoute: false);
builder.Services.AddPage<OnboardingPage, OnboardingVm>(shouldRegisterRoute: false);
And With this AppShell is injected in App.xaml.cs
public App(AppShell appShell)
{
InitializeComponent();
MainPage = appShell;
}
I have a markup extension that makes a simple calculation to change the size of the view according to device density and screen height and width, I'm not sure if that has something to do with this, cause the animated splash screen (which uses that markup extension as well) is working fine.
[CODE] OnnoardingPage.xaml
<Grid Margin="0,10,0,0" RowSpacing="{mk:SizeAdapter DefaultSize=20, AttributeType=Height}">
<Grid.RowDefinitions>
<RowDefinition Height="{mk:SizeAdapter DefaultSize=316, AttributeType=Height}" />
<RowDefinition Height="{mk:SizeAdapter DefaultSize=286, AttributeType=Height}" />
<RowDefinition Height="{mk:SizeAdapter DefaultSize=10, AttributeType=Height}" />
<RowDefinition Height="{mk:SizeAdapter DefaultSize=100, AttributeType=Height}" />
</Grid.RowDefinitions>
<Image
Aspect="AspectFill"
Grid.Row="0"
HeightRequest="{mk:SizeAdapter DefaultSize=316,
AttributeType=Height}"
Source="onboard_image_0.png" />
<Grid ColumnSpacing="{mk:SizeAdapter DefaultSize=10, AttributeType=Width}" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{mk:SizeAdapter DefaultSize=20, AttributeType=Width}" />
<ColumnDefinition Width="{mk:SizeAdapter DefaultSize=315, AttributeType=Width}" />
<ColumnDefinition Width="{mk:SizeAdapter DefaultSize=20, AttributeType=Width}" />
</Grid.ColumnDefinitions>
<Border
Background="{StaticResource White}"
Grid.Column="0"
HeightRequest="{mk:SizeAdapter DefaultSize=222,
AttributeType=Height}"
StrokeShape="RoundRectangle 0,20,0,20" />
<custom:AnimatedStepper Grid.Column="1"
CurrentIndex="{Binding CurrentIndex}" >
<views:OnboardingItemView
ButtonText="Continuar"
SectionMessage="BLAH"
SectionTitle="BLAH TITLE" />
<views:OnboardingItemView
ButtonText="Continuar"
SectionMessage="BLAH"
SectionTitle="BLAH TITLE" />
<views:OnboardingItemView
ButtonText="Empezar"
SectionMessage="BLAH"
SectionTitle="BLAH TITLE" />
</custom:AnimatedStepper>
<Border
Background="{StaticResource White}"
Grid.Column="2"
HeightRequest="{mk:SizeAdapter DefaultSize=222,
AttributeType=Height}"
StrokeShape="RoundRectangle 20,0,20,0" />
</Grid>
<VerticalStackLayout Grid.Row="2" Spacing="20">
<IndicatorView HorizontalOptions="CenterAndExpand" x:Name="OnboardingIndicator">
<IndicatorView.IndicatorTemplate>
<DataTemplate>
<BoxView CornerRadius="4" HeightRequest="8" />
</DataTemplate>
</IndicatorView.IndicatorTemplate>
</IndicatorView>
<Button
BackgroundColor="{StaticResource Senary}"
Command="{Binding ForwardCommand}"
HorizontalOptions="Center"
Text="Saltar"
TextColor="{StaticResource White}"
VerticalOptions="Center"
WidthRequest="{mk:SizeAdapter DefaultSize=120,
AttributeType=Width}" />
</VerticalStackLayout>
</Grid>
OnboardinVm.cs
public partial class OnboardingVm : BaseViewModel
{
private object _lock = new object();
[ObservableProperty] private int _currentIndex;
public OnboardingVm(
INavigationService navigation,
ISessionService session,
IIdentityService identity,
ILoggerService logger,
IAlertService notification)
: base(navigation, session, identity, logger, notification)
{
}
[RelayCommand]
private Task GoNext()
{
lock (_lock)
{
if (CurrentIndex >= 2) return Forward();
CurrentIndex++;
return Task.CompletedTask;
}
}
[RelayCommand]
private Task Forward()
{
Session.HasPassedOnboarding = true;
return Navigation.NavigateToRoot("LoginSelection");
}
}
OnboardingItemView.xaml
<Grid>
<Border Background="{StaticResource White}" Stroke="{StaticResource White}">
<Border.StrokeShape>
<RoundRectangle CornerRadius="{mk:SizeAdapter DefaultSize=20, AttributeType=BorderRadius}" />
</Border.StrokeShape>
</Border>
<Grid Margin="20" RowSpacing="{mk:SizeAdapter DefaultSize=10, AttributeType=Height}">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="{mk:SizeAdapter DefaultSize=50, AttributeType=Height}" />
</Grid.RowDefinitions>
<Label
FontAttributes="Bold"
FontSize="{mk:SizeAdapter DefaultSize=30,
AttributeType=Height}"
Grid.Row="0"
HorizontalTextAlignment="Center"
Text="{Binding SectionTitle, Source={x:Reference OnboardItem}}"
TextColor="{StaticResource Black}" />
<Label
FontSize="{mk:SizeAdapter DefaultSize=18,
AttributeType=Height}"
Grid.Row="1"
HorizontalTextAlignment="Center"
Text="{Binding SectionMessage, Source={x:Reference OnboardItem}}"
TextColor="{StaticResource Black}" />
<Button
BackgroundColor="{StaticResource Primary}"
Command="{Binding GoNextCommand}"
Grid.Row="2"
Style="{StaticResource NormalButton}"
Text="{Binding ButtonText, Source={x:Reference OnboardItem}}"
TextColor="{StaticResource White}"
WidthRequest="{mk:SizeAdapter DefaultSize=238,
AttributeType=Width}" />
</Grid>
</Grid>
OnboardingItemView.xaml.cs
public partial class OnboardingItemView : ContentView
{
public static readonly BindableProperty SectionTitleProperty =
BindableProperty.Create(nameof(SectionTitle), typeof(string), typeof(OnboardingItemView), string.Empty);
public string SectionTitle
{
get => (string)GetValue(SectionTitleProperty);
set => SetValue(SectionTitleProperty, value);
}
public static readonly BindableProperty SectionMessageProperty =
BindableProperty.Create(nameof(SectionMessage), typeof(string), typeof(OnboardingItemView), string.Empty);
public string SectionMessage
{
get => (string)GetValue(SectionMessageProperty);
set => SetValue(SectionMessageProperty, value);
}
public static readonly BindableProperty ButtonTextProperty =
BindableProperty.Create(nameof(ButtonText), typeof(string), typeof(OnboardingItemView), string.Empty);
public string ButtonText
{
get => (string)GetValue(ButtonTextProperty);
set => SetValue(ButtonTextProperty, value);
}
public OnboardingItemView()
{
InitializeComponent();
}
}
This is an example of how MarkupExtension Size Adapter works for Height:
private double GetProportionateScreenHeight(double inputHeight)
{
var displayInfo = DeviceDisplay.MainDisplayInfo;
var displayHeight = displayInfo.Height / displayInfo.Density;
var rawNewHeight = (inputHeight / 812.0) * displayHeight;
return Math.Round(rawNewHeight,0);
}
Any idea would be much appreciated.