Click here to Skip to main content
15,877,675 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am having a very strange issue with upgrading an existing project from .net 7 to 8 in .net Maui. I have 2 projects, one a test project I am using to see why the issue I'm having is persisting, the other the main project. Both projects are using the Prism 9 library, specifically Prism 9.0.271, and for some reason, the test project is resolving pages just fine with the prism navigation builder and the prism navigation service as opposed to the current main project. Both projects are using an identical NaviagtionService class that uses Prism's navigation service internally. Below are the 2 MauiProgram files that inject the relevant objects along with the relevant code and error.

MauiProgram.cs for test project:

C#
namespace MauiApp2
{
internal static class PrismStartup
{
public static void Configure(PrismAppBuilder builder)
{
builder.RegisterTypes(RegisterTypes).OnAppStart((navService) =\>
{
var builder = navService.CreateBuilder();

                builder.AddSegment("/LoginPage");
    
                return builder.NavigateAsync();
            }); 
        }
        private static void RegisterTypes(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterForNavigation<LoginPage, LoginPageViewModel>();
            containerRegistry.RegisterForNavigation<StuffPage, StuffPageViewModel>();
        }
    }
    
    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder()
                .UseMauiCommunityToolkit()
                .UseMauiApp<App>()
                .UsePrism(PrismStartup.Configure)
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                    fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
                });

#if DEBUG
builder.Logging.AddDebug();
#endif

            builder.Services.AddSingleton<INavigationService, NavigationService>();
    
            return builder.Build();
        }
    }

}


MauiProgram.cs for main project (reduced to relevant lines for brevety)

C#
namespace ATrack.Mobile.Presentation.Pages.Home
{
public partial class Home
{
public Home(HomeViewModel homeViewModel)
{
InitializeComponent();

            BindingContext = homeViewModel;
        }
    
    }
    
    public class PageModel
    {
        public string DisplayName { get; set; }
        public string Description { get; set; }
        public ImageSource Image { get; set; }
        public string Route { get; set; }
        public List<KeyValuePair<string, object>> Parameters { get; set; }
    
        public PageModel(string name, string description, string imageSrc, string route, List<KeyValuePair<string, object>>? parameters = null)
        {
            DisplayName = name;
            Description = description;
            Image = ImageSource.FromResource(imageSrc);
            Route = route;
            Parameters = parameters ?? new List<KeyValuePair<string, object>>();
        }
    }
    
    [AddINotifyPropertyChangedInterface]
    public partial class HomeViewModel : BaseViewModel, INavigationAware
    {
        private readonly IApplicationState? _appState;
        private readonly INavigationService? _navigationService;
        private readonly ISyncService? _syncService;
    
        public ObservableCollection<PageModel>? Pages { get; set; }
    
        public ICommand PageSelected => new DelegateCommand<PageModel>(OnPageSelected);
    
        public HomeViewModel(
            IServiceProvider provider,
            Prism.Navigation.INavigationService pns
            )
        {
    
            //_appState = (IApplicationState?)provider.GetService(typeof(IApplicationState));
            _navigationService = (INavigationService?)provider.GetService(typeof(INavigationService));
            //_syncService = (ISyncService?)provider.GetService(typeof(ISyncService));
            SetItemsToDisplay();
        }
    
