Shell navigation key error

Eduardo Gomez 3,416 Reputation points
2024-02-27T07:47:44.1533333+00:00

White navigating, and selecting pages back and forth, I get this error An item with the same key has already been added. Key: 0 I do not understand it, I cheek the stack and I found this.

   at System.ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException[T](T key)
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
   at DemyAI.ViewModels.NewLecturePageViewModel.<GetTimeZones>d__26.MoveNext() in C:\Users\egome\Documents\StartUp\DemyAI\ViewModels\NewLecturePageViewModel.cs:line 68
   at DemyAI.ViewModels.NewLecturePageViewModel.<Appearing>d__22.MoveNext() in C:\Users\egome\Documents\StartUp\DemyAI\ViewModels\NewLecturePageViewModel.cs:line 36
   at CommunityToolkit.Mvvm.Input.AsyncRelayCommand.<AwaitAndThrowIfFailed>d__40.MoveNext()
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0(Object state)
   at Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext.<>c__DisplayClass2_0.<Post>b__0()


But where I am using a dictionary? App: I do not know if this happens in phones, but in Windows happens.

Well, I have an interface.

```xml
public interface IAppService {
   
    Task NavigateTo(string pageName, bool isAnimated, Dictionary<string, object> obj);
    
    Task NavigateTo(string pageName, bool isAnimated);
    
    Task DisplayToast(string message, ToastDuration toastDuration, double fontSize);
    
    Task DisplayAlert(string title, string message, string cancelMessage);
}




and I have done this to navigate.

```java
public class AppService : IAppService {
    public async Task DisplayAlert(string title, string message, string cancelMessage) {
        await Shell.Current.DisplayAlert(title, message, cancelMessage);
    }
    public async Task DisplayToast(string message, ToastDuration toastDuration, double fontSize) {
        var toast = Toast.Make(message, toastDuration, fontSize);
        await toast.Show();
    }
    public async Task NavigateTo(string pageName, bool isAnimated, Dictionary<string, object> obj) {
        await Shell.Current.GoToAsync(pageName, isAnimated, obj);
    }
    public async Task NavigateTo(string pageName, bool isAnimated) {
        await Shell.Current.GoToAsync(pageName, isAnimated);
    }
}

I was trying to share the app

public partial class AppShellViewModel(IAppService appService) : BaseViewModel {
    [ObservableProperty]
    User? user;
    [ObservableProperty]
    bool isCoordinator;
    [ObservableProperty]
    bool isStudent;
    [ObservableProperty]
    bool isTeacher;
    [ObservableProperty]
    bool isRegisterOpen;
    [RelayCommand]
    async Task SignOut() {
        SecureStorage.Default.RemoveAll();
        await appService.NavigateTo($"//{nameof(LoginPage)}", true);
    }
}


I use it here also

