I have a UWP app - the design has a "Back" button in the screen content, which I would like to use to trigger the system navigation event handled in my App.xaml.cs file. My current click handler, which is pasted to each file which needs it is:
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame.CanGoBack)
rootFrame.GoBack();
How would I instead trigger the back event, which would trigger the back event handler which already contains this code?
In App.xaml.cs, add this to OnLaunced(...):
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
...
if (rootFrame == null)
{
...
// Register a handler for BackRequested events
SystemNavigationManager.GetForCurrentView().BackRequested += this.OnBackRequested;
}
...
}
Where in OnBackRequested(...), which can also be in App.xaml.cs:
private void OnBackRequested(object sender, BackRequestedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame.CanGoBack)
{
e.Handled = true;
rootFrame.GoBack();
}
}
This could easily be adapted to support multiple frames if you implement any custom navigation, and you can also add global handling of showing/hiding the back button via something like:
public void UpdateBackButton(Frane frame)
{
bool canGoBack = (frame?.CanGoBack ?? false);
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = canGoBack
? AppViewBackButtonVisibility.Visible
: AppViewBackButtonVisibility.Collapsed;
}
You can programmatically call back via a function like this in App.xaml.cs or in a custom navigation manager:
public bool TryGoBack(Frame frame)
{
bool handled = false;
if (frame?.CanGoBack ?? false)
{
handled = true;
frame.GoBack();
}
this.UpdateBackButton(frame);
return handled;
}
Related
In a Windows 10 Universal app, I want to display a MessageDialog when the back button is pressed.
The code of my page is the following :
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
SystemNavigationManager.GetForCurrentView().BackRequested += GamePage_BackRequested;
}
private async void GamePage_BackRequested(object sender, BackRequestedEventArgs e)
{
var dialog = new Windows.UI.Popups.MessageDialog("Are you sure ?");
dialog.Commands.Add(new Windows.UI.Popups.UICommand("Yes"));
dialog.Commands.Add(new Windows.UI.Popups.UICommand("No"));
var result = await dialog.ShowAsync();
}
When I lauch the App in "local machine", the dialog is well displayed. But when I turn Windows to "tablet mode", or when i try it on my Windows Phone, the ShowAsync method crashes the App (with no error).
Why is the app crashing ?
The problem seems to be that the "dialog.ShowAsync()" method should be called from the UI thread.
This is how I solved it :
private void GamePage_BackRequested(object sender, BackRequestedEventArgs e)
{
e.Handled = true;
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame.CanGoBack)
{
var d = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => ShowConfirmationDialog(rootFrame));
}
}
public async void ShowConfirmationDialog(Frame rootFrame)
{
var dialog = new Windows.UI.Popups.MessageDialog("Are you sure ?");
dialog.Commands.Add(new Windows.UI.Popups.UICommand("Yes") { Id = 0 });
dialog.Commands.Add(new Windows.UI.Popups.UICommand("No") { Id = 1 });
var result = await dialog.ShowAsync();
if (result != null && result.Label == "Yes")
{
rootFrame.GoBack();
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher;
SystemNavigationManager.GetForCurrentView().BackRequested += GamePage_BackRequested;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
SystemNavigationManager.GetForCurrentView().BackRequested -= GamePage_BackRequested;
}
You should handle the backrequest ; e.handled = true;
private async void GamePage_BackRequested(object sender, BackRequestedEventArgs e)
{
e.handled = true;
var dialog = new Windows.UI.Popups.MessageDialog("Are you sure ?");
dialog.Commands.Add(new Windows.UI.Popups.UICommand("Yes"));
dialog.Commands.Add(new Windows.UI.Popups.UICommand("No"));
var result = await dialog.ShowAsync();
}
You should add onnavigatedfrom method to unregister the event, otherwise it will trigger twice !
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
if (gb.DetectPlatform() == Platform.WindowsPhone)
HardwareButtons.BackPressed -= HardwareButtons_BackPressed;
elde
SystemNavigationManager.GetForCurrentView().BackRequested -= GamePage_BackRequested;
};
}
There is no way to accomplish what you want -- the back key handler requires an immediate answer (handled or not) but the dialog is inherently async.
You can of course choose to mark the event as Handled before you show the dialog, but then if the user says "Yes" there's no way you can then navigate away. You can of course terminate the app, but that's a bad idea (see final paragraph)
That said, you don't typically need this dialog because in Windows 10, backing out of an app doesn't terminate it, it just switches to the previous app (or the Start menu). The user can trivially return to it via the task switcher (or launching it again).
My UWP application was crashing when I called MessageDialog.ShowAsync in the Application.UnhandledException event handler and this was the top result when I searched for an answer.
I resolved the issue by setting UnhandledExceptionEventArgs.Handled = true before calling MessageDialog.ShowAsync.
This is well documented in another SO question, but I didn't find that answer until after I figured out the solution because I didn't realize calling the method from Application.UnhandledException was significant.
In my Xaml Page I've got a Frame.
I'm trying to have a backButton event to just navigate inside frame .
so I tried to use this piece of code
public MainPage(){
this.InitializeComponent();
if(Windows.Foundation.Metadata.ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons")) {
Windows.Phone.UI.Input.HardwareButtons.BackPressed += HardwareButtons_BackPressed;
}
}
private void HardwareButtons_BackPressed(object sender,BackPressedEventArgs e) {
if(insideFrame.CanGoBack())insideFrame.GoBack();
else Application.Current.Exit();
}
but In phone after doing HardwareButtons_BackPressed event it close the application.
It seems to running some default back button behavior on MainPage...
How can I fix it? And In Windows10 does they add new events to handle back navigation?
[Update]
Now I found out it's better to Use SystemNavigationManager in Windows 10 instead of Input.HardwareButtons.BackPressed.
SystemNavigationManager currentView = SystemNavigationManager.GetForCurrentView();
Windows 10 (UWP) include SystemNavigationManager in Windows.UI.Core namespace for Navigation purpose only.
Because SystemNavigationManager is part of Windows Universal Platform, So, it's supported by all device family running on Windows 10 including Mobile and PC.
For Single Page
If you just want to handle navigation for single page. Follow the following steps
Step 1. Use namespace Windows.UI.Core
using Windows.UI.Core;
Step 2. Register back request event for current view. Best place for this is main constructor of class after InitializeComponent().
public MainPage()
{
this.InitializeComponent();
//register back request event for current view
SystemNavigationManager.GetForCurrentView().BackRequested += MainPage_BackRequested;
}
Step 3. Handle BackRequested event
private void Food_BackRequested(object sender, BackRequestedEventArgs e)
{
if (Frame.CanGoBack)
{
Frame.GoBack();
e.Handled = true;
}
}
For Complete Application at one place for single rootFrame
Best place for handling all backbutton for all Views is App.xaml.cs
Step 1. Use namespace Windows.UI.Core
using Windows.UI.Core;
Step 2. Register back request event for current view. Best place for this is OnLaunched just before Window.Current.Activate
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
...
SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;
Window.Current.Activate();
}
Step 3. Handle BackRequested event
private void OnBackRequested(object sender, BackRequestedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame.CanGoBack)
{
rootFrame.GoBack();
e.Handled = true;
}
}
References- Handle back button pressed in UWP
Hope this is helpful to someone!
You need to tell the system that you handled the backbutton press by setting the Handled property of the BackPressedEventArgs to true.
private void OnHardwareButtonsBackPressed(object sender, BackPressedEventArgs e)
{
// This is the missing line!
e.Handled = true;
// Close the App if you are on the startpage
if (mMainFrame.CurrentSourcePageType == typeof(Startpage))
App.Current.Exit();
// Navigate back
if (mMainFrame.CanGoBack)
{
mMainFrame.GoBack();
}
}
follow these steps:
Add two Global Variables in your page as below.
private NavigationHelper navigationHelper;
private RelayCommand _GoBackCommand;
Then add below code in constructor of specific page.
// below code is to override the back navigation
// hardware back button press event from navigationHelper
_GoBackCommand = new RelayCommand
(
() => this.CheckGoBack(),
() => this.CanCheckGoBack()
);
navigationHelper.GoBackCommand = _GoBackCommand;
// ---------
Then add both those methods we've just declared in constructor.
private bool CanCheckGoBack()
{
// this should be always true to make sure the app handles back buton manually.
return true;
}
private void CheckGoBack()
{
// this will be execute when back button will be pressed
}
ps. - for this you might need to use BasicPage instead of BlankPage while adding new page.
hope this will help..!
Try this.It will work for frame back navigation.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
HardwareButtons.BackPressed += HardwareButtons_BackPressed;
}
void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (Frame.CanGoBack)
{
e.Handled = true;
Frame.GoBack();
}
}
}
I think this is because you add HardwareButtons_BackPressed in your page instead on in app.xaml.cs.
In app.xaml.cs :
public App()
{
this.InitializeComponent();
this.Suspending += this.OnSuspending;
HardwareButtons.BackPressed += HardwareButtons_BackPressed;
}
void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame != null && rootFrame.CanGoBack)
{
e.Handled = true;
rootFrame.GoBack();
}
}
Now, back button of your phone will work on any pages.
And then, if you want to add a particular button doing back in any page :
In the particular page (or each pages if you want) :
public void btn_return_Tapped(object sender, TappedRoutedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame != null && rootFrame.CanGoBack)
{
e.Handled = true;
rootFrame.GoBack();
}
}
Source :
http://windowsapptutorials.com/tips/general-tips/handling-the-back-button-in-a-windows-phone-8-1-app/
How would I handle the back button for windows mobile 10 and the back button for windows 10 tablet mode? I've been looking everywhere but can't find any examples for it.
This topic is one of the examples used in the Guide to Universal Windows Platform apps . I strongly suggest reading that when getting started with Universal apps.
For the button on the page header use Windows.UI.Core.SystemNavigationManager and set the AppViewBackButtonVisibility property to show or hide the button and handle the BackRequested event to perform the navigation.
Windows.UI.Core.SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
Windows.UI.Core.SystemNavigationManager.GetForCurrentView().BackRequested += (s,a) =>
{
Debug.WriteLine("BackRequested");
if (Frame.CanGoBack)
{
Frame.GoBack();
a.Handled = true;
}
}
You wire up the hardware back button the same as you do in Windows Phone 8.1, but you should check for the PhoneContract (or the individual class and method) to make sure it is there:
if (ApiInformation.IsApiContractPresent ("Windows.Phone.PhoneContract", 1, 0)) {
Windows.Phone.UI.Input.HardwareButtons.BackPressed += (s, a) =>
{
Debug.WriteLine("BackPressed");
if (Frame.CanGoBack)
{
Frame.GoBack();
a.Handled = true;
}
};
}
Add the following code to your App.xaml.cs and it will handle the navigation on desktop, tablet and mobile (I tested it on the mobile emulator)
for better highlighted differences and explanation (Handling The Back Button In Windows 10 UWP Apps by JEFF PROSISE)
sealed partial class App : Application
{
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
}
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)
{
// 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 = 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;
}
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
// Ensure the current window is active
Window.Current.Activate();
}
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
private void OnNavigated(object sender, NavigationEventArgs e)
{
// Each time a navigation event occurs, update the Back button's visibility
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
((Frame)sender).CanGoBack ?
AppViewBackButtonVisibility.Visible :
AppViewBackButtonVisibility.Collapsed;
}
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
// TODO: Save application state and stop any background activity
deferral.Complete();
}
private void OnBackRequested(object sender, BackRequestedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame.CanGoBack)
{
e.Handled = true;
rootFrame.GoBack();
}
}
}
I'm using the Prism MVVM library for WinRT in a Windows Phone 8.1 project. Is it possible to prevent back navigation via the phone's back button and handle the back button press in the ViewModel?
Concrete scenario:
The user can select one item (the "active" item) from a list of items - like a player in a game. That item is a reference for the rest of the app's functionality, e.g. database queries.
Selecting one item returns the user to the previous (main) page.
In the same list, the user can also delete an item that is no longer needed. It should be possible to delete all items.
Problem: If the user deletes the active item or the last item and then taps the back button, I end up having an invalid active item.
To prevent that, I'd like to cancel the back button navigation and prompt the user to select or create another active item, ideally from the ViewModel.
Update: I have now added an event handler to the App.xaml.cs based on how I understood Nate's comment below. This should override it application-wide:
private void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e)
{
Frame currentFrame = Window.Current.Content as Frame;
if (currentFrame == null)
{
return;
}
if (currentFrame.Content is SelectionPage)
{
e.Handled = true;
}
else if (currentFrame.CanGoBack)
{
currentFrame.GoBack();
e.Handled = true;
}
}
and subscribing to the event in the constructor:
#if WINDOWS_PHONE_APP
HardwareButtons.BackPressed += HardwareButtons_BackPressed;
#endif
This seems to be handling the back button press just fine but it does not stop the existing navigation. So it goes back in any case and twice in the default case.
This is possible. Here is the solution (mostly inspired by this discussion):
Create an interface that allows view models to disable the back navigation:
public interface IRevertState
{
bool CanRevertState();
void RevertState();
}
In the view model implement the interface:
public class myViewModel : ViewModel, IRevertState {
public bool CanRevertState() {
return (...) //condition under which back navigation should be disabled
}
public void RevertState() {
(...) // optionally reset condition if required
}
In App.Xaml.cs handle the back navigation:
#if WINDOWS_PHONE_APP
protected override void OnHardwareButtonsBackPressed(object sender, BackPressedEventArgs e) {
var page = (Page)((Frame)Window.Current.Content).Content;
if (page.DataContext is IRevertState) {
var revertable = (IRevertState)page.DataContext;
if (revertable.CanRevertState()) {
revertable.RevertState();
e.Handled = true;
return;
}
}
base.OnHardwareButtonsBackPressed(sender, e);
}
#endif
Upon pressing the Lens tile icon for my application within the default camera application on Windows Phone, I would like to navigate to my application. I have this working, although I am using the CameraCaptureTask to take photos and then I save and display them within my MainPage. I am calling CameraCaptureTask on a click event, so my question is how might I access this click event when I select the Application Tile in the Lens picker?
App.xaml.cs
private void InitializePhoneApplication()
{
if (phoneApplicationInitialized)
return;
// Create the frame but don't set it as RootVisual yet; this allows the splash
// screen to remain active until the application is ready to render.
//RootFrame = new PhoneApplicationFrame();
RootFrame = new TransitionFrame();
RootFrame.Navigated += CompleteInitializePhoneApplication;
// Assign the lens example URI-mapper class to the application frame.
RootFrame.UriMapper = new LensUriMapper();
// Handle navigation failures
RootFrame.NavigationFailed += RootFrame_NavigationFailed;
// Handle reset requests for clearing the backstack
RootFrame.Navigated += CheckForResetNavigation;
// Ensure we don't initialize again
phoneApplicationInitialized = true;
}
LensUriMapper.cs
class LensUriMapper : UriMapperBase
{
private string tempUri;
public override Uri MapUri(Uri uri)
{
tempUri = uri.ToString();
// Look for a URI from the lens picker.
if (tempUri.Contains("ViewfinderLaunch"))
{
// Launch as a lens, launch viewfinder screen.
//return new Uri("/Views/MainPage.xaml", UriKind.Relative);
return new Uri("/Views/MainPage.xaml?Viewfinder=1", UriKind.Relative);
}
}
// Otherwise perform normal launch.
return uri;
}
}
MainPage.xaml.cs
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (NavigationContext.QueryString.ContainsKey("Viewfinder"))
{
newButton_Click(null, null);
}
}
void newButton_Click(object sender, EventArgs e)
{
_cameraTask.Show();
}
In addition, how might I navigate directly back to the default camera application upon the back key press as stated in MSDN's Lens application guidelines?
you can add a parameter to the ViewfinderLaunch uri:
return new Uri("/Views/MainPage.xaml?Viewfinder=1", UriKind.Relative);
Next you have to override the "OnNavigatedTo" method of your MainPage to check if Viewfinder parameter is setted:
if (NavigationContext.QueryString.ContainsKey("Viewfinder"))
{
newButton_Click(null, null);
}