Implementing the navigation service within your app

In this section, we will begin by setting up the basic structure for our TrackMyWalks solution to include the folder that will be used to represent our Services. Let's take a look at how we can achieve this, by performing following the steps:

  1. Launch the Xamarin Studio application and ensure that the TrackMyWalks solution is loaded within the Xamarin Studio IDE.
  2. Next, create a new folder within the TrackMyWalks Portable Class Library project, called Services, as shown in the following screenshot:

    Implementing the navigation service within your app

Now that we have created the folder structure that will be used to store our navigation services, we can begin to start building the Navigation Service Interface class that will be used by our Navigation Service class and in turn used by our ViewModels.

Creating the navigation service interface for the TrackMyWalks app

In this section, we will begin by creating a navigation service interface class that will extend from the Xamarin.Forms navigation abstraction layer. This is so that we can perform ViewModel to ViewModel navigation within our MVVM design pattern, and in turn bind with our content pages to allow navigation between these Views to happen.

We will first need to define the interface for our navigation service, as this will contain and define its methods, and will make it a lot easier if we ever wanted to add new method implementations for our service, without the need to change each of our ViewModels.

Let's take a look at how we can achieve this, by performing the following steps:

  1. Create an empty class within the Services folder, as shown in the following screenshot:

    Creating the navigation service interface for the TrackMyWalks app

  2. Next, choose the Empty Interface option located within the General section, and enter in IWalkNavService for the name of the new interface file to create, as shown in the following screenshot:

    Creating the navigation service interface for the TrackMyWalks app

  3. Next, click on the New button to allow the wizard to proceed and create the new empty class file, as shown in the preceding screenshot.

Up until this point, all we have done is create our IWalkNavService class file. This Interface class will be used and will act as the base NavigationService class that each of our ViewModels will inherit from. As we start to build the Navigation Service Interface class, you will see that it contains a couple of class members that will be used by our content pages and ViewModels, as we will be using this as our base class within our ViewModels used by the TrackMyWalks application.

To proceed with creating the base IWalkNavService interface, ensure that the IWalkNavService.cs file is displayed within the code editor, and enter in the following code snippet:

        // 
        //  IWalkNavService.cs 
        //  TrackMyWalks Navigation Service Interface 
        // 
        //  Created by Steven F. Daniel on 03/09/2016. 
        //  Copyright © 2016 GENIESOFT STUDIOS. All rights reserved. 
        // 
        using System.Threading.Tasks; 
        using TrackMyWalks.ViewModels; 
 
        namespace TrackMyWalks.Services 
        { 
            public interface IWalkNavService 
            { 
                // Navigate back to the Previous page in 
                 the NavigationStack 
                Task PreviousPage(); 
 
                // Navigate to the first page within 
                 the NavigationStack  
                Task BackToMainPage(); 
 
                // Navigate to a particular ViewModel within
                 our MVVM Model, 
                // and pass a parameter 
                Task NavigateToViewModel<ViewModel,  
                     TParameter>(TParameter parameter)         
                      where ViewModel : WalkBaseViewModel; 
            } 
        }  

In the preceding code snippet, we started by creating a new Interface class for our IWalkNavService that allows the ability to navigate to each of our ViewModels, as well as navigating back to the PreviousPage method, and back to the first page within our hierarchical model, as determined by the BackToMainPage method.

Note

An interface contains only the methods, properties, and event signature definitions. Any class that implements the interface must implement all members of the interface that are specified in the interface definition.

The NavigateToViewModel method declares a generic type which is used to restrict the ViewModel to its use to objects of the WalkBaseViewModel base class, and a strongly-typed TParameter parameter to be passed along with the navigation.

Note

The term strongly-typed means that, if a variable has been declared of a specific type (string, integer, or defined as a user-defined type), it cannot be assigned a value of a different type later on, as this will result in the compiler notifying you of an error. An example would be: int i = 10; i = "Ten"

The Task class is essentially used to handle asynchronous operations. This is done by ensuring that the asynchronous method you initiated will eventually finish, thus completing the task. The Task object is used to return back information once it has finished by returning back a Task object almost instantaneously, although the underlying work within the method would likely finish later.

To handle this, you can use the await keyword to wait for the task to complete which will block the current thread and wait until the asynchronous method has completed.

Creating a navigation service to navigate within our ViewModels

In the previous section, we created our base interface class for our navigation service and defined a number of different methods, which will be used to navigate within our MVVM ViewModel.

These will be used by each of our ViewModels, and the Views (pages) will implement these ViewModels and use them as their BindingContext.

