Можете ли вы вставить блок ячеек в одном кадре, используя Excel Interop без буфера обмена? - PullRequest
4 голосов
/ 06 апреля 2011

Я пытаюсь перенести данные из набора данных в книгу Excel. К сожалению, мне нужно больше контроля, чем я могу получить, просто связавшись с Excel через ADO.NET и используя стандартный SQL для выбора и вставки данных, поэтому я использую взаимодействие с Excel.

Мой оригинальный алгоритм включал в себя циклическое прохождение всех таблиц / строк / элементов набора данных и индивидуальную настройку Formula каждой ячейки в Excel. Это сработало, но передача всех данных заняла почти полминуты.

Я решил попробовать другое решение: преобразовать каждую таблицу в строку с разделителями табуляции (используя комбинацию StringBuilder и string.Join(), скопировать строку в буфер обмена и использовать метод interop для вызова команды Вставить в Excel Worksheet объект.

Это также работает и сокращает время чуть больше, чем на 50%, но я немного параноидально отношусь к использованию буфера обмена для передачи данных. Что произойдет, если пользователь начнет выполнять другие действия, связанные с буфером обмена, во время передачи? Мне также интересно, может ли это быть еще быстрее, если бы я мог вставить строку напрямую, а не использовать буфер обмена в качестве посредника.

Итак, это мой вопрос ... есть ли какая-нибудь команда, которая позволила бы мне "вставлять" блок данных в Excel сразу из строки C # без использования буфера обмена?

Ответы [ 4 ]

6 голосов
/ 07 апреля 2011

Создайте массив двумерных объектов с теми же размерами, что и целевой диапазон (первый индекс массива - это число строк, а второй - количество столбцов). Затем установите свойство значения диапазона с этим массивом.

Пример:

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;
}
0 голосов
/ 07 апреля 2011

Я рекомендую создать библиотеку в VB.NET, которая поддерживает необязательные параметры (более чистый код), а затем вызывать ее из 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[,],

Вы можете фактически преобразовать массив 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
0 голосов
/ 07 апреля 2011

Вы можете использовать объект HttpResponse и его метод write () для сброса строки в Excel.Сначала создайте строку шаблона Excel.Обратитесь к этому примеру.

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);
}
0 голосов
/ 07 апреля 2011

Можете ли вы не просто создать строку CSV, а использовать:

File.WriteAllText()
...