        public void SetItemsToDisplay()
        {
            try {
                var newPageList = new ObservableCollection<PageModel>{
                    new (Constants.ATrack.CASES, Constants.ATrack.CASES_DESCRIPTION, Constants.Images.ButtonImages.CASES, Routes.CASE_SELECTION),
                    new ($"{Constants.ATrack.UNSYNCED_ITEMS} ({_syncService?.GetUnsyncedEntities()?.Count.ToString() ?? "-"})", Constants.ATrack.UNSYNCED_ITEMS_DESCRIPTION, Constants.Images.ButtonImages.SYNC, Routes.UNSYNCED_ITEMS)
                };
    
                Pages = newPageList;
    
                if (_appState?.CurrentUser?.SystemConfig?.FinalizationsEnabled == true)
                {
                    var pageToAdd = new PageModel(
                        Constants.ATrack.FINALIZATIONS,
                        Constants.ATrack.FINALIZATIONS_DESCRIPTION,
                        Constants.Images.ButtonImages.CHECK,
                        Routes.FINALIZATIONS
                    );
    
                    Pages?.Add(pageToAdd);
                }
            }
            catch(Exception ex)
            {
                Debug.WriteLine(ex);
                Crashes.TrackError(ex);
            } 
        }

//Call to navigation service here:        
public async void OnPageSelected(PageModel page)
{
if (page != null)
{
var parameters = new NavigationParameters
{
{ Constants.General.TITLE.ToLowerInvariant(), page.DisplayName }
};

                foreach (var p in page.Parameters)
                {
                    parameters.Add(p.Key, p.Value);
                }
    
                await _navigationService.NavigateHere(page.Route, parameters);
            }
        }
    
        public void OnNavigatedFrom(Prism.Navigation.INavigationParameters parameters)
        {
        }
    
        public void OnNavigatedTo(Prism.Navigation.INavigationParameters parameters)
        {
           // stackCount = Application.Current.MainPage.Navigation.NavigationStack.Count;
            parameters.GetNavigationMode();
        }
    }

}


Navigation service from both projects:

C#
namespace ATrack.Mobile.Infrastructure.Services
{
public interface INavigationService
{
Task\<INavigationResult\> NavigateHere(string route, INavigationParameters parameters = null, bool animate = false);
Task\<INavigationResult\> GoBackAsync(INavigationParameters parameters = null, bool animate = false);
}

    public class NavigationService : INavigationService
    {
        private readonly IServiceProvider _sp;
        private readonly Prism.Navigation.INavigationService _pns;
        private readonly GlobalNavigationParameters _globalParameters;
    
        public NavigationService(IServiceProvider sp, Prism.Navigation.INavigationService pns, GlobalNavigationParameters globalParameters)
        {
            _sp = sp;
            _pns = pns;
            _globalParameters = globalParameters;
        }
    
        public async Task<INavigationResult> GoBackAsync(INavigationParameters parameters = null, bool animate = false)
        {
            try
            {
                var previousPage = Application.Current.MainPage.Navigation.NavigationStack[Application.Current.MainPage.Navigation.NavigationStack.Count - 2];
                var currentPage = Application.Current.MainPage.Navigation.NavigationStack[Application.Current.MainPage.Navigation.NavigationStack.Count - 1];
    
                var previousPageTitle = previousPage.GetType().Name;
    
                if (parameters != null)
                {
                    _globalParameters.CurrentParameters = parameters;
    
                    var pparameters = new Prism.Navigation.NavigationParameters();
    
                    foreach (var p in parameters)
                    {
                        pparameters.Add(p.Key, p.Value);
                    }
    
                    await _pns.NavigateAsync(previousPageTitle, pparameters);
                }
                else
                {
                    await currentPage.Navigation.PopAsync();
                }
            }
            catch (Exception e)
            {
                return new NavigationResult(false, e);
            }
    
            return new NavigationResult(true);
        }
    
        public async Task<INavigationResult> NavigateHere(string route, INavigationParameters parameters = null, bool animate = false)
        {
    
            var pparameters = new Prism.Navigation.NavigationParameters();
    
            foreach (var p in parameters)
            {
                var k = p.Key;
                var v = p.Value;
                pparameters.Add(p.Key, p.Value);
            }
    
            Prism.Navigation.INavigationResult res = null;
    
            res = await _pns.CreateBuilder().AddSegment(route).WithParameters(pparameters).NavigateAsync();
    
            return new NavigationResult(res.Success, res.Exception);
        }
    }
    
    public interface INavigationResult
    {
        bool Success { get; set; }
        Exception Exception { get; set; }
    }
    
    public class NavigationResult : INavigationResult
    {
        public bool Success { get; set; }
        public Exception Exception { get; set; }
    
