I have the following command inside a VM:
ICommand LaunchGameCommand => new Command(() =>
{
//Navigation.PushAsync(...
});
According to the answers here I should be able to use something akin to the navigation in the commented code; however, the Navigation object seems to reside in Android.Content.Res... which seems to be something else entirely.
Is this still the correct method of navigating between views / viewmodels in Xamarin Forms, or has this now been superseded with an alternate method?
Navigation is part of a page, you can’t find navigation property if you don’t have the reference to a some page, you need to have access to your current page in your view model to see this property, you can have access to your current page using
Application.Current.MainPage.Navigation.Push...
Are you using any particular MVVM framework? Most of these include a way of navigating from VM to VM.
I use FreshMvvm. It allows you to perform the following to navigate between VMs and also pass data:
CoreMethods.PushPageModel<MyNextPageModel>(DataToPass);
More details here
Related
I am trying to determine which was the previous page for current page in .NET MAUI application.
For example, there are 3 pages Page1, Page2, Page3
When going from Page1 to Page2, in Page2, I would like to access PreviousPage property which gives me "Page1" as value.
When going from Page3 to Page2, in Page2, PreviousPage property gives me "Page3" as value.
^^
However, I can only see "PreviousPage" property member in Debug mode when VS hits breakpoint. I cannot access it in code. Intellisense does not show this too.
How can I access and use this "PreviousPage" in my code? Is there any other way?
See screenshot.
I am using:
Microsoft Visual Studio Community 2022 (64-bit) - Preview
Version 17.5.0 Preview 1.0
Thank you.
https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/shell/navigation?view=net-maui-7.0
Navigation events
The Shell class defines the Navigating event, which is fired when navigation is about to be performed, either due to programmatic navigation or user interaction. The ShellNavigatingEventArgs object that accompanies the Navigating event provides the following properties...
Technically you can implement your own custom logic, to see if something has been loaded, what was source, the navigation paths, etc...
Your requirement can be achieved with minimum amount of code.
However, I do not think that navigation stack is a healthy way to check if something is displayed on your page or not.
You could get all the page in Navigation Stack, simply used:
var stack = Application.Current.MainPage.Navigation.NavigationStack;
int count = Application.Current.MainPage.Navigation.NavigationStack.Count;
The current page is the last one, names (count - 1) index (as it is zero-based index) in the stack, so the previous page is (count - 2) index.
Hope it works for you.
Like #h-a-h mentioned, custom logic was the way.
I implemented sort of workaround to make it work. I used navigation parameters.
My viewmodel constructor has attribute like this:
[QueryProperty("IsBack", "IsBack")]
public class Page2ViewModel
{
//called from code-behind on OnNavigatedTo event using associated Command.Execute
async Task LoadData()
{
if(IsBack == false)
{
await _repos.GetListAsync();
}
}
public bool IsBack {get;set;}
}
When going from Page1 to Page2, I do:
await Shell.Current.GoToAsync($"{nameof(View.Page2)}?IsBack={false}");
When going back to Page2 from Page3, I do:
await Shell.Current.GoToAsync($"..?IsBack={true}");
At least this way I know "when" Page2 is visited so as to prevent loading of data again. Though this does not let me know if I am coming back to Page2 from Page3 or Page'n', it solves my current requirement.
One can always use query parameters to provide the parent page.
e.g., from Page5 to Page2, we can do:
await Shell.Current.GoToAsync($"..?IsBack={true}&ParentPageId={nameof(View.Page5)}");
provided Page2's ViewModel has this QueryParameter attribute.
[QueryProperty("ParentPageId", "ParentPageId")]
Not very elegant but workable.
Thank you all for your help.
I am working on xamarin.forms. Where I need to do navigation based on menu clicked. It's a dynamic menu, order of menus might change or based on the user logged in the menus will be changing. What I need to do is I will get from API to which page I should navigate, I need to pass that page name dynamically. Is it possible or not? If yes please help me out.
var dynamicpage = "MenuPage()";
new NavigationPage (new dynamicpage );// Like this
It is much easier with Shell, you can define your pages based on your hierarchical design inside <Shell> thanks to Route. Also you may benefit from efficient page loading by using DataTemplate which will load the page only when required (navigated to).
Regarding navigation you can explicitly navigate from code behind to any page by specifying the route of that page that you have already registered when you defined your <Shell> or in the code behind.
If you encounter the exception
System.ArgumentException: 'unable to figure out route for: //RegisterPage Parameter name: uri'
Then you probably didn't register the route before it got invoked or you made a typo on the route. You can refer to Bug? System.ArgumentException: 'unable to figure out route for:
For more details you can always check the linked Microsoft official documentation.
Have you tried using Activator.CreateInstance()?
var pageType = typeof(MenuPage);
var navPage = new NavigationPage((Page)Activator.CreateInstance(pageType));
I have a Windows Phone 8.1 app using Caliburn.Micro. In the app I have a few ViewModels that fetch data in different way and with different logic but show them in the same way. So I want to make all those ViewModel use the same one View.
I found out that ViewLocator.LocateTypeForModelType is a method that gets executed for mapping ViewModels to Views. So I override it to use my custom attribute when present:
var original = ViewLocator.LocateTypeForModelType;
ViewLocator.LocateTypeForModelType = (modelType, displayLocation, context) =>
{
var useViewAttributes = modelType.GetTypeInfo().GetCustomAttributes<UseViewAttribute>(true);
if (useViewAttributes.Count() == 1)
{
var viewTypeName = string.Concat(modelType.Namespace.Replace("Model", string.Empty), ".", useViewAttributes.First().ViewName);
var type = AssemblySource.FindTypeByNames(new List<string>() { viewTypeName });
return type;
}
return original(modelType, displayLocation, context);
};
Stepping through the it seems to work fine. If I navigate to a ViewModel and that ViewModel has a UseView, my method returs the correct View.
The app navigates to the correct View but the ViewModel is never created. Kind of like Caliburn.Micro forgot about the ViewModel, or was looking for one using a different convention, or something.
I found out that ViewModelLocator.LocateTypeForViewType is called after navigation to a View to resolve the ViewModel. The ViewModel type from the previous step seems to be forgotten completely.
In ViewModelLocator.LocateTypeForViewType I only have access to the View type and I do not know, how to make it resolve the correct ViewModel from the previous step. I could scan all the ViewModel and find the ones with the correct attribute, but I would not know which one to choose.
Any ideas on how to approach this?
Here is a minimal project showing my setup: https://dl.dropboxusercontent.com/u/73642/CMVMTest.zip
This sort of solution would work everywhere else except for the top level navigation. The reason for this is there is sort of a "double dispatch: going on when you navigate.
As you know the Frame or PhoneNavigationFrame control (depending on WinRT or Silverlight) is view based in it's navigation. So the steps look a little like this.
Your code tells the navigation servie=ce to navigate to ProductViewModel.
It uses ViewLocator (where you've injected your code) to locate ProductView and tells the Frame to navigate to that.
The navigation service then responds to the navigating event to ProductView and locates the correct view model using ViewModelLocator.
It then instantiates and binds this view model.
This sort of view model to view to view model step in navigation service causes the hiccup in your code.
You should be able to create dummy views that simply inherit the base view and add nothing. So if you have MySharedView.xaml then declaring what's below should be enough.
public class SecondView : MySharedView { }
It's not ideal I know, but does get you the reuse you're after. Having the navigation service remember the view model between the navigating and navigated events becomes complicated with all the external factors that can cause navigation as well.
I'm developing an Windows 8 Store App.
I want pass a parameter when I navigate to another page in my app.
I'm using the MVVMLight toolkit and I use the .Navigate(string Type, object param) method from the NavigationService which implements INavigationService.
I can navigate to the page and use the parameter, but just when I use the OnNavigateTo(NavigationArgs e) method.
In fact I just want the parameter in the ViewModel of the second page without using any code in the codebehind of the second page.
You can use GalaSoft.MvvmLight.Messaging.Messenger to communicate within your application.
Please refer http://blog.galasoft.ch/archive/2009/09/27/mvvm-light-toolkit-messenger-v2-beta.aspx
for more details.
Take a look at my implementation of MVVM Navigation via an Interface and it's implementation
It's as simple as doing _navigationService.Navigate<Map>(false);
(I'm navigating to the ViewModel Map, and my NavigationService just knows that X ViewModel is mapped to X.xaml page!)
More at https://github.com/cmorgado/MultiPlatform
In My app, i use an altered Example of the ViewPagerIndicator, which launches 5 Fragments. so far all works well, but the fragments do have to change their View (completly new layout) for other purposes. how do i do that? a Fragment doesn't have a function SetContentView like activities do. so is there a way to update the view or something like that?
Use Fragment.getView() to get the root view of the Fragment. On that View, call removeAllViews(). Then build your new views and add them to the view you got from getView().
Edit
I never used Mono for Android before, but looking at the documentation; I suggest you put the inflated View from inflater.Inflate in a private member of your Fragment. Later on, when needed, you can use that reference to edit your layout.