Экспорт списка списков C # в Excel - PullRequest
4 голосов
/ 29 июня 2009

Используя C #, существует ли прямой способ экспорта списка списков (т.е. List<List<T>>) в Excel 2003?

Я анализирую большие текстовые файлы и экспортирую в Excel. Запись одной ячейки за раз создает слишком много накладных расходов. Я решил использовать List<T>, чтобы не беспокоиться об указании количества строк или столбцов.

В настоящее время я жду до конца файла, затем помещаю содержимое моего List<List<object>> в 2-мерный массив. Тогда массив может быть установлен как значение объекта Excel.Range. Это работает, но кажется, что я должен иметь возможность взять свой список списков, не беспокоясь о количестве строк или столбцов, и просто сбросить его в рабочий лист от A1 до любого места.

Вот фрагмент кода, который я хотел бы заменить или улучшить:

object oOpt = System.Reflection.Missing.Value; //for optional arguments
Excel.Application oXL = new Excel.Application();
Excel.Workbooks oWBs = oXL.Workbooks;
Excel._Workbook oWB = oWBs.Add(Excel.XlWBATemplate.xlWBATWorksheet);
Excel._Worksheet oSheet = (Excel._Worksheet)oWB.ActiveSheet;

int numberOfRows = outputRows.Count;
int numberOfColumns = int.MinValue;

//outputRows is a List<List<object>>
foreach (List<object> outputColumns in outputRows)
{
        if (numberOfColumns < outputColumns.Count)
        { numberOfColumns = outputColumns.Count; }
}

Excel.Range oRng = oSheet.get_Range("A1", oSheet.Cells[numberOfRows,numberOfColumns]);

object[,] outputArray = new object[numberOfRows,numberOfColumns];

for (int row = 0; row < numberOfRows; row++)
{
        for (int col = 0; col < outputRows[row].Count; col++)
        {
                outputArray[row, col] = outputRows[row][col];
        }
}

oRng.set_Value(oOpt, outputArray);

oXL.Visible = true;
oXL.UserControl = true;

Это работает, но я бы предпочел использовать Список напрямую в Excel, чем на промежуточном этапе создания массива только для Excel. Есть идеи?

Ответы [ 4 ]

9 голосов
/ 30 июня 2009

Стратегически вы делаете это правильно. Как говорит Джо, гораздо быстрее выполнить присвоение значений ячейки, передавая целый массив значений за один снимок, а не циклически перебирая ячейки одну за другой.

Excel основан на COM и работает с Excel через взаимодействие .NET. К сожалению, взаимодействие не знает обобщений, поэтому вы не можете передать ему List или тому подобное. Двумерный массив действительно единственный путь.

Тем не менее, есть несколько способов очистить ваш код, чтобы сделать его немного более управляемым. Вот некоторые мысли:

(1) Если вы используете .NET 3.0, вы можете использовать LINQ, чтобы сократить код с:

int numberOfColumns = int.MinValue;

foreach (List<object> outputColumns in outputRows)
{
        if (numberOfColumns < outputColumns.Count)
        { numberOfColumns = outputColumns.Count; }
}

в одну строку:

int numberOfColumns = outputRows.Max(list => list.Count);

(2) Не используйте интерфейсы _Worksheet или _Workbook. Вместо этого используйте Worksheet или Workbook. Для обсуждения см. Здесь: Взаимодействие Excel: _Worksheet или Worksheet? .

(3) Подумайте об использовании метода Range.Resize, который обозначается как Range.get_Resize в C #. Это бросок вверх - мне действительно нравится, как вы устанавливаете размер диапазона. Но это то, о чем я подумала, вы можете захотеть узнать Например, ваша строка здесь:

Excel.Range oRng = oSheet.get_Range("A1", oSheet.Cells[numberOfRows,numberOfColumns]);

Может быть изменено на:

Excel.Range oRng = 
    oSheet.get_Range("A1", Type.Missing)
        .get_Resize(numberOfRows, numberOfColumns);

(4) Вам не нужно устанавливать Application.UserControl на true. Достаточно сделать Excel видимым для пользователя. Свойство UserControl не делает то, что вы думаете, оно делает. (См. Файлы справки здесь ) Если вы хотите контролировать, может ли пользователь контролировать Excel или нет, вам следует использовать защиту рабочего листа, или вы могли бы установить Application.Interactive = false, если хотите заблокировать своих пользователей. (Редко хорошая идея.) Но если вы хотите разрешить пользователю использовать Excel, то достаточно просто сделать его видимым.

В целом, учитывая это, я думаю, что ваш код может выглядеть примерно так:

object oOpt = System.Reflection.Missing.Value; //for optional arguments
Excel.Application oXL = new Excel.Application();
Excel.Workbooks oWBs = oXL.Workbooks;
Excel.Workbook oWB = oWBs.Add(Excel.XlWBATemplate.xlWBATWorksheet);
Excel.Worksheet oSheet = (Excel.Worksheet)oWB.ActiveSheet;

//outputRows is a List<List<object>>
int numberOfRows = outputRows.Count;
int numberOfColumns = outputRows.Max(list => list.Count);

Excel.Range oRng = 
    oSheet.get_Range("A1", oOpt)
        .get_Resize(numberOfRows, numberOfColumns);

object[,] outputArray = new object[numberOfRows, numberOfColumns];

for (int row = 0; row < numberOfRows; row++)
{
    for (int col = 0; col < outputRows[row].Count; col++)
    {
        outputArray[row, col] = outputRows[row][col];
    }
}

oRng.set_Value(oOpt, outputArray);

oXL.Visible = true;

Надеюсь, это поможет ...

Mike

1 голос
/ 29 июня 2009

Передача двумерного массива в Excel * на 1001 * намного быстрее, чем для обновления ячеек по одной за раз.

Создайте двумерный массив объектов со значениями из списка списков, измените размер диапазона Excel до размеров вашего массива, а затем вызовите range.set_Value, передав ваш двумерный массив.

0 голосов
/ 16 ноября 2018

Для будущих искателей: это правда, что если вы хотите скопировать список, чтобы преуспеть, вы должны вставить object[,] (2-мерный). На всякий случай, если у кого-то есть одномерный список, вы можете запустить только один for loop со вторым измерением 1:

List<DateTime> listDate; //I'm filling it with data from datagridview
...
object[,] outDate = new object[listDate.Count, 1];
for(int row = 0; row < listDate.Count; row++)
{
    outDate[row, 0] = listDate[row];
}
0 голосов
/ 05 ноября 2012
List<"classname"> getreport = cs.getcompletionreport();

var getreported = getreport .Select(c => new { demographic = c.rName);

надеется, что это поможет

, где cs.getcompletionreport() - это файл ссылочного класса Business Layer for App

...