Let's take a look at how we can achieve this, by performing the following the steps:

  1. Create an empty class within the Services folder, as shown in the next screenshot.
  2. Next, choose the Empty Class option located within the General section, and enter in WalkNavService for the name of the new class file to create, as shown in the following screenshot:

    Creating a navigation service to navigate within our ViewModels

  3. Next, click on the New button to allow the wizard to proceed and create the new empty class file, as shown in the preceding screenshot.
  4. Up until this point, all we have done is create our WalkNavService class file. This class will be used and will act as the base NavigationService class that will contain the functionality required that each of our ViewModels will inherit from, in order to navigate between each of the ViewModels within our MVVM model.
  5. As we start to build our Navigation class, you will see that it contains a number of method members that will be used to enable navigation between each of our ViewModels and it will implement the IWalkNavService Interface. To proceed with creating the base WalkNavService class, perform the following steps:
  6. Ensure that the WalkNavService.cs file is displayed within the code editor, and enter in the following code snippet:
            // 
            //  WalkNavService.cs 
            //  TrackMyWalks Navigation Service Class 
            // 
            //  Created by Steven F. Daniel on 03/09/2016. 
            //  Copyright © 2016 GENIESOFT STUDIOS. All rights reserved. 
            // 
            using System; 
            using Xamarin.Forms; 
            using System.Collections.Generic; 
            using System.Threading.Tasks; 
            using System.Reflection; 
            using System.Linq; 
            using TrackMyWalks.ViewModels; 
            using TrackMyWalks.Services;
    

    First, we need to initialize our navigation class to be marked as a dependency, by adding the Dependency metadata attribute so that it can be resolved by the Xamarin.FormsDependencyService class. This will enable so that it to find and use our method implementation as defined by our interface.

  7. Next, we also need to need to ensure that our WalkNavService class inherits from the IWalkNavService navigation interface class, so that it can access the method getters and setters. Proceed and enter in the following code snippet as shown here:
            [assembly: Dependency(typeof(WalkNavService))] 
            namespace TrackMyWalks.Services 
            { 
                public class WalkNavService : IWalkNavService 
                { 
  8. Next, we need to create a public INavigation property named navigation. The navigation property will provide our class with a reference to the current Xamarin.Forms.INavigation instance, and this will need to be set when the navigation service is first initialized. We will see how this is done as we progress through updating our TrackMyWalks app. Proceed and enter in the following code snippet:
            public INavigation navigation { get; set; } 
    
  9. Then we need to register the navigation service to handle ContentPage to ViewModel mappings by declaring a dictionary _viewMapping property variable that inherits from the IDictionary interface. Proceed and enter in the following code snippet:
            readonly IDictionary<Type, Type> _viewMapping = 
              new Dictionary<Type, Type>(); 
    
  10. Next, we need to declare a new method called RegisterViewMapping which will be used to populate our ViewModel and ContentPage (View) within the _viewMapping dictionary property object. Proceed and enter in the following code sections:
            // Register our ViewModel and View within our Dictionary 
            public void RegisterViewMapping(Type viewModel, Type view) 
            { 
                _viewMapping.Add(viewModel, view); 
            } 
    
  11. Then, we need to create the PreviousPage instance method for our WalkNavService class. This will be used to navigate back to the previous page contained within our NavigationStack, by first checking the NavigationStack property of the navigation property INavigation interface to ensure that it is not null and that we have more than one ViewModel contained within our NavigationStack to navigate back to. If we don't perform this check, it could result in our application crashing. Finally, we use the PopAsync method to remove the last view added to our NavigationStack, thus returning back to the previous ViewModel. Proceed and enter in the following code sections:
            // Instance method that allows us to move back to the 
            // previous page. 
            public async Task PreviousPage() 
            { 
                // Check to see if we can move back to the previous page 
                if (navigation.NavigationStack != null && 
                    navigation.NavigationStack.Count > 0) 
                { 
                    await navigation.PopAsync(true); 
                } 
            } 
    
  12. Next, we need to create the BackToMainPage instance method for our WalkNavService class. This will be used to take us back to the first ContentPage contained within our NavigationStack. We use the PopToRootAsync method of our navigation property and use the await operator to wait until the task completes, before removing all ViewModels contained within our NavigationStack and returning back a Task object. Proceed and enter the following code:
            // Instance method that takes us back to the main 
            // Root WalksPage 
            public async Task BackToMainPage() 
            { 
                await navigation.PopToRootAsync(true); 
            } 
    
  13. Then, we need to create the NavigateToViewModel instance method for our WalkNavService class. This will be used to navigate to a specific ViewModel that is contained within our _viewMapping dictionary object. Next, we use the TryGetValue method of the _viewMapping dictionary object to check to see if our ViewModel does indeed exist within our dictionary, and return the name of the ViewModel.
  14. The name of the returned view will be stored within the viewType object, and we then use the PushAsync method to navigate to that view. Finally, we set the BindingContext for the last pushed view that is contained within our NavigationStack, and then navigate to the view, passing in any parameters required. Proceed and enter in the following code sections:
            // Instance method that navigates to a specific ViewModel  
            // within our dictionary viewMapping 
            public async Task NavigateToViewModel<ViewModel,  
                   WalkParam>(WalkParam parameter) 
                   where ViewModel : WalkBaseViewModel 
            { 
                Type viewType; 
     
                if (_viewMapping.TryGetValue(typeof(ViewModel), out  
                    viewType)) 
                { 
                  var constructor = viewType.GetTypeInfo()
                  .DeclaredConstructors
                  .FirstOrDefault(dc => dc.GetParameters()
                  .Count() <= 0); 
     
                    var view = constructor.Invoke(null) as Page; 
                    await navigation.PushAsync(view, true); 
                } 
     
                if (navigation.NavigationStack.Last().BindingContext is  
                    WalkBaseViewModel<WalkParam>) 
                    await ((WalkBaseViewModel<WalkParam>)( 
                    navigation.NavigationStack.Last().BindingContext)). 
                    Init(parameter); 
                } 
              } 
            } 
    

In the preceding code snippet, we began by ensuring that our WalkNavService class inherits from our IWalkNavService class, and then moved on to create a navigation property that inherits from our INavigation class. We then created its associated getter and setter qualifiers.

Finally, we created the instance methods required for our WalkNavService class.

Updating the WalkBaseViewModel to use our navigation service

In this section we will proceed to update our WalkBaseViewModel class to include references to our IWalkNavService. Since our WalkBaseViewModel inherits and is used by each of our ViewModels, it makes sense to place it within this class. That way, if we need to add additional methods, we can just add them within this class. To proceed, perform the following steps:

  1. Ensure that the WalkBaseViewModel.cs file is displayed within the code editor, and enter in the following code snippet:
            // 
            //  WalkBaseViewModel.cs 
            //  TrackMyWalks Base ViewModel 
            // 
            //  Created by Steven F. Daniel on 22/08/2016. 
            //  Copyright © 2016 GENIESOFT STUDIOS. All rights reserved. 
            // 
            using System.ComponentModel; 
            using System.Runtime.CompilerServices; 
            using System.Threading.Tasks; using TrackMyWalks.Services;  
     
            namespace TrackMyWalks.ViewModels 
            { 
              public abstract class WalkBaseViewModel : INotifyPropertyChanged 
              { 
    
  2. Next, we need to create a protected IWalkNavService property named NavService. The NavService property will provide our class with a reference to the current navigation instance that is contained within our IWalkNavService interface class. Proceed and enter in the following code section:
    protected IWalkNavService NavService { get; private set; }
    
  3. Then, we need to modify the class constructor and declare a navService parameter that inherits from our IWalkNavService interface class. Next, we set the NavService property for our WalkBaseViewModel base class, to an instance of the navService parameter. Proceed and enter in the following highlighted code sections:
            protected WalkBaseViewModel(IWalkNavService navService)
          {
               NavService = navService;
          }
    
  4. Next, we need to create the Init abstract method for our WalkBaseViewModel class that returns back an asynchronous Task object. This will be used to initialize our WalkBaseViewModel. Proceed and enter in the following highlighted code sections:
            public abstract Task Init();  
     
            public event PropertyChangedEventHandler PropertyChanged; 
     
            protected virtual void OnPropertyChanged(
             [CallerMemberName] string propertyName = null) 
            { 
               var handler = PropertyChanged; 
               if (handler != null) 
               { 
                 handler(this, new PropertyChangedEventArgs(propertyName)); 
               } 
             } 
           } 
    
  5. Then, we need to create a secondary abstract class for our WalkBaseViewModel that inherits from the WalkBaseViewModel and defines a generic-typed TParameter object. We then proceed to overload the WalkBaseViewModel class constructor, and set this class to inherit from our navService base class. Proceed and enter in the following highlighted code sections:
    public abstract class WalkBaseViewModel<WalkParam> :
              WalkBaseViewModel
        {
            protected WalkBaseViewModel(IWalkNavService navService) : 
              base(navService)
            {
            }
    
  6. Next, we need to override the Init method for our WalkBaseViewModel<WalkParam> that accepts a default WalkParam value for our walkDetails model. Proceed and enter in the following highlighted code sections:
        public override async Task Init()
           {
            await Init(default(WalkParam));
        }
         public abstract Task Init(WalkParam walkDetails);
    } 
    }
    

In the preceding code snippet, we began by creating a NavService property that inherits from our IWalkNavService class, and then created its associated getter and setter qualifiers.

Next, we update the WalkBaseViewModel class constructor to set the NavService property to an instance of our navService, before creating our Init abstraction method that will be used to initialize our class.

In the next step, we create a new abstract class for our WalkBaseViewModel that implements from the WalkBaseViewModel class, and then overloads our class constructor so that it inherits from our navService class.

Next, we'll override the Init method for our WalkBaseViewModel<WalkParam> that accepts a default WalkParam value for our walkDetails model.

Updating the walks main page ViewModel and navigation service

