Xamarin Forms and .NET Standard: Basic Navigation

DZone's Guide to

Xamarin Forms and .NET Standard: Basic Navigation

Learn how to build a simple application with navigation controls using Xamarin Forms with .NET Standard for cross-platform app development.

· Mobile Zone
Free Resource

In the last post about Xamarin Forms, we started to see the basic concepts on how to begin the development of a cross-platform application. In this post, we're going to explore some basic navigation concepts and some tips to organize our solution. Also, we'll develop a simple cross-platform app that welcomes us with a main screen and a button to navigate to a new view where we can generate a random integer.

Organizing Our New Project

We've just created a new Xamarin Forms project and now we're ready to develop yet another mobile app. If you know how to create a brand new Xamarin Forms project, you can continue reading; otherwise, I suggest reading the first post (getting started) of this series.

When I start a new project, I like to organize things to be more comfortable and organized, so let's start by doing that.

The Views Folder

We create a new folder in the shared project and call it Views. In this folder, we'll put all the files to create the UI (the views).

We already have a view in our Project: the MainPage.xaml. We move the file into the Views folder.

Now we move the MainPage class into the Views namespace. Besides, every time we add a new file in the Views folder, the class will be created in the Views namespace as a standard behavior by Visual Studio.

To move the MainPage class into the Views namespace, we edit MainPage.xaml and MainPage.xaml.cs like this:

Now our project won't compile because the App class cannot find the MainPage class anymore, so we need to edit the App class as in the following image:

Now our solution compiles.

Set Up the Navigation

By navigation, we mean the features available in Xamarin Forms to drive the user experience from one view to another. In this example, we're going to implement a hierarchical navigation.

To support hierarchical navigation, we create a shell for our UI, where the views will be navigated. The shell is created with an instance of the NavigationPage class. We edit the App.xaml.cs file to set the MainPage of the app as a new NavigationPage. The root of our navigation is the MainPage, which is a ContentPage.

If we build and run our application we'll see no UI differences in the app but now we're ready to navigate from one page to another.

Let's Navigate

As we stated at the beginning of this post, the app has to show the user a button to navigate to another view. Let's edit the XAML in the MainPage class to do this.

<?xml version="1.0" encoding="utf-8" ? /&gt;

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:CrossPlatformApp" x:Class="CrossPlatformApp.Views.MainPage"<span style="display: inline !important; float: none; background-color: transparent; color: #3d596d; cursor: text; font-family: 'Noto Serif',Georgia,'Times New Roman',Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">/></span>

<StackLayout>
<Label Text="Welcome to cross platform random integer generator!" VerticalOptions="Center" HorizontalOptions="Center" <span style="display: inline !important; float: none; background-color: transparent; color: #3d596d; cursor: text; font-family: 'Noto Serif',Georgia,'Times New Roman',Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">/></span>
<Button Text="Start!" Clicked="OnStartClicked" x:Name="buttonStart" <span style="display: inline !important; float: none; background-color: transparent; color: #3d596d; cursor: text; font-family: 'Noto Serif',Georgia,'Times New Roman',Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">/></span>
</StackLayout<span style="display: inline !important; float: none; background-color: transparent; color: #3d596d; cursor: text; font-family: 'Noto Serif',Georgia,'Times New Roman',Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">/></span>
</ContentPage<span style="display: inline !important; float: none; background-color: transparent; color: #3d596d; cursor: text; font-family: 'Noto Serif',Georgia,'Times New Roman',Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">/></span>

In the respective code-behind file (MainPage.xaml.cs), we write:

public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private async void OnStartClicked(object sender, EventArgs e)
        {
            await Navigation.PushAsync(new RandomIntegerView(new RandomIntegerViewModel(new RandomGenerator())));
        }
    }

In the code above, we trigger the navigation in the handler of the click event of the button. We use the Navigation property to get a reference to the Navigation system, and then we call PushAsync to put the view where we'll generate a random number on the stack of the hierarchical navigation.

Before we move on, we give a closer look this line of code:

await Navigation.PushAsync(new RandomIntegerView(new RandomIntegerViewModel(new RandomGenerator())));

We now have some errors from the compiler because we've written some names of not existing classes. We move on and create those classes.

In the Views folders we create a new RandomIntegerView class like this:

In the RandomIntegerView.xaml file, we write:

