I have an asp:Button that fires a code behind function on the OnClick event. In that OnClick event several things happen, and among those things I do a check in the database for if I need to ask the user a yes or no question. For that I need a message box. First I did it like this:
protected void MyButton_Onclick(object sender, EventArgs e)
{
// lots of stuff happening
bool iNeedToAskTheUser = INeedToAskTheUser(stuff);
if (iNeedToAskTheUser)
{
DialogResult result = MessageBox.Show("Do you want to fix all objects?", "Fix objects", MessageBoxButtons.YesNo);
if (result == DialogResult.Yes) // do stuff
}
// some other stuff
}
This works fine locally but not when deployed, so I figure I would need to use ScriptManager.RegisterStartupScript instead. I could just add javascript on the ASPX page that fires up a dialog and saves the response in a hidden control that I can then look at, but I don't want to fire up the dialog unless I have to, which I check for before I do the DialogResult in the code above. So I can't do that immediately when the user clicks the button.
Is there any way I can use ScriptManager.RegisterStartupScript in "the middle" of my _OnClick code so that I can choose whether or not to actually show the button, and then also know if the user clicked yes or no, (preferably) without doing a postback?
I've been thinking and testing two different solutions:
Use ScriptManager.RegisterStartupScript in code behind to fire a JavaScript confirm function on the ASPX page. The JavaScript function would set a value in a hidden control depending on if the user answered yes or no and then my code behind stuff would check the value of that hidden field and act upon that. The problem with that is that once ScriptManager.RegisterStartupScript fires it doesn't wait for the JavaScript function to "finish", ie wait for the user to reply to the confirm(). So the value in the hidden control will always be empty because the code behind gets to the check of that control before the user has a chance to respond to the confirm(). So that's a no go.
Use ScriptManager.RegisterStartupScript in code behind to open up a new ASPX page that asks the user the question and then does all the work in response to the user's answer in that page. The problem then is to pass the object that the new ASPX page needs to do work on in response to the user's response.
I'm sure there are great solutions using Ajax or jQuery but this is a fairly simple function that shouldn't take too long to develop, so that is kind of out of scope for this.
Instead I'll go with a solution where I know what the user will respond to the question before they click the button. (While silently muttering under my breath: "It's 2019 and there's no good way to fire up a yes/no dialog from code behind in a .Net web project...". I need to get back to not working with web).
Related
(Note: Just to clarify the problem is not specifically related to C1WebDateEdit. It could be with any custom control which require JavaScript to render actual control)
I have many C1WebDateEdit controls in my page. On a button click based on some condition I am displaying JavaScript alert message ScriptManager.RegisterStartupScript. These controls are inside UpdatePanel. The issue I am facing with it is, when these C1WebDateEdit has not value and page displays alert message, it displays "01/01/0001" behind the alert box and on closing alert it shows empty textboxes.
The reason is, C1WebDateEdit creates actual control using JavaScript. and page renders alert message JavaScript before C1WebDateEdit controls' JavaScript.
For example:
HTML markup
alert JavaScript
C1WebDateEdit JavaScript
Logical solution is get alert JavaScript after C1WebDateEdit JavaScript because it will allow C1WebDateEdit to load fully before alert box.
I found no inbuilt way in asp.net to change sequence, so I tries solution given here but It didn't work for me.
One possible solution I am thinking that I create Custom or WebUserControl and place at it last at the page in the UpdatePanel and PreRender event I call ScriptManager.RegisterStartupScript to register alert message. Logically I think it will work but trying to find that any one implemented any other solution for it?.
Other possible solution is use "pageLoaded" event of PageRequestManager. I created below function for temporary fix:
function delayedAlertBox(strMsg)
{
var fnc = function (a, b)
{
//remove reference so on next call it do not show previous alerts.
Sys.WebForms.PageRequestManager.getInstance().remove_pageLoaded(fnc);
alert(strMsg);
}
//add function reference to call on Ajax pageloaded event
Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(fnc);
}
and calling in asp.net like simple function as given below:
ScriptManager.RegisterStartupScript(Page, Page.GetType(), "dd", "delayedAlertBox('Hi, how are you?')", True)
But still looking for any other good alternative. If I found other, I will post it.
This is not a cross-site attack because it happens on the same website.
Before we render to the browser, we figure out in server-side whether to render a button or not based on whether the user has sufficient credit in their account (example case). So, if they have insufficient credit, the check out button doesn't even make it to the page on page load.
Here's what they did:
Go to a purchase product page when they have sufficient credit. The check out button shows.
They look at Inspector (FireFox) or any other in-browser developer tool and copy the html input element that submits the form.
They purchase as normal. Now, they have insufficient credit.
They go to another purchase product page, and of course, the check out button will no longer show (because it didn't even make it on page load in the first place).
They open up their in-browser developer tool and paste the input element copied from the other previous page when they had sufficient credit. The button shows up on the rendered page. They click it, then they proceed as if they had sufficient credit.
The problem is, the submit button's event handler in code behind is unaware of the existence or non-existence of that submit button, and will execute if called, and that we give it a hard-coded id.
The obvious solution would be to do a credit vs. price check [again] on the click event handler. From inside the event handler, is there a way to determine whether the control existed on page load? I figure that the sender parameter would not be null if they pasted a control in-browser, so there's not much help there.
Any solutions on this?
The only safe solution to this is to check if the user has sufficient credits ON THE SERVER after the postback occurs.
protected void OnSubmit(object sender, eventargs e)
{
if (product.Price > User.Credits) {
throw new Exception();
}
purchase();
}
If you use the check the button approach then they can still use the JavaScript console to call __doPostBack
Never rely on the client side for authorization
You could store in ViewState whether the button was rendered or not; this is encrypted and cannot be changed on the client. If you set it as ViewState["ButtonRendered"] = true;, then you can check this to see if it's true or false, and act accordingly.
Because of the nature of the user opening up multiple browsers, and other tricks, I would 100% recommend you do another database query to make sure they have sufficient credit, and if not, display an error to the user. That would be the absolute best way of handling it. What would keep them from opening up firefox and chrome, and trying to attempt to simultaneously purchase two different items?
I know that ASP.NET controls such as the button have the postback event model. And checking whether the page.IsValid is dependent on events and postback in order for the validation to kick in.
But what if I have a button using regular HTML inside my .aspx (and I don't want to use the asp.net button...please do not ask me why) yet still want to take advantage of calling Page.IsValid?
For example, lets say my .aspx page has 2 buttons:
<asp:ImageButton runat="server" ID="cmdPlaceOrder" OnClick="cmdPlaceOrder_Click" ImageUrl="images/someButton.gif" />
and that was there in the page, someone else had created that a while back. In the cmdPlaceOrder we check for Page.IsValid:
protected void cmdPlaceOrder_Click(object sender, EventArgs args)
{
if (!IsValid)
return;
... rest of logic
}
That's standard. Now what if I add a non-ASP.NET button like this in the .aspx page, used to place the order (but for a different kind of order seperate from the existing place order button above):
<img src="images/buttonPayGoogleCheckout.gif" alt="Pay with Google"/>
So on click of the hyperlink, the page posts back to itself (same url). I check the url for a querystring param flag that if set calls the method below that I created which is basically a similar method as the above but with a bit of different logic in it:
protected void PlaceGoogleCheckoutOrder()
{
if (!IsValid)
return;
... rest of logic here, but I can't get to it because there is no event model to allow IsValid to work
}
Obviously I'm not tying in an event model to this therefore once it hits the check for Page.IsValid it errors with the following message at runtime:
Page.IsValid cannot be called before validation has taken place. It should be queried in the event handler for a control that has CausesValidation=True and initiated the postback, or after a call to Page.Validate.
But I still want to be able to call this validation, the validation that's already been setup in our .aspx. I don't want to reinvent the wheel on this and I don't in this case want to use an ASP.NET based button (do not ask me why, I have my reasons and it's too long to get into that).
I want to know how I can still get that Page.IsValid check to work for a non-event driven button using . I'm not sure how to hook up an event to do so that still hooks in after the redirect and allows that code to still validate.
I tried adding Page.Validate(); inside my PlaceGoogleCheckoutOrder() method right before the check for Page.IsValid but I still get the same error.
After looking at MSDN on Page.Validate() (http://msdn.microsoft.com/en-us/library/0ke7bxeh.aspx) and it states "The validation group is determined by the control that posted the page to the server. If no validation group is specified, then no validation group is used."
So that's why nothing happened. So I'm not sure how to get ASP.NET in this case to know about the control (in this case my ) that posted the page to the server so that a validation group IS used. I guess I could add a runat="server" to my ...but doubt that's all I need to do here.
The trick with your normal button is that it never does a post back because your href attribute has some other url there. If you want to check if the page is valid, you have to post back to your page class first to make that check and then redirect from there.
What you can do to make this happen with a normal anchor (<a >) tag is process that anchor's onclick event in javascript, do any client side work you want, and then call the __doPostBack() javascript function.
You can see an example of how to call __doPostBack() on msdn here:
http://msdn.microsoft.com/en-us/library/aa720099(VS.71).aspx
It's from .Net 1.1, but still accurate.
I have a User control (because I use the same in other page, so I thought I should reuse code and not double my work), but in this page I show a list of companies and each one has a company number, I need to pass this company number to that User Control and it has to reload using that passed company number.
How can I accomplish this?
what I have so far:
alt text http://www.balexandre.com/temp/2009-09-17_0917.png
the Show company structure link is made of
<a href="javascript:showStruct('112:201334607','5564967221');"
class="showStructLink">Show company structure</a>
the showStruct method is written like
function showStruct(pid, cnr) {
if (_showStrut == 0)
return;
// fancy stuff to be more apealing visually
$("#tdSearch").removeClass("tabTitleUp01").addClass("tabTitleDownUp01");
$("#tdStruct").removeClass("tabTitleDownUp02").addClass("tabTitleUp02");
$("#srtr1").hide();
$("#srtr2").hide();
$("#sttr1").show();
// enable Search Results tab to be clicked in order to get back
$("#tdSearch")
.addClass("pointer")
.bind("click", function() { hideStructure(); });
// pass the company number and reload wcCompanyStruture web user control
// __doPostBack('RefreshWebUserControl', cnr);
}
I can make a simple aspx page with the control inside and from jQuery invoke $.get() to run and populate the control correctly, but I really want to learn how to do this properly, using the ASP.NET AJAX Method to send a number and call RefreshData on it
using code-behind it is easy to refresh the user control, just invoking
wcCompanyStruture.RefreshData("companyNumberHere");
what do I need to do in my User Control side and well in the showStruct method to create this behavior?
All help is appreciated, Thank you.
I know this is not the answer to you question but I think you may be asking the wrong question.
It looks to me as if you have a search result+details view scenario that you are going about the wrong way.
When you click "Show Company structure" you want to see the details on the second tab right? If this is the case then the tab approach would be confusing to the user, it would be better with a modal popup that shows the details. No postback just AJAX load a page with the details into a modal popup window.
This is very easy with JQuery using the dialog widget in JQueryUI and the AJAX load function $('#SomeDiv').load('details.aspx?id='+companyid);
http://docs.jquery.com/Ajax/load#urldatacallback
It would give a much better user experience and it is surprisingly simple to code.
I hope this helps.
You can use a LinkButton for each "Show Company Structure" link, and set the CommandArgument property with the corresponding company id. The LinkButton will cause a postback.
A second solution would be to use a hidden variable : <input type="hidden" id="hiddenCompanyNumber"> and set it's value in the showStruct method. You can then call __doPostBack(), for which you need a control upon which to postback I think.
All in all, I think the first solution is less hacky.
You can find it here
http://codeclimber.net.nz/archive/2007/06/26/how-to-refresh-an-updatepanel-from-javascript.aspx
don't worry about the article title it has what you need Just do the four steps and you are ready to go.
How do you call a Javascript function from an ASPX control event?
Specifically, I want to call the function from the SelectedIndexChanged event of a DropDownList.
I get a little nervous whenever I see this kind of question, because nine times out of ten it means the asker doesn't really understand what's going on.
When your SelectedIndexChanged event fires on the server, it fires as part of a full postback. That means that for that code to run, the entire rest of your page's load code also had to run.
More than that, the code runs as the result of a new http request from the browser. As far as the browser is concerned, an entirely new page is coming back in the result. The old page, and the old DOM, are discarded. So at the time your SelectedIndexChanged event code is running, the javascript function you want to call doesn't even exist in the browser.
So what to do instead? You have a few options:
Change the page so the control doesn't post back to the server at all. Detect the change entirely in javascript at the client. This is my preferred option because it avoids odd onload scripts in the browser page and it saves work for your server. The down side is that it makes your page dependent on javascript, but that's not really a big deal because if javascript is disabled this was doomed from the beginning.
Set your desired javascript to run onload in the SelectedIndexChanged event using the ClientScript.SetStartupScript().
Apply the expected results of your javascript to the server-model of the page. This has the advantage of working even when javascript is turned off (accessibility), but at the cost of doing much more work on the server, spending more time reasoning about the logical state of your page, and possibly needing to duplicate client-side and server-side logic. Also, this event depends on javascript anyway: if javascript is disabled it won't fire.
Some combination of the first and third options are also possible, such that it uses javascript locally if available, but posts back to the server if not. Personally I'd like to see better, more intuitive, support for that built into ASP.Net. But again: I'm talking about the general case. This specific event requires javascript to work at all.
As Muerte said you have to just put the javascript, or a call to it on the page from the code behind. Personally I use this:
ClientScript.RegisterClientScriptBlock("customscript", "<script>simple script here</script>")
Of you can call the function if you already have a more complex one on the page instead of the stuff I have.
You can't do it directly from an event, because ASPX control event is server side.
What you can do is emit a Javascript in the ASPX event which will call the JavaScript function when the page reloads.
For example, if in your ASPX page you have a Javascript function called "DoSomething()", in you ASPX control event, add the following:
protected void btnSubmit_Click(object sender, EventArgs e)
{
Page.ClientScript.RegisterStartupScript(this.GetType(), "myEvent", "DoSomething()", true);
}
The last boolean parameter defines that tags are added automatically.
In the code behind, attach some markup to the server side control via its attributes collection. This assumes that the function is already in a client script file that is already available to the page.
MyServerDDLControl.Attributes.Add("SelectedIndexChanged", "MyClientSideFunction();");