Generate sheet separated excel with GridView in MVC web enviroment - c#

I use the following code, and I'm wondering if I can somehow make this a sheet separated excel file with each row in its own sheet.
DataTable dt = new DataTable();
SqlConnection objcon = new SqlConnection(connectionString);
string sql = "SELECT * FROM table";
SqlDataAdapter objda = new SqlDataAdapter(sql, objcon);
objda.Fill(dt);
GridView gvreport = new GridView();
gvreport.DataSource = dt;
gvreport.DataBind();
string date = DateTime.Now.ToString("yyyy_MM_dd_HHmmss");
string fileName = string.Format("TheFilename_{0}.xls", date);
Response.ClearContent();
Response.AddHeader("content-disposition", "attachment; filename=" + fileName);
Response.ContentType = "application/excel";
System.IO.StringWriter sw = new System.IO.StringWriter();
HtmlTextWriter htw = new HtmlTextWriter(sw);
gvreport.RenderControl(htw);
Response.Write(sw.ToString());
Response.End();
return null;
I know that this doesn't exactly generate a real excel file but rather tricks the browser to create one. Which is probably why its a bit more complicated to make advanced excel files.
I've already tried with things like setting a max amount of rows per gridview page and so on.
Are there any way of doing this, or in general create an excel file in another way that allows me to do this. Without needing Windows office or similar installed.
Thank you for your time.

I would use something like OpenXML. I'm guess the reason your creating the excel file on the fly is because you don't want to use interop objects because they are too heavy and bulky. However you still need the flexibility to manipulate objects. I would look at the OpenXML library as it contains many of the manipulations that your looking for the code would look something like below to get you started. In the above link you'll notice there is an example for mvc. Which really doesn't change the underlying code much.
using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Create(filename, SpreadsheetDocumentType.Workbook))
{
WorkbookPart workBookPart = spreadSheetDocument.AddWorkbookPart();
WorksheetPart worksheetPart = workBookPart.AddNewPart<WorksheetPart>();
WorkbookStylesPart workBookStylesPart = workBookPart.AddNewPart<WorkbookStylesPart>();
Stylesheet stylesheet = new CustomStyleSheet();
stylesheet.Save(workBookStylesPart);
string relId = workBookPart.GetIdOfPart(worksheetPart);
Workbook workbook = new Workbook();
Worksheet worksheet = new Worksheet();
var columns = CreateColumns();
Sheets sheets = new Sheets();
Sheet sheet = new Sheet { Name = "mySheet", SheetId = 1, Id = relId };
sheets.Append(sheet);
worksheet.Append(columns);
SheetData sheetData = CreateSheetData();
worksheet.Append(sheetData);
workbook.Append(sheets);
worksheetPart.Worksheet = worksheet;
worksheetPart.Worksheet.Save();
spreadSheetDocument.WorkbookPart.Workbook = workbook;
spreadSheetDocument.WorkbookPart.Workbook.Save();
//Now I want to add an empty sheet
sheet = new Sheet { Name = "mySheet2", SheetId = 2, Id = relId };
sheets.Append(sheet);
spreadSheetDocument.WorkbookPart.Workbook.Save();
spreadSheetDocument.Close();
}

Related

Generate Pivot Table in CSV Dynamically from Dictionary with Documentformat.Openxml

I'm using Documentformat.Openxml package
[https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet?view=openxml-2.8.1]
to generate a Pivot table in a CSV dynamically from a Dictionary<string, List<string>> object which has a structure similar to this:
I'm following the Sample here to create the table which should look somewhat like:
but not sure about how to create the table from the dictionary of data.
My current code snippet:
// Add a WorkbookPart to the document.
WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
// Add a WorksheetPart to the WorkbookPart.
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
// Add Sheets to the Workbook.
Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild(new Sheets());
// Append a new worksheet and associate it with the workbook.
Sheet sheet = new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "mySheet"
};
sheets.Append(sheet);
Worksheet worksheet = new Worksheet();
SheetData sheetData = new SheetData();
Row row = new Row();
Cell cell = new Cell()
{
CellReference = "A1",
DataType = CellValues.String,
CellValue = new CellValue("Microsoft")
};
row.Append(cell);
sheetData.Append(row);
worksheet.Append(sheetData);
worksheetPart.Worksheet = worksheet;
// Close the document.
spreadsheetDocument.Close();
In the above snippet, you can see that I'm hardcoding the cell value but in reality I want to populate it dynamically from the dictionary and there is a large amount of data in it. What is the best way to do this? Can someone please help?
As your question shows you want to generate excel file using Open XML. You should take a look at these answers
1 Using open XML to create excel file
2 How do you convert Excel to CSV using OpenXML SDK?
and for other websites you can visit
3 https://www.c-sharpcorner.com/article/export-and-import-excel-file-using-closedxml-in-asp-net-mvc/

