Uwp Conditional back navigation with a Message Dialog - c#

I want to ask the user whether they are sure that they wanna move back or not and only move back if they select "Yes", for that I am using a message dialog, but the problem is when I press the back button it moves back and after that the dialog pops up asking whether I want to move back or not.
I checked with break point as soon as messageDialog.ShowAsync executes the page moves back.
Code
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
SystemNavigationManager.GetForCurrentView().BackRequested += SampleConditionalNavigation_BackRequested;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Collapsed;
SystemNavigationManager.GetForCurrentView().BackRequested -= SampleConditionalNavigation_BackRequested;
}
private async void SampleConditionalNavigation_BackRequested(object sender, BackRequestedEventArgs e)
{
e.Handled = true; // I have also tried setting it to false
var messageDialog = new MessageDialog("Are you sure you want to move back?") { Title = "Confirmation" };
// Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers
messageDialog.Commands.Add(new UICommand("Yes"));
messageDialog.Commands.Add(new UICommand("No"));
// Set the command that will be invoked by default
messageDialog.DefaultCommandIndex = 0;
// Set the command to be invoked when escape is pressed
messageDialog.CancelCommandIndex = 1;
//ShowDialog
var result = await messageDialog.ShowAsync();
if (result.Label == "Yes")
{
var rootFrame = Window.Current.Content as Frame;
if (rootFrame?.CanGoBack is true)
{
rootFrame?.GoBack();
}
}
}
Update 1
even if I comment the code where dialog shows itself the page still navigates back, it seems that e.Handled = true is pretty much useless here

This code is absolutely correct and works perfectly, the issue was that my app had BackRequested event in app.xaml.cs as well and that event was executing and navigating back before this method on this page was able to execute. Removing it from app.xaml.cs fixed the issue.

Related

Frame.GoBack() succeeds on UI button click, fails on Windows software back button callback

I have a language-learning app with the following basic page navigation structure:
Start - App title screen
Languages - List of languages
Lessons - List of lessons for selected language
Activities - Page with activities to work on for selected lesson
The first three pages each have a button that navigates to the next page with a call similar to:
private void ButtonClick(object sender, RoutedEventArgs e) => Frame.Navigate(typeof(SomePage));
On the Activities page, after a user submits his last correct answer with a button press, the app navigates back like this:
private async void SubmitAnswer_Click(object sender, RoutedEventArgs e)
{
...
if (answerCorrect && allActivitiesComplete)
{
Frame.GoBack();
return;
}
...
}
This works; I am returned to the Lessons page. If I hover over Frame with IntelliSense, I see the BackStack property has a count of 3, one for each of the previous pages.
However, I also want to display the software back button. To do this I have the following code:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
...
var nav = SystemNavigationManager.GetForCurrentView();
nav.AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
...
}
Good, now the button shows in the top left in desktop-mode. But pressing it does nothing. So I update with the following:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
...
var nav = SystemNavigationManager.GetForCurrentView();
nav.AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
nav.BackRequested += (x, y) =>
{
Frame.GoBack();
};
...
}
This fails with:
Error HRESULT E_FAIL has been returned from a call to a COM component.
Frame.BackStack shows a count of 0 and Frame.CanGoBack is false. Why is this code flow inconsistent with the button press code flow?
You should ideally wire up the BackRequested event globally, not in the scope of a specific page. This is because if you add an event handler OnNavigatedTo, you will add another each time the page is navigated. This means that clicking the back button will navigate back several times. Also it will keep all the pages that attached the handler in memory, which is a serious memory leak. At the very least you should switch from using a lamda to an event handler method and unsubscribe it in OnNavigatedFrom.
To implement app title bar back button properly you should do this:
Observe the Frame Navigated method, to show/hide the back button as appropriate based on the CanGoBack property
In the BackRequested handler check for CanGoBack to make sure the navigation is possible
An example of the implementation for a blank UWP app would be as follows:
Add the following methods to your App.xaml.cs:
private void SetupAppBarBackButton()
{
_rootFrame.Navigated += RootFrame_Navigated;
SystemNavigationManager.GetForCurrentView().BackRequested += App_BackRequested;
}
private void App_BackRequested(object sender, BackRequestedEventArgs e)
{
if (_rootFrame.CanGoBack)
{
_rootFrame.GoBack();
}
}
private void RootFrame_Navigated(object sender, NavigationEventArgs e)
{
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
_rootFrame.CanGoBack
? AppViewBackButtonVisibility.Visible
: AppViewBackButtonVisibility.Collapsed;
}
Also add a private _rootFrame field:
private Frame _rootFrame;
Finally update the OnLaunched method to store the root frame and setup the back button:
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
...
// Place the frame in the current Window
Window.Current.Content = rootFrame;
_rootFrame = rootFrame;
SetupAppBarBackButton();
}
...
}
Please note, that if you are creating the root frame somewhere else as well (like for other activation paths), you need to store the frame and call the SetupAppBarBackButton method there as well.
try this :
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
...
Windows.UI.Core.SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
Windows.UI.Core.SystemNavigationManager.GetForCurrentView().BackRequested += (s,a) =>
{
if (Frame.CanGoBack)
{
Frame.GoBack();
a.Handled = true;
}
}
...
}

