I pass a PhoneApplicationPage instance to a classlibrary, and popup an usercontrol in this classlibrary, when I press back button, the whole application exit. Yesterday I sovled the problem in an application, but I cannot use the method in this classlibrary case.
I tried to subscribe to the event(BackKeyPress), but VS2012 says "parent_BackKeyPress" "System.EventHandler" override and delegate cannot match. I checked, they match.
PhoneApplicationPage mContext=...;
mContext.BackKeyPress += new EventHandler(parent_BackKeyPress);
void parent_BackKeyPress(CancelEventArgs e)
{
ppChangePIN.IsOpen = false;
Application.Current.RootVisual.Visibility = Visibility.Visible;
}
anything incorrect here? plus, can I use navigationservice in classlibrary? I did this before to navigate to a page created in the classlibrary like below, well it ends up crashing. Some say can't use pages in classlibrary, instead we should use Popup(usercontrol).
mContext.NavigationService.Navigate(new Uri("/ChangePINPage.xaml", UriKind.Relative));
I have successfully done just that:
// or some other method of accessing the current page
// - but via Application, to which you have access also in class library
var currentPage = (PhoneApplicationPage)((PhoneApplicationFrame)Application.Current.RootVisual).Content;
currentPage.BackKeyPress += (sender, args) =>
{
// Display dialog or something, and when you decide not to perform back navigation:
args.Cancel = true;
};
Of course you have to make sure that this code is executed if and only if the CurrentPage is the main page.
I also use Pages in class library. You can use NavigationService in class library: you can get it for example from current page obtained as above (currentPage.NavigationService). Or you could use the Navigate method of PhoneApplicationFrame:
((PhoneApplicationFrame)Application.Current.RootVisual)
.Navigate(
new Uri(
"/ClassLibraryName;component/SamplePage.xaml",
UriKind.Relative));
As the short Uris like "/SamplePage.xaml" will work in Application Project, to navigate to page in class library you have to give full location: "/ClassLibraryName;component/SamplePage.xaml".
But note, that if the application chooses to display message box to stop from exiting, it will not pass certification, as (from Technical certification requirements for Windows Phone):
5.2.4.2 – Back button: first screen
Pressing the Back button from the first screen of an app must close the app.
Related
On my Xamarin.Forms app I using a three different types of Buttons. These are their on click functionalities:
Navigation Buttons:
I use them to navigate to an other page if I will need to came back later to the previous page.
btn_navigate.Clicked += (sender,e) => {
Navigation.PushAsync(new Page());
};
Error: If the new page takes a few seconds to load, doing a quick multi click on these buttons open several pages.
Non returnable navigation Buttons:
I use them to navigate to an other page when I want to avoid the user came back to the previous page. I destroy the current page after I insert before my new page.
btn_non_returnable_navigate.Clicked += (sender,e) => {
Navigation.InsertPageBefore(new Page(), this);
Navigation.PopAsync();
};
Error: If the new page takes a few seconds to load, doing a quick multi click on these buttons throws an exception: Before must be in the pushed stack of the current context. This is because the first click create the new page and destroy the current one, the second click cant not destroy to the current page because it is already destroyed so it throws the exception.
HTTP request Buttons:
I use them to send a HTTP request to the server. Usually after the HTTP request was completed it navigate to an other page. Those are obviously the more important ones.
btn_http_request.Clicked += (sender,e) => {
Uri uri = new Uri("http://192.168.0.1:8080/request");
HttpClient client = new HttpClient();
HttpResponseMessage http_response = client.GetAsync(uri);
....
Navigation.InsertPageBefore(new Page(), this);
Navigation.PopAsync();
};
Error: If the request takes a few seconds to get the answer, doing a quick multi click on these buttons throws several HTTP request. It should not happen.
All of these buttons are combined on the same page multiple times. So if the user try to click multiple buttons repeatedly he will tear down the app.
These issues are caused because the asynchronously of the button actions. But I think it will be solve by using a locker for these buttons so they could only clicked once a time.
Have Buttons some property like this? If it is not, how could I create a Buttons extension that fix this problems? I would like to have as simple solution as it possible. I need to control this issue in a lots of Buttons and if it is a complicated solutions it may do a tricky code problem.
On WPF this code works as I need but on Xamarin there is not RoutedEventArgs:
C# Button extension
public partial class ButtonEx : Button{
public bool Active;
public ButtonEx(){
InitializeComponent();
Active = true;
}
private void Extension_Click(object sender, RoutedEventArgs e){
if (Active) {
Active = false;
} else {
e.Handled = true;
}
}
}
XAML Button extension
<Button x:Class="test.ButtonEx"
..
Click="Extension_Click">
</Button>
C# on the code
...
ButtonEx buttonEx = new ButtonEx()
//Click function of the button
buttonEx.Click += (sender, e) =>{
//This code only happen if buttonEx.active = true
...
(sender as Button).Active = true;
}
...
When you click the extended button it throws two events. The fist one, the Extension_Click and the second one the Click function of the button. The second event only happen when the buttonEx.active = true. The first even allows or blocks the second.
It is there an alternative like this to Xamarin.Formns?
Thank you
The best practice for this including on WPF is to make the button disabled while it performs some action and shouldn't/can't perform additional actions. That is by the book. Technically someone can make a new sort of button that would perform this automatically. I would guess that the fact that no one made it by now is that this is not worth the effort / brings at least as much troubles as it resolves. Unfortunately I would guess that if you need something like that you will have to make it yourself, but it is definitely possible.
I have tried to different methods to get my MainPage to change and it is not happening. Basically when my App starts it needs to do some Async tasks to initialize everything, and then get the real main page to display. I first tried an event handler in App.cs. I can confirm the event handler does fire off in my debugger. However the code to switch the main page is not successfully doing so. Nothing changes in my UI. This code is below:
private void UnitStatusModelChanged(object sender, EventArgs e)
{
UnitStatusModel = _unitStatusMonitor.UnitStatusModel;
UnitStatusViewModel = new UnitStatusViewModel(UnitStatusModel, InputResourceHandler);
MainPage = new MainTabbed(UnitStatusViewModel, InputResourceHandler);
Debug.WriteLine("Switched page to " + UnitStatusModel.Version.Name);
}
protected override void OnStart()
{
_unitStatusMonitor.UnitStatusChanged += new UnitStatusModelChangedEventHandler(UnitStatusModelChanged);
_commandQueue.StartQueue();
}
I thought maybe setting MainPage this way is not the way to go. So I tried making the MainPage a blank NavigationPage, and then pushing a model page on top once the app is ready. This also had no effect.
var newPage = new MainTabbed(
new UnitStatusViewModel(_unitStatusModel, inputResourceHandler),
inputResourceHandler
);
App.Current.MainPage.Navigation.PushModalAsync(newPage);
I can confirm that if I start the app with a MainTabbed page, the page does display, so it should not be a problem with MainTabbed. Again, I also verified the in the debugger these lines of code are being executed with no exceptions being thrown. Still, no change to MainPage. What gives?
Check your thread after your async completes, when you call PushModalAsync (or any UI thing). Make sure it's on the main thread.
In my page.xaml, i have hooked the Back hardware button as this:
Windows.Phone.UI.Input.HardwareButtons.BackPressed += HardwareButtons_BackPressed;
and implement the method:
private void HardwareButtons_BackPressed(object sender, Windows.Phone.UI.Input.BackPressedEventArgs e)
{
// Handle the Virtual Hardware Button: Back,
// When user taps it, I need to get the previous page name.
System.Diagnostics.Debug.WriteLine("CurrentSourcePageType = " + Frame.CurrentSourcePageType.FullName);
System.Diagnostics.Debug.WriteLine("Back button is pressed...");
}
But here, the Frame.CurrentSourcePageType.FullName is already the back navigated page name, how can i get the previous page name?
Maybe I need to describe my question better here:
Suppose I have 2 pages A and B,
Through page A I navigate to page B, in the page B I have done something, then I only want to use back button to trigger some customize action (I don't want to add button in my Page), but this action needs to get the page B's name first.
First of all, HardwareButtons.BackPressed is an app-wide event, so it's not a good idea to subscribe to it in Page, unless you do it very carefully and remember to unsubscribe when not needed any more. It's worth also to mention that you should pay special attention if anything has been susbscribed to this event before your Page - for example in app's constructor or anywhere else. (some typical places are shows here in Igrali's answer).
To do what you want you can for example either build an app-wide event that will be responsible for an action, or use the code as shown below (following Rob Caplan's answer). In both cases the name (type) of your previous page you can get from Frame.BackStack.
RelayCommand myGoBackCommand;
public BasicPage1()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
myGoBackCommand = new RelayCommand(() => GoBackAction());
this.navigationHelper.GoBackCommand = myGoBackCommand;
}
private void GoBackAction()
{
//print previous page name before going back
Debug.WriteLine(Frame.BackStack.Last().SourcePageType);
if (navigationHelper.CanGoBack())
navigationHelper.GoBack();
}
Note: To make the above code work, delete all other subscriptions to HardwareButtons.BackPressed and make your pages BasicPages (use navigation helper and so on). Of course you can have other subscriptions, but you need to handle them carefully, in most cases these events are fired with order they were subscribed.
Remark - in case you need also handle the case when user navigates back from other Page and you want the name of that page - then recognize type of navigation in OnNavigatedTo (forward/back navigation) and then read suitable Frame's stack - BackStack or ForwardStack.
The code above is using BasicPage template - if you don't have mentioned classes, then add to your project a new BasicPage and VS should ask you if you want to add the common files (NavigationHelper ans so on).
I am developing an app for Windows Phone 8 in VS2012 and My StartUp Project page is SetProfile.XAML which creates the profile for the first time, but if the user is entering the app second time, the page must not appear because there is already an existing profile.
So I have this code for the "Loaded" event handler, which checks if there is a created profile and if there is, navigates to MainPage page.
private void PhoneApplicationPage_Loaded_1(object sender, RoutedEventArgs e)
{
if (Flag.Contains("true"))
{
if (IsolatedStorageSettings.ApplicationSettings.Contains("player1"))
if (!(Flag.Contains("false")))
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
}
}
The problem is when I enter a second time, I see a blink of the SetProfile.XAML page then it navigates (it's like milliseconds), but I still can see it.
Is there any way I can make it faster so the user won't see it?
There is a very good blogpost about how to 'navigate' to a screen that is not always needed up on Shawn's blog here http://www.visuallylocated.com/post/2012/06/18/Using-a-custom-UriMapper-to-navigate-to-a-login-screen.aspx
The trick is to use a custom UriMapper class that checks all the info and determines the correct navigation uri.
You use this class in your app.xaml.cs - Application_Launching and Application_Activated
you see blink as you have used "Loaded" event,it loads page in run time and must not used unnecessary.
You must use "OnNavigatedTo" event for these normal scenarios.
I am currently working on an app for WP7 for my university, and need a temporary solution to a problem. Now this solution is, that I will be loading a webpage using the web browser control for WP7. For example: http://m.iastate.edu/laundry/
Now as you see on the webpage, there are certain elements I want to hide, for example the back button. For now, what I have done to handle the back button is something like this:
private void webBrowser1_Navigating(object sender, NavigatingEventArgs e)
{
// Handle loading animations
// Handle what happens when the "back" button is pressed
Uri home = new Uri("http://m.iastate.edu/");
// The the current loading address is home
// Cancel the navigation, and go back to the
// apps home page.
if (e.Uri.Equals(home))
{
e.Cancel = true;
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
}
}
Now that works beautifully, except for the part that there is a back button on the hardware.
So my second option is to completely hide the back button ONLY on that page, and not its children. So not on http://m.iastate.edu/laundry/l/0
I am still debating on just parsing the data and displaying it in my own style, but I'm not sure if that's completely needed seeing how the data needs constant internet service and is already in a well-put format. Plus, I feel like that would be a waste of resources? Throw in your opinions on that too :)
Thanks!
You should inject a script in the page with InvokeScript.
Here is the kind of Javascript code you need to remove the back button:
// get the first child element of the header
var backButton = document.getElementsByTagName("header")[0].firstChild;
// check if it looks like a back button
if(backButton && backButton.innerText == "Back") {
// it looks like a back button, remove it
document.getElementsByTagName("header")[0].removeChild[backButton];
}
Call this script with InvokeScript:
webBrowser1.InvokeScript("eval", "(function() { "+ script +"}()");
Warning: IsScriptEnabled must be set to true on the web control
If the removal of the back button depends of the page, just test the navigating URI in C# and inject the script if neeeded.