This is a simple question, but I still could not Google for a solution. What event in a C# UWP app runs only once per page navigation.
I added an event handler for back navigation right after:
this.InitializeComponent();
in the page constructor.
If I go back and forth the first and second page twice, it throws an error and debugging found that the event to handle back navigation was called twice, since the code in the page constructor also ran twice.
So in which event can I write code that I only want to run only once per page navigation. Or is there something similar to the below from ASP.net?
if (!Page.isPostback) {
//do something
}
There is a virtual method you can override on the page class: OnNavigatedTo which will be called by the framework when the page is finished loading and becomes the source of the parent frame.
https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.page.onnavigatedto#Windows_UI_Xaml_Controls_Page_OnNavigatedTo_Windows_UI_Xaml_Navigation_NavigationEventArgs_
Although I accepted another answer, in my specific situation the following code is better, cause it checks whether the back button handler is set or not.
private void App_BackRequested(object sender, BackRequestedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
return;
// If we can go back and the event has not already been handled, do so.
if (rootFrame.CanGoBack && e.Handled == false)
{
e.Handled = true;
rootFrame.GoBack();
}
}
Related
So, as per the docs I attach a handler when I navigate to a page:
protected override void OnNavigatedTo(NavigationEventArgs e) {
/* Attach back listener to handle the back press */
SystemNavigationManager.GetForCurrentView().BackRequested += NavigationPage_BackRequested;
...
}
And I detach it when leaving:
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) {
SystemNavigationManager.GetForCurrentView().BackRequested -= NavigationPage_BackRequested;
e.Cancel = false;
}
And I mark the event as handled to prevent the system from handling it:
private void NavigationPage_BackRequested(object sender, BackRequestedEventArgs e) {
e.Handled = true;
}
But the system still handles the back click and I get navigated away. Why?
Your code shown above is working fine (tested on my machine), but the big question is: do you try to cancel a system provided navigation event or a Frame.GoBack() event implemented by the software button of your application?
SystemNavigationManager: Provides a way for an app to respond to system provided back-navigation events.
If you look at the backwards navigation documentation, only certain back buttons (hardware/software) are system provided events, e.g. the software button at the bottom in Tablet mode.
However quite a few project templates (in Visual Studio or from MVVM libraries) also provide a software back button (quite often at the top left) and wire this event to the Frame.GoBack() method. This is NOT a system provided event and can't be cancelled this way. Reasoning: You (or the framework used) is calling the GoBack() explicitly, so why would it have to be cancelled in this scenario.
I want to know if user opened app by clicking SecondaryTile (and also which one was clicked).
Now I have OnNavigatedTo method:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
if (!String.IsNullOrEmpty(e.Parameter.ToString()))
{
//e.Parameter is not null, LiveTile was used
//do something
}
else
{
//No of SecondaryTiles were clicked
}
}
This of course works, but only if app was previously closed. But when app was previously opened, runs in background and user click on LiveTile, then app is being shown but this method is not executed.
How can I handle this scenario?
MSDN: Override the OnLaunched method to perform any general app initialization that should occur only when the user launches your app normally (for example, by tapping the app tile).
You should override or update the OnLaunched event to track app activation moment and analyze corresponding arguments (app.xaml.cs file).
In short, if you look on how OnNavigatedTo event is rised -- it comes from the OnLaunched event that checks whether rootFrame's content already exists or not:
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
...
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);
}
...
}
As you may imagine in your scenario it is not true, so navigation event never happens.
This question already has answers here:
Is it possible to avoid multiple button clicks on a Winform?
(3 answers)
Closed 2 years ago.
I have an app with many user controls and many buttons on each, each button has an OnClick event which does some important stuff and then sends to a new user control.
The problem comes when the user clicks really fast multiple times, the event code gets executed more than once before exitting to a new user control, causing problems.
My current solution is to disable the button on the very first line of the event handler, but doing this to every window and handler would be troublesome, what can I do?
EDIT: Would it be a good solution to derive from Button, and override the OnClick event so it always does a check for a "working" variable, and if it is true, it doesnt start the event ? Something like:
public class MyButton : Button
{
private static bool isWorking = false;
protected override void OnClick(EventArgs e)
{
if (!isWorking)
{
isWorking = true;
base.OnClick(e);
isWorking = false;
}
//Else do nothing
}
}
You can use some timeStamp to delay between 2 clicks:
DateTime timeStamp;
//this will handle the clicks with the allowed interval being 0.5 second
//Note that a tick is equal to 1/10,000,000 of second.
private void click_Handler(object sender, EventArgs e) {
if ((DateTime.Now - timeStamp).Ticks < 5000000) return;
timeStamp = DateTime.Now;
//your code goes here ....
}
If you want all buttons to wait until one button's work is done, add a bool isProcessing variable to your form. Wrap the work of each button inside an if (!isProcessing), and set that flag to true in the first line inside the if statement. Then don't forget to set it back to false right before you exit the if.
I'm assuming you're doing all of this asynchronously, since if it's all in the same thread, the form will lock while it's processing the work. This will solve your issue though.
Disabling controls while sensitive operation is on-going is a typical solution that I always apply.
But since there can be quite a few controls on one screen that are affected by some click or change in UI, I typically design forms to have a specialized method which walks through all the affected controls and disables/enables them accordingly.
Something like this:
void EnableControls(bool enable)
{
foreach (Control ctl in this.Controls)
ctl.Enabled = enable;
}
Similarly, you could group controls into related buckets, so to disable/enable only one of them etc. Depends on your precise needs.
There is an alternative solution to use timer - disable the button, but enable it after 1 sec. This prevents nervous users from clicking multiple times if that would cause damage to data (i.e. each click is treated as a new operation).
I would call the same function from every button and then perform the specific task:
private void Button_Click(object sender, EventAgrs e)
{
Button btn = sender;
btn.disable = true;
switch (btn.AccessibleName)
// call specific function for the particular button or do it all here
}
I'm not sure if this would even work, but just an idea...
You could try with aspect oriented approach (with the help of Postsharp for example):
Create two aspects, one for method entry and one for method exit. In the method entry mark the current method as 'processing' (add the method name to a hash set for example). In the method exit mark the method as 'not processing' (remove it from the hash set). Then in the method entry check if the method is processing and if it is, then cancel the method (like this: https://stackoverflow.com/a/2437794/113858)
Mark all of your event handlers with this aspect.
I'm trying to create an application containing a flipview that automatically flips to the next page when the user hasn't interacted with it for a while. This works fine using a basic DispatcherTimer that gets restarted when the flipview's selection changes.
So far so good, but I also don't want the timer to run when the user is interacting with the current item in the flipview, like a listview or something. I figured that I could just wire up a PointerPressed and PointerReleased handler to the page and stop the timer whenever a pointer is pressed, and restart it when a pointer is released.
This works, except when the pointer is on the flipview: the pressed handler gets executed, but FlipView gobbles up all the other pointer events, so the PointerReleased handler never gets executed.
I can't figure out how to get this to work. In WPF, I'd just use a tunneling event, but that whole concept seems to have disappeared with WinRT? Any advice on how to get this to work?
Update with code
Sure. I have a page containing a flipview and a dispatchertimer:
public sealed partial class MainPage : Page
{
private DispatcherTimer slideScrollTimer;
public MainPage()
{
// Set up a timer that'll flip to the next page every 5 seconds.
this.slideScrollTimer= new DispatcherTimer()
{
Interval = TimeSpan.FromSeconds(5)
};
slideScrollTimer.Tick += slideScrollTimer_Tick;
slideScrollTimer.Start();
}
void slideScrollTimer_Tick(object sender, object e)
{
// When the timer runs out, go to the next page, or back
// to the first.
if (flipView.SelectedIndex < flipView.Items.Count - 1)
{
flipView.SelectedIndex++;
}
else
{
flipView.SelectedIndex = 0;
}
}
private void flipView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// restart the timer if someone flips to a different page
if (this.slideScrollTimer != null)
{
this.slideScrollTimer.Start();
}
}
}
Basically what I want is for the timer to reset whenever someone touches the application. I tried adding a PointerPressed/PointerReleased handler using AddHandler, but released only fires if you're not on the flipview, or just tapping it instead of scrolling it or manipulating its contents.
You can use UIElement.AddHandler to handle events that have already been marked as handled elsewhere.
Adds a routed event handler for a specified routed event, adding the handler to the handler collection on the current element. Specify handledEventsToo as true to have the provided handler be invoked even if the event is handled elsewhere.
I have a button that calls function A()
When I click on it I want the calls to be made in that order:
A()
Page_Load()
Right now it's doing:
Page_Load()
A()
Is there a way around that or is it just by design and there's nothing I can do about it?
The easiest way to do this would be to use a HTML Submit button and check to see if it is in the Form on every postback in Page_Init
public void Page_Init(object o, EventArgs e)
{
if(!string.IsNullOrEmpty(Request.Form["MyButtonName"]))
{
A();
}
}
And in your ASP.NET code:
<Button Type="Submit" Name="MyButtonName" Value="Press Here To Do Stuff Early!" />
I think that will work.
Control events (such as the click events of buttons) are called after page_load. The controls are not guarenteed to be fully initialized prior to page_load. If you really need to call a function before page_load has been called based on whether a button has been pressed you'll have to examine the request to check if the button has been pressed (basically old school ASP)
You need to call your function in the Page_Init. Page_Init will happen before Page_Load.
Here's an Overview of the ASP.NET Page Lifecycle.
Not exactly: ASP.NET will always call Page_Load before handling postback events like Button_Click.
However, you can accomplish what you want by redirecting to your page after handling the postback event. (Using the Post-Redirect-Get pattern.)
Inside your Page_Load method, you can avoid running any relevant code twice by checking to see if it's a postback first:
if (!this.IsPostBack) {
// Do something resource-intensive that you only want to do on GETs
}
As Jeff Sternal answered, The Post-Redirect-Get pattern is a good way of solving a problem like this.
In my circumstances i had a calendar and if you clicked a date it would add that to a scheduler. The scheduler would have buttons on each new date that needed to have onclick functions tied to them.
Because the new row was being added with a linkbutton(on the calendar), in the code the new scheduler date was being added at the Postback event handling meaning that the new set of buttons wouldn't have a command tied to them.
The page life Cycle
Post Get Redirect
I don't think it's possible, at least, not in the way described by your question. When you click a button it will send a request to the server which in turn will start processing it, and follow the ASP.NET Page Lifecycle as posted by Joseph.
Alternatively you could try making an AJAX call to a page without reloading the current one you're on and do whatever processing you require.
This is what you want to do for Page Init is called before Page Load.
Take a look at the ASP.net Page Life Cycle
public void Page_Init(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
//CALL YOU FUNCTION A()
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
}
}
If your actual goal here is to have your "page loading code" happen after your event handler runs -- for example, if clicking your button changes something in your database, and you want the updated data to be reflected on the page when it loads -- then you could have your "page loading code" get called from a method that gets called later in the ASP.NET page life cycle than your event handler, such as Page_PreRender, instead of calling it from Page_Load.
For example, here's a simplified excerpt from an .aspx.cs page class that has a button event handler that runs before the page population logic, and a confirmation message that is visible on the page only after the button was clicked:
// Runs *before* the button event handler
protected void Page_Load() {
_myConfirmationMessage.Visible = false;
}
// Runs *after* the button event handler
protected void Page_PreRender() {
// (...Code to populate page controls with database data goes here...)
}
// Event handler for an asp:Button on the page
protected void myButton_Click(object sender, EventArgs e) {
// (...Code to update database data goes here...)
_myConfirmationMessage.Visible = true;
}