We have created our IWalkNavService Interface class and updated the NavService class to include all of the necessary class instance methods. We also made some changes to our WalkBaseViewModel class to inherit from our IWalkNavService navigation service. We have also included an additional abstraction class that will be used to initialize our WalkBaseViewModel when navigating between ViewModels within our MVVM model.

Our next step is to modify the walks main page. In this section, we will be taking a look at how to update our WalksPageViewModel so that it can take advantage of our navigation service.

Let's take a look at how we can achieve this, by performing the following steps:

  1. Ensure that the WalksPageViewModel.cs file is displayed within the code editor, and enter in the following highlighted code sections:
            // 
            //  WalksPageViewModel.cs 
            //  TrackMyWalks ViewModels 
            // 
            //  Created by Steven F. Daniel on 22/08/2016. 
            //  Copyright © 2016 GENIESOFT STUDIOS. All rights reserved. 
            // 
            using System; 
            using System.Collections.ObjectModel; 
            using System.Threading.Tasks; 
            using TrackMyWalks.Models; 
            using TrackMyWalks.Services; 
            using Xamarin.Forms; 
     
            namespace TrackMyWalks.ViewModels 
            { 
                public class WalksPageViewModel : WalkBaseViewModel 
                { 
                    ObservableCollection<WalkEntries> _walkEntries; 
     
                    public ObservableCollection<WalkEntries> walkEntries 
                    { 
                        get { return _walkEntries; } 
                        set 
                        { 
                            _walkEntries = value; 
                            OnPropertyChanged(); 
                        } 
                    } 
    
  2. Now we need to modify the WalksPageViewModel class constructor, which will need to include a parameter navService that is included within our IWalkNavService interface class. Then we need to set the ViewModel's class constructor to access all instance class members contained within the navService within the WalksPageViewModel by using the base keyword. We then set up an ObservableCollection called walkEntries. This accepts a list parameter containing our WalkEntries model, which will be used to determine whenever the collection has changed within the WalkEntries model.
  3. Next, we create the Init method within our WalksPageView model, and a method called LoadWalkDetails to populate the WalkDetails. Proceed and enter in the following highlighted code sections:
        public WalksPageViewModel(IWalkNavService navService) :
    
              base(navService)
        {
                walkEntries = new ObservableCollection<WalkEntries>();
        }
          public override async Task Init()
        {
            await LoadWalkDetails();
        }
    
  4. Next, we need to create a new async method called LoadWalkDetails that will be used to add each of the walk entries within our model. We use the Task.Factory.StartNew to start and execute our task, and then proceed to populate each of our lists of WalkEntries asynchronously. We then use and specify the await keyword to wait until our Task completes. Proceed and enter in the following highlighted code sections:
            public async Task LoadWalkDetails()
        {
            await Task.Factory.StartNew(() =>
            {
                walkEntries = new ObservableCollection<WalkEntries>() {
                new WalkEntries {
                    Title = "10 Mile Brook Trail, Margaret River",
                    Notes = "The 10 Mile Brook Trail starts in the 
                        Rotary Park near Old Kate, a preserved steam " +
    
                        "engine at the northern edge of Margaret River. ",
                        Latitude = -33.9727604,
                        Longitude = 115.0861599,
                        Kilometers = 7.5,
                        Distance = 0,
                        Difficulty = "Medium",
                        ImageUrl =
                             "http://trailswa.com.au/media/cache/media/images/
                              trails/_mid/" +
    "FullSizeRender1_600_480_c1.jpg"
                },
                new WalkEntries
                {
                    Title = "Ancient Empire Walk, Valley of the Giants",
                    Notes = "The Ancient Empire is a 450 metre walk trail
                        that takes you around and through some of " +
    
                        "the giant tingle trees including the most popular
                         of the gnarled veterans, known as " +
    
                         "Grandma Tingle.",
                        Latitude = -34.9749188,
                        Longitude = 117.3560796,
                        Kilometers = 450,
                        Distance = 0,
                        Difficulty = "Hard",
                        ImageUrl ="http://trailswa.com.au/media/cache
                             /media/images/trails/_mid/" +
    
                             "Ancient_Empire_534_480_c1.jpg"
                },
                };
            });
        }
    
  5. Next, we need to create a Command property for our class. This will be used within our WalksPage and will be used to bind to the Add WalkToolBarItem. The Command property will run an action upon being pressed, and then execute a class instance method, to determine whether the command can be executed. Proceed and enter in the following highlighted code sections:
    Command _createNewWalk;
        public Command CreateNewWalk
        {
            get
            {
                return _createNewWalk
                    ?? (_createNewWalk = 
                    new Command(async () =>
                    await NavService.NavigateToViewModel<WalkEntryViewMod
                    el, WalkEntries>(null)));
            }
        } 
    
  6. Then, create a Command property to our class. This will be used within the WalksPage and will be used to handle clicks on a walk item within the ListView. The Command property will run an action upon being pressed, and then execute a class instance method to determine whether the command can be executed or not, prior to navigating to the WalksTrailViewModel and passing in the trailDetails for the chosen walk within the ListView. Proceed and enter in the following highlighted code sections:
        Command<WalkEntries> _trailDetails;
        public Command<WalkEntries> WalkTrailDetails
        {
            get
            {
                return _trailDetails
    ?? (_trailDetails =
     
    new Command<WalkEntries>(async (trailDetails) =>
                   await NavService.NavigateToViewModel
                       <WalksTrailVi
    ewModel, WalkEntries>(trailDetails)));
            }
           }
        }
    } 
    

Now that we have modified our WalksPageViewModel to include the navigation service class, which will be used by our main WalksPage, our next step is to modify our walks main page so that it points to a reference of our WalksPageViewModel, and ensures that all of the necessary Command bindings and BindingContexts have been set up correctly.

Updating the walks main page to use the updated ViewModel

Now that we have modified our MVVM ViewModel to take advantage of the navigation service, we need to modify our walks main page to bind the WalksPageBindingContext to the WalksPageViewModel so that the walk entry details can be displayed and all of the navigational aspects are working as expected.

