将dataGridView以所有单元格格式导出到Excel

13

我有这段代码,我知道它运行得很快

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); }));
}
代码运行正常,但只复制了Excel中的值,并没有复制单元格格式(换行文本、背景色、字体、边框等)。有人能帮我吗?如何使代码完全复制DataGridView中的格式?

3个回答

15

更新:现在可以在GitHub上获取: https://github.com/MeaningOfLights/DataGridToHTML


我很难理解为什么这不是一个重复问题。网上到处都有示例,还有这里
令我惊讶的是,在互联网上没有关于将DataGridView导出为带格式的HTML或Excel的详细示例 - 直到现在:)
查看您问题中的此代码,您已经发现使用Interop复制大型数据集的速度非常慢,并选择改用剪贴板:
dataGridViewControl.SelectAll();
DataObject dataObj = dataGridViewControl.GetClipboardContent();
if (dataObj != null)
    Invoke((Action)(() => { Clipboard.SetDataObject(dataObj); }));

这个问题的核心是 - 在 DataGridView 上使用剪贴板不包含单元格格式。因为剪贴板不包含格式,你又回到了最初的性能问题,需要逐个设置单元格样式,而使用 Interop 很慢。
在这种情况下,使用 XML 创建 Excel 文件可能会更好,而不是使用 Interop。所以,虽然我最初认为这将是一个很好的解决方法,并且 DartAlex 的另一个答案在这里证明了这一点,但我想编写一个可以使用剪贴板方法的答案。获取带有格式的 DataGridView 的 HTML 副本,并将其粘贴到 Excel 中:
DataGridView 转换为带有格式的 HTML 表,然后插入 Excel。

enter image description here

//====================================================
//DataGridView Export To HTML by Jeremy Thompson: https://dev59.com/0FkT5IYBdhLWcg3wAK1E
//====================================================
public string ConvertDataGridViewToHTMLWithFormatting(DataGridView dgv)
{
    StringBuilder sb = new StringBuilder();
    //create html & table
    sb.AppendLine("<html><body><center><table border='1' cellpadding='0' cellspacing='0'>");
    sb.AppendLine("<tr>");
    //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("</td>");
    }
    sb.AppendLine("</tr>");
    //create table body
    for (int rowIndex = 0; rowIndex < dgv.Rows.Count; rowIndex++)
    {
        sb.AppendLine("<tr>");
        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("</td>");
        }
        sb.AppendLine("</tr>");
    }
    //table footer & end of html file
    sb.AppendLine("</table></center></body></html>");
    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("<td");
    sb.Append(DGVCellColorToHTML(dgv.Columns[col].HeaderCell.Style.ForeColor, dgv.Columns[col].HeaderCell.Style.BackColor));
    sb.Append(DGVCellAlignmentToHTML(dgv.Columns[col].HeaderCell.Style.Alignment));
    sb.Append(">");
    return sb.ToString();
}

public string DGVCellToHTMLWithFormatting(DataGridView dgv, int row, int col)
{
    StringBuilder sb = new StringBuilder();
    sb.Append("<td");
    sb.Append(DGVCellColorToHTML(dgv.Rows[row].Cells[col].Style.ForeColor, dgv.Rows[row].Cells[col].Style.BackColor));
    sb.Append(DGVCellAlignmentToHTML(dgv.Rows[row].Cells[col].Style.Alignment));
    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("<b>");
    if (font.Italic) sb.Append("<i>");
    if (font.Strikeout) sb.Append("<strike>");
    
    //The <u> element was deprecated in HTML 4.01. The new HTML 5 tag is: text-decoration: underline
    if (font.Underline) sb.Append("<u>");
    
    string size = string.Empty;
    if (font.Size != this.Font.Size) size = "font-size: " + font.Size + "pt;";

    //The <font> tag is not supported in HTML5. Use CSS or a span instead. 
    if (font.FontFamily.Name != this.Font.Name)
    {
        sb.Append("<span style=\"font-family: ");
        sb.Append(font.FontFamily.Name);
        sb.Append("; ");
        sb.Append(size);
        sb.Append("\">");
    }
    sb.Append(value);
    if (font.FontFamily.Name != this.Font.Name) sb.Append("</span>");

    if (font.Underline) sb.Append("</u>");
    if (font.Strikeout) sb.Append("</strike>");
    if (font.Italic) sb.Append("</i>");
    if (font.Bold) sb.Append("</b>");

    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<string[]> 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:

魔法,Abra,Cadabra,Boom!
编程,有趣,YeeHaa,ABS TableName
你好,世界,Population.html,TABLE 1。
人口统计学,310102.xls,Comp.html,TABLE 2。


这种方法在大多数情况下表现良好,但对于全部为数字的文本数据会出现错误。例如,“0814180649644754”这样的字符串最终显示为8.14181E+14,并将其转换为数字(删除关键的前导0)。添加单引号以强制转为字符串也不可行,因为在我们的情况下,这些值将呈现为条形码,附加字符会造成问题。这种解决方案虽然适用于格式化,但处理数据类型还不够完善。 - Frank Ball
1
这就是Excel的工作方式 https://dev59.com/3E3Sa4cB1Zd3GeqPwp6E#13983731。如果您想使用大数字,您需要将其放在引号中。不可接受是主观的,在呈现之前请删除引号。还要注意,该代码是开源的,供人们贡献其他功能,例如用带零开头的大数字引用字段或提供数据类型列映射器。无论如何,如果您以编程或手动方式执行此操作,都可以添加和剥离引号并格式化单元格。 - Jeremy Thompson

3
你的问题的关键是在DataGridView上使用剪贴板不包含单元格格式。由于剪贴板不包含格式,您需要逐个设置单元格样式,这会导致原始的缓慢性能问题,使用Interop非常缓慢。

在这种情况下,使用XML创建Excel文件比使用Interop更好。以下是使用ClosedXML将DataGridView导出到带格式的Excel的方法。

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");
    }
}

0

看起来我找到了一个使用Interop和EPPlus的解决方案。我使用上面的代码仅仅是为了复制Excel中的值,然后使用下面的代码(EPPlus代码)从dataGridView中获取格式。 这个代码取决于你想从dataGridView中获取什么。在下面的代码中,我想要从第一行中获取WrapText以及每个已写入单元格的背景颜色。

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();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接