Exporte dataGridView a Excel con todo el formato de celdas

Tengo este código que sé que funciona rápido

CopyAlltoClipboard(dataGridViewControl); Microsoft.Office.Interop.Excel.Application xlexcel; Microsoft.Office.Interop.Excel.Workbook xlWorkBook; Microsoft.Office.Interop.Excel.Worksheet xlWorkSheet; object misValue = System.Reflection.Missing.Value; xlexcel = new Excel.Application(); xlexcel.Visible = true; xlWorkBook = xlexcel.Workbooks.Add(misValue); xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1); xlWorkSheet.Name = page.Name; Excel.Range CR = (Excel.Range)xlWorkSheet.Cells[1, 1]; CR.Select(); xlWorkSheet.PasteSpecial(CR, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, true); ((Microsoft.Office.Interop.Excel.Range)xlWorkSheet.Range["A1"]).EntireColumn.Delete(null); // delete the first column that has rows indexes xlWorkBook.SaveAs(fileName); private void CopyAlltoClipboard(DataGridView dataGridViewControl) { dataGridViewControl.SelectAll(); DataObject dataObj = dataGridViewControl.GetClipboardContent(); if (dataObj != null) Invoke((Action)(() => { Clipboard.SetDataObject(dataObj); })); } 

El código funciona bien, pero solo copia los valores es el Excel, no copia también el formato de las celdas (texto de envoltura, color de fondo, fuente, bordes, etc.) ¿Alguien puede ayudarme con este? ¿Cómo completar este código tiene el formato exacto como en DataGridView?

Actualización: ahora disponible en GitHub: https://github.com/MeaningOfLights/DataGridToHTML


Estoy luchando por entender por qué esto no es un duplicado. Hay ejemplos en toda la red y aquí .

Para mi sorpresa y después de una gran cantidad de investigaciones, no hay ejemplos completos de Exportar DataGridView a HTML o Excel con formato en cualquier lugar de Internet, hasta ahora 🙂

Al mirar este código en su pregunta, ha descubierto cuán lento es copiar grandes conjuntos de datos con Interop y ha optado por usar el Portapapeles en su lugar:

 dataGridViewControl.SelectAll(); DataObject dataObj = dataGridViewControl.GetClipboardContent(); if (dataObj != null) Invoke((Action)(() => { Clipboard.SetDataObject(dataObj); })); 

El quid de esta pregunta es: usar el portapapeles en un DataGridView no contiene el formato de celda. Debido a que el portapapeles no contiene el formato, vuelve al problema original de la lentitud en el funcionamiento al tener que establecer Estilos de celda individualmente, lo que con Interop es muy, muy lento.

En este caso, podría funcionar mejor para su proyecto crear archivos de Excel utilizando XML en lugar de Interop . Aunque primero pensé que esto sería una buena solución y la otra respuesta aquí de DartAlex demuestra que, pensé en codificar una respuesta que puede usar con el método del Portapapeles. Obtener una copia HTML de DataGridView con formato y pegarlo en Excel:

DataGridView a la tabla HTML con formato y luego a Excel

enter image description here

 //==================================================== //DataGridView Export To HTML by Jeremy Thompson: https://stackoverflow.com/questions/39210329/ //==================================================== public string ConvertDataGridViewToHTMLWithFormatting(DataGridView dgv) { StringBuilder sb = new StringBuilder(); //create html & table sb.AppendLine("
