I have two pages with similar logic in them. Load the page, click some buttons that will show/hide other buttons, continue to next page. When I hit the next page, if I click the back button I am returned to the previous page.
The difference is that one page (FirstPage) will have the constructor called when I click the back button, which has a call to reset the defaults. The other page (SecondPage) doesn't get the constructor called and I'm not sure why.
public FirstPage()
{
InitializeComponent();
DisplayStuff();
}
FirstPage has KeepAlive set to False.
public SecondPage(object arg1, object arg2)
{
InitializeComponent();
DisplayStuff(arg1, arg2);
}
This page also has KeepAlive set to False. These two pages don't inherit from anything and there is nothing that overrides any of the properties. The only difference I can see is the empty constructor, so I tried giving SecondPage an empty constructor and still no luck.
I'm relatively new to WPF (I work on it for an hour or two every 6 months), so what am I missing?
Here is the back button in case it is relevant.
<Button Command="{x:Static NavigationCommands.BrowseBack}" />
Edit: When I click the back button, SecondPage doesn't keep its state. It just loads an empty page because DisplayStuff hasn't been called yet.
Navigation Code:
NavigateTo(new SecondPage(arg1, arg2));
protected void NavigateTo(Page page)
{
NavigationService.Navigate(page);
}
I created a similar sample application and had similar behaviour. What I figured out that when you go back to a page the constructor is not called unless the page is the first page in the journal
Read this section in Navigation in WPF:
When the page Page is navigated back to, using the journal, the following steps take place:
The Page (the top journal entry on the back stack) is instantiated.
The Page is refreshed with the state that was stored with the journal entry for the Page.
The Page is navigated back to.
Good luck!
After reading Paul Stovell's article on WPF navigation, the way I want to display stuff is not going to work.
When navigating, if you click "Back", WPF can't possibly know what values to pass to the constructor; therefore it must keep the page alive. Here's the trace output:
Since WPF can't call the constructor, it won't. It'll just keep the page alive.
He goes on to mention that KeepAlive doesn't work if you're not navigating via URI, and Loaded and Unloaded are called each time, so I can just move all my logic there and I won't need the constructor to be called on the back navigation.
Related
On a C# application that I am working on, I have a frame on my main window.
<Frame x:Name="frame" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" NavigationUIVisibility="Hidden"/>
I click a button on my main window, and then I use the frame that I created to navigate to a page using the code below.
//Create a new object for the page
CameraPage camera = new CameraPage();
//Navigate to the new page
frame.NavigationService.Navigate(camera);
What I would like to do is if I click a button on the camera page that is contained within the frame, then it only exits the page while still keeping the main window intact.
I have tried something like this.
NavigationService.GoBack();
But then I get an error:
System.InvalidOperationException: 'Cannot navigate because there is no entry in the Back stack of the journal.'
I believe this error is happening because the navigation stack I used to go the page is apart of the main window code and not the page that I navigated to.
So my question really is, how do I close a page contained within a frame with a button contained within the page, without closing the entire application?
If you want to end up with an empty Frame again, all you have to do is clear the Frame's contents. According to this answer, you can do so like this:
frame.Content = null;
That will get you back to how things started, now you just to trigger it. For that, I'll refer you to the question WPF Frame and Page Get event. The answer there shows you how to use DelegateCommand (a.k.a. RelayCommand) to accomplish this. This is the way I would go, because it keeps the Page nice and separate from whatever Window is hosting it.
Technically, you could also pass your Page a reference to frame when you initialize it, then have it set frame.Content = null; that way. But that's a sort of "quick and dirty" approach, not really a best practice.
I want to use template 10 to develop my app, but I dont know how can I hide the back button once I pass from one page to another, like for example passing from a login page to my main page.
With template 10 it always show the back button, I know how to prevent from going back, but still I can see the damn back button on the shell, I dont wish to see it.
How could I hide it?
ClearHistory is located on the HamburgerButtonInfo as a DP and if that isn't enough you can thru the NavigationService call ClearHistory as well which looks like public void ClearHistory() => FrameFacade.BackStack.Clear(); in the source so you know what it actually calls...
I am currently trying to implement a navigation scheme that closely resembles that of the Internet Explorer app on Windows Phone 8.
The IE app can have multiple tabs that the user can switch between. Each of these tabs has its own history. Hitting the Back Button on the phone takes you to the previous page in that tab's Navigation history (Not the PhoneApplicationFrame.BackStack). If there are no previous pages, the back button takes you to the previous opened tab or, if none, exits the app.
Why this is troubling me
Application.RootVisual can only be set once. So you can't have two PhoneApplicationFrames, each with its own BackStack, to swap RootVisual between the two.
You cannot traverse the BackStack (it is a Stack, after all). Can only call GoBack(). Calling GoForward() will throw an Exception.
PhoneApplicationFrame.GoBack() removes entries from the BackStack which can only be added again through the PhoneApplicationFrame.Navigate(...) method. So, manipulating the BackStack is a no-go.
Bright Ideas
Keep a Dictionary<enum, List<string>> which is updated with each call to a custom NavigationService.Navigate(tabTypeEnum, uriString, params). This will keep the Navigation history for each tabType, allowing us to possibly Navigate through the current Tab's history when the BackKeyPress event is handled. Bad thing is, calling Navigate(...) to go to previous pages (instead of GoBack) will add to the BackStack. So requires maintenance that hurts my brain right now.
Create a custom NavigationAwareTabPage : PhoneApplicationPage, which keeps track of its own navigation history and fakes navigation by animating a transition when its Content is changed. The only time we call a true Navigate is when we switch from one tab to another. (I think this is what the IE app does.) And the BackKeyPress would have to look like below.
This:
void RootFrame_BackKeyPress(object sender, CancelEventArgs e)
{
var rootFrame = sender as PhoneApplicationFrame;
if (rootFrame.CanGoBack)
{
// Get the NavigationAwarePage
var navAwarePage = rootFrame.Content as NavigationAwareTabPage;
if(navAwarePage.CanGoBack())
{
// This method "navigates" to the next page
// by changing the navAwarePage.Content
navAwarePage.GoBackToPreviousPage();
e.Cancel = true;
}
}
}
Has anyone been down this road?
All the magic of how ReactiveUI overrides the Back button is here:
https://github.com/reactiveui/ReactiveUI/blob/master/ReactiveUI.Mobile/WP8AutoSuspendApplication.cs#L91
The way that this works in ReactiveUI is that there is a content control named RoutedViewHost that is listening to the Back being signaled (you can do whatever you want in response to the hardware Back button and cancel the default action). ReactiveUI maintains its own ViewModel-based back stack and manipulates that instead of using WP8s, and you never call WP8s navigation methods.
This effectively means that, from WP8's perspective, there is only ever one page in the entire application. WP8 really wants to create that page itself, and it's specified in WMAppManifest.xml.
Don't try to participate in WP8's Frame system, it really wants to work its own way and you won't be able to convince it otherwise.
One last important thing, if you're at the bottom of your back stack, you must allow the default Back action to happen (i.e. what WP8 wanted to do, take you out of the app). Otherwise you'll probably fail Certification and you're Doing It Wrong™.
I am building a Windows 8 app (C#/XAML) and using the Frame.Navigate() property to move between pages. Each page has an AppBar icon that will refresh that data on a page, and set the DataContext property. This works, and the UI updates accordingly after the button is pressed.
The problem I'm seeing shows up when I navigate to a different page, then click the back arrow to return to the previous page.
When the OnNavigatedTo(NavigationEventArgs e) method runs (after clicking the back arrow), the e.Parameter value is and old value (before I clicked the refresh button, and the DataContext was updated).
I don't know how to update the parameter value any other way than using Frame.Navigate(typeof(PageTypeName), paramValue);, but I don't want to initiate a navigation action.
My question is two fold.
How can I persist DataContext changes so that when I return to pages, the value I've set is exposed via e.Parameter in the OnNavigatedTo(NavigationEventArgs e) method.
Is there some kind of reference metrial that explains the Navigation lifecycle in Win8 Apps?
... or should I be doing this a different way?
I found the solution.
NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Required;
This tells the frame that it should always cache the old instance of my page, and not create a new one when i navigate back to it.
This way my new DataContext value is not overwritten when I return to the page.
I am using the Business Silverlight application. I have incorporated some MVVM into this and were off an running with it. We are using some telerik controls, mostly the ribbon control and the docking. We register all the telerik ribbon controls in the about.xaml.cs file, the method is DisplayUI - its here where we register the docking control then we register the ribbon after this. What happens is that when you click the ABOUT link it shows our first tab with buttons(perfect). when you click the HOME link next to the ABOUT link, we go back to the home page..but when you click the ABOUT link again it registers the controls again so we end up with two tabs that are the same.
Is there a way to check to see if this about.xaml.cs file has already been initialized? Im guessing that is has a handle on the first call in memory as I am able to see the first tabs rendering..
Thanks
here is the about code
public About()
{
InitializeComponent();
DisplayUI();
this.Title = ApplicationStrings.AboutPageTitle;
}
that display UI does all the work in registering the dockpanel and the ribbons. We'd like to not have the DisplayUI() called if this has already been rendered once.
If you do it by event handler can you unsubscribe from the event at the end of the method? Without seeing some code it's hard to work out what to change.
It's not the nicest way of doing it, but if this code needs to run once and only once then you could have a static boolean variable on the class set to false and when you call DisplayUI you check the value of this. If it's false you set it to true and run the method, and if it's true you just return.