您可以使用Excel Interop W / o使用剪贴板粘贴一个单元格块吗?

我试图从DataSet传输数据到Excel工作簿。 不幸的是,我需要更多的控制,比通过ADO.NET简单地链接到Excel,并使用标准的SQL来select和插入数据,所以我使用的是互操作。

我原来的algorithm是循环遍历数据集的所有表格/行/项目,并在Excel中单独设置每个单元格的Formula 。 这工作,但转移所有的数据花了差不多半分钟。

我决定尝试一个不同的解决scheme:将每个表转换为制表符分隔的string(使用StringBuilderstring.Join()的组合,将该string复制到剪贴板,并使用interop调用Excel Worksheet对象上的粘贴命令。

这也可以使时间缩短50%以上,但是我对使用剪贴板传输数据有点偏执。 如果用户在传输过程中开始执行涉及剪贴板的其他操作,会发生什么情况? 我还想知道,如果我可以直接粘贴一个string,而不是使用剪贴板作为媒介,速度会更快。

所以,这是我的问题… 是否有一些命令可以让我直接从C#string在Excel中“粘贴”一块数据,而无需使用剪贴板?

创build一个与目标范围尺寸相同的二维对象数组(第一个数组索引是行数;第二个是列数)。 然后用该数组设置范围的value属性。

例:

 void SetRange(Worksheet worksheet, DataSet dataSet) { object[] values = GetValuesFromDataSet(dataSet); int rowCount = values.GetUpperBound(0); int columnCount = values.GetUpperBound(1); Range range = worksheet.Range(worksheet.Cells(1, 1), worksheetCells.(rowCount, columnCount)); range.Value = values; } 

你不能只是创build一个CSVstring,并使用:

 File.WriteAllText() 

我build议在VB.NET中创build一个支持可选参数(清洁代码)的库,然后从C#调用它。 像其他答案一样,这取决于Range.Value2调用。

这里是VB.NET中的一些示例代码

 Public Function GetObjectArray(ByVal range_ref As Range) As Object(,) If range_ref.Count > 0 Then Return CType(range_ref.Value2, Object(,)) Else Return New Object(,) {{range_ref.Value2}} End If End Function Public Sub SetObjectArray(ByVal range_ref As Range, ByVal values As Object(,)) If range_ref.Count > 0 Then range_ref.Value2 = values Else range_ref.Value2 = values(0, 0) End If End Sub 

检查计数的原因是因为对于一个单引用.Value2返回一个值,而不是一个数组,可以转换为object[,]

实际上你可以使用Array.Copy() 这里发布的技巧将object[,]数组转换回来,并从double[,]数组中Array.Copy()

 Public Function GetValueArray(ByVal range_ref As Range) As Double(,) Dim temp As Object(,) = GetObjectArray(range_ref) Dim N As Integer = temp.GetLength(0) Dim M As Integer = temp.GetLength(1) Dim res As Double(,) = New Double(N - 1, M - 1) {} Array.Copy(temp, res, temp.Length) Return res End Function Public Sub SetValueArray(ByVal range_ref As Range, ByVal values As Double(,)) Dim N As Integer = values.GetLength(0) Dim M As Integer = values.GetLength(1) Dim temp As Object(,) = Array.CreateInstance( _ GetType(Object), _ New Integer() {N, M}, _ New Integer() {1, 1}) Array.Copy(values, temp, values.Length) SetObjectArray(range_ref, temp) End Sub 

您可以使用HttpResponse对象和它的write()方法将string刷新到excel中。 首先创build一个excel模板string。 参考这个例子。

 private static string getWorkbookTemplate() { StringBuilder sb = new StringBuilder(818); sb.AppendFormat(@"<?xml version=""1.0""?>{0}", Environment.NewLine); sb.AppendFormat(@"<?mso-application progid=""Excel.Sheet""?>{0}", Environment.NewLine); sb.AppendFormat(@"<Workbook xmlns=""urn:schemas-microsoft-com:office:spreadsheet""{0}", Environment.NewLine); sb.AppendFormat(@" xmlns:o=""urn:schemas-microsoft-com:office:office""{0}", Environment.NewLine); sb.AppendFormat(@" xmlns:x=""urn:schemas-microsoft-com:office:excel""{0}", Environment.NewLine); sb.AppendFormat(@" xmlns:ss=""urn:schemas-microsoft-com:office:spreadsheet""{0}", Environment.NewLine); sb.AppendFormat(@" xmlns:html=""http://www.w3.org/TR/REC-html40"">{0}", Environment.NewLine); sb.AppendFormat(@" <Styles>{0}", Environment.NewLine); sb.AppendFormat(@" <Style ss:ID=""Default"" ss:Name=""Normal"">{0}", Environment.NewLine); sb.AppendFormat(@" <Alignment ss:Vertical=""Bottom""/>{0}", Environment.NewLine); sb.AppendFormat(@" <Borders/>{0}", Environment.NewLine); sb.AppendFormat(@" <Font ss:FontName=""Verdana"" x:Family=""Swiss"" ss:Size=""12"" ss:Color=""#0000A0""/>{0}", Environment.NewLine); sb.AppendFormat(@" <Interior/>{0}", Environment.NewLine); sb.AppendFormat(@" <NumberFormat/>{0}", Environment.NewLine); sb.AppendFormat(@" <Protection/>{0}", Environment.NewLine); sb.AppendFormat(@" </Style>{0}", Environment.NewLine); sb.AppendFormat(@" <Style ss:ID=""s62"">{0}", Environment.NewLine); sb.AppendFormat(@" <Font ss:FontName=""Calibri"" x:Family=""Swiss"" ss:Size=""11"" ss:Color=""#000000""{0}", Environment.NewLine); sb.AppendFormat(@" ss:Bold=""1""/>{0}", Environment.NewLine); sb.AppendFormat(@" </Style>{0}", Environment.NewLine); sb.AppendFormat(@" <Style ss:ID=""s63"">{0}", Environment.NewLine); sb.AppendFormat(@" <NumberFormat ss:Format=""Short Date""/>{0}", Environment.NewLine); sb.AppendFormat(@" </Style>{0}", Environment.NewLine); sb.AppendFormat(@" </Styles>{0}", Environment.NewLine); sb.Append(@"{0}\r\n</Workbook>"); return sb.ToString(); } private static string replaceXmlChar(string input) { input = input.Replace("&", "&amp"); input = input.Replace("<", "&lt;"); input = input.Replace(">", "&gt;"); input = input.Replace("\"", "&quot;"); input = input.Replace("'", "&apos;"); return input; } private static string getCell(Type type, object cellData) { Object data = (cellData is DBNull) ? "" : cellData; if (type.Name.Contains("Int") || type.Name.Contains("Double") || type.Name.Contains("Decimal")) return string.Format("<Cell><Data ss:Type=\"Number\">{0}</Data></Cell>", data); if (type.Name.Contains("Date") && data.ToString() != string.Empty) { return string.Format("<Cell ss:StyleID=\"s63\"><Data ss:Type=\"DateTime\">{0}</Data></Cell>", Convert.ToDateTime(data).ToString("yyyy-MM-dd")); } return string.Format("<Cell><Data ss:Type=\"String\">{0}</Data></Cell>", replaceXmlChar(data.ToString())); } private static string getWorksheets(DataSet source) { StringWriter sw = new StringWriter(); if (source == null || source.Tables.Count == 0) { sw.Write("<Worksheet ss:Name=\"Sheet1\">\r\n<Table>\r\n<Row><Cell><Data ss:Type=\"String\"></Data></Cell></Row>\r\n</Table>\r\n</Worksheet>"); return sw.ToString(); } foreach (DataTable dt in source.Tables) { if (dt.Rows.Count == 0) sw.Write("<Worksheet ss:Name=\"" + replaceXmlChar(dt.TableName) + "\">\r\n<Table>\r\n<Row><Cell ss:StyleID=\"s62\"><Data ss:Type=\"String\"></Data></Cell></Row>\r\n</Table>\r\n</Worksheet>"); else { //write each row data int sheetCount = 0; for (int i = 0; i < dt.Rows.Count; i++) { if ((i % rowLimit) == 0) { //add close tags for previous sheet of the same data table if ((i / rowLimit) > sheetCount) { sw.Write("\r\n</Table>\r\n</Worksheet>"); sheetCount = (i / rowLimit); } sw.Write("\r\n<Worksheet ss:Name=\"" + replaceXmlChar(dt.TableName) + (((i / rowLimit) == 0) ? "" : Convert.ToString(i / rowLimit)) + "\">\r\n<Table>"); //write column name row sw.Write("\r\n<Row>"); foreach (DataColumn dc in dt.Columns) sw.Write(string.Format("<Cell ss:StyleID=\"s62\"><Data ss:Type=\"String\">{0}</Data></Cell>", replaceXmlChar(dc.ColumnName))); sw.Write("</Row>"); } sw.Write("\r\n<Row>"); foreach (DataColumn dc in dt.Columns) sw.Write(getCell(dc.DataType, dt.Rows[i][dc.ColumnName])); sw.Write("</Row>"); } sw.Write("\r\n</Table>\r\n</Worksheet>"); } } return sw.ToString(); } public static string GetExcelXml(DataTable dtInput, string filename) { string excelTemplate = getWorkbookTemplate(); DataSet ds = new DataSet(); ds.Tables.Add(dtInput.Copy()); string worksheets = getWorksheets(ds); string excelXml = string.Format(excelTemplate, worksheets); return excelXml; } public static string GetExcelXml(DataSet dsInput, string filename) { string excelTemplate = getWorkbookTemplate(); string worksheets = getWorksheets(dsInput); string excelXml = string.Format(excelTemplate, worksheets); return excelXml; } public static void ToExcel(DataSet dsInput, string filename, HttpResponse response) { string excelXml = GetExcelXml(dsInput, filename); response.Clear(); response.AppendHeader("Content-Type", "application/vnd.ms-excel"); response.AppendHeader("Content-disposition", "attachment; filename=" + filename); response.Write(excelXml); response.Flush(); response.End(); } public static void ToExcel(DataTable dtInput, string filename, HttpResponse response) { DataSet ds = new DataSet(); ds.Tables.Add(dtInput.Copy()); ToExcel(ds, filename, response); }