"); sb.AppendLine(""); //create table header for (int i = 0; i < dgv.Columns.Count; i++) { sb.Append(DGVHeaderCellToHTMLWithFormatting(dgv, i)); sb.Append(DGVCellFontAndValueToHTML(dgv.Columns[i].HeaderText, dgv.Columns[i].HeaderCell.Style.Font)); sb.AppendLine(""); } sb.AppendLine(""); //create table body for (int rowIndex = 0; rowIndex < dgv.Rows.Count; rowIndex++) { sb.AppendLine(""); foreach (DataGridViewCell dgvc in dgv.Rows[rowIndex].Cells) { sb.AppendLine(DGVCellToHTMLWithFormatting(dgv, rowIndex, dgvc.ColumnIndex)); string cellValue = dgvc.Value == null ? string.Empty : dgvc.Value.ToString(); sb.AppendLine(DGVCellFontAndValueToHTML(cellValue, dgvc.Style.Font)); sb.AppendLine(""); } sb.AppendLine(""); } //table footer & end of html file sb.AppendLine("
"); return sb.ToString(); } //TODO: Add more cell styles described here: https://msdn.microsoft.com/en-us/library/1yef90x0(v=vs.110).aspx public string DGVHeaderCellToHTMLWithFormatting(DataGridView dgv, int col) { StringBuilder sb = new StringBuilder(); sb.Append(""); return sb.ToString(); } public string DGVCellToHTMLWithFormatting(DataGridView dgv, int row, int col) { StringBuilder sb = new StringBuilder(); sb.Append(""); return sb.ToString(); } public string DGVCellColorToHTML(Color foreColor, Color backColor) { if (foreColor.Name == "0" && backColor.Name == "0") return string.Empty; StringBuilder sb = new StringBuilder(); sb.Append(" style=\""); if (foreColor.Name != "0" && backColor.Name != "0") { sb.Append("color:#"); sb.Append(foreColor.R.ToString("X2") + foreColor.G.ToString("X2") + foreColor.B.ToString("X2")); sb.Append("; background-color:#"); sb.Append(backColor.R.ToString("X2") + backColor.G.ToString("X2") + backColor.B.ToString("X2")); } else if (foreColor.Name != "0" && backColor.Name == "0") { sb.Append("color:#"); sb.Append(foreColor.R.ToString("X2") + foreColor.G.ToString("X2") + foreColor.B.ToString("X2")); } else //if (foreColor.Name == "0" && backColor.Name != "0") { sb.Append("background-color:#"); sb.Append(backColor.R.ToString("X2") + backColor.G.ToString("X2") + backColor.B.ToString("X2")); } sb.Append(";\""); return sb.ToString(); } public string DGVCellFontAndValueToHTML(string value,Font font) { //If no font has been set then assume its the default as someone would be expected in HTML or Excel if (font == null || font == this.Font && !(font.Bold | font.Italic | font.Underline | font.Strikeout)) return value; StringBuilder sb = new StringBuilder(); sb.Append(" "); if (font.Bold) sb.Append(""); if (font.Italic) sb.Append(""); if (font.Strikeout) sb.Append(""); //The element was deprecated in HTML 4.01. The new HTML 5 tag is: text-decoration: underline if (font.Underline) sb.Append(""); string size = string.Empty; if (font.Size != this.Font.Size) size = "font-size: " + font.Size + "pt;"; //The tag is not supported in HTML5. Use CSS or a span instead. if (font.FontFamily.Name != this.Font.Name) { sb.Append(""); } sb.Append(value); if (font.FontFamily.Name != this.Font.Name) sb.Append(""); if (font.Underline) sb.Append(""); if (font.Strikeout) sb.Append(""); if (font.Italic) sb.Append(""); if (font.Bold) sb.Append(""); return sb.ToString(); } public string DGVCellAlignmentToHTML(DataGridViewContentAlignment align) { if (align == DataGridViewContentAlignment.NotSet) return string.Empty; string horizontalAlignment = string.Empty; string verticalAlignment = string.Empty; CellAlignment(align, ref horizontalAlignment, ref verticalAlignment); StringBuilder sb = new StringBuilder(); sb.Append(" align='"); sb.Append(horizontalAlignment); sb.Append("' valign='"); sb.Append(verticalAlignment); sb.Append("'"); return sb.ToString(); } private void CellAlignment(DataGridViewContentAlignment align, ref string horizontalAlignment, ref string verticalAlignment) { switch (align) { case DataGridViewContentAlignment.MiddleRight: horizontalAlignment = "right"; verticalAlignment = "middle"; break; case DataGridViewContentAlignment.MiddleLeft: horizontalAlignment = "left"; verticalAlignment = "middle"; break; case DataGridViewContentAlignment.MiddleCenter: horizontalAlignment = "centre"; verticalAlignment = "middle"; break; case DataGridViewContentAlignment.TopCenter: horizontalAlignment = "centre"; verticalAlignment = "top"; break; case DataGridViewContentAlignment.BottomCenter: horizontalAlignment = "centre"; verticalAlignment = "bottom"; break; case DataGridViewContentAlignment.TopLeft: horizontalAlignment = "left"; verticalAlignment = "top"; break; case DataGridViewContentAlignment.BottomLeft: horizontalAlignment = "left"; verticalAlignment = "bottom"; break; case DataGridViewContentAlignment.TopRight: horizontalAlignment = "right"; verticalAlignment = "top"; break; case DataGridViewContentAlignment.BottomRight: horizontalAlignment = "right"; verticalAlignment = "bottom"; break; default: //DataGridViewContentAlignment.NotSet horizontalAlignment = "left"; verticalAlignment = "middle"; break; } } //Easy repro - copy/paste all this code in a Winform app! public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { string configFile = System.IO.Path.Combine(Application.StartupPath.Replace("\\bin\\Debug", ""), "testData.csv"); List rows = System.IO.File.ReadAllLines(configFile).Select(x => x.Split(',')).ToList(); DataTable dataTable = new DataTable(); dataTable.Columns.Add("testing"); dataTable.Columns.Add("one"); dataTable.Columns.Add("two"); dataTable.Columns.Add("three"); rows.ForEach(x => { dataTable.Rows.Add(x); }); this.dgv.DataSource = dataTable; dgv.Columns[0].HeaderCell.Style.Font = new Font(this.Font, FontStyle.Strikeout); dgv[0, 0].Style.BackColor = Color.Aqua; dgv[1, 0].Style.Alignment = DataGridViewContentAlignment.BottomRight; dgv[2, 0].Style.Font = new Font(new FontFamily("Calibri"),(float)16); dgv[3, 0].Style.ForeColor = Color.Red; dgv[0, 1].Style.Font = new Font(this.Font, FontStyle.Bold); dgv[1, 1].Style.Font = new Font(this.Font, FontStyle.Underline); dgv[2, 1].Style.Font = new Font(this.Font, FontStyle.Italic); dgv[3, 1].Style.Font = new Font(this.Font, FontStyle.Bold | FontStyle.Underline); dgv[3, 1].Style.ForeColor = Color.Green; dgv[3, 1].Style.BackColor = Color.Yellow; dgv[0, 2].Style.Font = new Font(new FontFamily("Times New Roman"), (float)18); dgv[1, 2].Style.Font = new Font(new FontFamily("Georgia"), (float)12); dgv[2, 2].Style.Font = new Font(new FontFamily("Arial"), (float)14); dgv[3, 2].Style.Font = new Font(new FontFamily("Verdana"), (float)18); dgv[0, 3].Style.Font = new Font(new FontFamily("Courier New"), (float)11); dgv[1, 3].Style.Font = new Font(new FontFamily("Lucida Console"), (float)18); dgv[2, 3].Style.Font = new Font(new FontFamily("Times"), (float)14); dgv[3, 3].Style.Font = new Font(new FontFamily("serif"), (float)12); } private void button1_Click(object sender, EventArgs e) { string dgvToHTMLTable = ConvertDataGridViewToHTMLWithFormatting(dgv); Clipboard.SetText(dgvToHTMLTable); }