Let's take a look at how we can achieve this, by performing the following steps:

  1. Ensure that the WalksPage.cs file is displayed within the code editor, and enter in the following highlighted code sections:
            // 
            //  WalksPage.cs 
            //  TrackMyWalks 
            // 
            //  Created by Steven F. Daniel on 04/08/2016. 
            //  Copyright © 2016 GENIESOFT STUDIOS. All rights reserved. 
            // 
            using System.Collections.Generic; 
            using Xamarin.Forms; 
            using TrackMyWalks.Models; 
            using TrackMyWalks.ViewModels; 
            using TrackMyWalks.Services;  
     
     
            namespace TrackMyWalks  
            { 
                public class WalksPage : ContentPage 
               { 
    
  2. Next, we need to create a new private property named _viewModel within our WalksPage class. This is of the WalksPageViewModel type, and will essentially provide us with access to the ContentPage's BindingContext object. Proceed and enter in the following highlighted code sections:
            WalksPageViewModel _viewModel
     {
      
              get { return BindingContext as WalksPageViewModel;
              }
        
            }  
     
            public WalksPage() 
            { 
                var newWalkItem = new ToolbarItem 
                { 
                    Text = "Add Walk" 
                }; 
  3. Then, we need to set up a Binding to the Command property that we defined within our WalksPageViewModel class. This will be called when the user chooses the Add Walk button. Proceed and enter in the following highlighted code sections:
            // Set up our Binding click event handler
     
              newWalkItem.SetBinding(ToolbarItem.CommandProperty,
     
              "CreateNewWalk");  
     
            // Add the ToolBar item to our ToolBar 
            ToolbarItems.Add(newWalkItem); 
    
  4. Next, we need to declare and initialize our WalksPageViewModelBindingContext to include our IWalkNavService constructor, which is used by the WalkBaseViewModel class, and is retrieved from the Xamarin.Forms DependencyService class. Proceed and enter in the following highlighted code sections:
            // Declare and initialize our Model Binding Context
     
               BindingContext = new WalksPageViewModel(DependencyService.Get
               <IWalkNavS
    ervice>()); 
           // Define our Item Template 
            var itemTemplate = new DataTemplate(typeof(ImageCell)); 
            itemTemplate.SetBinding(TextCell.TextProperty, "Title"); 
            itemTemplate.SetBinding(TextCell.DetailProperty, "Notes"); 
            itemTemplate.SetBinding(ImageCell.ImageSourceProperty, "ImageUrl"); 
     
                var walksList = new ListView 
                { 
                    HasUnevenRows = true, 
                    ItemTemplate = itemTemplate, 
                    SeparatorColor = Color.FromHex("#ddd"), 
                }; 
     
                // Set the Binding property for our walks Entries 
                walksList.SetBinding(ItemsView<Cell>.ItemsSourceProperty,  
                "walkEntries"); 
    
  5. Then, we need to change the way in which an item gets selected from the ListView. We need to make a call to the WalksTrailDetails command that is included within our WalksPageViewModel class, so that it can navigate to the WalksTrailViewModel, whilst passing in the chosen item from within the ListView. Proceed and enter the following highlighted code sections:
            // Initialize our event Handler to use when
              the item is tapped 
            walksList.ItemTapped += (object sender, 
             ItemTappedEventArgs e) => 
            { 
                var item = (WalkEntries)e.Item; 
                if (item == null) return; 
                _viewModel.WalkTrailDetails.Execute(item);  
                item = null; 
            }; 
              Content = walksList; 
            } 
    
  6. Finally, we need to create an OnAppearing instance method of the navigation hierarchy that will be used to display our WalksEntries prior to the ViewModel appearing on screen.
  7. We need to ensure that our ViewModel has been properly initialized by checking to see that it isn't null, prior to calling the Init method of our WalksPageViewModel. Proceed and enter the following highlighted code sections:
            protected override async void OnAppearing()
     
            {
      
               base.OnAppearing();
      // Initialize our WalksPageViewModel
    
               if (_viewModel != null)
    
               await _viewModel.Init();
     
             }  
            } 
           }  
    

In this section, we looked at the steps involved in modifying the WalksPage so that it can take advantage of our updated WalksPageViewModel. We looked at how to set the content page to an instance of the WalksPageViewModel so that it knows where to get the list of walk entries. The list will be used and displayed within the ListView control, and will then update the BindingContext property for the WalksPage to point to an instance of the IWalkNavService interface. As you can see, by using a navigation service within your ViewModels, it makes navigating between each of the ViewModels quite easy.

Updating the walks entry page ViewModel and navigation service

Now that we have modified the MVVM ViewModel that will be used for the main WalksPage, our next step is to begin modifying the WalkEntryViewModel to take advantage of the navigation service, which will be used to create new walk entries, and save this information back to the WalkBaseViewModel. This will be covered in a later chapter as we progress throughout this book.

Let's take a look at how we can achieve this, by performing the following steps:

  1. Ensure that the WalkEntryViewModel.cs file is displayed within the code editor, and enter in the following highlighted code sections:
            // 
            //  WalkEntryViewModel.cs 
            //  TrackMyWalks ViewModels 
            // 
            //  Created by Steven F. Daniel on 22/08/2016. 
            //  Copyright © 2016 GENIESOFT STUDIOS. All rights reserved. 
            // 
            using System; 
            using System.Diagnostics.Contracts;
    
            using System.Threading.Tasks; 
            using TrackMyWalks.Models; 
            using TrackMyWalks.Services; 
            using TrackMyWalks.ViewModels; 
            using Xamarin.Forms; 
     
            namespace TrackMyWalks.ViewModels 
            { 
                public class WalkEntryViewModel : WalkBaseViewModel 
                { 
                    string _title; 
                    public string Title 
                    { 
                        get { return _title; } 
                        set 
                        { 
                            _title = value; 
                            OnPropertyChanged(); 
                            SaveCommand.ChangeCanExecute(); 
                        } 
                    } 
     
                    string _notes; 
                    public string Notes 
                    { 
                        get { return _notes; } 
                        set 
                        { 
                            _notes = value; 
                            OnPropertyChanged(); 
                        } 
                    } 
     
                    double _latitude; 
                    public double Latitude 
                    { 
                        get { return _latitude; } 
                        set 
                        { 
                            _latitude = value; 
                            OnPropertyChanged(); 
                        } 
                    } 
     
                    double _longitude; 
                    public double Longitude 
                    { 
                        get { return _longitude; } 
                        set 
                        { 
                            _longitude = value; 
                            OnPropertyChanged(); 
                        } 
                    } 
     
                    double _kilometers; 
                    public double Kilometers 
                    { 
                        get { return _kilometers; } 
                        set 
                        { 
                            _kilometers = value; 
                            OnPropertyChanged(); 
                        } 
                    } 
     
                    string _difficulty; 
                    public string Difficulty 
                    { 
                        get { return _difficulty; } 
                        set 
                        { 
                            _difficulty = value; 
                            OnPropertyChanged(); 
                        } 
                    } 
     
                    double _distance; 
                    public double Distance 
                    { 
                        get { return _distance; } 
                        set 
                        { 
                            _distance = value; 
                            OnPropertyChanged(); 
                        } 
                    } 
     
                    string _imageUrl; 
                    public string ImageUrl 
                    { 
                        get { return _imageUrl; } 
                        set 
                        { 
                            _imageUrl = value; 
                            OnPropertyChanged(); 
                        } 
                     } 
    
  2. In the next step, we need to modify the WalksEntryViewModel class constructor which will now need to include a parameter navService that is included within the IWalkNavService interface class. Then we'll set the ViewModel's class constructor to access all instance class members contained within the navService within the WalksEntryViewModel, by using the base keyword. Next, we'll initialize the constructor with default values for our Title, Difficulty and Distance properties.
  3. Locate the WalkEntryViewModel class constructor, and enter the following highlighted code sections:
        public WalkEntryViewModel(IWalkNavService navService) : 
            base(navService)
        {
            Title = "New Walk";
            Difficulty = "Easy";
            Distance = 1.0;
        }
    
  4. Next, we need to modify the SaveCommand command property to include the async and await keywords. This command property will be used to bind to the SaveToolBarItem and will run an action upon being pressed. It will then execute a class instance method to determine whether the command can be executed. Proceed and enter in the following highlighted code sections:
            Command _saveCommand; 
            public Command SaveCommand 
            { 
                get 
                { 
                    return _saveCommand ?? (_saveCommand = 
    
                    new Command(async () => await ExecuteSaveCommand(), 
     
                    ValidateFormDetails)); 
                } 
            } 
    
  5. Next, we locate and modify the ExecuteSaveCommand instance method to include the async Task keywords to the method definition, and then include a reference to our PreviousPage method that is defined within our IWalkNavService interface to allow our WalkEntryPage to be dismissed upon the user clicking on the Save button. Proceed and enter in the following highlighted code sections:
            async Task ExecuteSaveCommand()
            {
            var newWalkItem = new WalkEntries
            {
            Title = this.Title,
            Notes = this.Notes,
            Latitude = this.Latitude,
            Longitude = this.Longitude,
            Kilometers = this.Kilometers,
            Difficulty = this.Difficulty,
            Distance = this.Distance,
            ImageUrl = this.ImageUrl
            };
    
           // Here, we will save the details entered in a later chapter.
            await NavService.PreviousPage();
            }
    
            // method to check for any form errors
            bool ValidateFormDetails()
            {
             return !string.IsNullOrWhiteSpace(Title);
            }
    
    
  6. Finally, create the Init method within the WalkEntryViewModel. This will be used to initialize the WalkEntryPage when it is called. We use the Task.Factory.StartNew method to give the ViewModel enough time to display the page on screen, prior to initializing the ContentPage contents. Proceed and enter in the following highlighted code sections:
            public override async Task Init()
            {
            await Task.Factory.StartNew(() =>
            {
            Title = "New Walk";
            Difficulty = "Easy";
            Distance = 1.0;
            });
            }
           }
          }
    
    