Suspending UWP apps using back button

Is there a way i can "suspend" a UWP app by pressing the back button.
I'm not even sure suspending is the right term but what i want to do is to close the app when the user presses the back button rather than going back to the Signup/Login page in my app.
I want the app go close but also still be shown when the user opens the recent apps menu by holding the back button.
Heres my code in the App.Xaml.Cs
private void OnBackRequested(object sender, Windows.UI.Core.BackRequestedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame.CurrentSourcePageType == typeof(Pages.Home))
{
App.Current.Exit();
}
if (rootFrame.CanGoBack)
{
e.Handled = true;
rootFrame.GoBack();
}
}
The Problem is with the App.Current.Exit();
it terminates the app where what i want to do is just close and and not go back to the MainPage.
How can I do that?
To leave your app without terminating it, just remove the App.Current.Exit(); and ignore the back event (e.Handled = false;). You will then get the default behavior which is to leave the app on mobile devices. On desktop, your app will stay on the page it is. You will then have to do anything that fit your need to save/restore your app in its appropriate state using the suspending/resuming event handlers.
void OnBackRequested(object sender, Windows.UI.Core.BackRequestedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame.CurrentSourcePageType == typeof(Pages.Home))
{
// ignore the event. We want the default system behavior
e.Handled = false;
}
else if (rootFrame.CanGoBack)
{
e.Handled = true;
rootFrame.GoBack();
}
}

Windows 10 Development: How to check if the current page is a specific page?

I want to check if the current page that the user is on/viewing is a specific page - MainPage. If the user is on MainPage, I want to ensure pressing the back button does not navigate to the previous page but exit the application instead. Is there anyway to check what is the page the user is on currently?
Or is there any better way to achieve what I want to achieve: to ensure pressing the back button on MainPage does not navigate to previous page but exit the application instead?
Have a look at NavigationEventArgs.SourcePageType in the navigation methods. It will return the type of the current page.
NOTE
Until there is entry in the backstack the back button will navigate to that entry (page). If the backstack is empty pressing backbutton will close the app. In my apps when the users presses the home button (navigating to mainpage), I always remove everything from the backstack except for the MainPage, and this ensures that pressing back button on MainPage will always exit.
EDIT
Code that I use to go back to MainPage (The pro for this solution is that this way the navigation events of the pages in the backstack won't be called, because they are removed. If they weren't removed their navigation events would be called while traversing through the backstack, which could cause unexpected behavior.):
private void goHome() {
var bs = Frame.BackStack.Where(b => b.SourcePageType.Name == "MainPage").FirstOrDefault();
if (bs != null)
{
Frame.BackStack.Clear();
Frame.BackStack.Add(bs);
}
this.Frame.GoBack();
}
You can use this code for that:
Put this in App.xaml.cs at end of OnLaunched method,
// Register a handler for BackRequested events and set the
// visibility of the Back button
SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
rootFrame.CanGoBack ?
AppViewBackButtonVisibility.Visible :
AppViewBackButtonVisibility.Collapsed;
And write this method,
private void OnBackRequested(object sender, BackRequestedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame.CanGoBack)
{
e.Handled = true;
rootFrame.GoBack();
}
//you can check for this here rootFrame.BackStack[rootFrame.BackStack.Count-1].SourcePageType.Name
}
Also you can check for pages and control the visibility of back button in Desktop apps like this,
private void OnNavigated(object sender, NavigationEventArgs e)
{
// Each time a navigation event occurs, update the Back button's visibility
Frame rootFrame = (Frame)sender;
if (rootFrame.BackStack != null && rootFrame.BackStack.Count == 1)
{
// take care in page names
if (rootFrame.BackStack[0].SourcePageType.Name == "MainPage"
|| rootFrame.BackStack[0].SourcePageType.Name == "AnyOtherPage")
{
rootFrame.BackStack.RemoveAt(0);
}
}
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
((Frame)sender).CanGoBack ?
AppViewBackButtonVisibility.Visible :
AppViewBackButtonVisibility.Collapsed;
}

