locked
How do you guys handle navigation using MVVM and PCL? RRS feed

  • General discussion

  • I'm trying to get more familiar with MVVM and PCL's. I have an app that I'd like to add a PCL to share code between the phone and Win8 versions and want to handle page navigation. Following a guide, I came up with this for an ApplicationViewModel, but it assumes a WPF app and just set the content of the app bound to a property of the vm. I was thinking about maybe making an abstract method "Navigate" in the ApplicationViewModel class and extending it in each of the different projects and having those handle the navigation instead of the current ChangeViewModel method.

    public class ApplicationViewModel : BaseViewModel
        {
            #region Fields
    
            private ICommand _changePageCommand;
            private IPageViewModel _currentPageViewModel;
            private List<IPageViewModel> _pageViewModels;
    
            #endregion
    
            public ApplicationViewModel()
            {
                // available pages
                PageViewModels.Add(new CommunityViewModel);
    
                // the starting page
                CurrentPageViewModel = PageViewModels[0];
            }
    
            #region Properties / Commands
    
            public ICommand ChangePageCommand
            {
                get
                {
                    if (_changePageCommand == null)
                    {
                        _changePageCommand = new RelayCommand(
                            p => ChangeViewModel((IPageViewModel)p),
                            p => p is IPageViewModel);
                    }
    
                    return _changePageCommand;
                }
            }
    
            public List<IPageViewModel> PageViewModels
            {
                get
                {
                    if (_pageViewModels == null)
                        _pageViewModels = new List<IPageViewModel>();
    
                    return _pageViewModels;
                }
            }
    
            public IPageViewModel CurrentPageViewModel
            {
                get
                {
                    return _currentPageViewModel;
                }
                set
                {
                    if (_currentPageViewModel != value)
                    {
                        _currentPageViewModel = value;
                        NotifyPropertyChanged("CurrentPageViewModel");
                    }
                }
            }
    
            #endregion
    
            #region Methods
    
            private void ChangeViewModel(IPageViewModel viewModel)
            {
                if (!PageViewModels.Contains(viewModel))
                    PageViewModels.Add(viewModel);
    
                CurrentPageViewModel = PageViewModels
                    .FirstOrDefault(vm => vm == viewModel);
            }
    
            #endregion
        }


    Michael DiLeo

    Tuesday, November 26, 2013 8:21 PM

All replies

  • I would not put your Navigation logic directly in a ViewModel. Instead, I typically create a NavigationService that consists of an interface and an abstract class in the PCL core library.  You can then further implement it in the platform specific libraries based on their respective Navigation behavior.  And if you're doing MVVM then of course you'll want to navigate between Views from within your ViewModels so you'll want to also have a way of "mapping" your Views to your ViewModels so you can navigate within ViewModels (which should reside in your PCL) with no knowledge or direct access to the Views (which reside in your platform specific libraries).  I use the SimpleIoc that comes with MVVMLight to make this happen in the ViewModelLocator which also ensures every ViewModel will be able to leverage the NavigationService.

    Here is what my interface in my PCL core libraries typically looks like:

    namespace CorePCL
    {
         public interface INavigationService
         {
              event EventHandler CanGoBackChanged;
    
              bool CanGoBack { get; }
    
              void GoBack();
    
              void NavigateTo<TViewModel>()
                   where TViewModel : ViewModelBase;
    
              void NavigateToUrl(Uri address);
    
              void RemoveBackEntry(); // for Windows Phone
         }
    }

    And then in the same PCL core library I implement this interface with an abstract class like this that can be extended in the platform specific libraries (ie Win8 and WP8):

    namespace CorePCL
    {
        public abstract class NavigationService<TViewId> : INavigationService
        {
            readonly Dictionary<Type, TViewId> _map = new Dictionary<Type, TViewId>();
    
            protected NavigationService()
            {
            }
    
            public event EventHandler CanGoBackChanged;
    
            public abstract bool CanGoBack { get; }
    
            public void GoBack()
            {
                GoBackCore();
                OnCanGoBackChanged(EventArgs.Empty);
            }
    
            public void Register(Type type, TViewId id)
            {
                this._map.Add(type, id);
            }
    
            public void NavigateTo<TViewModel>()
                where TViewModel : ViewModelBase
            {
                // Get the View that is mapped to the ViewModel
                TViewId id;
    
                if (!_map.TryGetValue(typeof(TViewModel), out id))
                    throw new ArgumentException("There is no View mapped to " + typeof(TViewModel).FullName);
    
                // Use the platform specific implementation to perform the View navigation
                NavigateTo(id);
    
                OnCanGoBackChanged(EventArgs.Empty);
            }
    
            #region Abstract methods
    
            protected abstract void GoBackCore();
    
            protected abstract void NavigateTo(TViewId id);
    
            public abstract void NavigateToUrl(Uri address);
    
            public abstract void RemoveBackEntry();
    
            protected virtual void OnCanGoBackChanged(EventArgs e)
            {
                var handler = CanGoBackChanged;
                if (handler != null)
                {
                    handler(this, e);
                }
            }
    
            #endregion
        }
    }

    Hope this helps get you started in the right direction for doing Navigation in MVVM with a PCL.


    Ed Snider

    Thursday, November 28, 2013 4:39 PM
  • Thanks! this is super helpful!

    Michael DiLeo

    Wednesday, December 4, 2013 1:18 AM