In this section, we began by ensuring that our ViewModel inherits from the WalkBaseViewModel class and then modifies the WalksEntryViewModel class constructor to include the parameter navService which is included within the IWalkNavService interface class. In our next step, we'll initialize the class constructor with default values for the Title, Difficulty, and Distance properties and then modify the SaveCommand command method to include a reference to the NavService.PreviousPage method. This is declared within the IWalkNavService interface class to allow our WalkEntryPage to navigate back to the previous calling page when the Save button is clicked.

Updating the WalksEntryPage to use the updated ViewModel

In this section, we need to bind our model binding context, BindingContext, to the WalkEntryViewModel so that the new walk information, which will be entered within this page, can be stored within the WalkEntries model. Let's take a look at how we can achieve this, by performing the following steps:

  1. Ensure that the WalkEntryPage.cs file is displayed within the code editor, and enter in the following highlighted code sections:
            // 
            //  WalkEntryPage.cs 
            //  TrackMyWalks 
            // 
            //  Created by Steven F. Daniel on 04/08/2016. 
            //  Copyright © 2016 GENIESOFT STUDIOS. All rights reserved. 
            // 
            using Xamarin.Forms; 
            using TrackMyWalks.Models; 
            using System.Collections.Generic; 
     
            namespace TrackMyWalks 
            { 
                public class WalkEntryPage : ContentPage 
                { 
    
  2. Next, we need to create a new private property named _viewModel within the WalkEntryPage class that is of the WalksEntryViewModel type, and which will essentially provide us with access to the ContentPage's BindingContext object. Proceed and enter in the following highlighted code sections:
            WalkEntryViewModel _viewModel
     
           {
     
               get 
               { return BindingContext as WalkEntryViewModel;
               }
       
           }  
     
            public WalkEntryPage() 
            { 
                // Set the Content Page Title  
                Title = "New Walk Entry";
  3. Next, we need to declare and initialize our WalkEntryViewModelBindingContext to include the IWalkNavService constructor, which is used by the WalkBaseViewModel class, and is retrieved from the Xamarin.FormsDependencyService class. Proceed and enter in the following highlighted code sections:
            // Declare and initialize our Model Binding Context  
              
     BindingContext = new WalkEntryViewModel(
                DependencyService.
    Get<IWalkNavService>());  
     
                // Define our New Walk Entry fields 
                var walkTitle = new EntryCell 
                { 
                    Label = "Title:", 
                    Placeholder = "Trail Title" 
                }; 
                walkTitle.SetBinding(EntryCell.TextProperty,  
                "Title", BindingMode.TwoWay); 
     
                var walkNotes = new EntryCell 
                { 
                    Label = "Notes:", 
                    Placeholder = "Description" 
                }; 
                walkNotes.SetBinding(EntryCell.TextProperty,  
                "Notes", BindingMode.TwoWay); 
     
                var walkLatitude = new EntryCell 
                { 
                    Label = "Latitude:", 
                    Placeholder = "Latitude", 
                    Keyboard = Keyboard.Numeric 
                }; 
                walkLatitude.SetBinding(EntryCell.TextProperty, 
                "Latitude", BindingMode.TwoWay); 
     
                var walkLongitude = new EntryCell 
                { 
                    Label = "Longitude:", 
                    Placeholder = "Longitude", 
                    Keyboard = Keyboard.Numeric 
                }; 
                walkLongitude.SetBinding(EntryCell.TextProperty,  
                "Longitude", BindingMode.TwoWay); 
     
                var walkKilometers = new EntryCell 
                { 
                    Label = "Kilometers:", 
                    Placeholder = "Kilometers", 
                    Keyboard = Keyboard.Numeric 
                }; 
                walkKilometers.SetBinding(EntryCell.TextProperty,  
                "Kilometers", BindingMode.TwoWay); 
     
                var walkDifficulty = new EntryCell 
                { 
                    Label = "Difficulty Level:", 
                    Placeholder = "Walk Difficulty" 
                }; 
                walkDifficulty.SetBinding(EntryCell.TextProperty,  
                "Difficulty", BindingMode.TwoWay); 
     
                var walkImageUrl = new EntryCell 
                { 
                    Label = "ImageUrl:", 
                    Placeholder = "Image URL" 
                }; 
                walkImageUrl.SetBinding(EntryCell.TextProperty,  
                "ImageUrl", BindingMode.TwoWay); 
     
                // Define our TableView  
                Content = new TableView 
                { 
                    Intent = TableIntent.Form, 
                    Root = new TableRoot 
                    { 
                        new TableSection() 
                        { 
                            walkTitle, 
                            walkNotes, 
                            walkLatitude, 
                            walkLongitude, 
                            walkKilometers, 
                            walkDifficulty, 
                            walkImageUrl 
                        } 
                    } 
                }; 
     
                var saveWalkItem = new ToolbarItem 
                { 
                    Text = "Save" 
                }; 
     
                saveWalkItem.SetBinding(MenuItem.CommandProperty, 
                    "SaveCommand"); 
     
                ToolbarItems.Add(saveWalkItem);         } 
            } 
        } 
    

In this section, we looked at the steps involved in modifying the WalkEntryPage so that it can take advantage of our updated WalkEntryViewModel. We looked at how to set the content page to an instance of the WalkEntryViewModel so that the BindingContext property for the WalkEntryPage will now point to an instance of the IWalkNavService interface.

Updating the walks trail page ViewModel and navigation service

Now that we have modified the MVVM ViewModel that will be used for our WalkEntry page, our next step is to begin modifying the WalksTrailViewModel to take advantage of the navigation service, so that it will be used to display the walk entry information that has been associated with the chosen walk.

