如何导出dataGridView数据立即到Excel点击按钮

本文关键字:Excel 按钮 何导出 dataGridView 数据 | 更新日期: 2023-09-27 18:27:13

我的数据网格视图中有10k行和15列。我想通过点击按钮将这些数据导出到excel表格中。我已经尝试使用以下代码。

private void btExport_Click(object sender, EventArgs e)
    {
        Microsoft.Office.Interop.Excel._Application app  = new Microsoft.Office.Interop.Excel.Application();
        Microsoft.Office.Interop.Excel._Workbook workbook =  app.Workbooks.Add(Type.Missing);        
        Microsoft.Office.Interop.Excel._Worksheet worksheet = null;                   
        app.Visible = true;
        worksheet = workbook.Sheets["Sheet1"];
        worksheet = workbook.ActiveSheet;                  
        for(int i=1;i<dataGridView1.Columns.Count+1;i++)
        {
             worksheet.Cells[1, i] = dataGridView1.Columns[i-1].HeaderText;
        }    
        for (int i=0; i < dataGridView1.Rows.Count-1 ; i++)
        {
            for(int j=0;j<dataGridView1.Columns.Count;j++)
            {
                if (dataGridView1.Rows[i].Cells[j].Value != null)
                {
                    worksheet.Cells[i + 2, j + 1] = dataGridView1.Rows[i].Cells[j].Value.ToString();
                }
                else
                {
                    worksheet.Cells[i + 2, j + 1] = "";
                }
            }
        }
    }

这对我来说很有效,但完成导出过程需要花费大量时间。

是否可以在点击按钮后立即从dataGridView(有10k行)导出到excel

除此之外,当我尝试将所有dataGridview内容复制到剪贴板,然后手动将其粘贴到excel表时,这种情况几乎立即发生。

那么,有没有一种方法可以将所有dataGridView单元格复制到剪贴板中,然后点击按钮将其粘贴到excel表中(带有单元格格式)

我有如下复制到剪贴板的代码,但我不知道如何通过打开它将其粘贴到新的excel工作表中。

        private void copyAllToolStripMenuItem_Click(object sender, EventArgs e)
    {
        dataGridView1.SelectAll();
        DataObject dataObj = dataGridView1.GetClipboardContent();
        if (dataObj != null)
            Clipboard.SetDataObject(dataObj);
    }

请举个例子。我是C#的新手。

如何导出dataGridView数据立即到Excel点击按钮

我通过简单的复制粘贴方法解决了这个问题。我不知道这是最好的方法,但对我来说,它效果很好,几乎是即时的。这是我的密码。

    private void copyAlltoClipboard()
    {
        dataGridView1.SelectAll();
        DataObject dataObj = dataGridView1.GetClipboardContent();
        if (dataObj != null)
            Clipboard.SetDataObject(dataObj);
    }
    private void button3_Click_1(object sender, EventArgs e)
    {
        copyAlltoClipboard();
        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);
        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);          
    }

谢谢。

这是一个很好的问题,我很惊讶找到一个清晰完整的答案有多难,我找到的大多数答案要么是sudo代码,要么不是100%完整。

我能够根据Jake的回答创建一个完整的解决方案,将数据从我的DataGridView复制并保存到excel文件中,所以我发布了我的完整解决方案,希望它能帮助其他像我一样的新用户:)

首先,您的项目中需要Microsoft.Office.Interop.Excel参考。请参阅MSDN了解如何添加它。

我的代码:

using Excel = Microsoft.Office.Interop.Excel;
private void btnExportToExcel_Click(object sender, EventArgs e)
{
    SaveFileDialog sfd = new SaveFileDialog();
    sfd.Filter = "Excel Documents (*.xls)|*.xls";
    sfd.FileName = "Inventory_Adjustment_Export.xls";
    if (sfd.ShowDialog() == DialogResult.OK)
    {
        // Copy DataGridView results to clipboard
        copyAlltoClipboard();
        object misValue = System.Reflection.Missing.Value;
        Excel.Application xlexcel = new Excel.Application();
        xlexcel.DisplayAlerts = false; // Without this you will get two confirm overwrite prompts
        Excel.Workbook xlWorkBook = xlexcel.Workbooks.Add(misValue);
        Excel.Worksheet xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
        // Format column D as text before pasting results, this was required for my data
        Excel.Range rng = xlWorkSheet.get_Range("D:D").Cells;
        rng.NumberFormat = "@";
        // Paste clipboard results to worksheet range
        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);
        // For some reason column A is always blank in the worksheet. ¯'_(ツ)_/¯
        // Delete blank column A and select cell A1
        Excel.Range delRng = xlWorkSheet.get_Range("A:A").Cells;
        delRng.Delete(Type.Missing);
        xlWorkSheet.get_Range("A1").Select();
        // Save the excel file under the captured location from the SaveFileDialog
        xlWorkBook.SaveAs(sfd.FileName, Excel.XlFileFormat.xlWorkbookNormal, misValue, misValue, misValue, misValue, Excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue);
        xlexcel.DisplayAlerts = true;
        xlWorkBook.Close(true, misValue, misValue);
        xlexcel.Quit();
        releaseObject(xlWorkSheet);
        releaseObject(xlWorkBook);
        releaseObject(xlexcel);
        // Clear Clipboard and DataGridView selection
        Clipboard.Clear();
        dgvItems.ClearSelection();
        // Open the newly saved excel file
        if (File.Exists(sfd.FileName))
            System.Diagnostics.Process.Start(sfd.FileName);
    }
}
private void copyAlltoClipboard()
{
    dgvItems.SelectAll();
    DataObject dataObj = dgvItems.GetClipboardContent();
    if (dataObj != null)
        Clipboard.SetDataObject(dataObj);
}
private void releaseObject(object obj)
{
    try
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
        obj = null;
    }
    catch (Exception ex)
    {
        obj = null;
        MessageBox.Show("Exception Occurred while releasing object " + ex.ToString());
    }
    finally
    {
        GC.Collect();
    }
}

我不打算窃取@Jake和@Cornelius的答案,所以我尝试编辑它。但被拒绝了。无论如何,我必须指出的唯一改进是避免粘贴后excel中出现多余的空白列。添加一行dataGridView1.RowHeadersVisible = false;会隐藏出现在DataGridView最左边的所谓"行标题",因此在执行dataGridView1.SelectAll(); 时不会选中它并将其复制到剪贴板

private void copyAlltoClipboard()
    {
        //to remove the first blank column from datagridview
        dataGridView1.RowHeadersVisible = false;
        dataGridView1.SelectAll();
        DataObject dataObj = dataGridView1.GetClipboardContent();
        if (dataObj != null)
            Clipboard.SetDataObject(dataObj);
    }
    private void button3_Click_1(object sender, EventArgs e)
    {
        copyAlltoClipboard();
        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);
        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);          
    }
using Excel = Microsoft.Office.Interop.Excel;