[UWP][C#] Override backpress Navigation set in Appx.cs

A bit stuck here. I have a splitview app that has the event for backward navigation when the backkey is pressed residing in Appx.cs.
I want to define a different action in one of the pages navigated inside the splitviews content page (for example, when a certain element is visible to dismiss the element) however the app always follows the event set in appx.cs, and ignores the event in the page that is loaded in the content frame. Here is the code in appx.cs:
protected async override void OnLaunched(LaunchActivatedEventArgs e)
{
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (Window.Current.Content == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
_rootFrame = new Frame();
_rootFrame.NavigationFailed += OnNavigationFailed;
_rootFrame.Navigated += OnNavigated;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = new MainPage(_rootFrame);
// Register a handler for BackRequested events and set the
// visibility of the Back button
SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
_rootFrame.CanGoBack ?
AppViewBackButtonVisibility.Visible :
AppViewBackButtonVisibility.Collapsed;
}
}
private void OnBackRequested(object sender, BackRequestedEventArgs e)
{
if (_rootFrame != null && _rootFrame.CanGoBack)
{
e.Handled = true;
_rootFrame.GoBack();
}
}
And here is the code in one of the pages that is loaded into the splitviews content pane:
public CalcOutputPage()
{
this.InitializeComponent();
// add page specific handling of back navigation when entering this page
SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;
}
// page specific back button navigation
private void OnBackRequested(object sender, BackRequestedEventArgs e)
{
if (MobileStackPanel.Visibility == Visibility.Visible)
{
MobileStackPanel.Visibility = Visibility.Collapsed;
e.Handled = true;
}
else
{
e.Handled = false;
}
}
But this isn't working. It works if its the first page loaded in the navigation stack, but at all other times, the app follows the navigation instructions in appx.cs
So to clarify:
the OnBackRequested method in Appx.cs is handling the back navigation in my app.
I have a page (lets call it PageTwo.xaml) open in the splitview content pane.
in PageTwo.xaml, I want to have an event for when OnBackRequested is pressed
I cant at the moment, as the one in Appx.cs is the one that is always called
i would like to know if there is a way to have the OnBackRequested in the PageTwo.xaml.cs page fire, instead of the OnBackRequested in the Appx.cs Page.
Any help would be appreciated. I'm losing my mind trying to get this working :P
Event handlers are triggered in the same order they have been added, so no luck on this side. One alternative is to make your own event, wrapping the original one. In App.xaml.cs:
public event EventHandler<BackRequestedEventArgs> BackRequested;
private void OnBackRequested(object sender, BackRequestedEventArgs e)
{
// Raise child event
var eventHandler = this.BackRequested;
if (eventHandler != null)
{
eventHandler(sender, e);
}
if (!e.Handled)
{
if (_rootFrame != null && _rootFrame.CanGoBack)
{
e.Handled = true;
_rootFrame.GoBack();
}
}
}
Then in your page, subscribe to the child event instead of the main one:
((App)(App.Current)).BackRequested += OnBackRequested;
Make sure to unsubscribe to the event when leaving the page, or you may end up with a memory leak.

WPF Detecting Window Close Event and coupling with a button event

For two days, I have tried various methods for making it so that on loadButton click, it opens a secondary window and disables the loadButton; Then, when that secondary window has been closed, the loadButton will be re-enabled. Although, obviously all of my attempts have been unsucessful.
I have been reading about using the isClosing event, although, I haven't figured out how to properly implement it. So I decided to go with this route.
private void loadButton_Click(object sender, RoutedEventArgs e)
{
var richWindow = RichTextWindow.GetWindow(new RichTextWindow());
if (richWindow.IsActive != true)
{
loadButton.IsEnabled = false;
richWindow.Show();
}
else
{
loadButton.IsEnabled = true;
}
}
Issue here is, the first half is executed. Once I click the loadButton, it does disable. However, on closing the new Window, the loadButton is still disabled.
Could anyone point me in the right direction on where I need to go with this?
I think what you want is something like this:
private void loadButton_Click(object sender, RoutedEventArgs e)
{
var richWindow = RichTextWindow.GetWindow(new RichTextWindow());
richWindow.Closed += (s, e) => loadButton.IsEnabled = true;
loadButton.IsEnabled = false;
richWindow.Show();
}
Basically, disable the button before opening the window. Then, listen for the window to close and enable the button again.
richWindow.Loaded +=
{
loadedButton.IsEnabled = false;
};
richWindow.Closing +=
{
loadedButton.IsEnabled = true;
}

Categories

Resources