I have a 4.0 webpage with a button click that runs a server side process that takes about 5-10 seconds to run. I thought I’d be smart and search for some code that made the button hide and showed an animated gif image while the process was running. I got this working but started to notice “file being used by another process” errors being thrown server side (part of the process is deleting a directory). I debugged through and started to see what appeared to be a second button event click being fired at the same time. Turns out that the piece of Js code I added to refresh the page to allow the animated gif to run seems to be firing the server click event a second time. I had to call the Js SetTimeOut() method to get the gif image running, it's this that is appearing to cause the problem.
I did a bit of searching to try to find a different approach to getting the animated gif to run, but couldn’t find one, so I went for the ambulance at the bottom of the cliff approach and decided to add a server side timestamp into session and only proceed with my code if it is more than 20 seconds since session was last set.
I’ve had some very erratic results with this approach and any input would be appreciated.
Is there a better way I can get the gif running?
Has anyone else come across this issue of the event firing twice?
Why should my lock not work first time through?
Any suggestion of a better approach to do this is appreciated, Thanks!
Logging results:
First time through (no session existing)
2011-06-21 11:46:14.8968|DEBUG|FileViewer.copyfiles|Count = 1 & Locked = False
2011-06-21 11:46:14.8968|DEBUG|FileViewer.copyfiles|Count = 2 & Locked = True
2011-06-21 11:46:19.0619|DEBUG|FileViewer.copyfiles|Count = 1 & Locked = False
2011-06-21 11:46:19.0619|DEBUG|FileViewer.copyfiles|Count = 2 & Locked = True
2011-06-21 11:46:23.1959|DEBUG|FileViewer.copyfiles|Count = 3 & Locked = True
2011-06-21 11:46:28.8119|DEBUG|FileViewer.copyfiles|Count = 3 & Locked = True
Run again:
2011-06-21 11:49:47.7798|DEBUG|FileViewer.copyfiles|Count = 1 & Locked = False
2011-06-21 11:49:47.7798|DEBUG|FileViewer.copyfiles|Count = 2 & Locked = True
2011-06-21 11:49:55.9697|DEBUG|FileViewer.copyfiles|Count = 3 & Locked = True
2011-06-21 11:49:59.8697|DEBUG|FileViewer.copyfiles|Count = 1 & Locked = True
2011-06-21 11:49:59.8697|DEBUG|FileViewer.copyfiles|Count = 3 & Locked = True
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
PostBackOptions optionsSubmit = new PostBackOptions(btnGo);
btnGo.OnClientClick = "HideControlOnClick(this);";
btnGo.OnClientClick += ClientScript.GetPostBackEventReference(optionsSubmit);
}
}
protected void btnGo_Click(object sender, EventArgs e)
{
bool locked = true;
if (Session["ClickTime"] == null || (DateTime)Session["ClickTime"] < DateTime.Now.AddSeconds(-20))
{
Session["ClickTime"] = DateTime.Now;
locked = false;
}
WriteToLog(1, locked);
if (Page.IsValid && !locked)
{
locked = true;
WriteToLog(2, locked);
// Do all my processing
}
WriteToLog(3, locked);
}
<script language="javascript" type="text/javascript">
function HideControlOnClick(btnGo)
{
// IE uses className for the css property.
btnGo.setAttribute('className', 'hide');
document.getElementById('MainContent_imgWait').setAttribute('className', 'show');
setTimeout("UpdateImg('MainContent_imgWait','Images/loading.gif');",50);
}
function UpdateImg(ctrl, imgsrc)
{
var img = document.getElementById(ctrl);
img.src = imgsrc;
}
</script>
I think its better use some web debugger tools to track the request. I suggest use the Fiddler
I suspect that issue here is probably nothing to do with animated gif but button being pressed twice (i.e. two clicks getting registered instead one). You can try disabling the button to supress the second click (yet another way is to have flag at button level) - for example,
btnGo.OnClientClick = "return HideControlOnClick(this);";
function HideControlOnClick(btnGo)
{
if (btnGo["My_Is_Clicked"]) {
// already clicked, ignore
return false;
}
btnGo["My_Is_Clicked"] = true;
...
return true;
}
I've solved the issue by dropping all of the relevant controls into an update pannel, this seems to allow the animated gif to run without calling the setTimeout("UpdateImg('MainContent_imgWait','Images/loading.gif');",50); JS method. Working fine now with the server event only firing once.
The issue is most likely that you're using a server side button control (since you're referencing it server side), and those fire a postback when they're clicked typically (hard to say without knowing the exact object you're using). Use an HTML input to fire your client side event, do your work, and then perform the postback from your JS code (with an appropriate ID to catch on the server side).
Something like this server side:
/// <summary>
/// Page init event, setup the core of the page.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected override void Page_Init(object sender, EventArgs e)
{
try
{
// See if we're in a postback.
if (IsPostBack)
{
// If we have a target...
if (Request.Params["__EVENTTARGET"] != null)
{
// See what the target is.
switch (Request.Params["__EVENTTARGET"])
{
case "btnGo":
// Maybe make this a parameterless method rather than an event handler to avoid parameters that you don't need.
btnGo_Click(null, null);
break;
}
}
}
}
}
And client side:
function Go() {
// Show loading...
// Call server.
__doPostBack("btnGo", "My Args");
}
EDIT: As an alternative I think you can also add "return false;" after the client side event, such as:
btnGo.OnClientClick = "HideControlOnClick(this); return false;";
and that should stop the postback as well.
Related
I'm recording Coded UI Tests with VS 2012, which shall test the functions of a web application.
After I loaded the web page, I click on a button to start p.e. a job application.
After the next page has loaded on the same site, my problem begins.
The entry controls are at the end of the web site.
To take a look and input data into the entry controls, I must scroll down.
The recording produced the following method in the UIMap.
Designer.cs:
public void Scrollen()
{
#region Variable Declarations
Playback.PlaybackSettings.WaitForReadyLevel = WaitForReadyLevel.AllThreads;
this.UIGoogleMozillaFirefoxWindow.UIItemPropertyPage.UIBewerbungDemoFirmaDocument.WaitForControlExist();
this.UIGoogleMozillaFirefoxWindow.UIItemPropertyPage.UIBewerbungDemoFirmaDocument.WaitForControlReady();
Playback.PlaybackSettings.WaitForReadyLevel = WaitForReadyLevel.UIThreadOnly;
WinControl uIBewerbungDemoFirmaDocument = this.UIGoogleMozillaFirefoxWindow.UIItemPropertyPage.UIBewerbungDemoFirmaDocument;
#endregion
// Click "Job application" document
Point pt = new Point(1390, 553);
int count = 0;
while (!uIBewerbungDemoFirmaDocument.TryGetClickablePoint(out pt) && count < 20)
{
count++;
System.Threading.Thread.Sleep(10);
if (count == 20)
Console.WriteLine("ClickablePoint not found");
}
Mouse.Click(uIBewerbungDemoFirmaDocument, new Point(1390, 553));
Mouse.MoveScrollWheel(10);
}
As You can see, I tried WaitForControlExist, WaitForControlReady, TryGetClickablePoint and the method MoveScrollWheel.
But neither Mouse.Click nor Mouse.MoveScrollWheel are working.
And in the next method, where I click into the first of the entry fields, I get a message at execution time, that the click event produces an error, because the control is hidden (because it's down below on the website, out of visible range).
After several tests this is making me crazy.
Any idea what has gone wrong and how can I scroll down the web site, so my entry controls are in visible range?
You can try Control.EnsureClickable(). Or you can use below mentioned function to scroll the page until the control is not clickable.
public static void ScrollAndClick(HtmlControl Control)
{
bool isClickable = false;
if (Control.TryFind())
{
while (!isClickable)
{
try
{
Control.EnsureClickable();
Mouse.Click(Control);
isClickable = true;
}
catch (FailedToPerformActionOnHiddenControlException)
{
Mouse.MoveScrollWheel(-1);
throw;
}
}
}
else
{
throw new AssertInconclusiveException("Control Not Found");
}
}
You can also add condition related to timeout to make sure it don't go to infinite loop.
Let me know if you are having issue with this at your end.
I am perplexed, and maybe I am just familiar with the properties of pageLoad, IsPostBack, or IsCallback. I created a Boolean variable called "first" and set it to True.
First time through PageLoad, there is a line of code if first = False, if so, write = true.
Then I have a Run_write routine attached to a button, when it runs, if the user response Yes, to the initial question, I make another group of radio buttons visible and set first to false. (i ran this in debug, and I know it hits this line of code) ... so the write to sql is ignored because write == false and the window reappears with the new set of Buttons... Great!
Furthermore, I go through the PageLoad routine again, and it hits the line if (!first), set write to TRUE. my issue is first has been re-set to true? What am I missing?
Note, I was able to work around this by utilizing whether the new set of buttons is checked, but I may not want to go this route, and I do want to understand what is going on.
code is below.
namespace MEAU.Web.Components.SupportCenter
{
public partial class feedback : System.Web.UI.Page
{
String login;
String myurl;
String response;
String s_call;
String p_ship;
String wrnty;
Boolean write;
Boolean first = true;
protected void Page_Load(object sender, EventArgs e)
{
login = Sitecore.Security.Accounts.User.Current.Profile.Email;
myurl = Request.QueryString["value"];
s_call = "No";
p_ship = "No";
wrnty = "No";
// Hide the question Buttons
scall.Visible = false;
parts.Visible = false;
wrnt.Visible = false;
lit.Visible = false;
write = false;
if (!first)
write = true;
}
protected void Run_Write(object sender, EventArgs e)
{
// Get Reponse
if (yes.Checked)
{
response = "Yes";
// Display the quesiton buttons, and Hide the NO button
scall.Visible = true;
parts.Visible = true;
wrnt.Visible = true;
lit.Visible = true;
no.Visible = false;
first = false;
// Did this Prevent a Service call?
if (scall.Checked)
{
s_call = "Yes";
write = true;
}
// Did this Prevent a parts shipment?
if (parts.Checked)
{
p_ship = "Yes";
write = true;
}
// Is this under warranty?
if (wrnt.Checked)
{
wrnty = "Yes";
write = true;
}
// write = true;
}
if (no.Checked)
{
response = "No";
write = true;
}
if (write == true)
{
SqlConnection conn = new SqlConnection(Sitecore.Configuration.Settings.GetConnectionString("feedback"));
SqlCommand cmd = new SqlCommand("Insert_fb", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#login", login);
cmd.Parameters.AddWithValue("#url", myurl);
cmd.Parameters.AddWithValue("#response", response);
cmd.Parameters.AddWithValue("#dateTime", DateTime.Now);
cmd.Parameters.AddWithValue("#serviceCall", s_call);
cmd.Parameters.AddWithValue("#partsShipment", p_ship);
cmd.Parameters.AddWithValue("#warranty", wrnty);
try
{
conn.Open();
cmd.ExecuteNonQuery();
Response.Write("<script type='text/javascript'>parent.$.fancybox.close();</script>");
Response.Write("<script type='text/javascript'>return false;</script>");
}
catch (Exception ex)
{
throw new Exception("Error on file update" + ex.Message);
}
finally
{
conn.Close();
}
}
}
}
}
Every HTTP request to your site creates a new instance of your page class.
Instance state is not preserved.
Instead, you need to store the state in session or ViewState, depending on what you want to apply to.
Page_Load will be called every time your server is requested for the page. This includes post-backs.
You can check IsPostBack to see if the current Page_Load execution is for the first display of the page, or a subsequent post-back.
protected void Page_Load(object sender, EventArgs e)
{
if (this.IsPostBack)
{
// Do first-time things
}
else
{
// Do non-first-time things
}
}
Note that the particular instance of your page object will not persist from access to access. So there may be some information that you need to initialize every time the page is called.
Every time you visit the page, you're creating a new instance of the class.
To distinguish a page load from a user clicking a button vs. a user arriving on the page for the first time, you want to check the IsPostBack property.
So rewrite your if along the lines of
// Code that always executes
if (IsPostBack)
{
// Code that only executes on initial page load
}
else
{
// Code that only executes when a postback event occurs
// e.g. A user clicks on a button.
}
There are some ways by which you can maintain the state or value of the controls, so I have been maintaining the Viewstate of the controls in the not is postback, as check of
(!IspostBack) // means when page loads first time
and whatever written in the else means when postback occurs in which you can maintain the viewstate of the objects.
Also we can use the session, if Viewstate is not used.
Rykiel,
the basic concept of the web is (state-less), and that why you have to handling, but anyway you can read about page life cycle I recommend you read it http://www.codeproject.com/Articles/20659/The-ASP-NET-Page-Lifecycle-A-Basic-Approach
you can use
if(!isPostBack)
{
first=true;
this portion of code only run when the page is requested first time
}else
{
first = false;
}
if you change the value of control in the page or click a button isPostBack will be true. but if you refresh the page or hit F5 you page will be requested again and isPostBack will be false;.
also you can use cookies or session variable (I also recommend do not load too much the Session Variable). try to read the prior link and you will be more clear where put your code to get the best performance.
J.S.
The answers on here are good, but technical. I will try to explain a little of what is happening.
When a browser requests your page, and on the server, a new instance of your class is created.
ASP.NET then runs the rest of the page, starting with your page_load. This will call all your other functions and then render the HTML as a response to your request and send it back to the browser. I like to explain this as a disconnected environment. Once the response is sent, everything is disposed of in a sense. Your variables, previous work, etc... are all gone. The Server as far as it is concerned, never expects to get anything from the browser again... its done its job. It took your request for a page, created a result and posted it back to the browser. Done.
So, think of your code as a new request each time it is called.
You can use the IsPostback as stated by ThatBlairGuy, because that will return true if you are responding to a postback from the browser, meaning that it has already served up this page to the browser on the previous postback.
I have a Button_click event. While refreshing the page the previous Postback event is triggering again. How do I identify the page refresh event to prevent the Postback action?
I tried the below code to solve it. Actually, I am adding a visual webpart in a SharePoint page. Adding webpart is a post back event so !postback is always false each time I'm adding the webpart to page, and I'm getting an error at the else loop because the object reference is null.
if (!IsPostBack){
ViewState["postids"] = System.Guid.NewGuid().ToString();
Cache["postid"] = ViewState["postids"].ToString();
}
else{
if (ViewState["postids"].ToString() != Cache["postid"].ToString()){
IsPageRefresh = true;
}
Cache["postid"] = System.Guid.NewGuid().ToString();
ViewState["postids"] = Cache["postid"].ToString();
}
How do I solve this problem?
using the viewstate worked a lot better for me as detailed here. Basically:
bool IsPageRefresh = false;
//this section of code checks if the page postback is due to genuine submit by user or by pressing "refresh"
if (!IsPostBack)
{
ViewState["ViewStateId"] = System.Guid.NewGuid().ToString();
Session["SessionId"] = ViewState["ViewStateId"].ToString();
}
else
{
if (ViewState["ViewStateId"].ToString() != Session["SessionId"].ToString())
{
IsPageRefresh = true;
}
Session["SessionId"] = System.Guid.NewGuid().ToString();
ViewState["ViewStateId"] = Session["SessionId"].ToString();
}
This article could be of help to you
http://www.codeproject.com/Articles/68371/Detecting-Refresh-or-Postback-in-ASP-NET
you are adding a Guid to your view state to uniquely identify each page. This mechanism works fine when you are in the Page class itself. If you need to identify requests before you reach the page handler, you need to use a different mechanism (since view state is not yet restored).
The Page.LoadComplete event is a reasonable place to check if a Guid is associated with the page, and if not, create one.
check this
http://shawpnendu.blogspot.in/2009/12/how-to-detect-page-refresh-using-aspnet.html
This worked fine for me..
bool isPageRefreshed = false;
protected void Page_Load(object sender, EventArgs args)
{
if (!IsPostBack)
{
ViewState["ViewStateId"] = System.Guid.NewGuid().ToString();
Session["SessionId"] = ViewState["ViewStateId"].ToString();
}
else
{
if (ViewState["ViewStateId"].ToString() != Session["SessionId"].ToString())
{
isPageRefreshed = true;
}
Session["SessionId"] = System.Guid.NewGuid().ToString();
ViewState["ViewStateId"] = Session["SessionId"].ToString();
}
}
Simple Solution
Thought I'd post this simple 3 line solution in case it helps someone. On post the session and viewstate IsPageRefresh values will be equal, but they become out of sync on a page refresh. And that triggers a redirect which resets the page. You'll need to modify the redirect slightly if you want to keep query string parameters.
protected void Page_Load(object sender, EventArgs e)
{
var id = "IsPageRefresh";
if (IsPostBack && (Guid)ViewState[id] != (Guid)Session[id]) Response.Redirect(HttpContext.Current.Request.Url.AbsolutePath);
Session[id] = ViewState[id] = Guid.NewGuid();
// do something
}
If you want to detect a refresh on an HTTP GET rather than only POSTs, here's a hacky work-around that, in modern browsers, mostly works.
Javascript:
window.onload = function () {
// regex for finding "loaded" query string parameter
var qsRegex = /^(\?|.+&)loaded=\d/ig;
if (!qsRegex.test(location.search)) {
var loc = window.location.href + (window.location.search.length ? '&' : '?') + 'loaded=1';
window.history.replaceState(null, document.title, loc);
}
};
C#:
public bool IsPageRefresh
{
get
{
return !string.IsNullOrEmpty(Request.QueryString["loaded"]);
}
}
When the page loads, it will change add a QueryString parameter of loaded=1 without reloading the page (again, this--window.history.replaceState--only works in post-archaic browsers). Then, when the user refreshes the page, the server can check for the presence of the loaded parameter of the query string.
Caveat: mostly works
The case where this doesn't work is when the user clicks the Address Bar and presses enter. That is, the server will produce a false-positive, detecting a refresh, when odds are, the user actually meant to reload the page fresh.
Depending on your purposes, maybe this is desirable, but as a user, it would drive me crazy if I expected it to reset the page.
I haven't put too much thought into it, but it might be possible to write some magic in order to distinguish a refresh from a reset via the address bar using any/all of:
SessionState (assuming SessionState is enabled) and the value of the loaded QueryString parameter
the window.onbeforeunload event listener
keyboard events (detecting F5 and Ctrl + R to quickly change the URL back to removing the loaded QueryString parameter--though this would have a false-negative for clicking the browser's refresh button)
cookies
If someone does come up with a solution, I'd love to hear it.
Another way to check page refresh. I have written custom code without java script or any client side.
Not sure, it's the best way but I feel good work around.
protected void Page_Load(object sender, EventArgs e)
{
if ((Boolean)Session["CheckRefresh"] is true)
{
Session["CheckRefresh"] = null;
Response.Write("Page was refreshed");
}
else
{ }
}
protected void Page_PreInit(object sender, EventArgs e)
{
Session["CheckRefresh"] = Session["CheckRefresh"] is null ? false : true;
}
I have a button click event handler with a switch case in it that controls multiple buttons in one event handler.
I need to use a queue because while one button is clicked and doing some processing, second button click won't interfere with the first button click, but added to the queue. I don't want to use .enabled=false; because it'll discard the second click completely, and I'm currently editing someone's software at work so I don't want to break things that I don't know, so what are you suggesting?
The best idea, I think, is to create a producer/consumer queue.
Another question is explaining this technique.
Basically, the idea is to have a worker thread that will consume a queue to get the job to do, while other thread produce job by queuing operation in the queue.
I did succeed this with System.Collections.Queue
The code is :
private Queue<Button> Button_Queue = new Queue<Button>();
private bool isProcessing = false;
private void Button_Click((object sender, EventArgs e){
if(isProcessing){
Button_Queue.Enqueue(this);
}
else
{
isProcessing = true;
// code here
isProcessing = false;
while(Button_Queue.Count > 0){
Button_Queue.Dequeue().PerformClick();
}
}
of course mine is slightly different from this because I need to pass some variables and my click method is modified for this.
Dirty, but simple solution.
public partial class DataRefresh : Form //DataRefresh is just "some form"
{
...
...
public DateTime ClickTime; //Time when click is processed by system
public DateTime LastExecutionRunTime = DateTime.MinValue; //Time when the all the click code finish
private void buttonDataRefresh_Click(object sender, EventArgs e)
{
ClickTime = DateTime.Now;
if (ClickTime.Subtract(LastExecutionRunTime).TotalSeconds < 5 )
{
//It will keep returning - hopefully until all events in que are satisfied
return;
}
//Long running code
//Importing whole table from remote DB
...
...
//End of the Long running code
LastExecutionRunTime = DateTime.Now;
}
}
I am working on a wpf .My requirement is to change selection of tab according to user confirmation it means every time when user changes tab a message box opens and confirm with user whether he wants to change the tab or not.
But problem with me is when I press no first time it works fine .but after that on second time it asks two times for user confirmation
can anyone help me to solve this ?
private void tabcontrol_SelectionChanged(object sender,SelectionChangedEventArgs e)
{
try
{
if (handleSelection && e.OriginalSource == tbUserProfileMainControl)
{
//Ask user for change
if (isUserAllowedToChanged)
{
int currentIndex = (tabcontrol.SelectedIndex);
GeneralDeclaration.currentSelectedTabIndex = currentIndex;
LoadUserControl(GeneralDeclaration.currentSelectedTabIndex);
}
else
{
//e.Handled = true;
handleSelection = false;
tbUserProfileMainControl.SelectedIndex = Math.Abs(tbUserProfileMainControl.SelectedIndex - 1);
}
}
handleSelection = true;
}
catch (Exception ex)
{
//
}
}
It sounds like you're adding handlers during the click event itself. This causes your subsequent click to perform the action one more time (3rd click 3 times, 4th click 4 times, etc).
Check how you bind the event to the handler and check where you are defining the handler itself. You're doing something twice that should only be done once.
This is my estimation based on your findings, without code, I'm just taking a wild stab in the dark.