2009-06-29 2 views
4

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

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

В настоящее время я жду окончания файла, а затем поставлю содержимое своего List<List<object>> в двумерный массив. Затем массив можно установить как значение объекта 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. Есть идеи?

ответ

8

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

Excel основан на COM и поэтому работает с Excel через .NET-взаимодействие. К сожалению, interop не знает дженериков, поэтому вы не можете передать ему список <T> или тому подобное. Единственный путь - это двухмерный массив.

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

(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 interop: _Worksheet or 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 не делает то, что вы думаете об этом. (См. Файлы справки here). Если вы хотите контролировать, может ли пользователь управлять Excel или нет, вы должны использовать защиту Worksheet, или вы можете установить 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; 

Надеется, что это помогает ...

Майка

+0

Спасибо! Хотя мы не на .NET 3.0, я ценю все ваши разъяснения. – reverendlarry

+0

Без проблем, рад, что это помогает. :-) Только элемент №1 связан с .NET 3.0, все остальные будут применяться в любой .NET Framework. LINQ является основной причиной рассмотрения перехода на 3.0. Это не революционизирует весь ваш код, но в определенных областях LINQ - невероятно приятная функция. –

+1

Спасибо! Это сработало отлично, определенно upvote! Однако одна небольшая деталь, которая может помочь; Мне пришлось добавить к этому коду предложение 'oXL.Quit;', потому что если нет, процессы продолжали работать, и в какой-то момент я начал получать ошибки. – Soph

1

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

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

+0

Это то, что я делаю в настоящее время; но казалось, что если я собираюсь использовать «List » вместо массива в первую очередь, возможно, был другой способ получить данные в Excel, не загружая их сначала в промежуточный массив. – reverendlarry

0
List<"classname"> getreport = cs.getcompletionreport(); 

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

надежды, что это помогает

, где cs.getcompletionreport() является эталонным классом, является бизнес-слоем для App