Once you created Xamarin.Forms Shell App, by adapting Blank template or by reducing shell template to bare minimum, you might also need to know how to navigate to content page which is not defined in AppShell.xaml
.
Here are steps on how to achieve that:
Add New Page
Let's add new page called OtherView
(under the Views directory) which will be content page we will navigate from MainView
.
Change the Label
text in created OtherView.xaml
so that it's clear that it's different page when we navigate to it:
<Label
HorizontalOptions="CenterAndExpand"
Text="Welcome to Other Page!"
VerticalOptions="CenterAndExpand" />
Register Route to New Page
Open AppShell.xaml.cs
and change it like this:
using Xamarin.Forms;
using XamarinNavigation.Views;
namespace XamarinNavigation
{
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
RegisterRoutes();
}
private void RegisterRoutes()
{
Routing.RegisterRoute("other", typeof(OtherView));
}
}
}
What we added is private method called RegisterRoutes
in constructor of AppShell
. Its implementation contains registration of a global route to our new page called OtherView
.
For more information, see:
https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/shell/navigation=
Add a Button for Navigation
Add a Button
in MainView.xaml
.
<StackLayout>
<Label
HorizontalOptions="CenterAndExpand"
Text="Welcome to Xamarin.Forms!"
VerticalOptions="CenterAndExpand" />
<Button Text="Press me!" />
</StackLayout>
When we press this button now, nothing happens. This is expected, because we didn't bind it to any command yet.
Let's do that in next step.
Add MainViewModel
Create new class called MainViewModel
in ViewModels
directory.
namespace XamarinNavigation.ViewModels
{
public class MainViewModel : BaseViewModel
{
}
}
This new class derives from BaseViewModel
class, so that we don't have to implement again INotifyPropertyChanged
interface again.
Join View and ViewModel
There are multiple ways on how to join View
and ViewModel
to work together.
Here is one way how to do that:
Adapt MainView.xaml
. Add
xmlns:vm="clr-namespace:XamarinNavigation.ViewModels"
to <ContentPage>
declaration so that we have reference to ViewModels
directory (namespace).
Note, that vm
is just name for the declared namespace, you can name it as you like (e.g. viewModels
).
and add BindingContext
like this:
<ContentPage.BindingContext>
<vm:MainViewModel />
</ContentPage.BindingContext>
the result MainView
content page looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="XamarinNavigation.Views.MainView"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:XamarinNavigation.ViewModels"
mc:Ignorable="d">
<ContentPage.BindingContext>
<vm:MainViewModel />
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<Label
HorizontalOptions="CenterAndExpand"
Text="Welcome to Xamarin.Forms!"
VerticalOptions="CenterAndExpand" />
<Button Text="Press me!" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
When we press the button now, it still doesn't do anything, but now we have MainViewModel
that we can use for creating a Command
for binding the button.
Add Command to Button
Change a button in our MainView.xaml
like this:
<Button Command="{Binding PressMeCommand}" Text="Press me!" />
We defined Command
property on button and bind it to property called PressMeCommand
on our MainViewModel
view model.
This property doesn't exist yet on view model, so let's create it:
using Xamarin.Forms;
namespace XamarinNavigation.ViewModels
{
public class MainViewModel : BaseViewModel
{
public MainViewModel()
{
PressMeCommand = new Command(PressMe);
}
public Command PressMeCommand { get; }
private void PressMe(object obj)
{
}
}
}
The Command
implementation comes from defined using Xamarin.Forms
.
With this implementation the button is always enabled and when button is pressed, PressMe
method is called.
Parameter object obj
is optionally used to bind from xaml
to pass additional information.
Currently it will be null
.
Convension says that we usually name our command properties with suffix Command
(e.g. SaveComand, OpenCommand, TapCommand,...)
Navigate to Other Page
Now, when we have implemented a button with MVVM (Model-View-ViewModel)
binded to Command
and registered OtherView
content page in our AppShell.xaml.cs
, let's implement actual code used to navigate to this page:
public class MainViewModel : BaseViewModel
{
public MainViewModel()
{
PressMeCommand = new Command(async obj => await PressMe(obj));
}
public Command PressMeCommand { get; }
private async Task PressMe(object obj)
{
await Shell.Current.GoToAsync("other");
}
}
For more information, see this link:
https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/shell/navigation
Let's explain little bit what's going on there:
There are couple of important changes:
- Implemented
PressMe(object obj)
method:
await Shell.Current.GoToAsync("other");
"other"
parameter to GoToAsync
method represents globally defined route for the OtherView
page.
- Changed return value of
PressMe(object obj)
method toasync Task
:
private async Task PressMe(object obj)
(Very) simply said, if you awaits some async
method (like we did with await Shell.Current.GoToAsync("other");
)
you need to change declaration of containing method to async
and change return type in case of void
to Task
.
What it means is that actual navigation to other page will be performed in different thread.
For more information about asynchronous programming in C# see link:
https://docs.microsoft.com/en-us/dotnet/csharp/async
- The way how
PressMeCommand
is created is changed and instead ofnew Command(PressMe)
there isnew Command(async obj => await PressMe(obj))
. Reason for that is that we changedvoid
toTask
and addedasync
, so instead of passing delegate parameter we need to pass async lambda. Note, that from asynchronous programming point of view, it's still fire and forget.
Now, if we build and start the application here is the navigation in action:
MainView:
When you press PRESS ME!
button located at the bottom of the page, you will navigate to other view:
OtherView:
Notice that back buttom is automatically added at the top left corner and it can be used to navigate back to the MainView
.