It is impossible to overlay an object of type "Page" on type "Windows.UI.Xaml.Controls.Frame" when using the navigation service mvvm-light in the universal application win 10

I use the following error in my new 10 universal C # / XAML applications:

An exception of type "System.InvalidCastException" occurred in GalaSoft.MvvmLight.Platform.dll, but was not processed in the user code Additional information: It is not possible to overlay an object of type '' on type 'Windows.UI.Xaml.Controls.Frame'.

in the following navigation command in one of my pageview models:

_navigationService.NavigateTo(ViewModelLocator.MedicineBoxPageKey); 

I am trying to use hamburger style navigation (see this example ). Microsoft application on the example of how to do this):

1- have a convenient solution for all my pages. The sample mentioned above uses the AppShell page as the application root instead of a frame that encapsulates the navigation menu and some back button behavior. That would be perfect.

2- Use the MVVM-Light navigation service to conveniently handle all navigation from my view model.

This is how App.xml.Cs initializes the onLaunched shell page:

  AppShell shell = Window.Current.Content as AppShell; // Do not repeat app initialization when the Window already has content, // just ensure that the window is active if (shell == null) { // Create aa AppShell to act as the navigation context and navigate to the first page shell = new AppShell(); // Set the default language shell.Language = Windows.Globalization.ApplicationLanguages.Languages[0]; shell.AppFrame.NavigationFailed += OnNavigationFailed; if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { //TODO: Load state from previously suspended application } } // Place our app shell in the current Window Window.Current.Content = shell; if (shell.AppFrame.Content == null) { // When the navigation stack isn't restored, navigate to the first page // suppressing the initial entrance animation. shell.AppFrame.Navigate(typeof(MedicinesStorePage), e.Arguments, new Windows.UI.Xaml.Media.Animation.SuppressNavigationTransitionInfo()); } // Ensure the current window is active Window.Current.Activate(); 

And here is the definition of the AppShell class:

 public sealed partial class AppShell : Page { public static AppShell Current = null; public AppShell() { this.InitializeComponent(); } } 

From what I have tried so far, the mvvm-light navigation service only works when Frame uses the root of the application and marks the page (otherwise we will get this error). However, using a frame does not seem to be an option, because since the sample application puts it:

Using the page as the root for the application provides development time, and also ensures that when it runs on Mobile, the contents of the application will not be displayed under the StatusBar system, which is visible by default with a transparent background. It will also take into account the availability of software navigation buttons if they are displayed on the device. An application may refuse by switching to UseCoreWindow.

I also tried to override the navigationTo method from the mvvm-light navigation service, but the error seems to have occurred before I could catch it.

Does anyone have a solution to use the mvvm-light navigation service and the shell page as the root of the application (which controls the hamburger menu, etc.)?

Thanks a lot!

+4
source share
1 answer

I talked with Laurent Bugnion, and he recommended that I implement my navigation service, which deals with navigation. To do this, I created the PageNavigationService, which implements the INavigationService interface for MVVM Light.

 public class PageNavigationService : INavigationService { /// <summary> /// The key that is returned by the <see cref="CurrentPageKey" /> property /// when the current Page is the root page. /// </summary> public const string RootPageKey = "-- ROOT --"; /// <summary> /// The key that is returned by the <see cref="CurrentPageKey" /> property /// when the current Page is not found. /// This can be the case when the navigation wasn't managed by this NavigationService, /// for example when it is directly triggered in the code behind, and the /// NavigationService was not configured for this page type. /// </summary> public const string UnknownPageKey = "-- UNKNOWN --"; private readonly Dictionary<string, Type> _pagesByKey = new Dictionary<string, Type>(); /// <summary> /// The key corresponding to the currently displayed page. /// </summary> public string CurrentPageKey { get { lock (_pagesByKey) { var frame = ((AppShell) Window.Current.Content).AppFrame; if (frame.BackStackDepth == 0) { return RootPageKey; } if (frame.Content == null) { return UnknownPageKey; } var currentType = frame.Content.GetType(); if (_pagesByKey.All(p => p.Value != currentType)) { return UnknownPageKey; } var item = _pagesByKey.FirstOrDefault( i => i.Value == currentType); return item.Key; } } } /// <summary> /// If possible, discards the current page and displays the previous page /// on the navigation stack. /// </summary> public void GoBack() { var frame = ((Frame) Window.Current.Content); if (frame.CanGoBack) { frame.GoBack(); } } /// <summary> /// Displays a new page corresponding to the given key. /// Make sure to call the <see cref="Configure" /> /// method first. /// </summary> /// <param name="pageKey"> /// The key corresponding to the page /// that should be displayed. /// </param> /// <exception cref="ArgumentException"> /// When this method is called for /// a key that has not been configured earlier. /// </exception> public void NavigateTo(string pageKey) { NavigateTo(pageKey, null); } /// <summary> /// Displays a new page corresponding to the given key, /// and passes a parameter to the new page. /// Make sure to call the <see cref="Configure" /> /// method first. /// </summary> /// <param name="pageKey"> /// The key corresponding to the page /// that should be displayed. /// </param> /// <param name="parameter"> /// The parameter that should be passed /// to the new page. /// </param> /// <exception cref="ArgumentException"> /// When this method is called for /// a key that has not been configured earlier. /// </exception> public void NavigateTo(string pageKey, object parameter) { lock (_pagesByKey) { if (!_pagesByKey.ContainsKey(pageKey)) { throw new ArgumentException( string.Format( "No such page: {0}. Did you forget to call NavigationService.Configure?", pageKey), "pageKey"); } var shell = ((AppShell) Window.Current.Content); shell.AppFrame.Navigate(_pagesByKey[pageKey], parameter); } } /// <summary> /// Adds a key/page pair to the navigation service. /// </summary> /// <param name="key"> /// The key that will be used later /// in the <see cref="NavigateTo(string)" /> or <see cref="NavigateTo(string, object)" /> methods. /// </param> /// <param name="pageType">The type of the page corresponding to the key.</param> public void Configure(string key, Type pageType) { lock (_pagesByKey) { if (_pagesByKey.ContainsKey(key)) { throw new ArgumentException("This key is already used: " + key); } if (_pagesByKey.Any(p => p.Value == pageType)) { throw new ArgumentException( "This type is already configured with key " + _pagesByKey.First(p => p.Value == pageType).Key); } _pagesByKey.Add( key, pageType); } } } 

This is basically a copy of its implementation. But instead of parsing in Frame, I parse AppShell and use the AppFrame property for navigation.

I put this in my ViewModelLocator. Instead:

 var navigationService = new NavigationService(); 

I just use:

 var navigationService = new PageNavigationService(); 

EDIT: I noticed that NavMenuListView has an expiration when you use the backlink after navigating with the new navigation service, since the selected item is null. I fixed it by setting the SetSelectedItem method and adding nullcheck to the for loop after execution:

  public void SetSelectedItem(ListViewItem item) { var index = -1; if (item != null) { index = IndexFromContainer(item); } for (var i = 0; i < Items.Count; i++) { var lvi = (ListViewItem) ContainerFromIndex(i); if(lvi == null) continue; if (i != index) { lvi.IsSelected = false; } else if (i == index) { lvi.IsSelected = true; } } } 

But there may be a more elegant solution than this.

+5
source

All Articles