Prevent duplicate postback in ASP.Net (C#) - c#

Simple one here... is there a clean way of preventing a user from double-clicking a button in a web form and thus causing duplicate events to fire?
If I had a comment form for example and the user types in "this is my comment" and clicks submit, the comment is shown below... however if they double-click, triple-click or just go nuts on the keyboard they can cause multiple versions to be posted.
Client-side I could quite easily disable the button onclick - but I prefer server-side solutions to things like this :)
Is there a postback timeout per viewstate that can be set for example?
Thanks

I dont think that you should be loading the server for trivial tasks like these. You could try some thing jquery UI blocking solution like this one. Microsoft Ajax toolkit should also have some control which does the same. I had used it a long time ago, cant seem to recall the control name though.

With jQuery you can make use of the one event.
Another interesting read is this: Build Your ASP.NET Pages on a Richer Bedrock.

Set a session variable when the user enters the page like Session["FormXYZSubmitted"]=false.
When the form is submitted check that variable like
if((bool) Session["FormXYZSubmitted"] == false) {
// save to db
Session["FormXYZSubmitted"] = true;
}

Client side can be tricky if you are using Asp.Net validation.
If you have a master page, put this in the master page:
void IterateThroughControls(Control parent)
{
foreach (Control SelectedButton in parent.Controls)
{
if (SelectedButton is Button)
{
((Button)SelectedButton).Attributes.Add("onclick", " this.disabled = true; " + Page.ClientScript.GetPostBackEventReference(((Button)SelectedButton), null) + ";");
}
if (SelectedButton.Controls.Count > 0)
{
IterateThroughControls(SelectedButton);
}
}
}
Then add this to the master page Page_Load:
IterateThroughControls(this);

I have had the same scenario. The solution of one of my coworkers was to implement a kind of Timer in Javascript, to avoid considering the second click as a click.
Hope that helps,

Disable the button on click, utilize jquery or microsoft ajax toolkit.

Depending on how important this is to you, could create an array of one time GUID's which you remove from the array once the update has been processed (ie posted back in viewstate/hidden field)
If the guid is not in the array on postback, the request is invalid.
Substitute database table for array in a clustered environment.

Related

Prevent resending form without page.redirect