private void btnExportExcel_Click(object sender, EventArgs e)
{
    try
    {
        Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
        excel.Visible = true;
        Microsoft.Office.Interop.Excel.Workbook workbook = excel.Workbooks.Add(System.Reflection.Missing.Value);
        Microsoft.Office.Interop.Excel.Worksheet sheet1 = (Microsoft.Office.Interop.Excel.Worksheet)workbook.Sheets[1];
        int StartCol = 1;
        int StartRow = 1;
        int j = 0, i = 0;
        //Write Headers
        for (j = 0; j < dgvSource.Columns.Count; j++)
        {
            Microsoft.Office.Interop.Excel.Range myRange = (Microsoft.Office.Interop.Excel.Range)sheet1.Cells[StartRow, StartCol + j];
            myRange.Value2 = dgvSource.Columns[j].HeaderText;
        }
        StartRow++;
        //Write datagridview content
        for (i = 0; i < dgvSource.Rows.Count; i++)
        {
            for (j = 0; j < dgvSource.Columns.Count; j++)
            {
                try
                {
                    Microsoft.Office.Interop.Excel.Range myRange = (Microsoft.Office.Interop.Excel.Range)sheet1.Cells[StartRow + i, StartCol + j];
                    myRange.Value2 = dgvSource[j, i].Value == null ? "" : dgvSource[j, i].Value;
                }
                catch
                {
                    ;
                }
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
}

互操作速度慢且存在其他问题,使用剪贴板似乎不可扩展。以下是另外两种完成的方法

  1. 直接使用Excel2007+文件,而不是使用Excel,它会更快。您可以使用OpenXML(http://openxmldeveloper.org/)这是微软的SDK。学习OpenXML的最佳方法是下载Productivity工具(http://www.microsoft.com/en-us/download/details.aspx?id=5124),ittake一个现有文件并生成创建它所需的代码。另一个可能更简单的选项是使用ClosedXML(http://closedxml.codeplex.com/)。它似乎更容易使用(看看这个例子http://closedxml.codeplex.com/wikipage?title=Showcase&referringTitle=Home),但我没有使用它的经验。我相信还有其他库可以使用Excel包装工作。

  2. 通过OLEDB使用excel。这使您可以像使用数据库一样使用Excel。看见http://www.codeproject.com/Articles/8500/Reading-and-Writing-Excel-using-OLEDB或OLEDB的性能来阅读Excel以获取示例和更多细节。

我会从ClosedXML开始。

我喜欢Jake的解决方案。通过执行以下可以解决没有标头的问题

xlWorkSheet.Cells[1, 1] = "Header 1";
xlWorkSheet.Cells[1, 2] = "Header 2";
xlWorkSheet.Cells[1, 3] = "Header 3";

当然,只有提前知道标题应该是什么才有效。

最好使用closedxml.codeplex.com库。请参阅@https://closedxml.codeplex.com/wikipage?title=Adding%20DataTable%20as%20Worksheet&referringTitle=文档

var wb = new ClosedXML.Excel.XLWorkbook();
DataTable dt = GetTheDataTable();//Refer documentation

wb.Worksheets.Add(dt);
Response.Clear();
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment;filename='"FileName.xlsx'"");
using (var ms = new System.IO.MemoryStream()) {
    wb.SaveAs(ms);
    ms.WriteTo(Response.OutputStream);
    ms.Close();
}
Response.End();

这就是我在网格视图中使用的内容,试着将其用于yr数据,它非常有效:

        GridView1.AllowPaging = false;
        GridView1.DataBind();
        StringBuilder sb = new StringBuilder();
        for (int k = 0; k < GridView1.Columns.Count; k++)
        {
            //add separator
            sb.Append(GridView1.Columns[k].HeaderText+";");
        }

        //append new line
        sb.Append("'r'n");
        for (int i = 0; i < GridView1.Rows.Count; i++)
        {
            for (int k = 0; k < GridView1.Columns.Count; k++)
            {
                sb.Append(GridView1.Rows[i].Cells[k].Text+";");
            }
            sb.AppendLine();
        }

这是第一个问题的答案,为什么它需要这么多时间,它提供了一个将DataGridView导出到Excel的替代解决方案。

MS Office Interop速度较慢,甚至Microsoft也不建议在服务器端使用Interop,也不能用于导出大型Excel文件。有关详细信息,请参阅为什么不从Microsoft的角度使用OLE Automation。

Interop将Excel文件保存为XLS文件格式(旧的Excel 97-2003文件格式),对Office 2003的支持已结束。Microsoft Excel在Office 2007中发布了XLSX文件格式,并建议使用OpenXML SDK而不是Interop。但是XLSX文件并没有那么快,也不能很好地处理大型Excel文件,因为它们是基于XML文件格式的。这就是为什么微软还在Office2007中发布了XLSB文件格式,该文件格式建议用于大型Excel文件。它是一种二进制格式。因此,最好、最快的解决方案是保存XLSB文件。

您可以使用此C#Excel库来保存XLSB文件,但它也支持XLS和XLSX文件格式。

请参阅以下代码示例作为将DataGridView导出到Excel的替代方案:

// Create a DataSet and add the DataTable of DataGridView 
DataSet dataSet = new DataSet();
dataSet.Tables.Add((DataTable)dataGridView);
//or ((DataTable)dataGridView.DataSource).Copy() to create a copy
// Export Excel file 
ExcelDocument workbook = new ExcelDocument();
workbook.easy_WriteXLSBFile_FromDataSet(filePath, dataSet, 
     new EasyXLS.ExcelAutoFormat(EasyXLS.Constants.Styles.AUTOFORMAT_EASYXLS1), 
     "Sheet1");

如果您还需要导出DataGridView的格式,请检查此代码示例,了解如何在C#中将DataGridView导出到Excel。

此行仅适用于Windows窗体上的DataGridView控件:

DataObject dataObj = dataGridView1.GetClipboardContent();

这一个解决了相同的问题,但对于WPF框架的DataGrid控制:

    private void copyDataGridContentToClipboard()
    {
        datagridGrupeProductie.SelectAll();
        datagridGrupeProductie.ClipboardCopyMode = DataGridClipboardCopyMode.IncludeHeader;
        ApplicationCommands.Copy.Execute(null, datagridGrupeProductie);
        datagridGrupeProductie.UnselectAll();
    }

    private void rightClickGrupeProductie_Click(object sender, RoutedEventArgs e)
    {
        copyDataGridContentToClipboard();
        Microsoft.Office.Interop.Excel.Application excelApp;
        Microsoft.Office.Interop.Excel.Workbook excelWkbk;
        Microsoft.Office.Interop.Excel.Worksheet excelWksht;
        object misValue = System.Reflection.Missing.Value;
        excelApp = new Microsoft.Office.Interop.Excel.Application();
        excelApp.Visible = true;
        excelWkbk = excelApp.Workbooks.Add(misValue);
        excelWksht = (Microsoft.Office.Interop.Excel.Worksheet)excelWkbk.Worksheets.get_Item(1);
        Microsoft.Office.Interop.Excel.Range CR = (Microsoft.Office.Interop.Excel.Range)excelWksht.Cells[1, 1];
        CR.Select();
        excelWksht.PasteSpecial(CR, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, true);
    }

如果DataGridView的RightToLeft设置为Yes,则反向复制数据。因此,您应该使用以下代码来正确复制数据。

private void copyAlltoClipboard()
{
    dgvItems.RightToLeft = RightToLeft.No;
    dgvItems.SelectAll();
    DataObject dataObj = dgvItems.GetClipboardContent();
    if (dataObj != null)
        Clipboard.SetDataObject(dataObj);
    dgvItems.RightToLeft = RightToLeft.Yes;
}

或者您可以在不使用Office dll的情况下执行快速导出,因为Excel可以毫无问题地解析csv文件。

这样做(对于少于65.536行的标题):

  Try
            If (p_oGrid.RowCount = 0) Then
                MsgBox("No data", MsgBoxStyle.Information, "App")
                Exit Sub
            End If
            Cursor.Current = Cursors.WaitCursor
            Dim sText As New System.Text.StringBuilder
            Dim sTmp As String
            Dim aVisibleData As New List(Of String)
            For iAuxRow As Integer = 0 To p_oGrid.Columns.Count - 1
                If p_oGrid.Columns(iAuxRow).Visible Then
                    aVisibleData.Add(p_oGrid.Columns(iAuxRow).Name)
                    sText.Append(p_oGrid.Columns(iAuxRow).HeaderText.ToUpper)
                    sText.Append(";")
                End If
            Next
            sText.AppendLine()
            For iAuxRow As Integer = 0 To p_oGrid.RowCount - 1
                Dim oRow As DataGridViewRow = p_oGrid.Rows(iAuxRow)
                For Each sCol As String In aVisibleData
                    Dim sVal As String
                    sVal = oRow.Cells(sCol).Value.ToString()
                    sText.Append(sVal.Replace(";", ",").Replace(vbCrLf, " ; "))
                    sText.Append(";")
                Next
                sText.AppendLine()
            Next
            sTmp = IO.Path.GetTempFileName & ".csv"
            IO.File.WriteAllText(sTmp, sText.ToString, System.Text.Encoding.UTF8)
            sText = Nothing
            Process.Start(sTmp)
        Catch ex As Exception
            process_error(ex)
        Finally
            Cursor.Current = Cursors.Default
        End Try

我之所以添加这个答案,是因为其他方法都不使用OpenXMLWriter,尽管它比OpenXMLDOM更快,也比COM/Interop更快、更可靠,而且因为其他一些方法使用剪贴板,我要小心,因为它的输出不可靠。

详细信息可以在下面的链接中找到我的答案,但该示例是针对DataTable的,但您可以通过更改行/列循环来引用dgv而不是dt来调整它以使用DataGridView。

如何将DataTable导出到Excel