Reputation: 85
Extremely frustrated with Shell Navigation, hope someone can help
Flow
App Shell XAML
<ShellContent
Title="LandingPage"
ContentTemplate="{DataTemplate local:LandingPage}"
Route="LandingPage" />
</Shell>
AppShell.xaml.cs
public AppShell()
{
InitializeComponent();
We dont register this route because its already defined in xaml
// Routing.RegisterRoute(nameof(LandingPage), typeof(LandingPage));
Routing.RegisterRoute(nameof(StoreItems), typeof(StoreItems));
Routing.RegisterRoute(nameof(Register), typeof(Register));
Routing.RegisterRoute(nameof(PasswordRecovery), typeof(PasswordRecovery));
Routing.RegisterRoute(nameof(NewStoreRequest), typeof(NewStoreRequest));
Routing.RegisterRoute(nameof(AccountInfo), typeof(AccountInfo));
Routing.RegisterRoute(nameof(AllPromotions), typeof(AllPromotions));
Routing.RegisterRoute(nameof(AllStores), typeof(AllStores));
Routing.RegisterRoute(nameof(ChangePassword), typeof(ChangePassword));
Routing.RegisterRoute(nameof(Checkout), typeof(Checkout));
Routing.RegisterRoute(nameof(CreateAddress), typeof(CreateAddress));
Routing.RegisterRoute(nameof(TrackOrder), typeof(TrackOrder));
Routing.RegisterRoute(nameof(TrackOrderDetails), typeof(TrackOrderDetails));
Routing.RegisterRoute(nameof(StoreBasket), typeof(StoreBasket));
Routing.RegisterRoute(nameof(ProcessingPayment), typeof(ProcessingPayment));
Routing.RegisterRoute(nameof(PaymentConfirmed), typeof(PaymentConfirmed));
Routing.RegisterRoute(nameof(Login), typeof(Login));
}
MauiProgram.cs (UPDATED)
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureSyncfusionCore()
.UseMauiCommunityToolkit()
.ConfigureFonts(fonts =>
{
fonts.AddFont("FontAwesome6BrandsRegular400.otf", "BrandsRegular");
fonts.AddFont("FontAwesome6DuotoneSolid900.otf", "DuoToneSolid");
fonts.AddFont("FontAwesome6ProLight300.otf", "ProLight");
fonts.AddFont("FontAwesome6ProRegular.otf", "ProRegular");
fonts.AddFont("FontAwesome6ProSolid900.otf", "ProSolid");
fonts.AddFont("FontAwesome6ProThin100.otf", "ProThin");
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
// Services
builder.Services.AddSingleton<INippyDataService, NippyDataService>();
// Views
builder.Services.AddTransient<LandingPage>();
builder.Services.AddTransient<StoreItems>();
builder.Services.AddSingleton<Login>();
builder.Services.AddSingleton<Register>();
builder.Services.AddSingleton<PasswordRecovery>();
builder.Services.AddTransient<AccountInfo>();
builder.Services.AddTransient<AllPromotions>();
builder.Services.AddTransient<AllStores>();
builder.Services.AddSingleton<ChangePassword>();
builder.Services.AddTransient<Checkout>();
builder.Services.AddTransient<CreateAddress>();
builder.Services.AddSingleton<NewStoreRequest>();
builder.Services.AddTransient<TrackOrder>();
builder.Services.AddTransient<TrackOrderDetails>();
builder.Services.AddTransient<StoreBasket>();
builder.Services.AddTransient<ProcessingPayment>();
builder.Services.AddTransient<PaymentConfirmed>();
// View Models
builder.Services.AddTransient<LandingPageVM>();
builder.Services.AddTransient<StoreItemsVM>();
builder.Services.AddSingleton<LoginVM>();
builder.Services.AddSingleton<RegisterVM>();
builder.Services.AddSingleton<PasswordRecoveryVM>();
builder.Services.AddTransient<AccountInfoVM>();
builder.Services.AddTransient<AllPromotionsVM>();
builder.Services.AddTransient<AllStoresVM>();
builder.Services.AddSingleton<ChangePasswordVM>();
builder.Services.AddTransient<CheckoutVM>();
builder.Services.AddTransient<CreateAddressVM>();
builder.Services.AddSingleton<NewStoreRequestVM>();
builder.Services.AddTransient<TrackOrderVM>();
builder.Services.AddTransient<TrackOrderDetailsVM>();
builder.Services.AddTransient<StoreBasketVM>();
builder.Services.AddTransient<ProcessingPaymentVM>();
builder.Services.AddTransient<PaymentConfirmedVM>();
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}
Ive tried these
await Shell.Current.GoToAsync("..", true); (Hangs and eventually times out)
await Shell.Current.GoToAsync(nameof(LandingPage), true); (Relative routing error try ..// etc)
Crazy thing, back from a different page
LandingPage -> AllStores
await Shell.Current.GoToAsync("..", true);
Its takes me to CreateAddress???
** Updated ** (Account button click code that takes the user to login)
[RelayCommand]
async Task BtnAccount()
{
if (CurrentUser != null)
{
await Shell.Current.GoToAsync(nameof(AccountInfo), true);
}
else
{
bool result = await Application.Current.MainPage.DisplayAlert("Account Required", $"You must be logged in to view Account details, do you wish to login now?", "Yes", "No");
if (result)
{
await Shell.Current.GoToAsync(nameof(Login), true);
}
}
}
Login Button click
[RelayCommand]
async Task Login()
{
if (IsBusy)
return;
try
{
IsBusy = true;
if (ValidateFormData())
{
User user = await _dataService.LoginUserAsync(Email, Password);
if (user != null)
{
user.Order.Charity = await _dataService.GetCharityAsync();
if (user.ContactDetails.Email != null)
{
SharedSettings.SetCurrentUser(user);
if (!user.ChangePassword)
{
try
{
var p = Shell.Current.Navigation.NavigationStack;
await Shell.Current.GoToAsync(nameof(LandingPage), true);
}
catch (Exception ex)
{
var p = ex.Message;
}
}
else
{
await Shell.Current.GoToAsync(nameof(ChangePassword), true);
}
}
else
{
await Shell.Current.DisplayAlert("Error!", "Login Failed", "OK");
}
}
else
{
await Shell.Current.DisplayAlert("Error!", $"Login Failed, account not found", "OK");
}
}
else
{
await Shell.Current.DisplayAlert("Error!", ErrorMessage, "OK");
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
await Shell.Current.DisplayAlert("Error!", $"Service is not responding, please try again later", "OK");
}
finally
{
IsBusy = false;
}
}
UPDATED The navigation stack in postion 0 is null and position 1 is the login page. I have change the navigation to use nameof(LandingPage) to create a new instance of the page instead of the ".." (pop) method however im still getting the hang and time out
Upvotes: 1
Views: 604
Reputation: 85
Problem solved
The issue is not with navigation rather a deadlock situation when the LandingPage is loading after a user logs in. The below http method caused the hang.
Adding .ConfigureAwait(false) to the call resolved it. Info: https://devblogs.microsoft.com/dotnet/configureawait-faq/
public async Task<List<Address>> GetUserAddressesAsync(string email)
{
List<Address> data = new List<Address>();
if (WebHelper.HasInternetConnection())
{
try
{
HttpResponseMessage response = await _httpClient.GetAsync($"{_api}/address/GetUserAddresses.php?email={email}");
string json = await response.Content.ReadAsStringAsync();
data = JsonConvert.DeserializeObject<List<Address>>(json);
}
catch (Exception ex)
{
Debug.WriteLine("----> " + ex.Message);
}
}
return data;
}
Thanks everyone for the help, 3 days of pain to get moving again.
Upvotes: 0
Reputation: 85
Turns out the Create Address page was corrupted somehow. Navigating to a different page than Login and attempting to navigate back using GoToAsync("..",true) kept taking me to Create Address.
I looked over everything on this page, including the code behind and View Model and i couldnt fault it. Once it was removed (Backup and ill create it again), i was able to Navigate from login back to the LandingPage using GoToAsync("..",true)
Upvotes: 0
Reputation: 3897
Make all pages transient.
You can navigate to your ShellContents, and keep them the only element on the stack, you cannot do this to routes however.
This is a problem in your case, 9/10 you will want login to be the only page in the stack. And then again, the shell main page, would be naturally another root.
So you need them both registered at the same level. (The top most is the entry)
Fix this, if you still have problems, ask.
Edit: To clarify a bit. You will want to jump between with "//MainPage" , "//LoginPage" to clear the stack.
Upvotes: 0