如何使用Visual C# 2005或Visual C# .NET向Excel工作簿传输数据
内容提示:本文分步介绍了多种从 Microsoft Visual C# 2005 或 Microsoft Visual C# .NET 程序向 Microsoft Excel 2002 传输数据的方法。本文还提供了每种方法的优点和缺点,以便您可以选择最适合您的情况的解决方案。
概述
最常用于向 Excel 工作簿传输数据的方法是“自动化”。利用“自动化”功能,您可以调用特定于 Excel 任务的方法和属性。“自动化”功能为您提供了指定数据在工作簿中所处的位置、将工作簿格式化以及在运行时进行各种设置的最大的灵活性。
利用“自动化”功能,您可以使用多种方法来传输数据:
• 逐个单元格地传输数据。
• 将数组中的数据传输到单元格区域。
• 使用“CopyFromRecordset”方法向单元格区域传输 ADO 记录集中的数据。
• 在 Excel 工作表上创建一个“QueryTable”对象,该对象包含对 ODBC 或 OLEDB 数据源进行查询的结果。
• 将数据传输到剪贴板,然后将剪贴板内容粘贴到 Excel 工作表中。
还可以使用多种未必需要利用“自动化”功能来向 Excel 传输数据的方法。如果您正在运行服务器端程序,这会是一种将批量数据处理从客户端移走的好方法。
要在不使用“自动化”功能的情况下传输数据,您可以使用下列方法:
• 将数据传输到制表符分隔的或逗号分隔的文本文件,然后 Excel 可以将该文本文件分析为工作表上的单元格。
• 使用 ADO.NET 将数据传输到工作表。
• 将 XML 数据传输到 Excel(仅限于 2002 和 2003 版)以提供可以被格式化和排列为行和列的数据。
本文讨论了其中的每一种方法并提供了每一种方法的代码示例。本文的创建完整的示例 Visual C# 2005 或 Visual C# .NET 项目一节(在本文稍后部分)演示了如何创建执行每一种方法的 Visual C# .NET 程序。
方法
使用“自动化”功能逐单元格传输数据
利用“自动化”功能,您可以逐单元格地向工作表传输数据:
// Start a new workbook in Excel.
m_objExcel = new Excel.Application();
m_objBooks = (Excel.Workbooks)m_objExcel.Workbooks;
m_objBook = (Excel._Workbook)(m_objBooks.Add(m_objOpt));
// Add data to cells in the first worksheet in the new workbook.
m_objSheets = (Excel.Sheets)m_objBook.Worksheets;
m_objSheet = (Excel._Worksheet)(m_objSheets.get_Item(1));
m_objRange = m_objSheet.get_Range("A1", m_objOpt);
m_objRange.Value = "Last Name";
m_objRange = m_objSheet.get_Range("B1", m_objOpt);
m_objRange.Value = "First Name";
m_objRange = m_objSheet.get_Range("A2", m_objOpt);
m_objRange.Value = "Doe";
m_objRange = m_objSheet.get_Range("B2", m_objOpt);
m_objRange.Value = "John";
// Apply bold to cells A1:B1.
m_objRange = m_objSheet.get_Range("A1", "B1");
m_objFont = m_objRange.Font;
m_objFont.Bold=true;
// Save the Workbook and quit Excel.
m_objBook.SaveAs(m_strSampleFolder + "Book1.xls", m_objOpt, m_objOpt,
m_objOpt, m_objOpt, m_objOpt, Excel.XlSaveAsAccessMode.xlNoChange,
m_objOpt, m_objOpt, m_objOpt, m_objOpt);
m_objBook.Close(false, m_objOpt, m_objOpt);
m_objExcel.Quit();
如果您具有少量的数据,则逐单元格地传输数据是可以接受的方法。您可以灵活地将数据放到工作簿中的任何地方,并可以在运行时根据条件对单元格进行格式设置。不过,如果您具有大量需要传输到 Excel 工作簿的数据,则使用这种方法不是一个好主意。您在运行时获取的每一个“Range”对象都会产生一个接口请求,这意味着数据传输速度会变得较慢。此外,Microsoft Windows 95、Microsoft Windows 98 以及 Microsoft Windows Millennium Edition (Me) 都对接口请求有 64 KB 的限制。如果您具有 64 KB 以上的接口请求,则“自动化”服务器 (Excel) 可能会停止响应,或者您可能会收到指出内存不足的错误消息。有关其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:216400 (http://support.microsoft.com/kb/216400/) PRB:Win95/98 上的进程间 COM 自动化可使客户端应用程序挂起需要再次强调的是,逐单元格地传输数据仅对少量数据而言才可以接受。如果您必须向 Excel 传输大型数据集,则应考虑使用本文中讨论的其他方法之一来批量地传输数据。
有关其他信息以及如何利用 Visual C# .NET 自动运行 Excel 的示例,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
302084 (http://support.microsoft.com/kb/302084/) 如何在 Microsoft Visual C# .NET 中使 Microsoft Excel 自动运行使用“自动化”功能将数据数组传输到工作表上的区域
可以将数据数组一次性地传输到由多个单元格组成的区域:
// Start a new workbook in Excel.
m_objExcel = new Excel.Application();
m_objBooks = (Excel.Workbooks)m_objExcel.Workbooks;
m_objBook = (Excel._Workbook)(m_objBooks.Add(m_objOpt));
m_objSheets = (Excel.Sheets)m_objBook.Worksheets;
m_objSheet = (Excel._Worksheet)(m_objSheets.get_Item(1));
// Create an array for the headers and add it to cells A1:C1.
object[] objHeaders = {"Order ID", "Amount", "Tax"};
m_objRange = m_objSheet.get_Range("A1", "C1");
m_objRange.Value = objHeaders;
m_objFont = m_objRange.Font;
m_objFont.Bold=true;
// Create an array with 3 columns and 100 rows and add it to
// the worksheet starting at cell A2.
object[,] objData = new Object[100,3];
Random rdm = new Random((int)DateTime.Now.Ticks);
double nOrderAmt, nTax;
for(int r=0;r<100;r++)
{
objData[r,0] = "ORD" + r.ToString("0000");
nOrderAmt = rdm.Next(1000);
objData[r,1] = nOrderAmt.ToString("c");
nTax = nOrderAmt*0.07;
objData[r,2] = nTax.ToString("c");
}
m_objRange = m_objSheet.get_Range("A2", m_objOpt);
m_objRange = m_objRange.get_Resize(100,3);
m_objRange.Value = objData;
// Save the Workbook and quit Excel.
m_objBook.SaveAs(m_strSampleFolder + "Book2.xls", m_objOpt, m_objOpt,
m_objOpt, m_objOpt, m_objOpt, Excel.XlSaveAsAccessMode.xlNoChange,
m_objOpt, m_objOpt, m_objOpt, m_objOpt);
m_objBook.Close(false, m_objOpt, m_objOpt);
m_objExcel.Quit();
如果您使用数组而不是逐单元格地传输数据,则在传输大量数据时,传输性能会大大地增强。请考虑前面代码中的下面几行,这些行将数据传输到工作表中的 300 个单元格:
objRange = objSheet.get_Range("A2", m_objOpt);
objRange = objRange.get_Resize(100,3);
objRange.Value = objData;
上面的代码代表了两个接口请求:一个请求是针对“Range”方法返回的“Range”对象,另一个请求是针对“Resize”方法返回的“Range”对象。相比之下,逐单元格地传输数据却需要对“Range”对象发出 300 个接口请求。只要有可能,您就可以从批量地传输数据以及减少所发出的接口请求的数量当中受益。
有关通过 Excel 自动化并使用数组获取和设置区域中的值的其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
302096 (http://support.microsoft.com/kb/302096/) 如何在 Visual C# 中使 Excel 自动运行以使用数组填充或获取某个区域中的数据
使用“自动化”功能将 ADO 记录集传输到工作表区域
Excel 2000、Excel 2002 和 Excel 2003 的对象模型提供了“CopyFromRecordset”方法,用于向工作表上的区域传输 ADO 记录集。下面的代码说明了如何使用“CopyFromRecordset”方法使 Excel 自动运行,以传输 Northwind 示例数据库中的“订单”表的内容:
// Create a Recordset from all the records in the Orders table.
ADODB.Connection objConn = new ADODB.Connection();
ADODB._Recordset objRS = null;
objConn.Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
m_strNorthwind + ";", "", "", 0);
objConn.CursorLocation = ADODB.CursorLocationEnum.adUseClient;
object objRecAff;
objRS = (ADODB._Recordset)objConn.Execute("Orders", out objRecAff,
(int)ADODB.CommandTypeEnum.adCmdTable);
// Start a new workbook in Excel.
m_objExcel = new Excel.Application();
m_objBooks = (Excel.Workbooks)m_objExcel.Workbooks;
m_objBook = (Excel._Workbook)(m_objBooks.Add(m_objOpt));
m_objSheets = (Excel.Sheets)m_objBook.Worksheets;
m_objSheet = (Excel._Worksheet)(m_objSheets.get_Item(1));
// Get the Fields collection from the recordset and determine
// the number of fields (or columns).
System.Collections.IEnumerator objFields = objRS.Fields.GetEnumerator();
int nFields = objRS.Fields.Count;
// Create an array for the headers and add it to the
// worksheet starting at cell A1.
object[] objHeaders = new object[nFields];
ADODB.Field objField = null;
for(int n=0;n<nFields;n++)
{
objFields.MoveNext();
objField = (ADODB.Field)objFields.Current;
objHeaders[n] = objField.Name;
}
m_objRange = m_objSheet.get_Range("A1", m_objOpt);
m_objRange = m_objRange.get_Resize(1, nFields);
m_objRange.Value = objHeaders;
m_objFont = m_objRange.Font;
m_objFont.Bold=true;
// Transfer the recordset to the worksheet starting at cell A2.
m_objRange = m_objSheet.get_Range("A2", m_objOpt);
m_objRange.CopyFromRecordset(objRS, m_objOpt, m_objOpt);
// Save the Workbook and quit Excel.
m_objBook.SaveAs(m_strSampleFolder + "Book3.xls", m_objOpt, m_objOpt,
m_objOpt, m_objOpt, m_objOpt, Excel.XlSaveAsAccessMode.xlNoChange,
m_objOpt, m_objOpt, m_objOpt, m_objOpt);
m_objBook.Close(false, m_objOpt, m_objOpt);
m_objExcel.Quit();
// Close the recordset and connection.
objRS.Close();
objConn.Close();
注意:“CopyFromRecordset”只能与 ADO“Recordset”对象一起使用。使用 ADO.NET 创建的“DataSet”不能与“CopyFromRecordset”方法一起使用。以下几节中的多个示例演示了如何利用 ADO.NET 向 Excel 传输数据。
使用“自动化”功能在工作表上创建 QueryTable 对象
“QueryTable”对象代表了一个表,该表是用从外部数据源返回的数据生成的。当您自动运行 Excel 时,可以通过提供指向 OLE DB 或 ODBC 数据源的连接字符串和 SQL 字符串来创建“QueryTable”。Excel 将生成记录集并将该记录集插入到工作表中您所指定的位置。“QueryTable”对象提供了下列优于“CopyFromRecordset”方法的优点:
• Excel 处理记录集的创建并将其放置到工作表中。
• 您可以利用“QueryTable”对象保存查询,并在以后刷新它以获取更新的记录集。
• 当向工作表中添加新的“QueryTable”时,可以指定将工作表上的单元格中已经存在的数据移位,以处理新数据(有关更多信息,请查看“RefreshStyle”属性)。
下面的代码演示了如何自动运行 Excel 2000、Excel 2002 或 Excel 2003,以便使用 Northwind 示例数据库中的数据在 Excel 工作表中创建新的“QueryTable”:
// Start a new workbook in Excel.
m_objExcel = new Excel.Application();
m_objBooks = (Excel.Workbooks)m_objExcel.Workbooks;
m_objBook = (Excel._Workbook)(m_objBooks.Add(m_objOpt));
// Create a QueryTable that starts at cell A1.
m_objSheets = (Excel.Sheets)m_objBook.Worksheets;
m_objSheet = (Excel._Worksheet)(m_objSheets.get_Item(1));
m_objRange = m_objSheet.get_Range("A1", m_objOpt);
m_objQryTables = m_objSheet.QueryTables;
m_objQryTable = (Excel._QueryTable)m_objQryTables.Add(
"OLEDB;Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
m_strNorthwind + ";", m_objRange, "Select * From Orders");
m_objQryTable.RefreshStyle = Excel.XlCellInsertionMode.xlInsertEntireRows;
m_objQryTable.Refresh(false);
// Save the workbook and quit Excel.
m_objBook.SaveAs(m_strSampleFolder + "Book4.xls", m_objOpt, m_objOpt,
m_objOpt, m_objOpt, m_objOpt, Excel.XlSaveAsAccessMode.xlNoChange, m_objOpt, m_objOpt,
m_objOpt, m_objOpt);
m_objBook.Close(false, m_objOpt, m_objOpt);
m_objExcel.Quit();