<xml version="1.0" encoding="utf-8" ?&gt;
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"              x:Class="CrossPlatformApp.Views.RandomIntegerView"<span style="display: inline !important; float: none; background-color: transparent; color: #3d596d; cursor: text; font-family: 'Noto Serif',Georgia,'Times New Roman',Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">></span>
    <ContentPage.Content<span style="display: inline !important; float: none; background-color: transparent; color: #3d596d; cursor: text; font-family: 'Noto Serif',Georgia,'Times New Roman',Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">></span>
        <StackLayout<span style="display: inline !important; float: none; background-color: transparent; color: #3d596d; cursor: text; font-family: 'Noto Serif',Georgia,'Times New Roman',Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">></span>
            <Label Text="Generate your lucky number!" /<span style="display: inline !important; float: none; background-color: transparent; color: #3d596d; cursor: text; font-family: 'Noto Serif',Georgia,'Times New Roman',Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">></span>
            <Button x:Name="buttonGenerator" Text="Generate" Command="{Binding GenerateRandomInteger}" /<span style="display: inline !important; float: none; background-color: transparent; color: #3d596d; cursor: text; font-family: 'Noto Serif',Georgia,'Times New Roman',Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">></span>
            <Label Text="{Binding LuckyNumber}" FontSize="32" /<span style="display: inline !important; float: none; background-color: transparent; color: #3d596d; cursor: text; font-family: 'Noto Serif',Georgia,'Times New Roman',Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">></span>
        </StackLayout<span style="display: inline !important; float: none; background-color: transparent; color: #3d596d; cursor: text; font-family: 'Noto Serif',Georgia,'Times New Roman',Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">></span>
    </ContentPage.Content<span style="display: inline !important; float: none; background-color: transparent; color: #3d596d; cursor: text; font-family: 'Noto Serif',Georgia,'Times New Roman',Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">></span>
</ContentPage>

The code-behind of the view is:

using CrossPlatformApp.ViewModels;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace CrossPlatformApp.Views
{
    public partial class RandomIntegerView : ContentPage
    {
        public RandomIntegerView(RandomIntegerViewModel viewModel)
        {
            BindingContext = viewModel;

            InitializeComponent();
        }
    }
}

As we can see from the XAML code, we use the MVVM pattern with binding to link the action of the "Generate" button to a Command that will handle the number generation in the ViewModel. The visualization of the result is performed with a label with binding in the Text property to a LuckyNumber member of the ViewModel.

The RandomIntegerViewModel and the .Core Project

Now we need the RandomIntegerViewModel to give life to our UI. We create a ViewModels folder in the shared Project and create a new class in it, named RandomIntegerViewModel.

We also create a new .NET Standard assembly Project called CrossPlatformApp.Core where we'll put all the services and interface definitions that our app will need.

Inside this new Project, we create an interface that states what a lucky number generator has to do: we call it IIntegerGenerator and the code is:

namespace CrossPlatformApp.Core {
 public interface IIntegerGenerator {
  int GenerateInt();
 }
}

Now we create a RandomGenerator class that implements IIntegerGenerator like this:

using System;

namespace CrossPlatformApp.Core {
 public class RandomGenerator: IIntegerGenerator {
  public int GenerateInt() {
   Random r = new Random(DateTime.Now.Second);

   return r.Next();
  }
 }
}

The final result is:

Now we can go back to our RandomIntegerViewModel and write:

using CrossPlatformApp.Core;
using System.ComponentModel;
using System.Windows.Input;
using Xamarin.Forms;

namespace CrossPlatformApp.ViewModels {
 public class RandomIntegerViewModel: INotifyPropertyChanged {
  private ICommand _generateRandomInteger;
  private IIntegerGenerator _integerGeneratorService;
  private int _luckyNumber;

  public RandomIntegerViewModel(IIntegerGenerator integerGeneratorService) {
   _integerGeneratorService = integerGeneratorService;
  }

  public event PropertyChangedEventHandler PropertyChanged;

  public ICommand GenerateRandomInteger {
   get {
    if (_generateRandomInteger == null) {
     _generateRandomInteger = new Command(() = & gt; {
      LuckyNumber = _integerGeneratorService.GenerateInt();
     });
    }

    return _generateRandomInteger;
   }
  }

  public int LuckyNumber {
   get {
    return _luckyNumber;
   }
   private set {
    if (_luckyNumber != value) {
     _luckyNumber = value;

     PropertyChanged ? .Invoke(this, new PropertyChangedEventArgs(nameof(LuckyNumber)));
    }
   }
  }
 }
}

What we've done is to define the Command (the GenerateRandomInteger Command) to generate the lucky number and the property to allow the display of the result in the view (the LuckyNumber property). As we can see, we also injected into the constructor the dependency from the service that creates the random integer. This way, our class is more testable. We implemented the INotifyPropertyChanged to enable communication from the ViewModel to the View.

Now we can hit F5 and try the app.

Image title

We can navigate back to the main page if we press the back button of the OS:

Image title

TL; DR

In this post, we created a simple app to handle the basics of the navigation mechanism and implement a hierarchical navigation. We also organized our solution with a Core Project to host all the services and interfaces that are useful to all three versions of our app. A core Project is useful because promotes code reuse and testability. We also explored the concept of services where we can implement non-UI features and inject them in our view-models.

Source code: https://github.com/phenixita/CrossPlatformRandomGenerator

DOWNLOAD
Topics:
mobile ,.net ,xamarin forms ,cross platform ,app development

Published at DZone with permission of Michele Ferracin, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.