        public NavigationResult(bool success, Exception e = null)
        {
            Success = success;
            Exception = e;
        }
    }
    
    public enum NavigationMode
    {
        Back = 0,
        New = 1
    }
    
    public interface INavigationParameters : IEnumerable<KeyValuePair<string, object>>, IEnumerable
    {
    }

    public class NavigationParameters : ParametersBase, INavigationParameters, INavigationParametersInternal
    {
        private readonly Dictionary<string, object> _internalParameters = new Dictionary<string, object>();
    

        public NavigationParameters()
        {
        }


        public NavigationParameters(string query)
            : base(query)
        {
        }
    
        #region INavigationParametersInternal
        void INavigationParametersInternal.Add(string key, object value)
        {
            _internalParameters.Add(key, value);
        }
    
        bool INavigationParametersInternal.ContainsKey(string key)
        {
            return _internalParameters.ContainsKey(key);
        }
    
        T INavigationParametersInternal.GetValue<T>(string key)
        {
            return ParametersExtensions.GetValue<T>(_internalParameters, key);
        }
        #endregion
    }
    
    public class GlobalNavigationParameters
    {
        public INavigationParameters CurrentParameters { get; set; }
    }

    public interface INavigatedAware
    {

        void OnNavigatedFrom(INavigationParameters parameters);
    
        void OnNavigatedTo(INavigationParameters parameters);
    }
    
    public interface INavigationAware : INavigatedAware
    {
    }

    public interface INavigationParametersInternal
    {
       
        void Add(string key, object value);
    
        bool ContainsKey(string key);

        T GetValue<T>(string key);
    }

}



codebehind containing the call to the navigation service on main project. (Found in OnPageSelected method):

C#
namespace ATrack.Mobile.Presentation.Pages.Home
{
    public partial class Home
    {
        public Home(HomeViewModel homeViewModel)
        {
            InitializeComponent();

            BindingContext = homeViewModel;
        }

    }

    public class PageModel
    {
        public string DisplayName { get; set; }
        public string Description { get; set; }
        public ImageSource Image { get; set; }
        public string Route { get; set; }
        public List<KeyValuePair<string, object>> Parameters { get; set; }

        public PageModel(string name, string description, string imageSrc, string route, List<KeyValuePair<string, object>>? parameters = null)
        {
            DisplayName = name;
            Description = description;
            Image = ImageSource.FromResource(imageSrc);
            Route = route;
            Parameters = parameters ?? new List<KeyValuePair<string, object>>();
        }
    }

    [AddINotifyPropertyChangedInterface]
    public partial class HomeViewModel : BaseViewModel, INavigationAware
    {
        private readonly IApplicationState? _appState;
        private readonly INavigationService? _navigationService;
        private readonly ISyncService? _syncService;

        public ObservableCollection<PageModel>? Pages { get; set; }

        public ICommand PageSelected => new DelegateCommand<PageModel>(OnPageSelected);

        public HomeViewModel(
            IServiceProvider provider,
            Prism.Navigation.INavigationService pns
            )
        {

            //_appState = (IApplicationState?)provider.GetService(typeof(IApplicationState));
            _navigationService = (INavigationService?)provider.GetService(typeof(INavigationService));
            //_syncService = (ISyncService?)provider.GetService(typeof(ISyncService));
            SetItemsToDisplay();
        }

