41

我的数据网格视图中有 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# 新手。

4

14 回答 14

73

我通过简单的复制和粘贴方法解决了这个问题。我不知道这是做到这一点的最佳方式,但对我来说,它工作得很好,而且几乎是瞬间的。这是我的代码。

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

谢谢。

于 2013-08-12T19:19:07.783 回答
31

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

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

首先,您将需要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();
    }
}
于 2015-07-14T16:46:07.057 回答
10

我不打算窃取@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);          
    }
于 2016-07-20T20:39:23.750 回答
8
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());
    }
}
于 2015-08-14T07:19:50.347 回答
4

I like Jake's solution. The problem with no header is resolved by doing the following

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

of course this only works is you know what the headers should be ahead of time.

于 2014-06-13T20:28:47.587 回答
4

互操作很慢并且有其他问题,使用剪贴板似乎不可扩展。这里有另外两种方法可以做到这一点

  1. 直接使用 Excel 2007+ 文件而不是使用 Excel,它会快很多(很多)。您可以使用Microsoft 的 SDK 的OpenXML ( http://openxmldeveloper.org/ )。学习 OpenXML 的最佳方法是下载 Productivity 工具 ( http://www.microsoft.com/en-us/download/details.aspx?id=5124),它需要一个现有文件并生成创建它所需的代码. 另一个可能更简单的选项是使用 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-OLEDBOLEDB 性能以阅读 Excel

我将从 ClosedXML 开始。

于 2013-08-12T08:14:08.640 回答
3

最好的方法是使用 closedxml.codeplex.com Library.Refer it @ https://closedxml.codeplex.com/wikipage?title=Adding%20DataTable%20as%20Worksheet&referringTitle=Documentation

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();
于 2015-07-27T20:14:43.150 回答
1

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

MS Office Interop 很慢,甚至微软也不推荐在服务器端使用 Interop,也不能用于导出大型 Excel 文件。有关更多详细信息,请参阅为什么不从 Microsoft 的角度使用 OLE 自动化。

Interop 以 XLS 文件格式(旧的 Excel 97-2003 文件格式)保存 Excel 文件,并且对 Office 2003 的支持已结束。Microsoft Excel 在 Office 2007 中发布了 XLSX 文件格式,并建议使用 OpenXML SDK 而不是 Interop。但是 XLSX 文件并不是那么快,并且不能很好地处理大型 Excel 文件,因为它们基于 XML 文件格式。这就是为什么微软还在 Office 2007 中发布了 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

于 2014-11-25T10:53:11.393 回答
1

这就是我用于我的gridview的,尝试将它用于你的数据,它完美地工作:

        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();
        }
于 2013-08-12T08:15:18.127 回答
0

我添加这个答案是因为没有其他方法使用 OpenXMLWriter,尽管它比 OpenXML DOM 更快并且比 COM/Interop 更快更可靠,并且因为其他一些方法使用剪贴板,我会小心,因为它的输出是不可靠的。

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

如何将数据表导出到 Excel

于 2020-05-06T20:32:55.397 回答
0

alternatively you can perform a fast export without using Office dll, as Excel can parse csv files without problems.

Doing something like this (for less than 65.536 rows with titles):

  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
于 2019-07-19T06:13:15.913 回答
0

如果您的 DataGridViewRightToLeft设置为,Yes那么您的数据会反向复制。所以你应该使用下面的代码来正确复制数据。

private void copyAlltoClipboard()
{
    dgvItems.RightToLeft = RightToLeft.No;
    dgvItems.SelectAll();
    DataObject dataObj = dgvItems.GetClipboardContent();
    if (dataObj != null)
        Clipboard.SetDataObject(dataObj);
    dgvItems.RightToLeft = RightToLeft.Yes;
}
于 2019-03-30T11:24:46.413 回答
0

在我看来,这是导出 datagridview 的最简单和即时的工作方法。

 try
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Filter = "Excel Documents (*.xlsx)|*.xlsx";
            sfd.FileName = "ProfitLoss.xlsx";
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                DataTable dts = new DataTable();
                for (int i = 0; i < grdProfitAndLoss.Columns.Count; i++)
                {
                    dts.Columns.Add(grdProfitAndLoss.Columns[i].Name);
                }
                for (int j = 0; j < grdProfitAndLoss.Rows.Count; j++)
                {
                    DataRow toInsert = dts.NewRow();
                    int k = 0;
                    int inc = 0;
                    for (k = 0; k < grdProfitAndLoss.Columns.Count; k++)
                    {
                        if (grdProfitAndLoss.Columns[k].Visible == false) { continue; }
                        toInsert[inc] = grdProfitAndLoss.Rows[j].Cells[k].Value;
                        inc++;
                    }
                    dts.Rows.Add(toInsert);
                }
                dts.AcceptChanges();
                ExcelUtlity obj = new ExcelUtlity();
                obj.WriteDataTableToExcel(dts, "Profit And Loss", sfd.FileName, "Profit And Loss");
                MessageBox.Show("Exported Successfully");
            }
        }
        catch (Exception ex)
        {

        }
于 2019-11-13T06:37:14.770 回答
0

此行仅适用于 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);
    }
于 2018-11-07T12:21:02.377 回答