Losing backward navigation in ASP.NET - asp.net

I have an ASP.NET2.0 web page with a Submit button.
When the user clicks, I generate an XML file on the fly and return that as a result.
Here is the code:
protected void submitBtn_Click(object sender, EventArgs e)
{
string result = this.ProduceMyXmlResult();
this.Response.Clear();
this.Response.StatusCode = 200;
this.Response.ContentType = "application/xml";
this.Response.ContentEncoding = System.Text.Encoding.UTF8;
this.Response.Write(result);
this.Response.End();
}
The piece of code does exactly what I want. However, the browser does not recognize the XML file as a new page, so the BACK button does not take me back to my original page. Why and how can I overcome that?

The simplest way to do so, I think, would be to create a separate page that executes this code on Page_Load(), and redirect to it when the button is pressed.
The reason you have no backward navigation is because the browser is unaware the page has changed. Since the Submit button is preforming a postback, and you are returning XML data as the response to that postback, it appears to the browser as though this is just some transformation of the current page (just as though you'd, say, changed the text of a Label control).
The "correct" way to accomplish this would be with some type of HTTP handler, but I haven't the experience to suggest the proper way to do so and you already have working C# code-behind for this method.

Related

Approach to targetting HTTP Handler from Button Click

Main purpose here is to target the handler to download a file that is being previewed.
There are two different conditions of the file, one where it is already saved in the database, then the following parameters to the handler through a query string
COIHandler.axd?Action=preview_saved&letterId=xxxx
or
one that hasn't been saved yet, in which i store it in the session and target the handler in the following way
COIHandler.axd?Action=preview_unsaved
then the handler will handle clearing the session after I'm done.
Any thoughts on how to make this occur. Still new to http handlers.
CLARIFICATION
I have a OnClientClick property in my sub classed version of the Button class which allows me to append javascript to it that will be executed client-side. That is where I want the targeting to occur.
I want the user stay on the same page when they click the download button as well.
I dont want to simply do a redirect in javascript that will target the handler and I would like to avoid a pop-up window if possible. Not really sure if that leaves any options
In Response to Yads comment, you can use a button click handler on your page to download the file without a need for a popup or separate HTTP Handler.
protected void btnDownload_OnClick(object sender, EventArgs args)
{
PDFContentData data = GeneratePDFData(); //parses strings from some source (as mentioned in comments)
WebFileUtil.InvokePDFDownload(Page.Context, data);
}
public static class WebFileUtil
{
public static void InvokePDFDownload(HttpContext context, PDFContentData data)
{
context.Response.Clear();
context.Response.ClearContent();
context.Response.ClearHeaders();
FileStore.generateDocument(data, context.Response.OutputStream);
context.Response.ContentType = "application/pdf";
context.Response.ContentEncoding = System.Text.Encoding.UTF8;
context.Response.AddHeader("Content-Disposition", String.Format("attachment;filename={0}_Documenth.pdf", DateTime.Now.ToShortDateString()));
context.Response.End();
}
}
Your handler would actually be .ashx. Other than that your approach seems fine. You can examine the context that is passed into the PrcoessRequest method.
public void ProcessRequest(HttpContext context)
{
switch(context.Request.QueryString["Action"].ToLower())
{
case "preview_saved":
//do stuff for saved
case "preview_unsaved":
//do stuff for unsaved
}
}
UPDATE If you want a central handler, then you'll either want to redirect the user at the end of the call in which case you'll also want to pass a returnUrl parameter in the query string. Then you can just do a context.Response.Redirect(context.Request.QueryString["returnUrl"]). Or if you want the page to retain its state, you're going to have to make an Ajax call to your handler.
First off thank you very much for all the possible solutions they were helpful in getting outside the box.
I figured out a unique solution that I thought I would share.
I place an invisible iframe in the html markup of my page.
then I set a property called OnClientClick in my server control Download Button (this allows me to render a parameter string to the onclick attribute of the html)
I set the property to a javascript method which embodies a recursive algorithm to navigate to the top parent frame of the page, once it reaches that point, I use a simply JQuery selector to look for the iframe I spoke of in step 1. (The reason for the recursion is to maintain support with Master Pages where you will have frames inside of frames).
Set the src attribute of the iframe to the target HTTP Handler. The applied behavior causes the iframe to load the requested source ... but in this case it is a handler, and since it was made all originally from direct user input (via the Download button click), you won't even have the issue of getting the Yellow Bar with IE7+
The handler serves the request and the file is served to the browser
Yay!

How can I reset a asp.net form after postback?

I am trying after the btnCreate_OnClick event to reset the form to it's default value just like the first page_load. The problem is after PostBack, every textbox and other controls, reloads the ViewState value. I cannot deactivate viewstate because of server event on DropDownList selection. The only way I found so far is to Redirect to self after the click event, but this loads the page twice and is therefor a bad solution. I have try ViewState.Clear() and update the UpdatePanel, but was unsuccessful.
I could do a loop for all controls and set the txtXXXXX.Text == "", but I'm quite sure it's not the best idea.
Something like Page.Reset() would have been just perfect but it doesn't exist.
Any thought on this problem of mine?
Thanks
If workable, I usually just use Response.Redirect to reload the same page from scratch.
An initial GET request to a page usually costs less than subsequent POSTs anyway, so there's not much reason to avoid it.
We can reset the ASP.NET Form Page with just 2 lines of code
protected void Button_Reset_Click(object sender, EventArgs e)
{
Session["ViewState"] = null;
Response.Redirect("/Roster/DRAC/Create.aspx");
}
Self redirecting gets tricky because of viewstate.
There is an html input type "reset" for buttons, but I'm not sure what or any integration msft has put into viewstate/asp.net for this. It generally works for simple javascript forms.
ex:
<input type="button" value="Reset" onclick="document.<formId>.reset();">
from google ----^
One way, not necessarily ideal, is to reset the values to their defaults using Javascript. If it is a large form, it can be ugly, but will prevent the need to do a self-redirection.
You also might try Server.Transfer instead of Response.Redirect(/self/)
I dont know if this helps but i change the name of every input on the form that i want to get fresh values by using javascript before submiting the form, since the .net page can no longer match the values from the form to the controls for the page it reloads them as if there was no postback. i also append a new value to the form so i know what button submitted the form and use that logic to decide what to load into all the controls and how to process the form data of course.
$("#Bset").children().click(function() { //all the btns click function
$.each($("form").find("input"), function(e,v) { //could filter this to subset of inputs
$(v).attr("name", "_" + $(v).attr("name")); // ctrl1 becomes _cntrl1
});
$("form").append("<input type='hidden' id='b' name='b' value='" + $(this).text() + "' />").submit();
});
then in the code behind
protected void Page_Init(object sender, EventArgs e)
{
id = int.Parse(Request.QueryString["id"]);
bk = db.dc.getDetailBK(id).Single();
if (Request.Form.Count > 0)
doPostBack();
mrl = (from a in db.dc.getMetricQTD(id, null, null, loadSavedGoals) select a).ToList();
}
i can then do things in dopostback that process the form data, interact with the db etc. that may change the values mrl is loaded with and refresh the data in the inputs tied to mrl regardless of wether they were modified or not on the form.
the other alternative would be to tie some buttons to use a webservice to handle your db interaction then call window.location to refresh the page.
$.ajax({
url: "webservice/dbfunction?"
data: {which btn pressed, some form values etc...}
success: function() {window.location("samepage.aspx?id=xxx");}
...
});
this would also avoid having to response redirect on the server side.
In our case the best performance solution was to set manually for each control the default value in the click event ex:
textbox1.Text = null;
textbox2.Text = null;
This avoid the double page_load and the loop. We don't event have to update the UpdatePanel since it executes before render.
Maybe in a more complex web application we would have to Redirect as most people seem to accept this as a solution.
Setting per control the default value was better suited to our case.
Thank you

Setting focus on postback initiated by javascript

I am working with ASP.NET doing some client side javascript.
I have the following javascript to handle an XMLHTTPRequest callback. In certain situations, the page will be posted back, using the __doPostBack() function provided by ASP.NET, listed in the code below. However, I would like to be able to set the focus a dropdownlist controls after the post back occurs. Is there a way to set this using Javascript, or do I need to rig that up some other way.
function onCompanyIDSuccess(sender, e) {
if (sender == 0)
document.getElementById(txtCompanyIDTextBox).value = "";
document.getElementById(txtCompanyIDHiddenField).value = sender;
if (bAutoPostBack) {
__doPostBack(txtCompanyIDTextBox, '');
}
}
Since you're doing a full postback, you'd need to use Page.SetFocus on the server side to get the appropriate JavaScript emitted on the next page load.
Otherwise, in a pure AJAX solution - document.getElementById('id').focus() would do the trick.
i have found the solution for this one. In the code behind event handler being called for each particular item, I call the Control.Focus() as the last line. For instance, if a dropdownlist event handler is being triggered, and the next control to get focused is the zipcode text box:
protected void ddl_state_selectedValueChanged(Object sender, EventArgs e)
{
// ... here is all my code for the event handler
txtZipCode.Focus();
}
It was much easier that I what I was trying to do. I keep trying to overcomplicate things by creating Javascript on the fly that does exactly what Microsoft is already doing for me in the Framework.

How can I do <form method="get"> in ASP.Net for a search form?

I have a search form in an app I'm currently developing, and I would like for it to be the equivalent of method="GET".
Thus, when clicking the search button, the user goes to search.aspx?q=the+query+he+entered
The reason I want this is simply bookmarkable URLs, plus it feels cleaner to do it this way.
I also don't want the viewstate hidden field value appended to the URL either.
The best I could come up with for this is:
Capture the server-side click event of the button and Response.Redirect.
Attach a Javascript onclick handler to the button that fires a window.location.replace.
Both feel quirky and sub-optimal...
Can you think of a better approach?
Use a plain old html form, not a server side form (runat=server), and you should indeed be able to make it work.
This could however be a problem if you have an out of the box visual studio master page which wraps the entire page in a server side form, because you can't nest forms.
Web forms don't have to suck, but the default implementations often do. You don't have to use web forms for everything. Sometimes plain old post/get and process request code will do just fine.
I worked on a web site that had to post to a 3rd party site to do the search on the client's web site. I ended up doing a simple Response.Redirect and passed in the search parameters through the query string like so:
protected void Button1_Click(object sender, EventArgs e)
{
string SearchQueryStringParameters = #"?SearchParameters=";
string SearchURL = "Search.aspx" + SearchQueryStringParameters;
Response.Redirect(SearchURL);
}
And on your Search.aspx page in your pageload...
protected void Page_Load(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(Request.QueryString["SearchParameters"]))
{
// prefill your search textbox
this.txtSearch.Text = Request.QueryString["SearchParameters"];
// run your code that does a search and fill your repeater/datagrid/whatever here
}
else
{
// do nothing but show the search page
}
}
Hope this helps.
This function permits to submit a page using the GET method.
To submit a page using the get method you need to:
add this code Form.Method="get"; in the Page_Load method
Use this code < asp:Button runat="server" ID="btnGenerate" /> as a submit button
add rel="do-not-submit" attribute to all form elements that you don't want to include in your query string
change the codebehind logic of your page using Request.QueryString
disable the page viewstate with EnableViewState="false" (unless it's used for other purposes)
Code
$(document).ready(function(){ enableSubmitFormByGet(); });
function enableSubmitFormByGet(){
if($("form").attr("method") == "get"){
$("form").submit(function() {
$("[name^=" + "ctl00" + "]").each(function(i){
var myName = $(this).attr("name");
var newName = "p" + (i-1);
$(this).attr("name", newName);
});
var qs =$(this).find("input[rel!='do-not-submit'],textarea[rel!='do-not-submit'],select[rel!='do-not-submit'],hidden[rel!='do-not-submit']").not("#__VIEWSTATE,#__EVENTVALIDATION,#__EVENTTARGET,#__EVENTARGUMENT").serialize();
window.document.location.href = "?" + qs;
return false;
});
I would do (b) since (a) would require two round trips for a single query. Alternatively, you could disable viewstate on the page, remove any other hidden fields via javascript, and also use javascript to modify the form method from post to get. I've never done this for real, but my toy page using the included sample worked like a charm. It's arguably easier than encoding the search string and doing the get via javascript.
Actually, it sounds like you would be happier with ASP.NET MVC since this is easily doable there by simply setting the form method to GET in the view.
sample code using jquery
$(document).ready( function() {
$('input[type=hidden]').remove();
$('form').attr('method','get');
});
EDIT: It seems like you ought to be able to do the same thing server-side, too. Maybe in OnPreRenderComplete. Don't have access to Visual Studio right now to check.
I have always used Response.Redirect as it "works".
I don't think there is an optimal method.
Just use this in your .click event before the form submission:
$("#__VIEWSTATE").remove();
$("#__EVENTVALIDATION").remove();

Modifying the HTML of a page before it is sent to the client

I need to catch the HTML of a ASP.NET just before it is being sent to the client in order to do last minute string manipulations on it, and then send the modified version to the client.
e.g.
The Page is loaded
Every control has been rendered correctly
The Full html of the page is ready to be transferred back to the client
Is there a way to that in ASP.NET?
You can override the Render method of your page. Then call the base implementation and supply your HtmlTextWriter object. Here is an example
protected override void Render(HtmlTextWriter writer)
{
StringWriter output = new StringWriter();
base.Render(new HtmlTextWriter(output));
//This is the rendered HTML of your page. Feel free to manipulate it.
string outputAsString = output.ToString();
writer.Write(outputAsString);
}
You can use a HTTPModule to change the html. Here is a sample.
Using the answer of Atanas Korchev for some days, I discovered that I get JavaScript errors similar to:
"The message received from the server could not be parsed"
When using this in conjunction with an ASP.NET Ajax UpdatePanel control. The reason is described in this blog post.
Basically the UpdatePanel seems to be critical about the exact length of the rendered string being constant. I.e. if you change the string and keep the length, it succeeds, if you change the text so that the string length changes, the above JavaScript error occurs.
My not-perfect-but-working solution was to assume the UpdatePanel always does a POST and filter that away:
protected override void Render(HtmlTextWriter writer)
{
if (IsPostBack || IsCallback)
{
base.Render(writer);
}
else
{
using (var output = new StringWriter())
{
base.Render(new HtmlTextWriter(output));
var outputAsString = output.ToString();
outputAsString = doSomeManipulation(outputAsString);
writer.Write(outputAsString);
}
}
}
This works in my scenario but has some drawbacks that may not work for your scenario:
Upon postbacks, no strings are changed.
The string that the user sees therefore is the unmanipulated one
The UpdatePanel may fire for NON-postbacks, too.
Still, I hope this helps others who discover a similar issue. Also, see this article discussing UpdatePanel and Page.Render in more details.
Take a look at the sequence of events in the ASP.NET page's lifecycle. Here's one page that lists the events. It's possible you could find an event to handle that's late enough in the page's lifecycle to make your changes, but still get those changes rendered.
If not, you could always write an HttpModule that processes the HTTP response after the page itself has finished rendering.
Obviously it will be much more efficient if you can coax the desired markup out of ASP.Net in the first place.
With that in mind, have you considered using Control Adapters? They will allow you to over-ride how each of your controls render in the first place, rather than having to modify the string later.
I don't think there is a specific event from the page that you can hook into; here is the ASP.Net lifecycle: http://msdn.microsoft.com/en-us/library/ms178472.aspx
You may want to consider hooking into the prerender event to 'adjust' the values of the controls, or perform some client side edits/callbacks.

Resources