After save changes from dataGridView,when open xlsx file by excel this error occurs:
We found a problem with some content in 'Book1.xlsx' Do you want us to recover as much as we can? If you trust the source of this workbook, click Yes.
for (int i = 0; i < dataGridView1.RowCount - 1; i++)
{
if (sh.GetRow(i) == null)
sh.CreateRow(i);
for (int j = 0; j < dataGridView1.ColumnCount; j++)
{
if (sh.GetRow(i).GetCell(j) == null)
sh.GetRow(i).CreateCell(j);
if (dataGridView1[j, i].Value != null)
{
sh.GetRow(i).GetCell(j).SetCellValue(dataGridView1[j, i].Value.ToString());
}
using (var fs = new FileStream("Book1.xlsx", FileMode.Open, FileAccess.ReadWrite))
{
wb.Write(fs);
}
}
}
For one thing, you're writing the workbook multiple times, once for each cell. You really should only write the workbook once, at the end, when you are finished making changes.
Also, it is not clear from your question how you are creating the workbook or the sheet. I added some code below to make it clear how that should look as well.
Try it like this:
XSSFWorkbook wb = new XSSFWorkbook();
ISheet sheet = wb.GetSheet("Sheet1") ?? wb.CreateSheet("Sheet1");
for (int i = 0; i < dataGridView1.RowCount; i++)
{
IRow row = sheet.GetRow(i) ?? sh.CreateRow(i);
for (int j = 0; j < dataGridView1.ColumnCount; j++)
{
ICell cell = row.GetCell(j) ?? row.CreateCell(j);
if (dataGridView1[j, i].Value != null)
{
cell.SetCellValue(dataGridView1[j, i].Value.ToString());
}
}
}
using (var fs = new FileStream("Book1.xlsx", FileMode.Create, FileAccess.Write))
{
wb.Write(fs);
}
Related
I have a dataGridView in a WindowsForms. I want a button to export this dataGridView to an Excel Worksheet.
using Excel = Microsoft.Office.Interop.Excel;
Excel.Application ExcelApp;
Excel.Workbook ExcelWorkBook;
Excel.Worksheet ExcelWorkSheet;
ExcelApp = new Excel.Application();
ExcelWorkBook = ExcelApp.Workbooks.Add(Missing.Value);
ExcelWorkSheet = (Excel.Worksheet)ExcelWorkBook.Worksheets.get_Item(1);
try
{
for (int i = 0; i <= dataGridView1.RowCount - 1; ++i)
{
for (int j = 0; j <= dataGridView1.ColumnCount - 1; ++j)
{
DataGridViewCell cell = dataGridView1[j, i];
ExcelWorkSheet.Cells[i + 1, j + 1] = cell.Value;
}
}
} catch (Exception ex) { /*somestuff*/ }
// save ExcelWorkbook
This code works. But unfortunately the time complexity is bad. So I'm forced to implement a progressbar. If i wouldn't do it, the user would be thinking the program crashed, while exporting a big datagridview. Needless to say, this will of course slow down the export even more. (progressbar code is not included in this question)
I wonder, if there is a method to export a datagrid to an excel faster.
Thanks for the hints to use another third-party library.
I was giving EPPLus a chance.
Here is the code:
private void ExportWithEPPlus(string filepath)
{
using (ExcelPackage excelPackage = new ExcelPackage())
{
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets.Add("Sheet 1");
try
{
for (int i = 0; i <= dataGridView1.RowCount - 1; ++i)
{
for (int j = 0; j <= dataGridView1.ColumnCount - 1; ++j)
{
DataGridViewCell cell = dataGridView1[j, i];
worksheet.Cells[i + 1, j + 1].Value = cell.Value;
}
}
catch (Exception ex) { /*somestuff*/ }
finally
{
FileInfo fi = new FileInfo(#filepath);
excelPackage.SaveAs(fi);
}
}
}
It's fast like hell. The drawback is: It's memory hungry.
I export datagridview to Excel. Now, I want to save this Excel file with unique name like date wise in a specific folder. And also I want to save the file without using save dialog file.
This code is used for only exporting datagridview to Excel.
if (dataGridView1.Rows.Count > 0)
{
Microsoft.Office.Interop.Excel._Application XcelApp = new Microsoft.Office.Interop.Excel.Application();
XcelApp.Application.Workbooks.Add(Type.Missing);
for (int i = 1; i < dataGridView1.Columns.Count + 1; i++)
{
XcelApp.Cells[1, i] = dataGridView1.Columns[i - 1].HeaderText;
}
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
for (int j = 0; j < dataGridView1.Columns.Count; j++)
{
XcelApp.Cells[i + 2, j + 1] = dataGridView1.Rows[i].Cells[j].Value;
}
}
XcelApp.Columns.AutoFit();
XcelApp.Visible = true;
}
Assuming your excel file is populated correctly. You can use the SaveAs method like below:
XcelApp.ActiveWorkbook.SaveAs(filePath);
I've put the .AutoSizeColumn right before the write Method
int numberOfColumns = sheet.GetRow(rowcount - 1).PhysicalNumberOfCells;
for (int i = 0; i <= numberOfColumns; i++)
{
sheet.AutoSizeColumn(i);
GC.Collect();
}
using (var fileData = new FileStream(#"C:\Temp\Contatti.xlsx", FileMode.Create))
{
wb.Write(fileData);
}
this is an example of the result
The problem also migh be, that PhysicalNumberOfCells can return 1, even if you have a cell lets say in 'Z' column. There is LastCellNum property,you i instead of PhysicalNumberOfCells:
int lastColumNum = sheet.GetRow(0).LastCellNum;
for (int i = 0; i <= lastColumNum; i++)
{
sheet.AutoSizeColumn(i);
GC.Collect();
}
using (var fileData = new FileStream(#"D:\Contatti.xlsx", FileMode.Create))
{
wb.Write(fileData);
}
I have this sample:
IRichTextString richText = cell.RichStringCellValue;
for (int index = 0; index < richText.String.Length; index++)
{
short fontId = richText.GetFontAtIndex(index);
...
}
When calling GetFontAtIndex for the first time (index = 0), I'm getting Object reference not set to an instance of an object:
at NPOI.XSSF.UserModel.XSSFRichTextString.ToCTFont(CT_RPrElt pr) at
NPOI.XSSF.UserModel.XSSFRichTextString.GetFontOfFormattingRun(Int32
index)
The file has xlsx extention. For xls this code works fine.
Also tried this, with no luck:
for (int i = 0; i < richText.NumFormattingRuns; i++)
{
int startIdx = richText.GetIndexOfFormattingRun(i);
int length = richText.GetLengthOfFormattingRun(i);
IFont font = richText.GetFontOfFormattingRun(i);
...
}
Where should be the problem? For xlsx files is no way of getting the font for a text?
Edit:
The file is read by this:
IWorkbook iw = WorkbookFactory.Create(new FileStream(excelFilePath, FileMode.Open, FileAccess.Read));
ISheet sheet = iw.GetSheet(sheetName);
for (int j = rowStart; j <= sheet.LastRowNum; j++)
{
IRow row = sheet.GetRow(j);
if (row != null)
{
for (int i = row.FirstCellNum; i < row.LastCellNum; i++)
{
ICell cell = row.GetCell(i);
...
But the cell object has a lot of errors:
Do I have to include some references? Currently I've included:
using NPOI.HSSF.Model;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using NPOI.XSSF.UserModel;
using NPOI.XSSF.Model;
using NPOI.XSSF.Util;
private void write(object sender, EventArgs e)
{
FileStream outputFileStream = new FileStream("test.txt", FileMode.Create, FileAccess.Write);
StreamWriter writer = new StreamWriter(outputFileStream);
Random r = new Random();
for (int i = 0; i < dataGridView1.RowCount; i++)
{
for (int j = 0; j < dataGridView1.ColumnCount; j++)
{
double d = 0;
Double.TryParse(Convert.ToString(dataGridView1.Rows[i].Cells[j].Value), out d);
writer.Write(d + "\t");
}
writer.Write("\n");
}
writer.Close();
outputFileStream.Close();
}
So this is the method to write the text file. It works fine because I have opened it successfully with an Excel. I even tried to copy and paste it and it would work. Now the problem is.....
private void read(object sender, EventArgs e)
{
char TAB = '\t';
char NEWLINE = '\n';
FileStream inputFileStream = new FileStream("test.txt", FileMode.Open, FileAccess.Read);
StreamReader reader = new StreamReader(inputFileStream);
string line;
string[] fields;
for (int i = 0; i < dataGridView1.RowCount; i++)
{
for (int j = 0; j < dataGridView1.ColumnCount; j++)
{
line = reader.ReadLine();
fields = line.Split(TAB, NEWLINE);
dataGridView1.Rows[i].Cells[j].Value = fields;
}
}
inputFileStream.Close();
reader.Close();
}
However when I read the file back onto a DataGridView it does not work properly. Now this is the exact text file that I wrote on my code. Instead what happens is that it is displayed on only 1 row. How do I get back the amount of columns and rows from what the user entered? I prefer keeping it a text file.
I have used default properties for my dataGridView1
RowCount returns displayed rows count. So here it returns 1 because you are displaying one row and it's empty:
for (int i = 0; i < dataGridView1.RowCount; i++)
Instead of this, you should create new rows and add it to the Rows collection like this:
// Use File.ReadAllLines, it's easier
string[] lines = File.ReadAllLines("test.txt");
foreach(line in lines)
{
var text = line.Split('\t','\n');
dataGridView1.Rows.Add(text);
}
System.IO.StreamReader file = new System.IO.StreamReader("yourfile.txt");
string[] columnnames = file.ReadLine().Split(' ');
DataTable dt = new DataTable();
foreach (string c in columnnames)
{
dt.Columns.Add(c);
}
string newline;
while ((newline = file.ReadLine()) != null)
{
DataRow dr = dt.NewRow();
string[] values = newline.Split(' ');
for (int i = 0; i < values.Length; i++)
{
dr[i] = values[i];
}
dt.Rows.Add(dr);
}
file.Close();
dataGridView1.DataSource = dt;
try this one with your own delimiters i.e. \t and \n. And First try to search your problems on internet before posting queries and try to solve them by yourself. I am not giving full code.