Open XML Creating Multiple sheets

I am trying to create an Excel file using Open XML C#, where there are multiple sheets in my excel file that needs to be created. Please find my code below and help me with a solution.
When I open the excel only 1 sheet is present, sheet 2 is getting overwritten in sheet 1.
public void CreateExcelFile(string filePath , List<SheetData> excelSheet)
{
var sheetCount = GetSheetCount(filePath);
using (SpreadsheetDocument spreedDoc = SpreadsheetDocument.Create(filePath,
DocumentFormat.OpenXml.SpreadsheetDocumentType.Workbook))
{
Workbook workbook1 = new Workbook();
workbook1.AddNamespaceDeclaration("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");
FileVersion fileVersion1 = new FileVersion() { ApplicationName = "xl", LastEdited = "5", LowestEdited = "4", BuildVersion = "9302" };
WorkbookProperties workbookProperties1 = new WorkbookProperties() { FilterPrivacy = true, DefaultThemeVersion = (UInt32Value)124226U };
BookViews bookViews1 = new BookViews();
WorkbookView workbookView1 = new WorkbookView() { XWindow = 240, YWindow = 105, WindowWidth = (UInt32Value)14805U, WindowHeight = (UInt32Value)8010U, ActiveTab = (UInt32Value)2U };
bookViews1.Append(workbookView1);
Sheets sheetCol = new Sheets();
int ctr = 0;
foreach (var sheetToCreate in excelSheet)
{
ctr++;
//WorksheetPart worksheetPart = null;
//worksheetPart = wbPart.AddNewPart<WorksheetPart>();
//var sheetData = new SheetData();
//worksheetPart.Worksheet = new Worksheet(sheetData);
//worksheetPart.Worksheet = new Worksheet(sheetToCreate);
string sheetName = "Sheet";
sheetName += ctr.ToString();
var sheet = new Sheet()
{
Id = "rId" + ctr,
SheetId = (UInt32)ctr,
Name = sheetName
};
sheet.Append(sheetToCreate);
sheetCol.Append(sheet);
//wbPart.Workbook.Sheets.AppendChild(sheet);
//wbPart.Workbook.Append(sheetCol);
workbook1.Sheets.Append(sheet);
//var workingSheet = ((WorksheetPart)wbPart.GetPartById(sheet.Id)).Worksheet;
}
workbook1.Append(fileVersion1);
workbook1.Append(workbookProperties1);
workbook1.Append(bookViews1);
workbook1.Append(sheetCol);
//Set Border
//wbPark
workbook1.Save();
}
}
When dealing with OpenXML, the "OpenXML Productivity Tool" (downloadable from Microsoft's site) is your friend. When I work with creating documents with OpenXML, I do the following:
Create a document similar to what I want using Excel or Word (in this case, an Excel spreadsheet document with two sheets (I labeled them "First" and "Second")).
Open the document up in the productivity tool
Reflect the code (a button on the Tool's UI)
Copy/paste the code I want.
If I do that, I end up with all the code I need. I've boiled it down to the essentials to answer your question. Excel will complain that the document produced below is corrupt (since all it includes is the code to create the two sheets - and leaves lots of other required code out - all of which is stealable from the tool). However, if you tell Excel "Repair this for me", you will get a document with two sheets, labeled "First" and "Second"
WorkbookPart workbookPart1 = document.AddWorkbookPart();
Workbook workbook1 = new Workbook() { MCAttributes = new MarkupCompatibilityAttributes() { Ignorable = "x15 xr xr6 xr10 xr2" } };
//lots of namespace declarations - copy them from the tool (they are needed!!)
//lots of code for WorkbookProperties, etc (you can get this from Tool)
Sheets sheets1 = new Sheets();
Sheet sheet1 = new Sheet() { Name = "First", SheetId = (UInt32Value)1U, Id = "rId1" };
Sheet sheet2 = new Sheet() { Name = "Second", SheetId = (UInt32Value)2U, Id = "rId2" };
sheets1.Append(sheet1);
sheets1.Append(sheet2);
//More elided code
workbook1.Append(sheets1);
workbookPart1.Workbook = workbook1;
I strongly recommend that you keep the pluralization of things like Sheet and Sheets clear. Your code has Sheets sheetCol = new Sheets();, it's not clear after that that sheetCol is a Sheets and not a Sheet.
Then you have something called sheetToCreate which is a SheetData object. Again, that's confusing - call it something clear like thisSheetData or something. I really strongly recommend that you follow the OpenXML naming as much as you can, otherwise it is extremely hard to follow what's going on.
The Productivity Tool is also handy once you've written out your file. You can see what you created, often spotting what the error is. In addition, there's a validate button that usually (not always) points you to problems in the structure of your documents.
Good luck. Prepare to get better at cussing at your code!

The process cannot access the file because it is being used by another process using spreadsheet document

Please help me to solve the problem, when i save the excel file throw this error i convert the excel file to zip file.
zip.Save(#"" + root + "/" + "" + userid + ".zip");
the process cannot access the file because it is being used by another process
my code is below
using (SpreadsheetDocument document = SpreadsheetDocument.Create(excelFilename, SpreadsheetDocumentType.Workbook))
{
WriteExcelFile(ds, document);
}
private static void WriteExcelFile(DataSet ds, SpreadsheetDocument spreadsheet)
{
spreadsheet.AddWorkbookPart();
spreadsheet.WorkbookPart.Workbook = new DocumentFormat.OpenXml.Spreadsheet.Workbook();
spreadsheet.WorkbookPart.Workbook.Append(new BookViews(new WorkbookView()));
// If we don't add a "WorkbookStylesPart", OLEDB will refuse to connect to this .xlsx file !
WorkbookStylesPart workbookStylesPart = spreadsheet.WorkbookPart.AddNewPart<WorkbookStylesPart>("rIdStyles");
Stylesheet stylesheet = new Stylesheet();
workbookStylesPart.Stylesheet = stylesheet;
// Loop through each of the DataTables in our DataSet, and create a new Excel Worksheet for each.
uint worksheetNumber = 1;
foreach (DataTable dt in ds.Tables)
{
// For each worksheet you want to create
string workSheetID = "rId" + worksheetNumber.ToString();
string worksheetName = dt.TableName;
WorksheetPart newWorksheetPart = spreadsheet.WorkbookPart.AddNewPart<WorksheetPart>();
newWorksheetPart.Worksheet = new DocumentFormat.OpenXml.Spreadsheet.Worksheet();
// create sheet data
newWorksheetPart.Worksheet.AppendChild(new DocumentFormat.OpenXml.Spreadsheet.SheetData());
// save worksheet
WriteDataTableToExcelWorksheet(dt, newWorksheetPart);
newWorksheetPart.Worksheet.Save();
// create the worksheet to workbook relation
if (worksheetNumber == 1)
spreadsheet.WorkbookPart.Workbook.AppendChild(new DocumentFormat.OpenXml.Spreadsheet.Sheets());
spreadsheet.WorkbookPart.Workbook.GetFirstChild<DocumentFormat.OpenXml.Spreadsheet.Sheets>().AppendChild(new DocumentFormat.OpenXml.Spreadsheet.Sheet()
{
Id = spreadsheet.WorkbookPart.GetIdOfPart(newWorksheetPart),
SheetId = (uint)worksheetNumber,
Name = dt.TableName
});
worksheetNumber++;
}
spreadsheet.WorkbookPart.Workbook.Save();
}
I see that you have saved your document but you have not closed it. So, it will still be open with the spreadsheetdocument object.
Add the following line after you save the document.
spreadsheet.WorkbookPart.Workbook.Close();
If it doesn't help, kindly provide the full code so that we can look into it further. Good luck.
I think you are not closing all your Workbooks before you try to put them in the Zip-File.
Put this at the end of the Method:
private static void WriteExcelFile(DataSet ds, SpreadsheetDocument spreadsheet)
{
...
spreadsheet.WorkbookPart.Workbook.Close();
}

Setting Font.Bold and Column.Width for Cells in OPEN XML

I've been using OPEN XML to create a spread sheet which can then be downloaded. I want to set the column widths and also change the header to bold. I've done this using Office.Interop.Excel but I'm struggling with this format. My code is below, you can see I've set up a font but have been unable to assign it to my spread sheet, I have got messages saying it's not possible to assign to a tree.
SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(fileName, SpreadsheetDocumentType.Workbook); //Open(fileName, true);
try
{
WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new DocumentFormat.OpenXml.Spreadsheet.Workbook();
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new DocumentFormat.OpenXml.Spreadsheet.Worksheet(new SheetData());
DocumentFormat.OpenXml.Spreadsheet.Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<DocumentFormat.OpenXml.Spreadsheet.Sheets>(new DocumentFormat.OpenXml.Spreadsheet.Sheets());
Sheet sheet = new Sheet() { Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = "Models" };
sheets.Append(sheet);
DocumentFormat.OpenXml.Spreadsheet.Font boldFont = new DocumentFormat.OpenXml.Spreadsheet.Font();
Bold bFontBold = new Bold();
boldFont.Append(bFontBold);
DocumentFormat.OpenXml.Spreadsheet.Worksheet worksheet = new DocumentFormat.OpenXml.Spreadsheet.Worksheet();
SheetData sheetData = new SheetData();
........
Row row = new Row();
Cell cell = new Cell()
{
CellReference = "A" + (intI + 1),
DataType = CellValues.String,
CellValue = new CellValue(ModelBookrowDetail[0])
};
row.Append(cell);
Cell cell2 = new Cell()
{
CellReference = "B" + (intI + 1),
DataType = CellValues.String,
CellValue = new CellValue(ModelBookrowDetail[1])
};
row.Append(cell2);
.........
sheetData.Append(row);
}
worksheet.Append(sheetData);
worksheetPart.Worksheet = worksheet;
spreadsheetDocument.Close();
}
catch
{
spreadsheetDocument.Close();
}
I don't know how set you are on using OpenXml. I might suggest looking into ClosedXml. Up until now it has saved me a lot of manhours. It only has one downside to it, and that is that it doesn't manage Word documents like OpenXml does.
ClosedXml is a more object oriented Open Source Library, and is far more well documented in contrast to what i have experienced with OpenXml.
You can find ClosedXml here:
https://github.com/ClosedXML/ClosedXML
Best of luck
Olliver

opening .xlsx in office 2003

I have created a .xlsx using openxml.
I am not able to open this file in office 2003.. I have also tried using compatibility pack but still the file does not open. What can be done if i need to generate .xlsx that can be opened in office 2003 as well.
Code i am using to generate .xlsx is :
public static void HelloWorldXlsx(string docName)
{
SpreadsheetDocument package = SpreadsheetDocument.Create(docName, SpreadsheetDocumentType.Workbook);
package.AddWorkbookPart();
package.WorkbookPart.Workbook = new Workbook();
WorksheetPart wspart = package.WorkbookPart.AddNewPart<WorksheetPart>();
Cell cell = new Cell();
cell.DataType = CellValues.InlineString;
cell.InlineString = new InlineString(new DocumentFormat.OpenXml.Spreadsheet.Text("Hello World!"));
wspart.Worksheet = new Worksheet(new SheetData(new Row(cell)));
wspart.Worksheet.Save();
package.WorkbookPart.Workbook.AppendChild(new Sheets());
Sheet sheet = new Sheet();
sheet.Id = package.WorkbookPart.GetIdOfPart(wspart);
sheet.SheetId = 1;
sheet.Name = "Hello !";
package.WorkbookPart.Workbook.GetFirstChild<Sheets>().AppendChild<Sheet>(sheet);
package.WorkbookPart.Workbook.Save();
package.Close();
}
Thanks for suggestion. I got the answer to my question .. I had not set cellReference property of Cell in my code.

Categories