Let's take a look at how we can achieve this, by performing the following the steps:

  1. Ensure that the WalksTrailViewModel.cs file is displayed within the code editor, and enter in the following highlighted code sections:
            // 
            //  WalksTrailViewModel.cs 
            //  TrackMyWalks ViewModels 
            // 
            //  Created by Steven F. Daniel on 22/08/2016. 
            //  Copyright © 2016 GENIESOFT STUDIOS. All rights reserved. 
            // 
            using System.Threading.Tasks; 
            using TrackMyWalks.Models; 
            using TrackMyWalks.Services; 
            using Xamarin.Forms; 
     
            namespace TrackMyWalks.ViewModels 
            { 
                public class WalksTrailViewModel :
                  WalkBaseViewModel<WalkEntries> 
                { 
                    WalkEntries _walkEntry; 
     
                    public WalkEntries WalkEntry 
                    { 
                        get { return _walkEntry; } 
                        set 
                        { 
                            _walkEntry = value; 
                            OnPropertyChanged(); 
                        } 
                    } 
    
  2. Next, we need to create a Command property for our class. This will be used within our WalkTrailPage and will be used to handle when the user clicks on the Begin This Trial button. The Command property will run an action upon being pressed, and then execute a class instance method to determine whether the command can be executed or not, prior to navigating to the DistTravelledViewModel, and passing in the trailDetails for the chosen walk from the WalksPage. Proceed and enter in the following highlighted code sections:
            Command<WalkEntries> _command;
            public Command<WalkEntries> DistanceTravelled
            {
                get
                {
                    return _command
                    ?? (_command = 
                    new Command<WalkEntries>(async (trailDetails) =>
                    await NavService.NavigateToViewModel
                    <DistTravelledViewModel, WalkEntries>(trailDetails))); 
                 }
             }
     
    
  3. Next, we need to declare and initialize our WalksTrailViewModelBindingContext to include the IWalkNavService constructor, which is used by the WalkBaseViewModel class, and is retrieved from the Xamarin.FormsDependencyService class. Proceed and enter in the following highlighted code sections:
        public WalksTrailViewModel(IWalkNavService navService) : 
          base(navService)
        {
        }
    
    
  4. Finally, create the Init method within the WalksTrailViewModel. This will be used to initialize the WalkTrailPage when it is called. We use the Task.Factory.StartNew method to give the ViewModel enough time to display the page on screen, prior to initializing the ContentPage contents, using the passed in walkDetails for our model. Proceed and enter in the following highlighted code sections:
            public override async Task Init(WalkEntries walkDetails)
       
            {
     await Task.Factory.StartNew(() =>
    
             {
       
                 WalkEntry = walkDetails;
              
    });
      
             } 
            } 
           }  
    

In this section, we begin by ensuring that our ViewModel inherits from the WalkBaseViewModel class, and that it accepts the WalkEntries dictionary as its parameter. In our next step, we'll create a DistanceTravelledCommand method that will navigate to the DistanceTravelledPage content page within our NavigationStack that passes the WalkEntry dictionary to the DistTravelledViewModel ViewModel and pass a parameter containing the trailDetails of the chosen walk.

Updating the WalksTrailPage to use the updated ViewModel

In this section, we need to bind our model binding context, BindingContext, to the WalksTrailViewModel so that the walk information details will be displayed from the WalkEntries model when a walk has been clicked on within the main WalksPage. Let's take a look at how we can achieve this, by performing the following steps:

  1. Ensure that the WalkTrailPage.cs file is displayed within the code editor, and enter in the following highlighted code sections:
            // 
            //  WalkTrailPage.cs 
            //  TrackMyWalks 
            // 
            //  Created by Steven F. Daniel on 04/08/2016. 
            //  Copyright © 2016 GENIESOFT STUDIOS. All rights reserved. 
           // 
           using Xamarin.Forms; 
           using TrackMyWalks.Models; 
           using TrackMyWalks.ViewModels;
    
           using TrackMyWalks.Services;  
     
           namespace TrackMyWalks 
           { 
               public class WalkTrailPage : ContentPage 
               { 
                   public WalkTrailPage(WalkEntries walkItem) 
                   { 
                        Title = "Walks Trail"; 
    
  2. Next, we need to declare and initialize our WalkEntryViewModelBindingContext to include the IWalkNavService constructor, which is used by the WalkBaseViewModel class, and is retrieved from the Xamarin.FormsDependencyService class. Proceed and enter in the following highlighted code sections:
            // Declare and initialize our Model Binding Context
     
            BindingContext = new WalksTrailViewModel(DependencyService.
    
            Get<IWalkNavService>());  
     
            var beginTrailWalk = new Button 
            { 
                BackgroundColor = Color.FromHex("#008080"), 
                TextColor = Color.White, 
                Text = "Begin this Trail" 
            }; 
    
  3. Next, we need to modify the beginTrailWalk.Clicked handler for our button, so that upon being clicked, it will navigate to the DistTravelledViewModel and pass in the WalkEntry dictionary for the chosen walk from the WalksPage. Proceed and enter in the following highlighted code sections:
            // Declare and initialize our Event Handler 
            beginTrailWalk.Clicked += (sender, e) => 
            { 
              if (_viewModel.WalkEntry == null) return;
    
              _viewModel.DistanceTravelled.Execute(_viewModel.WalkEntry);  
            }; 
     
            var walkTrailImage = new Image() 
            { 
                Aspect = Aspect.AspectFill 
            }; 
            walkTrailImage.SetBinding(Image.SourceProperty,  
            "WalkEntry.ImageUrl"); 
     
            var trailNameLabel = new Label() 
            { 
                FontSize = 28, 
                FontAttributes = FontAttributes.Bold,
                 TextColor = Color.Black 
             }; 
     
                trailNameLabel.SetBinding(Label.TextProperty,  
                "WalkEntry.Title"); 
     
                var trailKilometersLabel = new Label() 
                { 
                    FontAttributes = FontAttributes.Bold, 
                    FontSize = 12, 
                    TextColor = Color.Black, 
                }; 
                trailKilometersLabel.SetBinding(Label.TextProperty,  
                "WalkEntry.Kilometers", 
                stringFormat: "Length: {0} km"); 
                var trailDifficultyLabel = new Label() 
                { 
                    FontAttributes = FontAttributes.Bold, 
                    FontSize = 12, 
                    TextColor = Color.Black 
                }; 
     
                trailDifficultyLabel.SetBinding(Label.TextProperty,  
                "WalkEntry.Difficulty", stringFormat: "Difficulty: {0}"); 
     
                var trailFullDescription = new Label() 
                { 
                    FontSize = 11, 
                    TextColor = Color.Black, 
                    HorizontalOptions = LayoutOptions.FillAndExpand 
                }; 
               
                trailFullDescription.SetBinding(Label.TextProperty,  
                "WalkEntry.Notes"); 
     
                this.Content = new ScrollView 
                { 
                    Padding = 10, 
                    Content = new StackLayout 
                    { 
                        Orientation = StackOrientation.Vertical, 
                        HorizontalOptions = LayoutOptions.FillAndExpand, 
                        Children = 
                        { 
                        walkTrailImage, 
                        trailNameLabel, 
                        trailKilometersLabel, 
                        trailDifficultyLabel, 
                        trailFullDescription, 
                        beginTrailWalk 
                        } 
                      } 
                    }; 
                  } 
                } 
              } 
    

In this section, we looked at the steps involved in modifying the WalksTrailPage so that it can take advantage of the WalksTrailViewModel. We looked at how to set the content page to an instance of the WalksTrailViewModel so that the BindingContext property for the WalkTrailPage will now point to an instance of the IWalkNavService interface.

