Edit existing excel files ASP.Net without losing formatting - asp.net

I have a situation where I need to use an Excel Template and insert values, then allow the user to download the updated file. I have done this using ClosedXML but as it uses a non native format for xlsx, I get a format error when opening the downloaded document and the saved instance of the document looses all formatting from the template.
I need a solution that outputs a xlsx document that is a copy of the original template, formatting and all, with the additional inserted values. If necessary, I can make a temporary copy of the template on the server.
Is there a tool I can use for this purpose?

I ended up using OpenXML. I am using VB.Net for this project and as I found very little example code in VB (mostly java or c#), I will attach it so it can be used as a reference.
The really nice thing about it is the document you get out of it does not have any validation errors or anything and all sheet formatting (text justification, font face, etc) survives.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
Dim path = Server.MapPath("~/ExcelTemplates/rfi.xlsx")
Dim templateBytes As Byte() = System.IO.File.ReadAllBytes(path)
Using templateStream As MemoryStream = New MemoryStream
templateStream.Write(templateBytes, 0, templateBytes.Length)
Using sheetInstance As SpreadsheetDocument = SpreadsheetDocument.Open(templateStream, True)
Dim worksheetPart As WorksheetPart = GetWorksheetPartByName(sheetInstance, "Sheet1")
If worksheetPart IsNot Nothing Then
'this writes "Test!!!" to cell C7
'Dim cell As Cell = GetCell(worksheetPart.Worksheet, "C", 7)
'cell.CellValue = New CellValue("Test!!!")
'another way of doing it
GetCell(worksheetPart.Worksheet, "C", 7).CellValue = New CellValue("Test12")
'this next line would change the cell's contents data type
'cell.DataType = New EnumValue(Of CellValues)(CellValues.String)
worksheetPart.Worksheet.Save()
templateStream.Position = 0
Using ms As MemoryStream = New MemoryStream
templateStream.CopyTo(ms)
Response.Buffer = True
Response.Clear()
Response.ContentType = "application/vnd.ms-excel"
Response.AppendHeader("Content-Disposition", "filename=RFI.xlsx")
ms.WriteTo(Response.OutputStream)
Response.End()
End Using
End If
End Using
End Using
End Sub
Private Shared Function GetWorksheetPartByName(ByVal document As SpreadsheetDocument, ByVal sheetName As String) As WorksheetPart
Dim sheets As IEnumerable(Of Sheet) = document.WorkbookPart.Workbook.GetFirstChild(Of Sheets)().Elements(Of Sheet)().Where(Function(s) s.Name = sheetName)
If sheets.Count() = 0 Then
Return Nothing
End If
Dim relationshipId As String = sheets.First().Id.Value
Dim worksheetPart As WorksheetPart = CType(document.WorkbookPart.GetPartById(relationshipId), WorksheetPart)
Return worksheetPart
End Function
Private Shared Function GetCell(ByVal worksheet As Worksheet, ByVal columnName As String, ByVal rowIndex As UInteger) As Cell
Dim row As Row = GetRow(worksheet, rowIndex)
If row Is Nothing Then Return Nothing
Return row.Elements(Of Cell)().Where(Function(c) String.Compare(c.CellReference.Value, columnName & rowIndex, True) = 0).First()
End Function
Private Shared Function GetRow(ByVal worksheet As Worksheet, ByVal rowIndex As UInteger) As Row
Return worksheet.GetFirstChild(Of SheetData)().Elements(Of Row)().Where(Function(r) CType(r.RowIndex, UInteger) = rowIndex).First()
End Function

Related

Export DataTable to Excel from VB.Net

The following code is my current attempt at opening some data in excel from a website button in VB.Net. I would like the data to show up barebones, but the formatting from the table on the website always follows. The paging and colors make the data near impossible to read and can only see the first page of data. Any quick fixes? I've tried a lot of things I've found on here but to no avail.
Private Sub DownloadExcel()
Response.Clear()
'Dim dt As DataTable = TryCast(ViewState("GridData"), DataTable)
Grid_Bad_Meters.AllowPaging = False
Grid_Bad_Meters.AllowSorting = False
'Grid_Bad_Meters.DataSource = dt
'Grid_Bad_Meters.DataBind()
Dim sfile As String = "Communication_Failures" & Now.Ticks
Response.AddHeader("content-disposition", "attachment;filename=" & sfile & ".xls")
Response.Charset = ""
' If you want the option to open the Excel file without saving then
' comment out the line below
' Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.ContentType = "application/vnd.ms-excel"
Dim stringWrite As New System.IO.StringWriter()
Dim htmlWrite As System.Web.UI.HtmlTextWriter = New HtmlTextWriter(stringWrite)
Grid_Bad_Meters.RenderControl(htmlWrite)
Response.Write(stringWrite.ToString())
Response.End()
'Grid_Bad_Meters.AllowPaging = True
'Grid_Bad_Meters.AllowSorting = True
'GridView1.DataSource = dt
'GridView1.DataBind()
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Try
Dim ScriptManager As ScriptManager = ScriptManager.GetCurrent(Me.Page)
ScriptManager.RegisterPostBackControl(Me.btnExportToExcel)
Catch ex As Exception
End Try
End Sub
Protected Sub btnExportToExcel_Click(sender As Object, e As EventArgs) Handles btnExportToExcel.Click
Try
Dim sw As New System.IO.StringWriter()
Dim hw As New System.Web.UI.HtmlTextWriter(sw)
Dim style As String = "<style>.textmode{mso-number-format:\#;}</style>"
Response.Clear()
Response.Buffer = True
Response.AddHeader("content-disposition", "attachment;filename=SignExport.xls")
Response.Charset = ""
Response.ContentType = "application/vnd.ms-excel"
For i As Integer = 0 To Me.Grid_Bad_Meters.Rows.Count - 1
Dim row As GridViewRow = Grid_Bad_Meters.Rows(i)
row.Attributes.Add("class", "textmode")
Next
'lblRptHeader.RenderControl(hw)
hw.WriteBreak()
'lblReportDateRange.RenderControl(hw)
Grid_Bad_Meters.RenderControl(hw)
Response.Write(style)
Response.Output.Write(sw.ToString())
Response.Flush()
Response.End()
Catch ex As Exception
End Try
End Sub
You could use one of the two approaches mentioned below. Of course, there are other ways of meeting your requirement like exporting to csv file as mentioned in a comment or using a .Net library meant for Excel exporting like epplus.
OpenXML Approach
If you are looking for a way to export to Excel without using the html approach, then you can use OpenXML approach that is explained very clearly with a working example at this URL: Export to Excel using OpenXML. This will eliminate all the CSS styles that can get associated with exporting using html approach and you seem to be using this html approach according to the code in your original post. However, if you want to use the html approach, then the code below should work and eliminate all CSS styles that can come in the way when viewing the excel file. I have actually tried this posted code on my machine before putting it here.
Html Approach
You can create a new instance of GridView in your export method rather than use an existing instance, and data bind it to same data as the existing gridview on your page before rendering it to excel. Before you data bind it in the export method you need to make sure that no styles are set and specifically the grid line are set to none as in code below.
You can see an actual video of how this works at this URL : Grid Export without any CSS Styles. This was how the code behaved on my laptop when I ran it.
You can use sample code below, but make sure the data source is set to data that includes all records across all pages of original gridview. I have used SqlDataSource1 as data source but you can replace it by an appropriate method in your situation.
Protected Sub btnExport_Click(sender As Object, e As EventArgs)
Dim GridView2 As New GridView()
GridView2.AllowPaging = False
GridView2.AllowSorting = False
GridView2.Style.Clear()
GridView2.CellPadding = 0
GridView2.CellSpacing = 0
GridView2.GridLines = GridLines.None
GridView2.BorderStyle = BorderStyle.None
GridView2.BorderWidth = Unit.Pixel(0)
GridView2.AlternatingRowStyle.BorderStyle = BorderStyle.None
GridView2.DataSource = SqlDataSource1
GridView2.DataBind()
' Clear the response
Response.Clear()
' Set the type and filename
Response.AddHeader("content-disposition", "attachment;filename=griddata.xls")
Response.Charset = ""
Response.ContentType = "application/vnd.xls"
' Add the HTML from the GridView to a StringWriter so we can write it out later
Dim sw As New System.IO.StringWriter()
Dim hw As System.Web.UI.HtmlTextWriter = New HtmlTextWriter(sw)
GridView2.RenderControl(hw)
' Write out the data
Response.Write(sw.ToString())
Response.[End]()
End Sub
Public Overrides Property EnableEventValidation() As Boolean
Get
Return False
End Get
'Do nothing
Set
End Set
End Property
Public Overrides Sub VerifyRenderingInServerForm(control As Control)
'Allows for printing
End Sub

parameters not passing to ssrs ServerReport - rendering report to pdf using ServerReport.Render

My code that sets up a ServerReport object on a web forms page, and then renders the report from SSRS to a pdf. Parameter and report name is passed by URL.
Private Sub Page_Load(sender As Object, e As EventArgs)
Dim reportname As String
'Dim parameter(0) As ReportParameter
reportname = Request("reportname").ToString
Dim v As New ReportViewer
v.ProcessingMode = ProcessingMode.Remote
Dim serverreport As New ServerReport
serverreport = v.ServerReport
serverreport.ReportServerUrl = New Uri("http://xxxxxx:80/ReportServer")
serverreport.ReportPath = "/Reports/Aramid/Sheeter/" & reportname
Select Case reportname
Case Is = "NomexBlockCard" 'Or "NomexBlockLabel" Or "NomexInternalLabel"
Dim paramList As New Generic.List(Of ReportParameter)
paramList.Add(New ReportParameter("paramBlock", Request("paramBlock").ToString, False))
serverreport.SetParameters(paramList)
Case Is = "NomexRoutingData"
Dim paramList As New Generic.List(Of ReportParameter)
paramList.Add(New ReportParameter("paramWO", Request("paramWO").ToString, False))
serverreport.SetParameters(paramList)
End Select
serverreport.ReportServerCredentials = New ReportViewerCredentials(user name here, password here, "CORE")
Save(serverreport, "C:\WebReports\" & reportname & ".pdf")
'now print
Response.Redirect("reports.ashx?fileName=" & reportname)
End Sub
Public Sub Save(ByVal sr As ServerReport, ByVal savePath As String)
Try
Dim warnings As Warning() = Nothing
Dim streamids As String() = Nothing
Dim mimeType As String = Nothing
Dim encoding As String = Nothing
Dim extension As String = Nothing
Dim deviceInfo As String
Dim bytes As Byte()
deviceInfo = "True" '<DeviceInfo><SimplePageHeaders>True</SimplePageHeaders></DeviceInfo>"
bytes = sr.Render("PDF", Nothing, mimeType, _
encoding, extension, streamids, warnings)
Using Stream As New FileStream(savePath, FileMode.Create)
Stream.Write(Bytes, 0, Bytes.Length)
Stream.Close()
End Using
Catch ex As Exception
End Try
End Sub
The report renders and saves as a pdf but the parameter value does not seem to be getting used in the report.
I have confirmed over and over that there is a value in paramBlock.
I don't know what I am doing wrong.
Am I missing a step or something?
Ryan
As your report is being rendered and saved, I'm assuming the credentials are set up correctly.
You can use the ServerReport.GetParameters function before saving the report to check what the parameters (and their values/properties) actually are.
Also, make sure you're setting every parameter required by the report (even those hidden or internal), and that every parameter value is within the allowed values of the parameter (if limits are set).
If the problem doesn't lie in passing the parameters, you might want to take a look at the report itself and how it handles its parameters.
From your recent comment about a "Parameter validation failed" error, it sounds like your issue is not with your calling code structure, but rather matching the parameters to the report definition.
The most common issue is hidden or internal parameters which you are ignoring. You need to carefully review the parameter design in SSRS Report Designer, and make sure your passed parameters comply with what it expects.
Consider following suggestions. It might help you to resolve your issue. After considering every point, you can try your code
Place following code just above the Select Case reportname statement line
serverreport.ReportServerCredentials = New ReportViewerCredentials(user name here, password here, "CORE")
You can also try changing code line
serverreport.ReportServerCredentials = New ReportViewerCredentials(user name here, password here, "CORE")
To
ServerReport.ReportServerCredentials.NetworkCredentials = System.Net.CredentialCache.DefaultCredentials
If your report has default parameters, then remove default parameters and test your code
And also remove exception handling from your method
Public Sub Save(ByVal sr As ServerReport, ByVal savePath As String)
You can try following code:
Private Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
Dim reportname As String = Request("reportname").ToString
'Dim parameter(0) As ReportParameter
Dim v As New ReportViewer
v.ProcessingMode = ProcessingMode.Remote
Dim ServerReport As ServerReport
ServerReport = v.ServerReport
serverreport.ReportServerUrl = New Uri("http://xxxxxx:80/ReportServer")
serverreport.ReportPath = "/Reports/Aramid/Sheeter/" & reportname
ServerReport.ReportServerCredentials.NetworkCredentials = System.Net.CredentialCache.DefaultCredentials
Select Case reportname
Case Is = "NomexBlockCard" 'Or "NomexBlockLabel" Or "NomexInternalLabel"
Dim paramList As New Generic.List(Of ReportParameter)
paramList.Add(New ReportParameter("paramBlock", Request("paramBlock").ToString, False))
serverreport.SetParameters(paramList)
Case Is = "NomexRoutingData"
Dim paramList As New Generic.List(Of ReportParameter)
paramList.Add(New ReportParameter("paramWO", Request("paramWO").ToString, False))
serverreport.SetParameters(paramList)
End Select
Save(serverreport, "C:\WebReports\" & reportname & ".pdf")
End Sub
Public Sub Save(ByRef sr As ServerReport, ByVal savePath As String)
Try
Dim warnings As Warning() = Nothing
Dim streamids As String() = Nothing
Dim mimeType As String = Nothing
Dim encoding As String = Nothing
Dim extension As String = Nothing
Dim deviceInfo As String
Dim bytes As Byte()
deviceInfo = "True" '<DeviceInfo><SimplePageHeaders>True</SimplePageHeaders></DeviceInfo>"
bytes = sr.Render("PDF", Nothing, mimeType, _
encoding, extension, streamids, warnings)
Using Stream As New FileStream(savePath, FileMode.Create)
Stream.Write(bytes, 0, bytes.Length)
Stream.Close()
End Using
Catch ex As Exception
End Try
End Sub
Maybe this is not the case, but I remember that Parameters value can be lost during PostBack.
So I would try a syntax like this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Me.Page.IsPostBack Then
'your code to setup and print report
End If
End Sub
If this solution doesn't work please provide the code used in Report Definition.

Delete Blank Row in excel worksheet

I Tried to delete blank rows from excel using the code
Dim wb As New Workbook("d:\test\book1.xls")
Dim sheets As WorksheetCollection = wb.Worksheets
Dim sheet As Worksheet = sheets(0)
sheet.Cells.DeleteBlankRows()
wb.Save("d:\test\mybook.xls")
But i am getting syntax error.Any one know the Namespace requiered to do this?
Try
ApplicationClass excel = new ApplicationClass();
Microsoft.Office.Interop.Excel.Range cellToBeDeleted = (Range)excel.Cells[rowIndex, columnIndex];
cellToBeDeleted.Delete();
Include the following namespace
using Excel = Microsoft.Office.Interop.Excel;
Try This
-Add Reference (from COM section ) Micsrosoft Excel Object Library to your project
Imports Microsoft.Office.Interop.Excel
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
delBlankRows("d:\test\book1.xls", 1)
End Sub
Private Sub delBlankRows(ByVal excelFileName As String, sheetIndex As Integer)
Dim excel As Microsoft.Office.Interop.Excel.Application = New Microsoft.Office.Interop.Excel.Application
Dim fileName = excelFileName
Dim w As Workbook = excel.Workbooks.Open(fileName)
Dim r As Range = w.Worksheets(sheetIndex).UsedRange.EntireRow.SpecialCells(XlCellType.xlCellTypeBlanks)
r.Delete()
w.Save()
w.Close()
End Sub
End Class

Exporting datatable to excel file with save dialog

I have a datatable being created with various inputs. Sometimes the resulting table is 35000+ rows. Currently, the datatable gets displayed onto a gridview. It loads fine after a couple minutes. Then, theres an option to export the gridview to an excel file. Everytime we have a large table to export, the conversion fails.
My goal is to bypass the gridview step and take the formatted table and put it directly into an excel file. Could also be a csv file if thats faster to write/load, as long as the data table is similar to the gridview output.
I tried the following code here Export DataTable to Excel File. I did my best to convert it to vb, here...
Protected Sub btnExportData_Click(sender As Object, e As EventArgs) Handles btnExportData.Click
Dim dt As DataTable
dt = CreateDataSource()
Dim filename As String = "attachment; filename=DistComplain.xls"
Response.ClearContent()
Response.AddHeader("content-disposition", filename)
Response.ContentType = "application/vnd.ms-excel"
Dim tab As String = ""
For Each dc As DataColumn In dt.Columns
Response.Write((tab + dc.ColumnName))
tab = "" & vbTab
Next
Response.Write("" & vbLf)
Dim i As Integer
For Each dr As DataRow In dt.Rows
tab = ""
i = 0
Do While (i < dt.Columns.Count)
Response.Write((tab + dr(i).ToString))
tab = "" & vbTab
i = (i + 1)
Loop
Response.Write("" & vbLf)
Next
Response.End()
End Sub
CreateDataSource() is the table that gets created in memory. Then theres other buttons that call it to fill the gridview. Right now it successfully complies and runs, and then it successfully creates the file. Although, when the file tries to open I get this error...
This happens when I try both xls and csv files. Something is not getting translated right. Any solutions?
(Written with help from Google) Create an export using the StringWriter class:
Public Shared Sub ExportDataSetToExcel(ds As DataSet, filename As String)
Dim response As HttpResponse = HttpContext.Current.Response
'Clean response object
response.Clear()
response.Charset = ""
'Set response header
response.ContentType = "application/vnd.ms-excel"
response.AddHeader("Content-Disposition", "attachment;filename=""" & filename & """")
'Create StringWriter and use to create CSV
Using sw As New StringWriter()
Using htw As New HtmlTextWriter(sw)
'Instantiate DataGrid
Dim dg As New DataGrid()
dg.DataSource = ds.Tables(0)
dg.DataBind()
dg.RenderControl(htw)
response.Write(sw.ToString())
response.[End]()
End Using
End Using
End Sub
You just need to pass the function the DataSet and the File Name. If you do not want to edit your CreateDataSource() function, you can merge it into a DataSet first like so:
Dim dt As DataTable = CreateDataSource()
Dim ds As New DataSet
ds.Merge(dt)
Your question is about why you're getting the message about being unable to open the file, correct?
According to Microsoft, this occurs when you have the "Ignore other applications that use Dynamic Data Exchange (DDE)" setting turned on. (See here). The link includes instructions to change the setting.

ASP.Net Generate and Save CSV using System.Web.HttpResponse

Hi I'm using the following code to generate a CSV file containing data from a dataset. The CSV generates fine and a prompt box displays so that the user can either open or save the csv file. What I'm wondering is whether it's possible to save the CSV file?
Thanks for any help
Edited with solution (NB I don't need the prompt box to open as I'll be writing additional code to generate and save a number of files and then display links to them). This saves the dataset as a CSV on our server.
Protected Sub exportBtn_click(ByVal sender As Object, ByVal e As System.EventArgs)
Dim ds1 As DataSet
ds1 = csvdownload.ReturnNewPledges()
Dim csv As New chartnew.csvhelper
Dim strData1 As String = csv.Export(ds1, True)
Dim todaydate As Date = Date.Now.Date
Dim todaystr As String = todaydate.ToString("ddMMyyyy")
Dim filename As String = todaystr & "_filename.csv"
Dim saveDir As String = "writedir\files\"
Dim appPath As String = Request.PhysicalApplicationPath
Dim filePath As String = appPath + saveDir + filename
Dim writer As New StreamWriter(filePath)
writer.Write(strData1)
writer.Close()
End Sub
Use a StreamWriter:
Dim SwFromFile As StreamWriter = New StreamWriter(fileName)
SwFromFile.Write(strData1)
SwFromFile.Flush()
SwFromFile.Close()

Resources