Introduction
This tip is aimed at providing a simple solution for simple page to page navigation system for your UWP apps. This is by no means the best solution but is a valid solution for 90% of the scenarios. This even automates the Navigation without the need to hooking up event handlers everywhere.
Background
- You are expected to know a bit about the Stack Datastructure and its functions
- You must have at least minimum experience with any Windows development platform
- You must have the curiosity to learn
- Singleton Pattern (because we are going to explore a whole new way of implementing a Singleton)
- Static classes and Static fields
Static-Keyword
A Static
field of a Class
can be accessed anywhere globally.
In the example below, b
is accessible without having to create an object while for k
to be accessed, an object of the animal
class should be created and then accessed.
public class Animal
{
public static int b = 90;
public int k;
}
public class Dog
{
public Dog()
{
var a = Animal.b;
}
}
The NEW Singleton Pattern
This pattern allows the user to instantiate an object of the specified class but only once throughout the application.
This is possible due to the static
keyword.
Here the Instance
is marked static
and hence is shared among all the Navigationservice
that will be instantiated by using the constructor. Hence allowing us to check if the instance is null
and if it isn't, it means one object has already been created and can't be created twice.
public class NavigationService
{
public static NavigationService Instance { get; protected set; }
public NavigationService()
{
if (Instance != null)
{
throw new Exception
("Only one navigation service can exist in a App.");
}
Instance = this;
}
The Main Article - NavigationService
The NavigationService
class uses the stack to hold the pages to navigateback.
public class NavigationService
{
public static NavigationService Instance { get; protected set; }
private Frame frame;
public Stack<Type> PageStack { get; protected set; }
#region CTOR
public NavigationService( ref Frame frame )
{
if (Instance != null)
{
throw new Exception("Only one navigation service can exist in a App.");
}
Instance = this;
this.frame = frame;
this.PageStack = new Stack<Type>();
SystemNavigationManager.GetForCurrentView().BackRequested +=
NavigationService_BackRequested;
if (ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))
{
Windows.Phone.UI.Input.HardwareButtons.BackPressed +=
HardwareButtons_BackPressed; ;
}
}
#endregion
#region Navigation Methods
public void NavigateTo( Type pageType , object parameter )
{
if (PageStack.Count > 0)
{
if (PageStack.Peek() == pageType)
return;
}
PageStack.Push(pageType);
frame.Navigate(pageType , parameter);
UpdateBackButtonVisibility();
}
public void NavigateBack()
{
if (frame.CanGoBack)
frame.GoBack();
PageStack.Pop();
UpdateBackButtonVisibility();
}
public void NavigateToHome()
{
while (frame.CanGoBack)
frame.GoBack();
}
#endregion
#region BackButtonVisibilty Region
void UpdateBackButtonVisibility()
{
SystemNavigationManager.GetForCurrentView().
AppViewBackButtonVisibility = frame.CanGoBack ?
AppViewBackButtonVisibility.Visible :
AppViewBackButtonVisibility.Collapsed;
}
#endregion
#region Event Methods for windows and phone
private void NavigationService_BackRequested
( object sender , BackRequestedEventArgs e )
{
this.NavigateBack();
}
private void HardwareButtons_BackPressed
( object sender , Windows.Phone.UI.Input.BackPressedEventArgs e )
{
this.NavigateBack();
}
#endregion
}
In this code, let's look at the NavigateTo()
method.
- First see if stack has anything.
- If it has, go ahead and check if the first one is equal to the page we are trying to navigate to, if it's equal, then we need not navigate anywhere.
- Else, we are going to simply push the page up the stack and then try Navigate to the page.
- Update the
AppBackButton
's Visibility in the UWP app.
Now, we need to instantiate it in the App so that it is globally accessible to all the classes in this project.
public static NavigationService NavService { get; protected set; }
The rootFrame
is hooked up in this way.
NavService = new NavigationService(ref rootFrame);
The ref
is passed because we want the original object itself but not a copy of it.
Consuming this services in a page:
<StackPanel HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Orientation="Horizontal">
<RadioButton GroupName="Tabs"
Content="Alarms"
Tag=""
FontSize="20"
Style="{StaticResource TabStyleWithGlyph}"
Checked="RadioButton_Checked"/>
<RadioButton GroupName="Tabs"
Content="Timer"
Tag=""
FontSize="20"
Checked="RadioButton_Checked"
Style="{StaticResource TabStyleWithGlyph}" />
<RadioButton GroupName="Tabs"
Content="StopWatch"
Tag=""
FontSize="20"
Checked="RadioButton_Checked"
Style="{StaticResource TabStyleWithGlyph}" />
</StackPanel>
<Frame Grid.Row="1"
x:Name="rootFrame"
x:FieldModifier="public">
</Frame>
The code behind is just a switch
case to navigate from one page to another:
private void RadioButton_Checked( object sender , RoutedEventArgs e )
{
switch ((sender as RadioButton).Content.ToString())
{
case "Alarms":
App.NavService.NavigateTo(typeof(Alarms) , null);
break;
case "Timer":
App.NavService.NavigateTo(typeof(TImer) , null);
break;
case "StopWatch":
App.NavService.NavigateTo(typeof(StopWatch) , null);
break;
default:
break;
}
}
The end result app as provided by the project:
Hope you guys had fun!