We also slightly modified our Clicked handler for the beginTrailWalk button so that it will now navigate to the DistanceTravelledPage content page within the NavigationStack, and pass in the WalkEntry dictionary object to the DistTravelledViewModel ViewModel.

Updating the distance travelled ViewModel and navigation service

Now that we have modified the MVVM ViewModel that will be used for our WalkTrailPage, our next step is to update the DistTravelledViewModel to take advantage of the navigation service, so that it can display the walk entry information that has been associated with the chosen walk.

Let's take a look at how we can achieve this, by performing the following steps:

  1. Ensure that the DistTravelledViewModel.cs file is displayed within the code editor, and enter in the following highlighted code sections:
             // 
            //  DistTravelledViewModel.cs 
            //  TrackMyWalks ViewModels 
            // 
            //  Created by Steven F. Daniel on 22/08/2016. 
            //  Copyright © 2016 GENIESOFT STUDIOS. All rights reserved. 
            // 
            using System; 
            using System.Threading.Tasks; 
            using TrackMyWalks.Models; 
            using TrackMyWalks.Services; 
            using TrackMyWalks.ViewModels; 
            using Xamarin.Forms; 
     
            namespace TrackMyWalks.ViewModels 
            { 
                public class DistTravelledViewModel : 
    
                 WalkBaseViewModel<WalkEntries> 
                { 
                    WalkEntries _walkEntry; 
     
                    public WalkEntries WalkEntry 
                    { 
                        get { return _walkEntry; } 
                        set 
                        { 
                            _walkEntry = value; 
                            OnPropertyChanged(); 
                        } 
                    } 
     
                    double _travelled; 
                    public double Travelled 
                    { 
                        get { return _travelled; } 
                        set 
                        { 
                            _travelled = value; 
                            OnPropertyChanged(); 
                        } 
                    } 
     
                    double _hours; 
                    public double Hours 
                    { 
                        get { return _hours; } 
                        set 
                        { 
                            _hours = value; 
                            OnPropertyChanged(); 
                        } 
                    } 
     
                    double _minutes; 
                    public double Minutes 
                    { 
                        get { return _minutes; } 
                        set 
                        { 
                            _minutes = value; 
                            OnPropertyChanged(); 
                        } 
                    } 
     
                    double _seconds; 
                    public double Seconds 
                    { 
                        get { return _seconds; } 
                        set 
                        { 
                            _seconds = value; 
                            OnPropertyChanged(); 
                        } 
                    } 
     
                    public string TimeTaken 
                    { 
                        get 
                        { 
                            return string.Format("{0:00}:{1:00}:{2:00}",  
                            this.Hours, this.Minutes, this.Seconds); 
                        } 
                    } 
    
  2. Next, we need to modify the DistTravelledViewModel class constructor, which will now need to include a navService parameter that is included within the IWalkNavService interface class. We then set the ViewModel's class constructor to access all instance class members contained within the navService by using the base keyword and initialize the constructor with default values for the Hours, Minutes, Seconds, and Travelled properties.
  3. Locate the DistTravelledViewModel class constructor, and enter the following highlighted code:
            public DistTravelledViewModel(IWalkNavService navService) : 
          base(navService)
        {
            this.Hours = 0;
            this.Minutes = 0;
            this.Seconds = 0;
            this.Travelled = 100;
        }
    
  4. Then, create the Init method within the DistTravelledViewModel, which will be used to initialize the DistanceTravelledPage content page when it is called. We need to specify and use the Task.Factory.StartNew method to give the ViewModel enough time to display the page on screen, prior to initializing the ContentPage contents, using the passed in walkDetails for our model. Proceed and enter in the following highlighted code sections:
          public override async Task Init(WalkEntries walkDetails)
        {
             await Task.Factory.StartNew(() =>
            {
                 WalkEntry = walkDetails;
            });
        }
    
  5. Next, we need to create the BackToMainPage command property that will be used to bind to the End This Trail button that will run an action upon being pressed. This action will execute a class instance method, to determine whether the Command can be executed.
  6. If the Command can be executed, a call will be made to the BackToMainPage method on the NavService navigation service class to take the user back to the TrackMyWalks main page, by removing all existing ViewModels within the NavigationStack, except the first page. Proceed and enter in the following highlighted code sections:
            Command _mainPage;
    
            public Command BackToMainPage
     
            {
     
                get
        
               {
       
                   return _mainPage
      ?? (_mainPage = new 
                   Command(async () => await 
    
                   NavService.BackToMainPage()));
     
                }
          
               } 
              } 
             } 
    

In this section, we updated the DistanceTravelledViewModel to inherit from our WalkBaseViewModel Interface class and then modify the DistTravelledViewModel class constructor to point to an instance of the IWalkNavService interface class.

We then created the Init method that will initialize the DistanceTravelledViewModel when it is called and use the Task.Factory.StartNew method to give the ViewModel enough time to display the DistanceTravelledPage content page on screen, prior to initializing the ContentPage contents, using the passed in walkDetails for our model.

We also created the BackToMainPage command property that will be used to bind to the End This Trail button that will run an action to execute a class instance method, to determine whether the Command can be executed, and then a call will be made to BackToMainPage method on the NavService navigation service class to take the user back to the first page within the NavigationStack.

Updating the DistanceTravelledPage to use the updated ViewModel

Now that we have modified the MVVM ViewModel that will be used by our DistanceTravelledPage content page, our next step is to begin modifying the DistanceTravelledPage page to take advantage of our navigation service, and display walk information details. The calculations and distance travelled will be displayed from the WalkEntries model.