        public void SetItemsToDisplay()
        {
            try {
                var newPageList = new ObservableCollection<PageModel>{
                    new (Constants.ATrack.CASES, Constants.ATrack.CASES_DESCRIPTION, Constants.Images.ButtonImages.CASES, Routes.CASE_SELECTION),
                    new ($"{Constants.ATrack.UNSYNCED_ITEMS} ({_syncService?.GetUnsyncedEntities()?.Count.ToString() ?? "-"})", Constants.ATrack.UNSYNCED_ITEMS_DESCRIPTION, Constants.Images.ButtonImages.SYNC, Routes.UNSYNCED_ITEMS)
                };

                Pages = newPageList;

                if (_appState?.CurrentUser?.SystemConfig?.FinalizationsEnabled == true)
                {
                    var pageToAdd = new PageModel(
                        Constants.ATrack.FINALIZATIONS,
                        Constants.ATrack.FINALIZATIONS_DESCRIPTION,
                        Constants.Images.ButtonImages.CHECK,
                        Routes.FINALIZATIONS
                    );

                    Pages?.Add(pageToAdd);
                }
            }
            catch(Exception ex)
            {
                Debug.WriteLine(ex);
                Crashes.TrackError(ex);
            } 
        }
//Call to navigation service here:        
public async void OnPageSelected(PageModel page)
        {
            if (page != null)
            {
                var parameters = new NavigationParameters
                {
                    { Constants.General.TITLE.ToLowerInvariant(), page.DisplayName }
                };

                foreach (var p in page.Parameters)
                {
                    parameters.Add(p.Key, p.Value);
                }

                await _navigationService.NavigateHere(page.Route, parameters);
            }
        }

        public void OnNavigatedFrom(Prism.Navigation.INavigationParameters parameters)
        {
        }

        public void OnNavigatedTo(Prism.Navigation.INavigationParameters parameters)
        {
           // stackCount = Application.Current.MainPage.Navigation.NavigationStack.Count;
            parameters.GetNavigationMode();
        }
    }
}


Class calling the navigation service in test project (Found in OnNextPageClickedCommand):

C#
`namespace MauiApp2
{
    public partial class LoginPage
    {
        public LoginPage(LoginPageViewModel vm)
        {
            InitializeComponent();
            BindingContext = vm;
        }
    }
        
    public partial class LoginPageViewModel : BaseViewModel, Prism.Navigation.INavigationAware
    {
        #region PRIVATE_VARS
        private readonly IServiceProvider _sp;
        private readonly MauiApp2.Services.INavigationService? _navigationService;
        #endregion

        [ObservableProperty]
        public int count;

        [ObservableProperty]
        public string countBtnText = "Click Me";

        [ObservableProperty]
        public int stackCount;

        public class ObjectHere
        {
            public string Name { get; set; }
            public string Description { get; set; }
        }

        #region COMMANDS
        public ICommand NextPageCommand => new DelegateCommand<ObjectHere>(OnNextPageClickedCommand);
        public ICommand CounterClickCommand => new DelegateCommand(OnCounterClickedCommand);
        #endregion

        public LoginPageViewModel(
            IServiceProvider sp
        )
        {
            _sp = sp;
            _navigationService = (MauiApp2.Services.INavigationService?)_sp.GetService(typeof(MauiApp2.Services.INavigationService));
        }

        #region PUBLIC METHODS
        private void OnCounterClickedCommand()
        {
            Count++;

            if (Count == 1)
                CountBtnText = $"Clicked {Count} time";
            else
                CountBtnText = $"Clicked {Count} times";

            SemanticScreenReader.Announce(CountBtnText);
        }

        //Call to navigation service here:  
              
        public async void OnNextPageClickedCommand(ObjectHere obj)
        {
            var parameters = new MauiApp2.Services.NavigationParameters()
            {
                { "Bob", "Bob" }
            };

            var obj2 = obj as ObjectHere;

            await _navigationService.NavigateAsync("/StuffPage", parameters);
        }

        public void OnNavigatedFrom(Prism.Navigation.INavigationParameters parameters)
        {
        }

        public void OnNavigatedTo(Prism.Navigation.INavigationParameters parameters)
        {
        }
        #endregion
    }
}`


Now, on the following line of code for the main project in the navigation service:

C#
public async Task\<INavigationResult\> NavigateHere(string route, INavigationParameters parameters = null, bool animate = false)
{

    var pparameters = new Prism.Navigation.NavigationParameters();
    
    foreach (var p in parameters)
    {
        var k = p.Key;
        var v = p.Value;
        pparameters.Add(p.Key, p.Value);
    }
    
    Prism.Navigation.INavigationResult res = null;
    
    res = await _pns.CreateBuilder().AddSegment(route).WithParameters(pparameters).NavigateAsync(); 
    
    return new NavigationResult(res.Success, res.Exception); //<== Here is where I am talking about.

}


