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.
Related
I'm new with Xamarin.Forms, and I'd like to know how to switch to a "previously loaded" Page.
Let say my App run, and it loads (as First Page) my MainPage. There I tweak some button/toggle (changing status), and than I click on a button that will load another Page (using Navigation model):
await Navigation.PushAsync(new MyNewPage());
Now here I want to return to the previous MainPage
await Navigation.PushAsync(new MainPage());
But at this point, all content/button status are vanished (basically it creates a new MainPage).
Since its a LIFO, I aspect to have it on memory/stack. How can I recall the previous loaded MainPage? Or is it dangerous since GC could clean it at any point?
Notice that in this specific case this would works (since I just back one time):
await Navigation.PopAsync();
But I'd like to use a selective approch (calling what I need where I am, learning the whole concept).
One thing to note is that the Navigation object you use to Push and Pop has a NavigationStack property you can use to get previously pushed pages - assuming you didn't pop them.
To the best of my knowledge, there aren't any methods to directly navigate to an existing Page in the stack so you may have to get somewhat creative in your approach.
If MainPage is always at the bottom of your NavigationStack, you can use
await Navigation.PopToRootAsync()
to go back to the Page at the bottom of the stack.
If 'MainPage' can be elsewhere in the stack, you may have to maintain your own list of Pages separate from the NavigationStack.
Something like the following may work if you don't care about keeping the Pages between MainPage and your current page:
Note that you will want to create a separate List of Pages to remove as calling RemovePage() on the NavigationStack in the foreach loop will cause an exception sometimes, something to the effect of "Enumeration can't complete, collection has changed":
List<Page> pagesToRemove = new List<Page>();
foreach( Page page in Navigation.NavigationStack )
{
if( page.GetType() != typeof(MainPage) && page != Navigation.NavigationStack.Last() )
{
pagesToRemove.Add(page);
}
}
// pagesToRemove now contains all Pages between your current page and MainPage
foreach( Page pageToRemove in pagesToRemove )
{
Navigation.RemovePage(pageToRemove);
}
// the NavigationStack only contains MainPage (now the root) and your current page
// Pop to MainPage
await Navigation.PopAsync();
Someday it would be nice to have a NavigateTo(Page) or the like but for now this approach seems to work.
I'm developing metro app using Windows 8 RTM and C#(VS 2012 RTM), I'm stuck with page reload,
Can any one explains me how to reload page with out navigating to same page again.
Brief: I'm developing metro app with multilingual support. When user selects the language I'm overriding primary language by below code
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = "de";
and reload the page by using this code
this.Frame.Navigate(this.GetType());
Language changed to "de",But when i press "Back" on page its navigating same page instead of navigating to previous page.Did i miss something, Can someone please explains me how to do this. Thanks in advance
This will refresh your page:
var _Frame = Window.Current.Content as Frame;
_Frame.Navigate(_Frame.Content.GetType());
_Frame.GoBack(); // remove from BackStack
Handling OnNavigatingFrom() you can save your page's data and state.
Handling OnNavigatingTo() you can load your page's data and state.
As a caveat, my sample does not account for page parameters, you may need to.
Also, another caveat, my sample reloads your page twice. But the GoBack() is necessary to remove the new entry from the BackStack. Unlike WP, Frame does not have Refresh(). Also, the BackStack does not have Remove().
UPDATE
I no longer use the above approach. I use this:
public bool Reload() { return Reload(null); }
private bool Reload(object param)
{
Type type = this.Frame.CurrentSourcePageType;
if (this.Frame.BackStack.Any())
{
type = this.Frame.BackStack.Last().SourcePageType;
param = this.Frame.BackStack.Last().Parameter;
}
try { return this.Frame.Navigate(type, param); }
finally { this.Frame.BackStack.Remove(this.Frame.BackStack.Last()); }
}
I’m not sure I fully understand what you are trying to do, so this may be wrong.
By calling that line of code when you are refreshing the page, you are creating a brand new object of the current type and navigating to it, so this does not save changes the user makes while they are on the current page.
Are you using any type of design pattern? For things like this I use MVVM (using the MVVM light library) which implements a really cool navigation service which will keep stuff like this in check.
I have two questions here about Windows Phone page navigation:
Is there a way to get the instance of the page I am navigating to? That is, if I am on page one and want to navigate to page2 to when button click, can I get the page2 instance after page2 is initialized by NavigationService.Navigate("page_2_uri") call in page 1?
Is there a way I can know which page I navigate from? For example, I am currently on page3, and I want to do something like: if page 3 is navigated from page 2, I will do this, otherwise I will do that.
Thank you.
Is there a way to get the instance of the page I am navigating to?
No.
Is there a way I can know which page I navigate from?
Yes. Traverse the NavigationService.BackStack
The idea for using the NavigationService to navigate between pages is that you don't need to know any details about your destination. So in your example, Page 2 isn't initialized until you've left Page 1, and therefore Page 1 is no longer in scope, and won't be able to do anything with Page 2. If you want to pass information/context to Page 2, id recommend using Query Parameters (see next answer). If you want to know where the navigation is going, you can override the OnNavigatedFrom event and look at the Uri property of the NavigationEventArgs.
I would recommend looking at the NavigationContext property of the Silverlight Page class. This property lets you view the QueryString of the navigation request. Using this approach, you could navigate to page 3 using a uri like "page_3?previous_page=2" and then extract the previous_page from the QueryString of the NavigationContext to see where you came from.
Platform:
Silverlight 4 / .NET 4
Background:
I have a page that consists of two parts. The left part is a tree view, the right one is the content area. When I select a tree item, an appropriate UserControl page should be loaded in the right part of the page, depending on the type of the tree item.
I am using a Frame object, defined in XAML.
When a user selects a tree view item, I resolve the item's type and then I navigate to the page defined for that type.
However, Frame.Navigate is an asynchronous method so if I try to get the frame's content after Navigate, the frame has not navigated yet, so I either get nothing or the last loaded page.
contentFrame.Navigate(new Uri("/PageA.xaml", UriKind.Relative));
PageA page = contentFrame.Content as PageA;
// page here is either null or a previously opened page
Problem:
I need to send some data (stored in treeview item's Tag) to the page being navigated to and the only Frame.Navigate overload is asynchronous (without callback). How can I send some data to the navigated page? Is there any other technique for accomplishing what I need?
You can use the NavigationService provided by Silverlight. It has query string support using which you can build RESTful URI. I am assuming that you need to pass some context based data to the landing page. Following links might help you
http://www.silverlightshow.net/items/The-Silverlight-3-Navigation-Framework.aspx
http://blogs.microsoft.co.il/blogs/alex_golesh/archive/2009/04/02/silverlight-3-quick-tip-6-navigation-framework-and-uri-routing.aspx
http://www.wintellect.com/CS/blogs/jprosise/archive/2009/04/07/silverlight-3-s-new-navigation-framework.aspx
Although the links says Silverlight 3, these features are supported in Silverlight 4 as well.
You definitely want to use some flavor of the M-V-VM pattern that Nilesh suggested.
I guess you could also have a static class that holds static references of objects.
You can simply refer to static objects here when your frames have completed their event (e.g. NavigatedTo).
I'm working on a sort of a CMS/Wiki application to help me experiment with the new Asp.Net MVC framework, and I'm trying to wrap my head around some of the code organization.
Right now, I have three views that cover displaying an article: Index, Edit, and Rename. All three views display the contents of the current page, or placeholder content stating that the page does not exist.
This is currently accomplished with the following code in the action method for each view:
MyPage myPage = null;
if (!string.IsNullOrEmpty(pageName)) {
myPage = mRepository.GetMyPage(pageName);
}
//Page does not exist.
if (myPage != null) {
ViewData["pageContent"] = myPage.GetParsedSource(new PageState());
ViewData["pageSource"] = myPage.Source;
ViewData["title"] = myPage.Title;
}
else {
ViewData["title"] = pageName;
ViewData["pageContent"] = "Page does not exist, feel free to create it!";
ViewData["pageSource"] = "";
}
ViewData["pageName"] = pageName;
My question is, where should this logic actually go?
1) The Controller (as it is now), which requires the above code to be replicated across action methods?
2) The Model, defaulting values for pageSource to the verbiage shown above? This would have the downside of moving display text into the model.
3) The View, using a null coalescing operator to convert null ViewData entries to their defaults?
4) In the Views, but add additional controllers to handle cases where the pageName does not exist.
EDIT:
Hopefully this should clarify things a little. The flow of the application is as follows:
When the user enters a URL (i.e. /pages/page_title), they arrive at a screen which displays the content of the article, along with hyperlinks labeled "edit" and "rename."
Clicking edit displays a page which contains the article content, as well as form controls to edit the article's source.
Clicking rename displays a page which contains the article content, as well as form controls to edit the article's name.
I would have several actions:
Lookup
Display
Create
Edit
Rename
In your default Lookup controller action (which gets hit when the user asks for, say, "/wiki/article-title"), you can redirect (RedirectToAction()) to the appropriate action as necessary. That encapsulates your Create logic into its own controller, and can also be called directly (RESTful). Same with the others. That also allows you to keep your views very, very stupid (always a good thing).
I would keep it in the controller but extract it out so that you don't have to replicate the code in each of the actions.
Maybe set some defaults in the controller's constructor and then have a separate private method (ie. not an action method) that takes your MyPage object and sets the viewdata that is shared between your actions.