XF vs PWA: XF Setting Up the Dashboard Page

The Goal

In this post we are going to setup the Dashboard page. The purpose of this page is to list the users favorite routes and stops so that no matter where they are, they can proceed straight to that information. It will be a relatively simple page, really just two lists. Tapping an item on one of the lists will navigate to the appropriate page and display the favorite route or stop.

Laying out the Page

This will be a pretty simple layout. Just use a grid, four rows, an auto height row for each title and an equal expanding height row for each of the lists.

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <Label Grid.Row="0" Text="My Favorite Stops" />
    <ListView Grid.Row="1" ItemsSource="{Binding Stops}"></ListView>

    <Label Grid.Row="2" Text="My Favorite Routes" />
    <ListView Grid.Row="3" ItemsSource="{Binding Routes}"></ListView>
</Grid>

ViewModel

The view model is going to be just as simple. Two ObservableCollections to handle the lists and a pair of commands to handle the user tapping on a list item. If you remember from setting up the hamburger menu, we are going to derive our view model from the BaseViewModel class.

public class DashboardPageViewModel : BaseViewModel
{
    private INavigationService _navigationService = null;
    private IFavoriteService _favoriteService = null;


    public DashboardPageViewModel(INavigationService navigationService, IFavoriteService favoriteService)
    {
        _navigationService = navigationService;
        _favoriteService = favoriteService;
    }


    // binds to the routes list view
    private ObservableCollection<FavoriteRouteViewModel> _routes = new ObservableCollection<FavoriteRouteViewModel>();
    public ObservableCollection<FavoriteRouteViewModel> Routes
    {
        get => _routes;
        set => SetProperty<ObservableCollection<FavoriteRouteViewModel>>(ref _routes, value);
    }

    // binds to the stops list view
    private ObservableCollection<FavoriteStopViewModel> _stops = new ObservableCollection<FavoriteStopViewModel>();
    public ObservableCollection<FavoriteStopViewModel> Stops
    {
        get => _stops;
        set => SetProperty<ObservableCollection<FavoriteStopViewModel>>(ref _stops, value);
    }


    // user taps on one of the stops
    private DelegateCommand<FavoriteStopViewModel> _commandGoToStop = null;
    public DelegateCommand<FavoriteStopViewModel> CommandGoToStop
    {
        get
        {
            return _commandGoToStop ??
                (_commandGoToStop = new DelegateCommand<FavoriteStopViewModel>((stop) => { }));
        }
    }

    // user taps on one of the routes
    private DelegateCommand<FavoriteRouteViewModel> _commandGoToRoute = null;
    public DelegateCommand<FavoriteRouteViewModel> CommandGoToRoute
    {
        get
        {
            return _commandGoToRoute ??
                (_commandGoToRoute = new DelegateCommand<FavoriteRouteViewModel>((route) => { }));
        }
    }
}

So how do we get the view model into the page itself? If you remember from the first post, we have to register all of our pages that we navigate to with the container, and we do that in our PrismApplication RegisterTypes method. In this case, we will register the dashboard page and the view model as follows:

public partial class App : PrismApplication
{
    // other stuff here

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.Register<IFavoriteService, TestFavoriteService>();
        containerRegistry.RegisterForNavigation<Views.DashboardPage, ViewModels.DashboardPageViewModel>(Pages.Dashboard);
        /// other registrations
    }
}

In the above code, we are telling the navigation service that when we navigate to the DashboardPage, we should also inject the view model into the page’s DataContext member. Of course the navigation service will make use of the Container to resolve any dependencies that might be required for the view model, such as IFavoriteService and INavigationService. Notice that we also register the IFavoriteService implementation in the RegisterTypes function. We don’t have to do that for INavigationService as it is a core part of the Prism framework and already taken care of in the PrismApplication base class.

Initializing the View Model

Things are looking pretty good, but how do we initialize the data in our view model? This is another part where the navigation service in Prism really helps us. After it finishes constructing the page, injecting the view model and making it the active view, it will test to see if the view model that was injected in the data context implements the INavigationAware interface (check out INavigatedAware and INavigatingAware for more granular implementations). INavigationAware looks like this:

public interface INavigationAware
{
    void OnNavigatingTo(INavigationParameters parameters);
    void OnNavigatedFrom(INavigationParameters parameters);
    void OnNavigatedTo(INavigationParameters parameters);
}

We will have our viewmodel implement the INavigationAware interface and then load our data when the ‘OnNavigatedTo’ method is called.

public class DashboardPageViewModel : BaseViewModel, INavigationAware
{
    /// all the other stuff from above here



    public void OnNavigatedFrom(INavigationParameters parameters)
    {
        // nothing doing here
    }

    public async void OnNavigatedTo(INavigationParameters parameters)
    {
        var favStops = await _favoriteService.GetFavoriteStopsAsync();
        /// do something here with the collection
        var favRoutes = await _favoriteService.GetFavoriteRoutesAsync();
        /// do something here with the routes collection
    }

    public void OnNavigatingTo(INavigationParameters parameters)
    {
        // nothing happening here
    }
}

And that is most of the work for the dashboard page. Really the only thing left to do is to make sure that the event that is raised from tapping one of the listview items is routed to our DelegateCommand objects in the view model. For an example of that, check out my article here.

Storing the Favorites Data

By now, you might be thinking, how am I going to store the data for the favorites in a platform independant way and still be able to use it with my shared view model? I am glad you asked, because I think that will be the next post!

code in GitHub