Navigation Service for Cross Platform Page/View Navigation from View Model

I already have a solution for allowing the view model to jump back onto the UI thread using the UIContext. However, I currently don’t have a mechanism that will allow one view model initiate navigation to a new page/view. What I want is the ability for one view model to request navigation by specifying the destination view model. Of course, this needs to work cross platform. Let’s take a look at the basics of how this could work – there are essentially two strategies that most mvvm style navigation frameworks use: The first is by convention where the lookup for the destination page/view is based on the name of the target view model; the second is by defining a mapping between view models and the corresponding page/view. In this case I’m going to go with the latter – In the Core library I define a couple of interfaces and an abstract implementation:

public interface INavigateService
{
    void Navigate<TViewModel>() where TViewModel : IDataViewModel;
}

public interface INativeNavigateService<TView> : INavigateService
    where TView : class,new()
{
    void Register<TViewModel, TViewType>() where TViewType : TView;
}

public abstract class CoreNavigateService<TView> : INativeNavigateService<TView> where TView : class, new()
{
    private readonly IDictionary<Type, Type> viewDictionary = new Dictionary<Type, Type>();

    protected Type ViewType<TViewModel>()
    {
        Type viewType = null;
        viewDictionary.TryGetValue(typeof(TViewModel), out viewType);
        return viewType;
    }

    public void Register<TViewModel, TViewType>() where TViewType : TView
    {
        viewDictionary[typeof(TViewModel)] = typeof(TView);
    }

    public void Navigate<TViewModel>() where TViewModel : IDataViewModel
    {
        var navType = ViewType<TViewModel>();
        NavigateToView(navType);
    }

    protected abstract void NavigateToView(Type viewType);
}

Next, in the client projects, I define a class that inherits from CoreNavigateService and implements the NavigateToView method. Here is the Windows Platform implementation:

public class WindowsPlatformNavigationService : CoreNavigateService<Page>
{
    protected override void NavigateToView(Type viewType)
    {
        (Window.Current.Content as Frame).Navigate(viewType);
    }
}

The ApplicationStartup method now looks like:

public void ApplicationStartup()
{
    CoreApplication.Startup(builder =>
    {

        builder.RegisterType<SignalRFactory>().As<ISignalR>();
#if NETFX_CORE
        builder.RegisterType<UniversalUIContext>().As<IUIContext>();

        builder.RegisterType<WindowsPlatformNavigationService>().SingleInstance().As<INavigateService>();
#endif
    });

    var navService = ServiceLocator.Current.GetInstance<INavigateService>() as WindowsPlatformNavigationService;

#if NETFX_CORE
    navService.Register<MainViewModel,MainPage>();
    navService.Register<SecondViewModel,SecondPage>();
#endif
}

Both the IDataViewModel, BaseViewModel and ViewModelLocator need to be extended to include an INavigateService property called NavigateService. Now from within the view model, navigation can be invoked by calling NavigateService.Navigate<SecondViewModel>()

Leave a comment