|
In the past few days I have been working to restructure the login and registration navigation for a Windows Phone 7 application I have been building. This project is for the startup I’m involved with called QONQR. QONQR is a complex interactive game with a backend service. Without going through the whole QONQR story, let me offer a quick summary: using GPS location to play, you must join a team with which you battle for world domination.
Why did I need to place structure on the way I had coded the login/registration process? It has everything to do with the WP7 Back Stack. WP7 remembers all the pages you have visited in your application and will send you back to each page in order when you hit the back button, without you the developer needing to keep track of the pages. Awesome, right? Not so much if the back button sends your users back through the registration process. You don’t ever want them to see those pages again once they have completed the registration. This is the problem we will explore below.
In the following section I outline the registration navigation for the QONQR application in detail. You can skip all that and go down to the solution below.
The needs of the game makes the signup process a long one. First we start with the welcome screen, which appears when the game loads. [See Welcome Screen]
When the user hits PLAY we show a pivot allowing the user to either LOGIN, or REGISTER. [See Login and Register Screens]
If the user is new to the game and must register, we collect the information you see on the screen shot and validate the necessary fields. The user must then learn all about QONQR by reading the back STORY. [See the Story Screen] Once they learn what they are fighting for, they must choose the FACTION (team) with which they want to fight for world domination. [See the Select Faction Screen]
At this point the user has registered and is logged into our application. For the sake of completeness, this is the place holder page I show once the user logs in. Let’s call this the HOME page. [See the Home Screen]
So let’s review. This is the navigation path of our application.
The top indicates normal navigation through the application for a variety of scenarios. You can get directly from the WELCOME screen to the HOME if you auto-login (saved credentials from a previous session). If you use the LOGIN page, you will jump to the HOME once login is complete. Finally there is the REGISTER path I outlined above. There is one additional step. Notice we need your birthday to make sure you are at least 13 years old. In WP7 this is done through a data picker control. When you display a date picker you actually trigger a navigation event, you leave the page that launched it and then navigate back to the source page. The date picker is not a popup. [See Select Birth Date Screen]
So the top arrows representing standard navigation forward through the application are pretty easy to follow, even if it looks less than simple at a high level. The back button navigation isn’t too difficult either. Notice that the pivots both go back to the previous page. The back button doesn’t go back to the previous pivot, but rather the previous page. This is standard back button behavior in WP7.
THE PROBLEM
The whole problem is the lowest (red) arrow in the navigation diagram. The back button needs to go from the HOME screen to the WELCOME screen, regardless of how the user got there. If it wasn’t for this one red arrow, I would not have had to do anything to restructure my app, which was previously setup with distinct pages for each registration step.
THE SOLUTION
The inspiration for the solution was found in this blog post by Peter Torr.
First, because the back button needed to go from the HOME screen to the WELCOME screen, all of the screens in blue (in the navigation diagram) must live on the same page. You cannot control the back stack, so the alternative is to manually manage the registration navigation forward and back in a single page, rather than using “Page Navigation”.
The Main Page has the WELCOME screen at its core. Then there is a pivot containing the LOGIN/REGISTER UI and another pivot that contains the STORY/FACTION UI. Both pivots start collapsed (hidden). I use behaviors to change the View State of the Main Page to show and hide the proper pivot, as the user moves through the navigation process. I also use Boolean properties on the view model to keep track of the current view state, and the currently selected tab in each pivot. These properties will become important when I discuss Tombstoning.
private bool _showWelcome;
public bool ShowWelcome
{
get { return _showWelcome; }
set { _showWelcome = value; RaisePropertyChanged("ShowWelcome"); }
}
private bool _showLoginPivot;
public bool ShowLoginPivot
{
get { return _showLoginPivot; }
set { _showLoginPivot = value; RaisePropertyChanged("ShowLoginPivot"); }
}
private bool _showFactionPivot;
public bool ShowFactionPivot
{
get { return _showFactionPivot; }
set { _showFactionPivot = value; RaisePropertyChanged("ShowFactionPivot"); }
}
private int _loginPTabIndex;
public int LoginTabIndex
{
get { return _loginTabIndex; }
set { _loginTabIndex = value; RaisePropertyChanged("LoginTabIndex"); }
}
private int _factionTabIndex;
public int FactionTabIndex
{
get { return _factionTabIndex; }
set { _factionTabIndex = value; RaisePropertyChanged("FactionTabIndex"); }
}
I use this code to catch the back button and correctly set the UI. This code is in the code behind of the main page. Notice the ViewModel decides if it should handle the back button, and thus cancel the back button event from using the built in back stack.
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
if (this.ViewModel != null)
{
e.Cancel = this.ViewModel.GoBack();
}
}
This code is in the View Model
public bool GoBack()
{
if (this.ShowLoginPivot)
{
GoToWelcomeView(true);
return true;
}
else if (this.ShowFactionPivot)
{
GoToLoginView(this.LoginTabIndex, true);
return true;
}
else
{
return false;
}
}
I didn’t include the GoTo[X]View methods referenced in the sample, but they set the ViewState of the view (to transition the UI) and set the Boolean flags to keep track of the current state of the UI. The true parameters indicate that storyboard transitions should be used.
By managing the long list of UIs in blue (in the navigation diagram) in this way, I have manual control of the back button for all the registration UIs, and the backstack that WP7 maintains works properly once the user has logged in.
TOMBSTONING
Finally, by tracking the state of the UI using the properties I’ve listed above, I can store these setting to Isolated Storage during tombstoning and rehydrate them during activation. The forward and back logic works perfectly after reactivation and there is no concern for restarting the application in the middle of a registration process. Oh… remember the date picker and how this triggers a navigation event? When you return from selecting a birthdate, the view model contains which pivot was visible and which tab of the pivot was the current tab when you left. The same logic runs to reactivate the Main Page on a navigation, in the same location and state as when you navigated away. No additional work needed, beyond what already was done to handle the reactivation from tombstoning.
CONCLUSION
Peter’s blog post I referenced earlier, suggests using a popup to handle multiple UIs in a single page, but I’ve read popups have some performance issues. Plus setting the visibility properties of the pivots (set to fill the entire screen) is simple and the storyboard animations can make slick transitions within the page. In the end, I wish I had considered the backstack issue identified by the red arrow in the navigation diagram before I started. This was clearly an instance where thinking ahead would have saved me significant rework. Hopefully this will help you avoid the same rework.
We hope to have QONQR in the WP7 marketplace by late summer or early fall.
|