I get the following exception here which is a {Prism.Ioc.ContainerResolutionException} of subtype {System.RuntimeType}, but NOT in the test project's navigation service:

Main error: An error occurred while resolving the page. This is most likely the result of invalid XAML or other type initialization exception
Error from inner exception : System.InvalidOperationException: DeclaringMethod can only be used on generic arguments


Stack trace (If needed) :

at Prism.Navigation.PageNavigationService.CreatePage(String segmentName) in D:\\a\\Prism\\Prism\\src\\Maui\\Prism.Maui\\Navigation\\PageNavigationService.cs:line 808
at Prism.Navigation.PageNavigationService.CreatePageFromSegment(String segment) in D:\\a\\Prism\\Prism\\src\\Maui\\Prism.Maui\\Navigation\\PageNavigationService.cs:line 815
at Prism.Navigation.PageNavigationService.ProcessNavigationForRootPage(String nextSegment, Queue`1 segments, INavigationParameters parameters, Nullable`1 useModalNavigation, Boolean animated) in D:\\a\\Prism\\Prism\\src\\Maui\\Prism.Maui\\Navigation\\PageNavigationService.cs:line 461
at Prism.Navigation.PageNavigationService.ProcessNavigation(Page currentPage, Queue`1 segments, INavigationParameters parameters, Nullable`1 useModalNavigation, Boolean animated) in D:\\a\\Prism\\Prism\\src\\Maui\\Prism.Maui\\Navigation\\PageNavigationService.cs:line 358
at Prism.Navigation.PageNavigationService.NavigateAsync(Uri uri, INavigationParameters parameters) in D:\\a\\Prism\\Prism\\src\\Maui\\Prism.Maui\\Navigation\\PageNavigationService.cs:line 301


NOTE: I was able to get the prism navigation service to work outside of a containing class in the main project by referencing it directly in a respective view's view model, but it is best to keep this logic inside of a containing class so that I don't have to change the implementation across the app, Obviously in keeping with SOLID design principles. ;) Here is a code example of that in the main project's home page view model shown earlier:

C#
public async void OnPageSelected(PageModel page)
{
    if (page != null)
    {
        var parameters = new Prism.Navigation.NavigationParameters
        {
            { Constants.General.TITLE.ToLowerInvariant(), page.DisplayName }
        };

        foreach (var p in page.Parameters)
        {
            parameters.Add(p.Key, p.Value);
        }

        await _pns.CreateBuilder().AddSegment(page.Route).WithParameters(parameters).NavigateAsync();
    }
}


What I have tried:

I have tried to ensure that all of my packages in NUGET are the correct versions between the 2 projects, deleted the bin and obj folders from the respective main project, and checked all of the discrepancies in the code within the 2 projects I could not find any

Any insight into how to fix this issue would be greatly appreciated. Thanks!
Posted
Comments
Pete O'Hanlon 24-Feb-24 17:06pm    
Don’t use void on async methods, they hide exceptions. Use Task as the return type.
Pete O'Hanlon 27-Feb-24 5:10am    
The stack trace clearly shows the line that the error occurs in PageNavigationService. What is happening on line 808 and line 815?
[no name] 27-Feb-24 16:26pm    
Your "style" makes it hard to place breakpoints; and therefore hard to debug. You're in for a lot of (future) pain.
Baraiboapex 2-Mar-24 17:28pm    
Hi Guys thanks for the tips! I do apologize for the confusing question. You are absolutely right this is a little much to take in all at once. Also, the avoiding the use of void on an async is a good idea I will see what I can do about this. I did manage to find another work around for this issue and now wondering if I try to implement this class again along with the work around that it will work. I will edit this question further afterwards. Cheers

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900