3
votes

Please take a look at the following click event...

 Protected Sub btnDownloadEmpl_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnDownloadEmpl.Click
        Dim emplTable As DataTable = SiteAccess.DownloadEmployee_H()
        Dim d As String = Format(Date.Now, "d")
        Dim ad() As String = d.Split("/")
        Dim fd As String = ad(0) & ad(1)
        Dim fn As String = "E_" & fd & ".csv"
        Response.ContentType = "text/csv"
        Response.AddHeader("Content-Disposition", "attachment; filename=" & fn)
        CreateCSVFile(emplTable, Response.Output)
        Response.Flush()
        Response.End()
        lblEmpl.Visible = True
    End Sub

This code simply exports data from a datatable to a csv file. The problem here is lblEmpl.Visible=true never gets hit because this code doesnt cause a postback to the server. Even if I put the line of code lblEmpl.Visible=true at the top of the click event the line executes fine, but the page is never updated. How can I fix this?

4
You're running into the same problem as this post: stackoverflow.com/questions/2731971/…Chris Haas

4 Answers

4
votes

This line:

lblEmpl.Visible = True

Never gets hit because this line:

Response.End()

Throws a ThreadAbortException

I think a cleaner way to handle this is to create a simple HttpHandler component, and 'open' it in a popup window. (The popup window shouldn't actually open. In most cases the browser will realize it's actually a download, and will suppress the tab/window.)

Research the IHttpHandler interface. They're actually quite simple to implement.

Here's a sample handler. Sorry it took awhile, I got called into a meeting:

public class CensusHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        string fileName = String.Format(
            CultureInfo.CurrentUICulture,
            "E_{0:00}{1:00}.csv",
            DateTime.Today.Month,
            DateTime.Today.Day
            );

        context.Response.ContentType = "text/csv";
        context.Response.AddHeader(
            "Content-Disposition", String.Format(null, "attachment; filename={0}", fileName)
            );

        //Dump the CSV content to context.Response

        context.Response.Flush();
    }

    public bool IsReusable { get { return false; } }
}

OK, try adding a javascript onclick event to trigger the download:

<asp:Button ID="Clickety" runat="server" Text="Click Me!" OnClick="Clickety_Click"
    OnClientClick="window.open('Handler.ashx', 'Download');" />

The regular OnClick event will fire your postback code. The javascript onclick (OnClientClick) event will launch the download via the HttpHandler.

1
votes

You are sending the CSV down the Response Stream and then ending it. Therefore, once all of your code has run, the page itself is now no longer being sent down the Response Stream and so not updated in the browser.

In order to achieve what you are after you need to create a second response stream. The easiest way to do this is via a new browser window / popup. Then your CSV can download in the popup and your initial page response can remain in the initial browser.

1
votes

You should go with a pop-up so you can still set your label on the original page. From what I see you could do something like this

  1. Create a seprate form for the pop-up to create the CSV, move your function to the load of the new page.cs and send it a querystring parm for its dataID to export.

    Protected Sub onLoad()
        Dim recordID As Integer = Request.Querystring("dID")
        Dim emplTable As DataTable = Nothing 
        Select Case recordID 
          case 1: emplTable = SiteAccess.DownloadEmployee_H() 
          case 2: emplTable = SiteAccess.DownloadManagers() 
        End Select 
        Response.Clear()
        Response.ContentType = "text/csv"
        Response.AddHeader("Content-Disposition", "attachment; filename=" & fn)
        CreateCSVFile(emplTable, Response.Output)
        Response.Flush()
        Response.End() 
        lblEmpl.Visible = True
    End Sub
    
  2. Launch your pop-up from the onClientClick property of the button, or register a script on the postback.

    function fnPopUpCSV(expType)
    {        
    window.open('/popUpPage.aspx?dID=' + expType,'CSVwindow', 
    'width=300,height=200,menubar=yes,status=yes,
    location=yes,toolbar=yes,scrollbars=yes');
    }
    
    <asp:Button ID="btnGenEmplCSV" runat="server" Text="Generate Employee CSV"
    onClientClick="javascript:return fnPopUpCSV(1);" />
    
    <asp:Button ID="btnGenMgrCSV" runat="server" Text="Generate Manager CSV"
    onClientClick="javascript:return fnPopUpCSV(2);" />
    
0
votes

Just create a new page with this code on Page_Load

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) 
    Dim emplTable As DataTable = SiteAccess.DownloadEmployee_H()
    Dim d As String = Format(Date.Now, "d")
    Dim ad() As String = d.Split("/")
    Dim fd As String = ad(0) & ad(1)
    Dim fn As String = "E_" & fd & ".csv"
    Response.ContentType = "text/csv"
    Response.AddHeader("Content-Disposition", "attachment; filename=" & fn)
    CreateCSVFile(emplTable, Response.Output)
    Response.Flush()
    Response.End()
End Sub

Then on your original page make you lblEmpl invisible and the register a script like the following to open a new window with your csv.

var csvPageJS = string.Format("window.open ('{0}','mywindow');", ResolveUrl("~/MyCSVPage.aspx"));
  ClientScript.RegisterStartupScript(typeof(Page), "popup", csvPageJS, true);