maybe some of you know the problem, that a form gets resubmitted if pressing refreshing the browser by pressing F5.
I am looking for a way to prevent this, and all I have found is the solution to do a redirect on the same page after submit
Response.Redirect(Request.RawUrl);
This DOES work, but my problem is, that I need to inject javascript into a placeholder after postback, and so this information is lost because of the Redirect.
PlaceHolder p1 = this.NamingContainer.Parent.FindControl("phPostScript") as PlaceHolder;
GenerateJsTag(script,p1);
Response.Redirect(Request.RawUrl);
Is there a way I can prevent a resubmit without Redirect?
Thanks
Is there a way I can prevent a resubmit without Redirect?
Even if there is , you should stick to that approach which is redirect after submit.
Regarding JS after post back , I believe that you want to see those JS output even after refresh after redirect.
What you can do is to redirect to Request.RawUrl+"#someValue" and then in the pageLoad (not within IsPostBack) you can inject again those scripts , knowing that you already showed that to the user via that hashkey ( could be cookie also , or query string , doesn't really matter) - or to show a message like ("Message was already accepted"...) or something like that.
Try adding following JavaScript code to your aspx page.
Also, you should not be trying to disable F5 since there are many users out there who may need to use F5 during browsing with your website.
//bind keydown event to method that disables F5
$(document).on("keydown", PreventRefresh);
function PreventRefresh(e) {
var code = e.which || e.keyCode;
if (code === 116) {
e.preventDefault();
}
}

Issues with Custom Server Controls and Events in the ASP.NET page lifecycle

I am starting to go nuts with this one, it "seems" I can't get this to work in an elegant way and I'm sure i should be able to.
On the righthand side of the page I dynamically create Custom Server controls that have events in them that fire, I have proved this bit works in testing.
I have a Treeview on the left side of the page and this is autopopulated at page load assuming it is not a postback, if it is then its viewstate handles this nicely. When you click on one of the branches of the tree it will post back and when the event fires it will then populate the right side with new custom server controls that hold the inforation relevant to what branch you clicked. The right panel always populates with the correct controls, however the events don't fire when they are clicked as the new branch Id is only know after the page_load event (when the treeview event fires) which is too late for all the Event cleverness to register I think.
I have looked into using Response.Redirect, but then I lose the viewstate on the Treeview so have started looking at forcing a Javascript post back but haven't got very far with that.
I really can't believe this is an unusual problem so hopeing there is an elegant solution. If its just i am going about it the total wrong way then happy to change all the logic if we can get this to work!!
Many many Thanks!!
Matt
*UPDATE**
I'm not allowed to close this yet so will paste below
seems typing that out got me thinking about the Javascript route and what .NET must have some where. A bit of reading and I have come up with this.
PostBackOptions pbo = new PostBackOptions(pnlEvents); pbo.ActionUrl = Request.Url.AbsoluteUri; pbo.PerformValidation = false; string postback = ClientScript.GetPostBackEventReference(pbo); ClientScript.RegisterStartupScript(typeof(Page), "AutoPostBackScript", postback, true);
This will cause another postback if you run this after the event fires that you get your information from, allowing you to load the Server Controls forn Init or Load as long as you store your value in viewstate or its in another controls viewstate.
Events can be registered in Page_Init or Page_Load. If you are still lost, check ASP.NET Page Life Cycle Overview on MSDN.
Update:
You can simply send the param in URL, and get it in Page_Load, not using postback mechanism at all.

Submit value of a TextBox without reloading page

I'm working on an ASP.NET based TicTacToe game. The problem I have with it is that:
The game is played between two users. When the first one types 'x' in the TextBox I want the 'x' to be shown on the second player's computer without reloading the page.
I don't know if some code will help but here is the way I did it without reloading(the user must reload the page manually... dumb):
protected void TopLeft_TextChanged(object sender, EventArgs e)
{
Application.Lock();
GameBoard gameBoard = new GameBoard();
gameBoard.board[0, 0] = char.Parse(this.TopLeft.Text);
Application["TopLeft"] = gameBoard.board[0, 0];
Application.UnLock();
}
And then, on page pre render:
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
Application.Lock();
if(Application["TopLeft"] != "0")
{
this.TopLeft.Text = Application["TopLeft"].ToString();
}
...
And so on...
I'd be very thankfull to anyone who can help!
You will need to use AJAX to do this. I recommend looking at some of the AJAX capabilities that jQuery offers but you can also look at the AJAX Toolkit from Microsoft.
Here is documentation for AJAX in jQuery:
http://api.jquery.com/jQuery.ajax/
I feel this is much "lighter" than what Microsoft offers out of the box. You can find out more about the Microsoft AJAX toolkit here:
http://www.asp.net/ajax/ajaxcontroltoolkit/samples/
You are asking about Partial Page Update.
First, you need to place the client TextBox or what ever other controls that you need to reload inside an UpdatePanel.
Then, you need to call the UpdatePanel.Update to update those controls whenever you need.
Check out AJAX. This will require client scripting to submit and detect updates without submitting or updating the entire page.
Note, however, that this is a fairly advanced topic and will not simply be a little snippet of code you can add. I would recommend a good AJAX/JavaScript/jQuery book.

Disable selection box without removing value from post

In my current asp.net-mvc project one of my pages allows the user to select a value in a dropdown box after wich a post request is made that updates several values.
To make sure the delay from the postback doesn't confuse the user into selecting another value (and thus creating another post, creating another delay etc) I set the select's disabled attribute to true.
Disabled inputs aren't submitted to the post call however.
How can I make it visually clear to the user that work is in progress and make it imposible to select a new value without removing the input from the post?
Yes, this annoys me too.
Basically what you need to do is hide the old button and replace it with a disabled one so it looks the same to the user. That way it's still submitted but can't be doubly submitted.
Actually I've found what seems to be a duplicate of this at Problem with disabling submit buttons on form submit.
From your answer, I gather you are already using jQuery. In that case why don't you get the value of the select box, disable it, then post the value yourself?
Bonus : BlockUI is a nice jQuery plugin to, well, block the UI.
None of the answers I found in Cletus' post was entirely what I was looking for.
Here is what I came up with. It's not 100% reusable, but it does what I need and feel free to improve/edit.
$('#productMixSelectorForm').change(function() { $(this).ChangeSelection() });
jQuery.fn.ChangeSelection = function() {
var html = $('<div class="hidden">');
$(this).find('select, input').each(function() {
if ($(this).hasClass('hidden') == false) {
//Clone the original one into the hidden div
html.append($(this).clone());
//Disable the original (visible) one and make it's name unique again
$(this).attr("disabled", true);
var name = $(this).attr("name");
$(this).attr("name", name + "disabledDummy");
}
});
//Add the collection of clones to the form so they get submitted
$(this).append(html);
$(this).submit();
}

user control event handler lost on postback

I have a menu usercontrol called LeftMenu that has a bulletedlist of linkitems. It's on the ascx page as such:
<asp:BulletedList ID="PublisherList" DisplayMode="LinkButton" OnClick="PublisherList_Click" cssClass="Menu" runat="server"></asp:BulletedList>
I databind the list in the page_load under if(!isPostBack)
I'm having an issue on a page that loads the control. When the page first loads, the event handler fires. However, when the page posts back it no longer fires and in IE8, when I'm debugging, I get "Microsoft JScript runtime error: Object expected" in Visual Studio pointing at "__doPostBack('LeftMenu$PublisherList','0')." In FF I don't get the error, but nothing happens. I'm not loading the control dynamically, it's loaded on the aspx page using:
<%# Register TagPrefix="Standards" TagName="LeftMenu" Src="LeftMenu.ascx" %>
<Standards:LeftMenu ID="LeftMenu" runat="server"/>
Any ideas of where I'm losing the event handler?
I just realized this is happening on another user control I have as well. A text box and a button and I'm using the default button to make sure pressing the enter key uses that button. .Net converts that in the html to:
<div id="SearchBarInclude_SearchBar" onkeypress="javascript:return WebForm_FireDefaultButton(event, 'SearchBarInclude_QuickSearchButton')">
so as soon as i enter a key in the box I get a javascript error at the line saying "object expected." It seems like the two issues are related.
Edit Again: I think I need to clarify. It's not that I'm clicking on the menu item and it can't find the selected item on postback. I have this search page with the left navigation on it and then the main content of the page is something that causes a postback. Everything is fine with this postback. Once that page has been posted back, now if I click on the bulleted list in the left navigation I get a javascript error and it fails. The page_init for the LeftMenu control is never called.
It sounds like you might be losing the click because you are not DataBinding the list on PostBack. Therefore, the post back is trying to refer to a control (a specific bulleted list item) that does not exist.
You should try binding the list again on PostBack just to see if that fixes your issue. BUT, what should REALLY happen is that the LeftMenu and the BulletedList should store their information into ViewState so that you can ensure that the data that was shown to the user on their initial page load is the same data that the PostBack is processing and working with.
If you have EnableViewState=true for your UserControl and all controls within it, everything should work fine. With ViewState enabled, ASP will reinflate your controls from ViewState after Init has fired. This means that the postback event arg (which points to an index in your control list) will still find the control in that list position. Otherwise the list is empty on postback.
However, ViewState is the work of the devil and was designed simply to foster the illusion that you are working in a stateful environment. It is okay to use it for small amounts of data but typically not advisable for templated controls like repeaters and lists because you have no idea how much data is going to be created in ViewState.
If you are dealing with static, or relatively static data, store it in the application cache and rebind your lists in Page.Init every time (note that it has to be in Init because post-init is when ASP rebinds from ViewState; if you get in there first, your data will be used instead).
If you are dealing with volatile data, you have a problem because the data you rebind must be exactly the same as the original page request, otherwise the postback events will be firing against the wrong rows. In that case you need to either store your initial data in Session or you simply store the list of rows ids (in a hidden variable or Session) and you recreate the data to bind against from the ids each time.
An even better solution is to not use postback events at all. Try to turn all your events into GETs that have an ID on the query string. You can still create the list using binding the first time through the page (as you are currently doing), and you can even GET the same page with a new ID.
If you need to keep state on the same page but need to respond to the user changing a radio button selection (or something else), think about using Ajax calls to update the screen. You also do that with an ID that you pass to the Ajax call.
In general, the more you move from using stateful ASP, the lighter and more responsive your pages will become. You will also be in a better position to move to stateless MVC if necessary. You will also save lots of time lost to debugging obscure problems because ViewState is not available when you need it to be.
The best analysis of ViewState I've read is in the link below. If you fully understand how it works, you can continue to use it without necessarily incurring the costs.
http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/truly-understanding-viewstate.aspx
It's possible that this might be javascript related, and that a script that is loading earlier in the page is throwing an error and causing the page to not be loaded properly.
Are your usercontrols loading any javascript onto the page? Can you check for javascript errors on the initial load of the page?
I moved the code into an existing project we have and for some strange reason, I stopped getting the javascript errors and instead got:
"Invalid postback or callback argument. Event validation is enabled using <pages enableEventValidation="true"/> in configuration or <%# Page EnableEventValidation="true" %> in a page.
For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them. If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation."
I haven't quite figured out where I'm supposed to put the register event validation with a user control, but in the mean time I just set enableeventvalidation=false and it seems to work now.
It looks like the doPostBack function is missing since its arguments are literals so they couldn't be the cause. Is that one of your own functions or did you mean to call the ASP __doPostBack function?
Have a look at the Firefox error console or allow script debugging in IE and see exactly what object can't be found. Even better, download Firebug and debug it.
I had a similar issue. It turned out that Akamai was modifying the user-agent string because an setting was being applied that was not needed.
This meant that some .NET controls did not render __doPostBack code properly. This issue has been blogged here.

Categories