TestData.csv:

Magia, Abra, Cadabra, Boom!
Codificación, Diversión, YeeHaa, ABS TableName
Hola, mundo, Population.html, TABLA 1.
Demografía, 310102.xls, Comp.html, TABLA 2.

El quid de la cuestión es utilizar el Portapapeles en un DataGridView que no contiene formato de celda. Como el Portapapeles no contiene el formato, vuelve al problema original de la lentitud en el funcionamiento al tener que establecer Estilos de celda individualmente, lo que con Interop es muy, muy lento.

En este caso, funcionará mejor para crear archivos de Excel utilizando XML en lugar de Interop. A continuación se muestra un método que utiliza ClosedXML para exportar un DataGridView a Excel con formato.

 using ClosedXML.Excel; public void ExportToExcelWithFormatting(DataGridView dataGridView1) { string fileName; SaveFileDialog saveFileDialog1 = new SaveFileDialog(); saveFileDialog1.Filter = "xls files (*.xlsx)|*.xlsx|All files (*.*)|*.*"; saveFileDialog1.Title = "To Excel"; saveFileDialog1.FileName = this.Text + " (" + DateTime.Now.ToString("yyyy-MM-dd") + ")"; if (saveFileDialog1.ShowDialog() == DialogResult.OK) { fileName = saveFileDialog1.FileName; var workbook = new XLWorkbook(); var worksheet = workbook.Worksheets.Add(this.Text); for (int i = 0; i < dataGridView1.Columns.Count; i++) { worksheet.Cell(1, i + 1).Value = dataGridView1.Columns[i].Name; } for (int i = 0; i < dataGridView1.Rows.Count; i++) { for (int j = 0; j < dataGridView1.Columns.Count; j++) { worksheet.Cell(i + 2, j + 1).Value = dataGridView1.Rows[i].Cells[j].Value.ToString(); if (worksheet.Cell(i + 2, j + 1).Value.ToString().Length > 0) { XLAlignmentHorizontalValues align; switch (dataGridView1.Rows[i].Cells[j].Style.Alignment) { case DataGridViewContentAlignment.BottomRight: align = XLAlignmentHorizontalValues.Right; break; case DataGridViewContentAlignment.MiddleRight: align = XLAlignmentHorizontalValues.Right; break; case DataGridViewContentAlignment.TopRight: align = XLAlignmentHorizontalValues.Right; break; case DataGridViewContentAlignment.BottomCenter: align = XLAlignmentHorizontalValues.Center; break; case DataGridViewContentAlignment.MiddleCenter: align = XLAlignmentHorizontalValues.Center; break; case DataGridViewContentAlignment.TopCenter: align = XLAlignmentHorizontalValues.Center; break; default: align = XLAlignmentHorizontalValues.Left; break; } worksheet.Cell(i + 2, j + 1).Style.Alignment.Horizontal = align; XLColor xlColor = XLColor.FromColor(dataGridView1.Rows[i].Cells[j].Style.SelectionBackColor); worksheet.Cell(i + 2, j + 1).AddConditionalFormat().WhenLessThan(1).Fill.SetBackgroundColor(xlColor); worksheet.Cell(i + 2, j + 1).Style.Font.FontName = dataGridView1.Font.Name; worksheet.Cell(i + 2, j + 1).Style.Font.FontSize = dataGridView1.Font.Size; } } } worksheet.Columns().AdjustToContents(); workbook.SaveAs(fileName); //MessageBox.Show("Done"); } } 

Parece que encontré una solución usando interop y EPPlus. Utilicé el código anterior solo para copiar los valores en Excel y luego uso este código a continuación (código EPPlus) para tomar el formato de dataGridView. Este código depende de lo que quiera tomar de dataGridView. En este código a continuación, quería tomar el WrapText de la primera fila y los colores de fondo de cada celda escrita

 private void FinalizeWorkbook(DataTableReportParam reportParam, DataGridView dataGridViewControl) { FileInfo newFile = new FileInfo(reportParam.FileName); ExcelPackage pck = new ExcelPackage(newFile); IWorksheet worksheet = pck.Workbook.Worksheets[1]; // wrap text and color the crashes with problems (header) for (int col = 1; col < = worksheet.Dimension.End.Column; col++) { worksheet[1, col].WrapText = true; worksheet[1, col].AutofitRows(); if (String.Compare(dataGridViewControl[col - 1, 0].Style.BackColor.Name, "0") != 0) worksheet[1, col].CellStyle.Color = dataGridViewControl[col - 1, 0].Style.BackColor; } // color the cells for (int row = 2; row <= worksheet.Dimension.End.Row; row++) { for (int col = 1; col <= worksheet.Dimension.End.Column; col++) { if (String.Compare(dataGridViewControl[col - 1, row - 1].Style.BackColor.Name, "0") != 0) worksheet[row, col].CellStyle.Color = dataGridViewControl[col - 1, row - 1].Style.BackColor; } } //save and dispose pck.Save(); pck.Dispose(); }