public class RoleVisibility {
    public static async Task ManageFlyoutItemsVisibility(AppShellViewModel appShellViewModel,
        IDataService<User> dataService, string userUID, IAppService appService) {
        var UsersList = await dataService.GetAllAsync<User>("Users");
        var currentUser = UsersList.FirstOrDefault(user => user.Object.Email == userUID)?.Object;
        if(currentUser != null) {
            switch(currentUser.Role) {
                case nameof(Roles.Coordinator):
                    appShellViewModel.IsCoordinator = true;
                    break;
                case nameof(Roles.Teacher):
                    appShellViewModel.IsTeacher = true;
                    break;
                case nameof(Roles.Student):
                    appShellViewModel.IsStudent = true;
                    break;
            }
            appShellViewModel.User = currentUser;
            await appService.NavigateTo($"//{nameof(WelcomePage)}", true);
        } else {
            // Handle the case where currentUser is null (e.g., user not found)
            // You might want to show an error message or take appropriate action
        }

and the login page

public partial class LoginPageViewModel(IDataService<User> dataService, IAppService appService,
    IAuthenticationService authenticationService, AppShellViewModel appShellViewModel) : BaseViewModel {
    [ObservableProperty]
    bool isPopOpen;
    [ObservableProperty]
    User user = new();
    [RelayCommand]
    void OpenPopUp() {
        IsPopOpen = true;
    }
    [RelayCommand]
    async Task Login() {
        IsBusy = true;
        //var user = await authenticationService.LoginWithEmailAndPassword(User.Email!, User.Password!);
        var user = await authenticationService.LoginWithEmailAndPassword("admin@admin.com", "123456");
        if(user != null) {
            await SecureStorage.SetAsync("CurrentUser", user.Info.Email);
            await RoleVisibility.ManageFlyoutItemsVisibility(appShellViewModel, dataService, "admin@admin.com", appService);
            //await RoleVisibility.ManageFlyoutItemsVisibility(appShellViewModel, dataService, User.Email!, appService);
        }
        IsBusy = false;
    }
    [RelayCommand]
    async Task Register() {
        var IsFilled = await VerifyUserAsync(User);
        if(IsFilled) {
            IsBusy = true;
            var user = await authenticationService.RegisterWithEmailAndPassword(User.Email!, User.Password!,
                User.Name!);
            if(user != null) {
                User.DemyId = NumberGenerator.GenerateRandomNumberString(8);
                User.Uid = await dataService.AddAsync("Users", User);
                await appService.DisplayAlert("Congratulations", "Registration successful", "OK");
                IsPopOpen = false;
                IsBusy = false;
            }
        }
    }
    private async Task<bool> VerifyUserAsync(User user) {
        if(string.IsNullOrEmpty(user.Name)
            && string.IsNullOrEmpty(user.Email)
            && string.IsNullOrEmpty(user.Role)) {
            await appService.DisplayAlert("Error", "Please make sure everything is filed", "OK");
            return false;
        }
        return true;
    }
}


XAML

<Shell
    x:Class="DemyAI.AppShell"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:badge="clr-namespace:Syncfusion.Maui.Core;assembly=Syncfusion.Maui.Core"
    xmlns:helpers="clr-namespace:DemyAI.Helpers"
    xmlns:mct="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    xmlns:sfavatar="clr-namespace:Syncfusion.Maui.Core;assembly=Syncfusion.Maui.Core"
    xmlns:view="clr-namespace:DemyAI.Views"
    xmlns:vm="clr-namespace:DemyAI.ViewModels"
    Title="DemyAI"
    x:DataType="vm:AppShellViewModel"
    FlyoutBehavior="{OnIdiom Desktop=Locked,
                             Phone=Flyout}">
    <Shell.FlyoutHeader>
        <Grid>
            <BoxView
                BackgroundColor="#522cd4"
                HeightRequest="142" />
            <VerticalStackLayout>
                <Image
                    HeightRequest="80"
                    Source="user.png"
                    WidthRequest="80" />
                <Label
                    HorizontalTextAlignment="Center"
                    Text="{Binding User.DemyId}"
                    TextColor="White" />
                <Label
                    HorizontalTextAlignment="Center"
                    Text="{Binding User.Email}"
                    TextColor="White" />
                <Label
                    HorizontalTextAlignment="Center"
                    Text="{Binding User.Role}"
                    TextColor="White" />
            </VerticalStackLayout>
        </Grid>
    </Shell.FlyoutHeader>
    <Shell.FlyoutFooterTemplate>
        <DataTemplate>
            <Button
                Margin="20"
                Command="{Binding SignOutCommand}"
                CornerRadius="8"
                Text="Log out" />
        </DataTemplate>
    </Shell.FlyoutFooterTemplate>
    <FlyoutItem
        Title="Notifications"
        IsVisible="{Binding IsTeacher}">
        <ShellContent
            ContentTemplate="{DataTemplate view:NotificationsPage}"
            Route="NotificationsPage" />
    </FlyoutItem>
    <ShellContent
        ContentTemplate="{DataTemplate view:LoginPage}"
        Route="LoginPage"
        Shell.FlyoutBehavior="Disabled"
        Shell.FlyoutItemIsVisible="False" />
    <ShellContent
        ContentTemplate="{DataTemplate view:WelcomePage}"
        Route="WelcomePage"
        Shell.FlyoutItemIsVisible="False" />
    <FlyoutItem
        Title="My courses"
        IsVisible="{Binding IsStudent}">
        <ShellContent
            ContentTemplate="{DataTemplate view:CoursesPage}"
            Route="CoursesPage" />
    </FlyoutItem>
    <FlyoutItem Title="Join Meeting">
        <ShellContent
            ContentTemplate="{DataTemplate view:JoinMeetingPage}"
            Route="JoinMeetingPage" />
    </FlyoutItem>
    <FlyoutItem
        Title="New lecture"
        IsVisible="{Binding IsTeacher}">
        <ShellContent
            ContentTemplate="{DataTemplate view:NewLecturePage}"
            Route="NewLecturePage" />
    </FlyoutItem>
    <FlyoutItem
        Title="New test"
        IsVisible="{Binding IsTeacher}">
        <ShellContent
            ContentTemplate="{DataTemplate view:NewTestPage}"
            Route="NewTestPage" />
    </FlyoutItem>
    <FlyoutItem
        Title="Schedule lecture"
        IsVisible="{Binding IsTeacher}">
        <ShellContent
            ContentTemplate="{DataTemplate view:ScheduleLecturePage}"
            Route="ScheduleLecturePage" />
    </FlyoutItem>
    <FlyoutItem
        Title="Schedule test"
        IsVisible="{Binding IsTeacher}">
        <ShellContent
            ContentTemplate="{DataTemplate view:ScheduleTestPage}"
            Route="ScheduleLecturePage" />
    </FlyoutItem>
    <FlyoutItem
        Title="Manage courses"
        IsVisible="{Binding IsCoordinator}">
        <ShellContent
            ContentTemplate="{DataTemplate view:ManageCoursePage}"
            Route="ManageCoursePage" />
    </FlyoutItem>
</Shell>


Finally I use it here.

public partial class App : Application {
    private readonly IAppService _appService;
    private readonly IAuthenticationService _authenticationService;
    private readonly IDataService<User> dataService;
    private readonly AppShellViewModel _shellViewModel;
    public App(AppShell shell, IAppService appService, IAuthenticationService authenticationService,
        IDataService<User> dataService, AppShellViewModel shellViewModel) {
        InitializeComponent();
        Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense(Constants.LICENSE);
        _appService = appService;
        _authenticationService = authenticationService;
        _shellViewModel = shellViewModel;
        MainPage = shell;
        GetUserUID(shellViewModel, dataService, appService);
    }
    private async void GetUserUID(AppShellViewModel shell, IDataService<User> dataService, IAppService appService) {
        var CurrentUserEmail = await SecureStorage.GetAsync("CurrentUser");
        if(string.IsNullOrEmpty(CurrentUserEmail)) {
            await _appService.NavigateTo($"//{nameof(LoginPage)}", true);
        } else {
            await RoleVisibility.ManageFlyoutItemsVisibility(shell, dataService, CurrentUserEmail, appService);
        }
    }
}
.NET MAUI
.NET MAUI
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
2,822 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Eduardo Gomez 3,416 Reputation points
    2024-02-28T19:46:58.7733333+00:00

