I'm creating a Xamarin.Forms project, using XAML for markup that includes an authentication page when the app starts. I am using a TabbedPage (also tried CarouselPage) as the container of my main UI and plan to have several pages displayed. The first page (myDashboard) checks to see if the user has authenticated and if not I want to display a modal login page. When they return I would like to load data based on their user id.
What's happening is that the Dashboard page loads and is immediately overlaid by the myLogin page, but then all the DisplayAlerts show in succession before I do anything. When I call PopModalAsync() on the login page the OnAppearing method doesn't continue, which indicates to me that the await call had no effect in interrupting the process flow, as if it's already completed the calls after the PushModalAsync() call. (I plan to remove the DisplayAlert calls when it's working as expected.)
My code looks like this...in the App class:
var mainPage = new TabbedPage() { Children = { new myDashboard() } }; return mainPage;
In the myDashboard() class:
protected async override void OnAppearing() { base.OnAppearing(); try { await DisplayAlert("Alert", "OnAppearing Start", "ok", null); if (App.context.IsLoggedIn == false) { await Navigation.PushModalAsync(new myLogin()); if (App.context.IsLoggedIn == true) { // await LoadData(); PageLabel.Text = "Hello " + App.context.UserInfo; } } await DisplayAlert("Alert", "OnAppearing LoggedIn", "ok", null); await LoadData(); await DisplayAlert("Alert", "OnAppearing Complete", "ok", null); } catch (Exception ex) { var err = ex.Message; } }
What am I missing?
It appears that because this code is in the OnAppearing() event which is called during a Pop or a Push that the behavior isn’t what is expected. I found an approach in the Xamarin Forums which wires up an event to the login page to send an event and then subscribe to it from the caller (see how-to-navigate-between-contentpages). I removed the code in the OnAppearing event, reserving it for handling core initialization and if the user is logged in I’ll load the data. To implement this approach subscribe to the event in the constructor of your main page…
public myDashboard() { InitializeComponent(); MessagingCenter.Subscribe<myLogin>(this, "LoginComplete",(sender) => LoadData()); }
Then in the login page add code to send the message right after calling PopModalAsync() to close the window.
public async void OnButtonClicked(object sender, EventArgs e) { var rc = await DisplayAlert("Alert!", "Logged in as " + myPhone.Text, "Ok", "Cancel"); if (rc == true) { App.context.IsLoggedIn = true; await Navigation.PopModalAsync(); MessagingCenter.Send<myLogin>(this, "LoginComplete"); } }
It works, but is there a better way?