I have the following code to download a file from the server to the client, that occurs when the button btnSavetoDB_ExportToExcel is clicked, after that I want to have the button disable, how can I achieve that?
string fileName = newFN + ".xlsx";
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("Content-Disposition", "attachment;filename=" + fileName);
Response.TransmitFile(DestFile);
Response.End();
btnSavetoDB_ExportToExcel.Enabled = false;
I noticed that the button did not become disable when placed the above code (Response), cause I had another code there and the button became disable with it. So it must be something related with the Response.
EDIT:
The button is within a ModalPopupExtender in case it matters.
The reason for this is that if you disable the button with server code you need to return the response to the browser window.
Here you return a stream that is captured by the browser as a file instead of to the browser window so the current display is never updated.
To get around this you can implement a javascript line on the button in the html itself.
You can use Attribute.Add() in code at Page_Load, f.ex:
btnSavetoDB_ExportToExcel.Attribute.Add("onclick","this.disabled=true")
There at least two possible solutions.
1) Instead of writing a file to the response you can generate file in the temp directory on the server and give the user the link to this file (or redirect to this link).
2) If you want use writing to the response, you can create new Generate.aspx page that writes the file to the response in its Page_Load method.
if (Request.QueryString["ID"] != null)
{
//find file by its ID
...
Response.AddHeader("Content-disposition", "attachment; filename=" + fileName));
Response.ContentType = "application/octet-stream";
Response.WriteFile(fullFileName);
Response.End();
}
On page with your btnSavetoDB_ExportToExcel in Page_Load add this code:
var script = #"<script language=JavaScript>function Export(fileID)
{
var iframe = document.createElement('iframe');
iframe.src = 'Generate.aspx?ID='+ fileID;
iframe.style.display = 'none';
document.body.appendChild(iframe);
}
</script>";
Page.RegisterStartupScript("Startup", script);
And then just call javascript Export method to send file to the user.
As mentioned in the other answer, the button should be disabled in client side (since you are using Response.End() in your code, ASP.NET will never render the page with the button having Enabled=False.
In your .aspx file add
<asp:Button ID="btnSavetoDB_ExportToExcel" OnClientClick="this.disabled=true;" [..] />
But also note that this might not be a good idea altogether - what if the user accidentally clicks "Cancel" from the file download window?
A better solution (understanding that you want to prevent the user from clicking the button multiple times) is to add a timer to re-enable the button:
<asp:Button ID="btnSavetoDB_ExportToExcel" OnClientClick="var a=this; a.disabled=true; window.setTimeout(function() { a.disabled=false; }, 5000)" [..] />
Value 5000 is the time in miliseconds after which the button will be re-enabled.
I have found the solution to my problem, credit goes to: Dave Ward (click here)
<asp:Button ID="btnSavetoDB_ExportToExcel" runat="server"
OnClientClick="this.disabled = true;"
UseSubmitBehavior="false"
Text="Save results to DB and export to Excel"
onclick="btnSavetoDB_ExportToExcel_Click" />
this does the trick:
OnClientClick="this.disabled = true;"
UseSubmitBehavior="false"
Related
So I know that this is common problem, page freezes (any buttons visually are clickable but doesn't perform any action) because it doesn't close some request after file download.
System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;
response.ClearContent();
response.Clear();
response.ContentType = "text/txt";
response.AddHeader("Content-Disposition", "attachment; filename=" + TreeView1.SelectedNode.Text + ";");
response.TransmitFile(TreeView1.SelectedNode.Value);
response.Flush();
response.Close();
HttpContext.Current.ApplicationInstance.CompleteRequest();
This code is in TreeView SelectedNodeChanged event, and this is a problem.
For example if will put that transmit code in some button, I can fix freezing of page by adding OnClientClick="javascript:setFormSubmitToFalse()" to the button and this small JavaScript fixes page freezing after download.
<script type="text/javascript">
//Fix page freeze after download dialog
function setFormSubmitToFalse() {
setTimeout(function () { _spFormOnSubmitCalled = false; }, 3000);
return true;
}
</script>
But I don't know how to fix this if I'm clicking on TreeView node. TreeView doesn't have OnClientClick, I also tried to run that JS function from code behind right before and after transmit code Page.ClientScript.RegisterStartupScript(this.GetType(), "CallMyFunction", "setFormSubmitToFalse()", true);
but it doesn't help, page is still freezing after file download.
Any ideas how can I fix this? Thanks.
This is webpart (almost asp.net page) on sharepoint site.
Edit:
I'll try to explain better my situation and what I'm trying to achive.
So my main goal: There is a shared folder with documents in local network, users that will be using this app should not have access to this shared folder, only entity that is running App pool has access to it.
I am making some SQL request that gives me path of some subfolder. I am building treeview of that subfolder and when user clicks on some file in treeview SelectedNodeChanged event shots and I perform transmitting file as app pool identity
SPSecurity.RunWithElevatedPrivileges(delegate()
{
/* transmit file code */
});
Everything is working fine but if I understand right, something on CLIENT side probably freezes page after TransmitFile shoots and prevents from sending any other post request to server, that's why my little JS solution worked when it was performed OnClientClick.
This is original how author of that fix explained that, but I don't know how to implement this into treeview https://stackoverflow.com/a/17186011/5805492
So solution was pretty simple but a little bit tricky.
Nodes doesn't have onClientClick event, but we can add JS click event to any page element, right? So here is the trick, when creating TreeView just add click to actual text of the node with help of Span:
...
TreeNode fileNode = new TreeNode
{
//before
//Text = file.Name,
//after
Text = "<span onclick=\"javascript:setFormSubmitToFalse();\">"+file.Name+"</span>",
Value = file.FullName,
};
...
And in transmitting file parse text of the node to have normal name of the file:
response.AddHeader("Content-Disposition", "attachment; filename=" +
TreeView1.SelectedNode.Text.Replace("<span onclick=\"javascript:setFormSubmitToFalse();\">", string.Empty).Replace("</span>", string.Empty) + ";");
Now everything is working and page is not freezing anymore after downloading.
I'm not sure I really understand your situation, but your app is likely being frozen due to the fact that the UI thread is hung up on code that you are trying to run synchronously.
In order to prevent that from happening, you need to run the logic asynchronously.
public async void OnSelectedNodeChanged(object sender, EventArgs e)
{
await Task.Run(() => {
System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;
response.ClearContent();
response.Clear();
response.ContentType = "text/txt";
response.AddHeader("Content-Disposition", "attachment; filename=" + TreeView1.SelectedNode.Text + ";");
response.TransmitFile(TreeView1.SelectedNode.Value);
response.Flush();
response.Close();
HttpContext.Current.ApplicationInstance.CompleteRequest();
}).ConfigureAwait(false);
}
This is an async void method, which means the code that fires this event will not wait for it to finish, nor will it know when it has finished.
This will prevent the thread from locking your UI, but if the user has the ability to "rapid fire" these events by clicking all around your UI, it may not be desirable to write the code this way.
Regardless, you will likely need some form of async logic to accomplish what you are wanting.
So I'm working with this form which, at the end, generates a Word document for the user to download, fires off some emails, and then displays a success panel for the user. The problem I'm having is that the .Visible flags don't end up getting changed whenever I call the downloadWordFile() method. Is there something in that method that would be messing with my ability to change visibility of ASP panels? I've tried removing almost every part of this to see where the issue is popping up and I haven't been able to make heads or tails of it. Everything else works fine, it's just this method causing the issue. Thanks in advance for your help!
protected void btnSubmit_Click(object sender, EventArgs e)
{
pnlForm.Visible = false;
pnlSuccess.Visible = true;
email();
adminEmail();
downloadWordFile();
}
protected void downloadWordFile(){
Response.ContentType = "application/msword";
Response.ContentEncoding = System.Text.UnicodeEncoding.UTF8;
Response.Charset = "UTF-8";
Response.Write("<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'>");
Response.Write("<head>");
Response.Write("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"\"text/html; charset=UTF-8\"\">");
Response.Write("<meta name=ProgId content=Word.Document>");
Response.Write("</head>");
Response.Write("<body>");
Response.Write("<div class=Section2>");
Response.Write(buildForm());
Response.Write("</body>");
Response.Write("</html>");
Response.AppendHeader("Content-Disposition", "attachment; filename=" + "Generic.doc");
HttpContext.Current.Response.Flush();
}
EDIT 1: I ended up going a different route entirely. Rather than trying to switch panels, generate emails, and generate word files all in one go, I made the submit button switch the panels, and the success panel now shows a preview of the word document to be downloaded and provides the user a chance to make changes. There is a download button on the success panel that generates the emails and downloads the word document. This required minimal changes to the way the code was already set up and solved another problem I was having with the email generation.
A response can only be one thing. You can't respond with page content and a file. You are setting the response to a file so that is how the browser handles the response.
I would handle this situation by the calling a JavaScript function in the button's click event that downloads the file via an IFrame.
First you would need a "download file" page. In the OnLoad event of that page you can call email();, adminEmail(); and downloadWordFile();
The JavaScript click event handler would look something like this.
function DownloadFile() {
var downloadFrame = document.createElement("IFRAME");
if (downloadFrame != null) {
downloadFrame.setAttribute("src", 'DownloadFile.aspx');
downloadFrame.style.width = "0px";
downloadFrame.style.height = "0px";
document.body.appendChild(downloadFrame);
//Set the visibility of pnlForm and pnlSuccess
}
}
EDIT:
Per your comment, I've had to do the same thing. I solved it by posting the form as normal and assembling the form values into a query string. Then use ScriptManager.RegisterStartupScript to add a script that calls the DownloadFile() function when the page loads. The DownloadFile() function takes the query string as a parameter and the DownloadFile.aspx page uses it to get the form data.
So the click event handler would be updated like this.
function DownloadFile(queryString) {
var downloadFrame = document.createElement("IFRAME");
if (downloadFrame != null) {
downloadFrame.setAttribute("src", 'DownloadFile.aspx' + queryString);
downloadFrame.style.width = "0px";
downloadFrame.style.height = "0px";
document.body.appendChild(downloadFrame);
//Set the visibility of pnlForm and pnlSuccess
}
}
I have both asp.net server button controls and html buttons in my asp.net web form.
<asp:LinkButton runat="server" ID="btnExportToExcel" OnClick="btnExportToExcel_Click">Excel</asp:LinkButton>
<button type="submit" class="btn btn-info btn-block">Refresh</button>
I use asp.net button OnClick event to send back an Excel file. Below is the code:
protected void btnExportToExcel_Click(object sender, EventArgs e)
{
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment; filename=ExcelDemo.xlsx");
Response.BinaryWrite(ExcelHelper.GetByteArray(datatable));
Response.End();
}
The export to Excel works perfectly fine, but after the export is done, all html buttons start to cause a postback with target id of the asp.net button control responsible for the Excel export, i.e. the OnClick event of btnExportToExcel button is fired again and I get the same Excel file exported again.
Am i doing something wrong with the responce in the OnClick event? Or am I missing something else?
NB. I can fix the problem by using just asp.net buttons, but I would like to understand the nature of the problem.
EDIT. After the reply from Igor, is it correct that the problem has nothing to do with the Response (i.e. I thought that the Response might be missing something) and it could be considered as an expected behavior and I need to be careful with the buttons I use?
This is what happens on your page:
asp:LinkButton is clicked. On its client-side click it calls __doPostBack that sets hidden __EVENTTARGET input value to btnExportToExcel.
The form is submitted to the server. Browser is waiting for the new content to arrive.
The new content is a downloadable file, so the browser page is not cleared and stays what it was during the submit.
Clicking on any button with type="submit" causes the form submission with __EVENTTARGET still having btnExportToExcel value.
Server-side page interprets POST request with Request.Form["__EVENTTARGET"]="btnExportToExcel" as the click of btnExportToExcel.
I'm missing a Response.Clear at the beginning.
protected void btnExportToExcel_Click(object sender, EventArgs e)
{
Response.Clear();
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment; filename=ExcelDemo.xlsx");
Response.BinaryWrite(ExcelHelper.GetByteArray(datatable));
Response.End();
}
Add OnServerClick="return false;" to the button markup, to avoid the postback
My problem looks like this.
I have a grid with documents (Id's). Now when somebody clicks at a row I would like to allow him to download or show that document. But to make it esier let's say that I would do this on a button click. I tried two approaches but none of them worked form me.
I tried to response.binarywrite on the button click:
Response.Clear();
Response.ClearHeaders();
Response.ClearContent();
Response.ContentType = "application/postscript mime";
Response.AddHeader("content-disposition", "attachment; filename=test.ps");
Response.AddHeader("content-length", _excuteGetDocumentResult.Length.ToString());
Response.ContentEncoding = new System.Text.UTF8Encoding();
Response.BinaryWrite(_excuteGetDocumentResult);
But nothing happens and when I try to modify this code I usually get some javascript errors saying sommething about changing the response...
The socond approach was opening new window and on page load adding the code above.
<asp:Button Text="ShowResult" OnClientClick="radopen('ShowResult.aspx','ShowDocumentDialog'); return false;"
runat="server" />
The socond approach works but my opened window still exists after saving or canceling the explorer saving file dialog window. I tried to add some javascript to close it but it only works where there is no response.binarywrite on the load page...
Any ideas how I can achive what I want?
In method 1.
try Response.End(); after Response.BinaryWrite(_excuteGetDocumentResult);
I am exporting a gridview to excel, using .Net 4.0 in a web application, on page load and need for the file to be generated and then the page to be redirected to the calling page. I am running into issues because my code to export to excel is as follows:
gvSummary.Style.Add("font-size", ".6em");
Response.Clear();
string attachment = "attachment; filename=filename.xls";
Response.ClearContent();
Response.AddHeader("content-disposition", attachment);
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
StringWriter sw = new StringWriter();
HtmlTextWriter htw = new HtmlTextWriter(sw);
gvSummary.GridLines = GridLines.Horizontal;
gvSummary.RenderControl(htw);
Response.Write(sw.ToString());
Response.End();
I know that if I put the Response.Redirect() before the .End(), I will get redirected but the file is never generated, and if I put the Response.Redirect() after the .End() I get the file but no redirection.
The code written above works just fine in generating the file, however when after the file is generated I am still stuck seeing my Loading animation because I can not break out of the page. Any ideas?
I suggest to add a redirect header. Something like this:
Response.AddHeader("Refresh", "3; url=index.html");
Where 3 is time of the delay and index.html is url you need to redirect to
The problem is Response.End() send the last bits of the page to the client in order to complete the request. In your case, the request contains the file.
One possible solution is be to open a new page for the download and keep the redirect in the current page.
I added the target="_blank" to the link that takes me to the page. So when it opens in a new window, it send the excel sheet to the browser, and closes the page as it has nothing else to do after the Response.End();
Hope this helps. Worked for me.
You can't do both open so you might want to try setting the link to the generated xls file in a new window or as source to a iframe
In JavaScript:
function refresh() { document.location.reload(true); }
button:
<td valign="top" style=" width:100px ">
<asp:Button runat="server" ID="btnX" Text="X" CssClass="ui-button ui-widget ui-state-default ui-corner-all ui-button-text ui-state-hover" OnClientClick="refresh();" Width="180px" />
</td>
I tried opening a separate page through ScriptManager that will download my file while doing the redirect in my original page.
So I added below code in my main page:
string strScript = "<script language='javascript'> window.open('CommonDownload.aspx','Report');</script>";
ScriptManager.RegisterStartupScript(Page, this.GetType(), "clientScript", strScript, false);
Response.Redirect("Page.aspx");
And then added the download code in the Page_Load method of my CommonDownload page. I passed some of the info I needed in the CommonDownload page through through the session.
OnClientClick call this method and it will automatically be refreshed.
function PageRefresh() {
setTimeout(function () {
window.location.reload(1);
}, 5000);
}