    I fixed the error thanks; I had every page as singleton

    But now if I am a Teacher I don't all the option that aa teacher should have.

     <Shell.FlyoutHeader>
           <Grid>
               <BoxView
                   BackgroundColor="#522cd4"
                   HeightRequest="142" />
               <VerticalStackLayout>
                   <Image
                       HeightRequest="80"
                       Source="user.png"
                       WidthRequest="80" />
                   <Label
                       HorizontalTextAlignment="Center"
                       Text="{Binding User.DemyId}"
                       TextColor="White" />
                   <Label
                       HorizontalTextAlignment="Center"
                       Text="{Binding User.Email}"
                       TextColor="White" />
                   <Label
                       HorizontalTextAlignment="Center"
                       Text="{Binding User.Role}"
                       TextColor="White" />
               </VerticalStackLayout>
           </Grid>
       </Shell.FlyoutHeader>
       <Shell.FlyoutFooterTemplate>
           <DataTemplate>
               <Button
                   Margin="20"
                   Command="{Binding SignOutCommand}"
                   CornerRadius="8"
                   Text="Log out" />
           </DataTemplate>
       </Shell.FlyoutFooterTemplate>
       <FlyoutItem
           Title="Notifications"
           IsVisible="{Binding IsTeacher}">
           <ShellContent
               ContentTemplate="{DataTemplate view:NotificationsPage}"
               Route="NotificationsPage" />
       </FlyoutItem>
       <ShellContent
           ContentTemplate="{DataTemplate view:LoginPage}"
           Route="LoginPage"
           Shell.FlyoutBehavior="Disabled"
           Shell.FlyoutItemIsVisible="False" />
       <ShellContent
           ContentTemplate="{DataTemplate view:WelcomePage}"
           Route="WelcomePage"
           Shell.FlyoutItemIsVisible="False" />
       <FlyoutItem
           Title="My courses"
           IsVisible="{Binding IsStudent}">
           <ShellContent
               ContentTemplate="{DataTemplate view:CoursesPage}"
               Route="CoursesPage" />
       </FlyoutItem>
       <FlyoutItem Title="Join Meeting">
           <ShellContent
               ContentTemplate="{DataTemplate view:JoinMeetingPage}"
               Route="JoinMeetingPage" />
       </FlyoutItem>
       <FlyoutItem
           Title="New lecture"
           IsVisible="{Binding IsTeacher}">
           <ShellContent
               ContentTemplate="{DataTemplate view:NewLecturePage}"
               Route="NewLecturePage" />
       </FlyoutItem>
       <FlyoutItem
           Title="New test"
           IsVisible="{Binding IsTeacher}">
           <ShellContent
               ContentTemplate="{DataTemplate view:NewTestPage}"
               Route="NewTestPage" />
       </FlyoutItem>
       <FlyoutItem
           Title="Schedule lecture"
           IsVisible="{Binding IsTeacher}">
           <ShellContent
               ContentTemplate="{DataTemplate view:ScheduleLecturePage}"
               Route="ScheduleLecturePage" />
       </FlyoutItem>
       <FlyoutItem
           Title="Schedule test"
           IsVisible="{Binding IsTeacher}">
           <ShellContent
               ContentTemplate="{DataTemplate view:ScheduleTestPage}"
               Route="ScheduleLecturePage" />
       </FlyoutItem>
       <FlyoutItem
           Title="Manage courses"
           IsVisible="{Binding IsCoordinator}">
           <ShellContent
               ContentTemplate="{DataTemplate view:ManageCoursePage}"
               Route="ManageCoursePage" />
       </FlyoutItem>
    