Let's take a look at how we can achieve this, by performing the following steps:

  1. Ensure that the DistanceTravelledPage.cs file is displayed within the code editor, and enter in the following highlighted code sections:
            // 
            //  DistanceTravelledPage.cs 
            //  TrackMyWalks 
            // 
            //  Created by Steven F. Daniel on 04/08/2016. 
            //  Copyright © 2016 GENIESOFT STUDIOS. All rights reserved. 
            // 
            using Xamarin.Forms; 
            using Xamarin.Forms.Maps; 
            using TrackMyWalks.Models; 
            using TrackMyWalks.Services; 
     
     
            namespace TrackMyWalks 
            { 
                public class DistanceTravelledPage : ContentPage 
                { 
    
  2. Next, we need to create a new private property named _viewModel within the DistanceTravelledPage class, which is of our DistTravelledViewModel type, and will essentially provide us with access to the ContentPage's BindingContext object. Proceed and enter in the following highlighted code sections:
            DistTravelledViewModel _viewModel
    
            {
     
               get { return BindingContext as DistTravelledViewModel; }
    
            }
       
            public DistanceTravelledPage() 
            { 
                Title = "Distance Travelled"; 
    
  3. Next, we need to declare and initialize the DistTravelledViewModelBindingContext to include our IWalkNavService constructor, which is used by the WalkBaseViewModel class, and is retrieved from the Xamarin.Forms DependencyService class. Proceed and enter in the following highlighted code sections:
        // Declare and initialize our Model Binding Context
         BindingContext = new DistTravelledViewModel
            (DependencyService.
    Get<IWalkNavService>()); 
    
  4. Then, we need to create a new method called LoadDetails which will be used to grab the name of the chosen walk and the Latitude and Longitude values from the DistTravelledViewModel as well as zoom into the user entry location, using the MoveToRegion method. Proceed and enter in the following highlighted code sections:
            public void LoadDetails() 
            { 
                // Instantiate our map object 
                var trailMap = new Map(); 
     
                // Place a pin on the map for the chosen  
                // walk type 
                trailMap.Pins.Add(new Pin 
                { 
                    Type = PinType.Place,
    
                    Label = _viewModel.WalkEntry.Title,
     
                    Position = new Position(_viewModel.WalkEntry.Latitude,
     
                    _viewModel.WalkEntry.Longitude) 
                }); 
     
                // Center the map around the list of
     
               // walks entry's location
     
               trailMap.MoveToRegion(MapSpan.FromCenterAndRadius(
    
                new Position(_viewModel.WalkEntry.Latitude,
     
                 _viewModel.WalkEntry.Longitude),
     
                  Distance.FromKilometers(1.0))); 
     
                var trailNameLabel = new Label() 
                { 
                    FontSize = 18, 
                    FontAttributes = FontAttributes.Bold, 
                    TextColor = Color.Black, 
                    HorizontalTextAlignment = TextAlignment.Center 
                }; 
     
                trailNameLabel.SetBinding(Label.TextProperty,  
                "WalkEntry.Title"); 
     
                var trailDistanceTravelledLabel = new Label() 
                { 
                    FontAttributes = FontAttributes.Bold, 
                    FontSize = 20, 
                    TextColor = Color.Black, 
                    HorizontalTextAlignment = TextAlignment.Center 
                }; 
     
               trailDistanceTravelledLabel.SetBinding(Label.TextProperty, 
               "Travelled", stringFormat: "Distance Travelled: {0} km"); 
     
                var totalTimeTakenLabel = new Label() 
                { 
                    FontAttributes = FontAttributes.Bold, 
                    FontSize = 20, 
                    TextColor = Color.Black, 
                    HorizontalTextAlignment = TextAlignment.Center 
                }; 
     
                totalTimeTakenLabel.SetBinding(Label.TextProperty,  
                "TimeTaken", stringFormat: "Time Taken: {0}"); 
     
                var walksHomeButton = new Button 
                { 
                    BackgroundColor = Color.FromHex("#008080"), 
                    TextColor = Color.White, 
                    Text = "End this Trail" 
                }; 
    
  5. Next, we need to modify the walksHomeButton.Clicked handler for our button so that, upon being clicked, it will allow the DistanceTravelledPage to navigate back to the first page within the NavigationStack. Proceed and enter in the following highlighted code sections:
                // Set up our event handler 
                walksHomeButton.Clicked += (sender, e) => 
                { 
                    if (_viewModel.WalkEntry == null) return;
    
                    _viewModel.BackToMainPage.Execute(0); 
                }; 
     
                this.Content = new ScrollView 
                { 
                    Padding = 10, 
                    Content = new StackLayout 
                    { 
                        Orientation = StackOrientation.Vertical, 
                        HorizontalOptions = LayoutOptions.FillAndExpand, 
                        Children = { 
                        trailMap, 
                        trailNameLabel, 
                        trailDistanceTravelledLabel, 
                        totalTimeTakenLabel, 
                        walksHomeButton 
                        } 
                    } 
                }; 
            }  
    
  6. Finally, we need to create an OnAppearing instance method of the navigation hierarchy that will be used to correctly plot the walk's Longitude and Latitude coordinates within the map, along with the walk information, prior to the ViewModel appearing on screen. We need to ensure that the ViewModel has properly been initialized by checking to see that it isn't null, prior to calling the Init method of the DistTravelledViewModel. Proceed and enter in the following highlighted code sections:
    protected override async void OnAppearing()
        {
            base.OnAppearing();
            // Initialize our DistanceTravelledViewModel
            if (_viewModel != null)
            {
                await _viewModel.Init();
                LoadDetails();
             }
          }
        }
    } 
    

In this section, we looked at the steps involved in modifying the DistanceTraveledPage so that it can take advantage of the DistTravelledViewModel. We looked at how to set the content page to an instance of the DistTravelledViewModel so that the BindingContext property for the DistanceTravelledPage will now point to an instance of the IWalkNavService interface.

We also slightly modified our Clicked handler for the WalksHomeButton button, so that it will now navigate to the NavService.BackToMainPage method, which is declared within the IWalkNavService interface class to allow the DistanceTravelledPage to navigate back to the first page within the NavigationStack.

Updating the Xamarin.Forms.App class to use the navigation service

In this section, we need to update our Xamarin.Forms.App class, by modifying the constructor in the main App class to create a new instance of the navigation service and register the application's ContentPage to ViewModel mappings.

Let's take a look at how we can achieve this, by performing the following steps:

  1. Open the TrackMyWalks.cs file and ensure that it is displayed within the code editor.
  2. Next, locate the App method and enter in the following highlighted code sections:
            // 
            //  TrackMyWalks.cs 
            //  TrackMyWalks 
            // 
            //  Created by Steven F. Daniel on 04/08/2016. 
            //  Copyright © 2016 GENIESOFT STUDIOS. All rights reserved. 
            //
    using TrackMyWalks.Services;
    
            using TrackMyWalks.ViewModels; 
            using Xamarin.Forms; 
     
            namespace TrackMyWalks 
            { 
                public class App : Application 
                { 
                    public App() 
                    { 
                        // Check the Target OS Platform 
                        if (Device.OS == TargetPlatform.Android) 
                       { 
                          MainPage = new SplashPage(); 
                        } 
                        else 
                        { 
                            // The root page of your application 
                            var walksPage = new NavigationPage(new WalksPage()
    
                            {
    
                                Title = "Track My Walks"
     
                             });
     
                            var navService = DependencyService.
    
                            Get<IWalkNavService>() as WalkNavService;
    
                           navService.navigation = walksPage.Navigation;
     
                           navService.RegisterViewMapping(typeof
                           (WalksPageViewModel
    ), typeof(WalksPage));
    
                           navService.RegisterViewMapping(
                           typeof(WalkEntryViewModel
    ),
                           typeof(WalkEntryPage));
    
                           navService.RegisterViewMapping(
                           typeof(WalksTrailViewModel
    ),
                           typeof(WalkTrailPage));
    
                           navService.RegisterViewMapping(
                           typeof(DistTravelledViewMo
    del),
                           typeof(DistanceTravelledPage));
    
                           MainPage = walksPage; 
                } 
            } 
     
            protected override void OnStart() 
            { 
                // Handle when your app starts 
            } 
     
            protected override void OnSleep() 
            { 
                // Handle when your app sleeps 
            } 
     
            protected override void OnResume() 
            { 
                // Handle when your app resumes 
            } 
           } 
          } 
    

In the preceding code snippet, we begin by declaring a navService variable that points to an instance of the navigation service as defined by our assembly attribute for the Xamarin.FormsDependencyService, as declared in the WalkNavService class.

In our next step, we set the navService.navigation property to point to an instance of the NavigationPage class that the walksPage.navigation property currently points to, and will be used as the main root page.

Finally, we call the RegisterViewMapping instance method for each of the ViewModels and specify the associated ContentPage for each.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.134.130