MVVM Light 5.0: How to use the Navigation service
Yes, MvvmLight
introduced the NavigationService
in their last version but they did't offer any implementation regarding Wpf
(you can use the Implemented NavigationService
in WP, Metroapps, ..) but unfortunately not Wpf
, you need to implement that by your self,
here how i am currently doing it (credit)
first create you navigation interface that Implements the MvvmLight
INavigationService
public interface IFrameNavigationService : INavigationService
{
object Parameter { get; }
}
the Parameter
is used to pass objects between ViewModels
, and the INavigationService
is part of GalaSoft.MvvmLight.Views
namespace
then implemente that interface like so
class FrameNavigationService : IFrameNavigationService,INotifyPropertyChanged
{
#region Fields
private readonly Dictionary<string, Uri> _pagesByKey;
private readonly List<string> _historic;
private string _currentPageKey;
#endregion
#region Properties
public string CurrentPageKey
{
get
{
return _currentPageKey;
}
private set
{
if (_currentPageKey == value)
{
return;
}
_currentPageKey = value;
OnPropertyChanged("CurrentPageKey");
}
}
public object Parameter { get; private set; }
#endregion
#region Ctors and Methods
public FrameNavigationService()
{
_pagesByKey = new Dictionary<string, Uri>();
_historic = new List<string>();
}
public void GoBack()
{
if (_historic.Count > 1)
{
_historic.RemoveAt(_historic.Count - 1);
NavigateTo(_historic.Last(), null);
}
}
public void NavigateTo(string pageKey)
{
NavigateTo(pageKey, null);
}
public virtual void NavigateTo(string pageKey, object parameter)
{
lock (_pagesByKey)
{
if (!_pagesByKey.ContainsKey(pageKey))
{
throw new ArgumentException(string.Format("No such page: {0} ", pageKey), "pageKey");
}
var frame = GetDescendantFromName(Application.Current.MainWindow, "MainFrame") as Frame;
if (frame != null)
{
frame.Source = _pagesByKey[pageKey];
}
Parameter = parameter;
_historic.Add(pageKey);
CurrentPageKey = pageKey;
}
}
public void Configure(string key, Uri pageType)
{
lock (_pagesByKey)
{
if (_pagesByKey.ContainsKey(key))
{
_pagesByKey[key] = pageType;
}
else
{
_pagesByKey.Add(key, pageType);
}
}
}
private static FrameworkElement GetDescendantFromName(DependencyObject parent, string name)
{
var count = VisualTreeHelper.GetChildrenCount(parent);
if (count < 1)
{
return null;
}
for (var i = 0; i < count; i++)
{
var frameworkElement = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;
if (frameworkElement != null)
{
if (frameworkElement.Name == name)
{
return frameworkElement;
}
frameworkElement = GetDescendantFromName(frameworkElement, name);
if (frameworkElement != null)
{
return frameworkElement;
}
}
}
return null;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
the MainFrame
in the above code is the x:Name of a simple Frame
control Defined in Xaml
used to navigate between pages (customize based on your needs)
Second: In the viewmodellocator
, init your navigation service (SetupNavigation()
), so you can use it in your viewmodels:
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SetupNavigation();
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<LoginViewModel>();
SimpleIoc.Default.Register<NoteViewModel>();
}
private static void SetupNavigation()
{
var navigationService = new FrameNavigationService();
navigationService.Configure("LoginView", new Uri("../Views/LoginView.xaml",UriKind.Relative));
navigationService.Configure("Notes", new Uri("../Views/NotesView.xaml", UriKind.Relative));
SimpleIoc.Default.Register<IFrameNavigationService>(() => navigationService);
}
Third: finaly, use the service, for example
public LoginViewModel(IFrameNavigationService navigationService)
{
_navigationService = navigationService;
}
...
_navigationService.NavigateTo("Notes",data);
..
EDIT
An explicit sample can be found at this repo.
MVVM Light Toolkit NavigationService implementation for WPF using a Frame
MvvmLight doesn't provide an implementation of the INavigationService
for WPF. This is because there is no standard navigation mechanism available in WPF as Laurent Bugnion - the creator of MvvmLight - has stated here: http://blog.galasoft.ch/posts/2014/10/announcing-mvvm-light-v5-for-windows-and-xamarin/
The Frame control has a NavigationService property. There is an example of how you could implement the INavigationInterface
in WPF available here:
MVVM Light 5.0: How to use the Navigation service
Navigation in WPF MVVM
like the comments mention you provided not much information but maybe following question helps:
MVVM Light 5.0: How to use the Navigation service
Or maybe you can look at the docs or posts from the author.
http://www.mvvmlight.net/doc/nav1.cshtml
or... if you use google you find blog posts like this...
https://marcominerva.wordpress.com/2014/10/10/navigationservice-in-mvvm-light-v5/
HTH
How to navigate through windows with MVVM Light for WPF?
Eventually I did it this way.
Following the idea of o_q, I created NavigationWindow as MainWindow and changed all the the views to page.
Then, I created an inteface and a class which using Navigation:
public interface INavigationService
{
event NavigatingCancelEventHandler Navigating;
void NavigateTo(Uri pageUri);
void GoBack();
}
public class NavigationService : INavigationService
{
private NavigationWindow _mainFrame;
#region Implementation of INavigationService
public event NavigatingCancelEventHandler Navigating;
public void NavigateTo(Uri pageUri)
{
if (EnsureMainFrame())
{
_mainFrame.Navigate(pageUri);
}
}
public void GoBack()
{
if (EnsureMainFrame()
&& _mainFrame.CanGoBack)
{
_mainFrame.GoBack();
}
}
#endregion
private bool EnsureMainFrame()
{
if (_mainFrame != null)
{
return true;
}
_mainFrame = System.Windows.Application.Current.MainWindow as NavigationWindow;
if (_mainFrame != null)
{
// Could be null if the app runs inside a design tool
_mainFrame.Navigating += (s, e) =>
{
if (Navigating != null)
{
Navigating(s, e);
}
};
return true;
}
return false;
}
}
Then, in viewModelLocator I created all the const string nedded to store the paths to my views:
public class ViewModelLocator
{
#region Views Paths
public const string FrontendViewPath = "../Views/FrontendView.xaml";
public const string BackendViewPath = "../Views/BackendView.xaml";
public const string StartUpViewPath = "../Views/StartUpView.xaml";
public const string LoginViewPath = "../Views/LoginView.xaml";
public const string OutOfOrderViewPath = "../Views/OutOfOrderView.xaml";
public const string OperativeViewPath = "../Views/SubViews/OperativeView.xaml";
public const string ConfigurationViewPath = "../Views/SubViews/ConfigurationView.xaml";
#endregion
}
In App.cs, in the Application_Startup event handler, with the help of Unity IoC I registered a singleton of NavigationService:
public partial class App : System.Windows.Application
{
private static IUnityContainer _ambientContainer;
public static IServiceLocator AmbientLocator { get; private set; }
...
private void Application_Startup(object sender, System.Windows.StartupEventArgs e)
{
_ambientContainer =
new UnityContainer();
_ambientContainer.RegisterType<INavigationService, NavigationService>(new ContainerControlledLifetimeManager());
AmbientLocator = new UnityServiceLocator(_ambientContainer);
ServiceLocator.SetLocatorProvider(() => AmbientLocator);
Now, in my ViewModelLocator, I can register a "Galasoft" message to catch all the events and navigate to a page; in the constructor I have:
public ViewModelLocator()
{
CreateMain();
CreateFrontend();
CreateBackend();
CreateStartUp();
CreateOperative();
CreateLogin();
CreateConfiguration();
CreateOutOfOrder();
// Set Startup Page...
ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(StartUpViewPath, UriKind.Relative));
Messenger.Default.Register<MoveToViewMessage>(this, message =>
{
switch (message.StateInfo.StateType)
{
case StateType.StartUpState:
ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(StartUpViewPath,UriKind.Relative));
break;
case StateType.LoginState:
ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(LoginViewPath, UriKind.Relative));
break;
case StateType.OperativeState:
ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(OperativeViewPath, UriKind.Relative));
break;
case StateType.ConfigurationState:
ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(ConfigurationViewPath, UriKind.Relative));
break;
case StateType.ClosedState:
case StateType.OutOfOrderState:
ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(OutOfOrderViewPath, UriKind.Relative));
break;
default:
ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(StartUpViewPath, UriKind.Relative));
break;
}
});
}
In this way I keep all the viewModels "ignorant"... they don't know anything about navigation, plus I don't have code behind.
If I need to navigate by using a button from a view I can resolve NavigationService from the connected viewModel and navigate to the Page I need.
And, most important thing, it works!
Mvvm light wpf navigation
I managed to solve / answer my own questions
Just create a dumb ViewModel class for the Detail view, provide it with the (Main)ViewModel instance and make that ViewModel instance a property so you can bind to it. Done! Easy after all, and with Dependency injection it can become a breath.
See this link page.DataContext not inherited from parent Frame?
I solved this one and tracked it in the following separate question. See answer here
WPF ViewBox blocks VisualTreeHelper search
Windows Phone Mvvm Light Navigate between Views
In MVVM Light 5 Laurent has introduce NavigationService
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
var navigationService = this.CreateNavigationService();
SimpleIoc.Default.Register<INavigationService>(() => navigationService);
SimpleIoc.Default.Register<IDialogService, DialogService>();
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<DetailsViewModel>();
}
private INavigationService CreateNavigationService()
{
var navigationService = new NavigationService();
navigationService.Configure("Details", typeof(DetailsPage));
// navigationService.Configure("key1", typeof(OtherPage1));
//From a working project.
navigationService.Configure("tnc", new System.Uri("/Views/TncAgreement.xaml", System.UriKind.Relative));
return navigationService;
}
Your ViewModel
public class MainViewModel : ViewModelBase
{
private INavigationService navigationService;
public RelayCommand DetailsCommand { get; set; }
public MainViewModel(INavigationService navigationService)
{
this.navigationService = navigationService;
//Show Terms and condition agreement;
navigationService.NavigateTo("tnc");
DetailsCommand = new RelayCommand(() =>
{
navigationService.NavigateTo("Details", "My data");
});
}
}
Similar SO Answer
How to implement custom navigation service in mvvmlight
Here's a full implementation example that would solve the problem by adding a new interface that fully replaces the one from MvvmLight and also allows you to choose whether to use animations or not. In this example, we add the ability to control whether the navigation should be animated or not:
Interface
public interface ICustomNavigationService
{
string CurrentPageKey { get; }
void GoBack(bool animate = true);
void NavigateTo(string pageKey, bool animate = true);
void NavigateTo(string pageKey, object parameter, bool animate = true);
}
Implementation
public class NavigationService : ICustomNavigationService
{
private readonly Dictionary<string, Type> _pagesByKey = new Dictionary<string, Type>();
private NavigationPage _navigation;
public NavigationPage Navigation
{
get
{
return _navigation;
}
}
public string CurrentPageKey
{
get
{
lock (_pagesByKey)
{
if (_navigation.CurrentPage == null)
{
return null;
}
var pageType = _navigation.CurrentPage.GetType();
return _pagesByKey.ContainsValue(pageType)
? _pagesByKey.First(p => p.Value == pageType).Key
: null;
}
}
}
public void GoBack(bool animate = true)
{
_navigation.PopAsync(animate);
MessagingCenter.Send<INavigationService>(this, "NAVIGATING");
}
public void NavigateTo(string pageKey, bool animate = true)
{
NavigateTo(pageKey, null, animate);
MessagingCenter.Send<INavigationService>(this, "NAVIGATING");
}
public void NavigateTo(string pageKey, object parameter, bool animate = true)
{
lock (_pagesByKey)
{
if (_pagesByKey.ContainsKey(pageKey))
{
var type = _pagesByKey[pageKey];
ConstructorInfo constructor;
object[] parameters;
if (parameter == null)
{
constructor = type.GetTypeInfo()
.DeclaredConstructors
.FirstOrDefault(c => !c.GetParameters().Any());
parameters = new object[]
{
};
}
else
{
constructor = type.GetTypeInfo()
.DeclaredConstructors
.FirstOrDefault(
c =>
{
var p = c.GetParameters();
return p.Count() == 1
&& p[0].ParameterType == parameter.GetType();
});
parameters = new[]
{
parameter
};
}
if (constructor == null)
{
throw new InvalidOperationException(
"No suitable constructor found for page " + pageKey);
}
var page = constructor.Invoke(parameters) as Page;
_navigation.PushAsync(page, animate);
}
else
{
throw new ArgumentException(
string.Format(
"No such page: {0}. Did you forget to call NavigationService.Configure?",
pageKey),
"pageKey");
}
}
}
public void Configure(string pageKey, Type pageType)
{
lock (_pagesByKey)
{
if (_pagesByKey.ContainsKey(pageKey))
{
_pagesByKey[pageKey] = pageType;
}
else
{
_pagesByKey.Add(pageKey, pageType);
}
}
}
public void Initialize(NavigationPage navigation)
{
_navigation = navigation;
}
}
From here, you can add other methods you would like. Make sure to inject this or use it directly where you were using your MvvmLight one before.
Related Topics
How to Initialize a C# Dictionary with Values
How to Get the Index of an Item in a List in a Single Step
How to Use Showdialog Without Blocking All Forms
Dependency Injection Using Azure Webjobs Sdk
What's the Difference Between the Webconfigurationmanager and the Configurationmanager
Calling a Generic Method with a Dynamic Type
How to Execute a Command in a Remote Computer
Generic Base Class for Winform Usercontrol
Is There Any Way in C# to Override a Class Method with an Extension Method
Does Stream.Dispose Always Call Stream.Close (And Stream.Flush)
Singleton by Jon Skeet Clarification
Upload Files and JSON in ASP.NET Core Web API
How to Create a Xsd Schema from a Class
Export Datatable to Excel with Open Xml Sdk in C#
Insert Data Using Entity Framework Model
How to Ignore Null Values for All Source Members During Mapping in Automapper 6
Addeventhandler Using Reflection
How to Capture the '#' Character on Different Locale Keyboards in Wpf/C#