Есть ли прямой способ экспортировать список списков (т.е. List<List<T>>) в Excel 2003 с помощью C #?
Я разбираю большие текстовые файлы и экспортирую в 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. Любые идеи?
Ответ 1 · рейтинг 10 · Mike Rosenblum
Стратегически вы делаете это правильно. Как говорит Джо, гораздо быстрее выполнять присвоение значений ячейкам, передавая весь массив значений за один раз, а не просматривая ячейки одну за другой.
Excel основан на COM и поэтому работает с Excel через взаимодействие .NET. К сожалению, при взаимодействии не используются универсальные шаблоны, поэтому вы не можете передать ему список ‹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: _Worksheet или Worksheet?.
(3) Рассмотрите возможность использования метода Range.Resize, который в C # обозначается как Range.get_Resize. Однако это подбрасывание - мне действительно нравится, как вы устанавливаете размер диапазона. Но я подумал, что это то, о чем вы, возможно, захотите узнать. Например, ваша строка здесь:
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;
Надеюсь это поможет...
Майк
Ответ 4 · Stana Macala
Для будущих искателей: это правда, что если вы хотите скопировать список, чтобы преуспеть, вам нужно вставить 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];
}