    public partial class AppShellViewModel(IAppService appService) : BaseViewModel {
        [ObservableProperty]
        User? user;
        [ObservableProperty]
        bool isCoordinator;
        [ObservableProperty]
        bool isStudent;
        [ObservableProperty]
        bool isTeacher;
        [ObservableProperty]
        bool isRegisterOpen;
        [RelayCommand]
        async Task SignOut() {
            SecureStorage.Default.RemoveAll();
            await appService.NavigateBack();
        }
    }
    
    
    

    MauiProgram

               builder.Logging.AddDebug();
    #endif
                builder.Services.AddSingleton<HttpClient>();
                builder.Services.AddSingleton(AudioManager.Current);
                builder.Services.AddTransient<AppShell, AppShellViewModel>();
                builder.Services.AddTransient<LoginPage, LoginPageViewModel>();
                builder.Services.AddTransient<WelcomePage, WelcomePageViewModel>();
                builder.Services.AddTransient<CoursesPage, CoursesPageViewModel>();
                builder.Services.AddTransient<NewLecturePage, NewLecturePageViewModel>();
                builder.Services.AddTransient<NewTestPage, NewTestPageViewMode>();
                builder.Services.AddTransient<ScheduleLecturePage, ScheduleLecturePageViewModel>();
                builder.Services.AddTransient<ScheduleTestPage, ScheduleTestPageViewModel>();
                builder.Services.AddTransient<NotificationsPage, NotificationsPageViewModel>();
                builder.Services.AddTransient<JoinMeetingPage, JoinMeetingPageViewModel>();
                builder.Services.AddTransient<NoInternetPage>();
                builder.Services.AddTransient<ManageCoursePage, ManageCoursePageViewModel>();
                builder.Services.AddSingleton<IAppService, AppService>();
                builder.Services.AddTransient(typeof(IDataService<>), typeof(DataService<>));
                builder.Services.AddSingleton<IAuthenticationService, AuthenticationService>();
                builder.Services.AddSingleton<IHttpService, HttpService>();
                builder.Services.AddSingleton(Connectivity.Current);
                builder.Services.AddSingleton(firebaseAuthClient);
                builder.Services.AddSingleton<IAuthenticationService>(serviceProvider => {
                    var authService = serviceProvider.GetRequiredService<FirebaseAuthClient>();
                    var appService = serviceProvider.GetRequiredService<IAppService>();
                    return new AuthenticationService(authService, appService);
                });
                builder.Services.AddSingleton<IMeetingService, MeetingService>();
    
    
    

    I think is because the sell loads firs before everything

    0 comments No comments