I\'m trying to create an UWP (Universal Windows App) application with C#. My problem is the Frame
control: If I use it without NavigationCacheMode = Requi
When you are navigating forward, can you set NavigationCacheMode to Disabled before you call Frame.Navigate? Then, in OnNavigatedTo() set NavigationCacheMode back to Enabled again.
That should make it so that when you navigate forward, caching is disabled. But when you arrive on the new page instance, OnNavigatedTo would enable it again. When you want to navigate back, you wouldn't touch the NavigationCacheMode before calling Frame.GoBack. That should give you the cached instance, I think.
I believe this would work but I haven't tested it. I'd be curious to know if it does. Interesting scenario there. I'd love to see the app in action and better understand the use of this behavior.
Because there was no solution to this problem, I had to reimplement all paging relevant classes: Page, Frame, SuspensionManager, etc...
The solution can be downloaded here: https://github.com/MyToolkit/MyToolkit/wiki/Paging-Overview
Update:
The page class now also provides the OnNavigatingFromAsync method to show for example an async popup and cancel navigation if required...
You use the NavigationCacheMode property to specify whether a new instance of the page is created for each visit to the page or if a previously constructed instance of the page that has been saved in the cache is used for each visit.
The default value for the NavigationCacheMode property is Disabled. Set the NavigationCacheMode property to Enabled or Required when a new instance of the page is not essential for each visit. By using a cached instance of the page, you can improve the performance of your application and reduce the load on your server.
Setting NavigationCacheMode to Required means that the page is cached regardless of the number of cached pages specified in the CacheSize property. Pages marked as Required do not count against the CacheSize total. Setting NavigationCacheMode to Enabled means the page is cached, but it is eligible for disposal if the number of cached pages exceeds the value of CacheSize.
Set the NavigationCacheMode property to Disabled if a new instance must be created for each visit. For example, you should not cache a page that displays information that is unique to each customer.
The OnNavigatedTo method is called for each request, even when the page is retrieved from the cache. You should include in this method code that must be executed for each request rather than placing that code in the Page constructor.
I had to derive a page2
class from my page
class and then when I want to navigate to a second version of the same page, I detect if the this
object is page
or page2
. I then navigate to page2
if I was in page
and navigate to page
if in page2
.
The only drawback, which is a huge one, is that there is no way to derive one XAML file from another. Thus, all of the C# code is in the page
class code-behind as expected, but there are two almost identical XAML files, one for each version of the page.
A small script could probably be added as a pre-build step to generate the second page class from the first, copying the XAML data and adjusting the class names.
It's ugly but it almost works perfectly and I never have to worry about C# code duplication or weird navigation cache issues. I just end up having duplicate XMAL code, which in my case really never changes anyhow. I also end up with two warnings about not using the new
keyword on the automatically generated code for page2.InitializeComponent()
and page2.Connect()
.
Interestingly, navigating to page
then to page2
then to page
doesn't cause a problem and the second instance of the page
class is an actual second instance unrelated to the first.
Note that this solution is probably recommended against by MS.
I achieved this by:
On clicking on Page2 button/link:
Traverse the Frame BackStack and find out if the Page2 is in BackStack. If Page2 is found call Frame.GoBack() required number of times. If not found just navigate to the new Page. This will work for any no of pages.
Code Sample:
public void Page2Clicked(object sender, RoutedEventArgs e)
{
int isPresent = 0;
int frameCount = 0;
//traverse BackStack in reverse order as the last element is latest page
for(int index= Frame.BackStack.Count-1; index>=0;index--)
{
frameCount += 1;
//lets say the first page name is page1 which is cached
if ("Page2".Equals(Frame.BackStack[index].SourcePageType.Name))
{
isPresent = 1;
//Go back required no of times
while (frameCount >0)
{
Frame.GoBack();
frameCount -= 1;
}
break;
}
}
if (isPresent == 0)
{
Frame.Content = null;
Frame.Navigate(typeof(Page2));
}
}
This will be helpful if forward/backward buttons will not be made much use of. as this solution will affect the forward/backward navigation. If you wish to use forward/backward navigation as well, then some additional cases has to be handled.
When NavigationCacheMode is enabled, you can call Dispose() (conditionally) in OnNavigatedFrom() to make sure that a new instance of the page will be created the next time the application navigates to it.