С шарп работа с excel

Время на прочтение
7 мин

Количество просмотров 69K

В современном мире разработки приложений нередко встает необходимость работы с Excel документами. Чаще всего это разного рода отчеты, но иногда xls/x файлы используются в качестве хранилища данных. Например, если пользователь должен иметь возможность загрузить данные в приложение или выгрузить, в человеко-читаемом виде, Excel де-факто является стандартом. Относительно дружелюбный интерфейс, прозрачная структура, в купе с его распространенностью… трудно навскидку назвать решение лучше.

Однако, у многих Excel до сих пор ассоциируется с чем-то тяжелым, неповоротливым и сложным. Давайте посмотрим, как мы — обычные C# разработчики, можем легко сформировать простой Excel документ, на примере табличного отчета.

Историческая справка

Времена, когда доминировал проприетарный формат .xls(Excel Binary File Format) давно прошли и сейчас мы имеем только .xlsx(Excel Workbook), в рамках Office Open XML. Последний представляет собой обычный .zip архив с XML файлами. Не будем углубляться в его структуру, я искренне надеюсь что вам это никогда не понадобится.

На github, и не только, можно найти ряд библиотек, бесплатных и не только. Пожалуй самой популярной является EPPlus. До определенной степени, она довольно хорошо отражает концепцию Excel, именно по этому я всегда использую EPPlus. Версия 4 полностью бесплатна, начиная с 5‐й версии вам потребуется приобрести лицензию для коммерческого использования.

Задача

Итак, предположим, продукт-мэнеджеру ударила в голову идея того, что возможность выгружать некий отчет в формате Excel увеличит кол-во пользователей на 100500%. Проджет-менеджер решает выкатить эту киллер-фичу как хотфикс прямо сегодня — ведь работы всего на пару часов.

Сам по себе, отчет содержит краткое описание компании и историю изменения некоторых экономических показателей. Для простоты все свойства компании — строки. Экономические показатели — большие целые числа и числа с плавающей точкой, а также даты. Предположим, что где-то в недрах микросервисного backend-да есть сервис-генератор подобных отчетов, например по id компании. Однако, поскольку id нет смысла выводить пользователю, идентификатор отсутствует в самой модели отчета.

Аналитик, в свою очередь, выдает задачу с феноменально точным описанием — «Сгенерировать excel отчет на базе данных MarketReport». Что ж, для нашего примера, создадим заглушку — генератор фейковых данных:

Первый запуск

Подключим EPPlus версии 4.5.3.3 и создадим базовую обвязку для будущего генератора.

Сердцем генератора будет метод Generate. ExcelPackage это модель документа, через которую мы и будем осуществлять все взаимодействия с ним. Также имеется конструктор для передачи пути к файлу или потока.

В методе main создается генератор отчетов, а также генератор Excel файлов. Далее полученный файл просто записывается на диск.

При попытке запустить приложение, получаем exception:InvalidOperationException: The workbook must contain at least one worksheet

Все правильно, Excel документ не может существовать без страниц, должна быть хотя бы одна. Добавляем ее, все интуитивно понятно:

var sheet = package.Workbook.Worksheets    
		.Add("Market Report");

Запускаем снова и… вот оно! Теперь наше приложение генерирует документ и, хотя там еще ничего нет, он уже весит 2,5KB — значит мы работаем с Excel правильно и все идет как надо.

Вывод данных

Давайте выведем основную информацию по компании в шапку. Для доступа к конкретной ячейки объект Cells на странице пакета снабжен удобным индексатором. При этом, до конкретной ячейки можно достучаться как через номер строки и столбца, так и по привычному всем буквенно-числовому коду:

sheet.Cells["B2"].Value = "Company:";
sheet.Cells[2, 3].Value = report.Company.Name;

Полный код вывода шапки.

sheet.Cells["B2"].Value = "Company:";
sheet.Cells[2, 3].Value = report.Company.Name;
sheet.Cells["B3"].Value = "Location:";
sheet.Cells["C3"].Value = $"{report.Company.Address}, " +
  												$"{report.Company.City}, " +                          
  												$"{report.Company.Country}";
sheet.Cells["B4"].Value = "Sector:";
sheet.Cells["C4"].Value = report.Company.Sector;
sheet.Cells["B5"].Value = report.Company.Description;

Для вывода исторических данных понадобится как минимум шапка таблицы и цикл по массиву History:

sheet.Cells[8, 2, 8, 4].LoadFromArrays(new object[][]{ new []{"Capitalization", "SharePrice", "Date"} });
var row = 9;
var column = 2;
foreach (var item in report.History)
{
  sheet.Cells[row, column].Value = item.Capitalization;
  sheet.Cells[row, column + 1].Value = item.SharePrice;
  sheet.Cells[row, column + 2].Value = item.Date;    
  row++;
}

Предлагаю обратить внимание на метод LoadFromArrays, который заполняет диапазон ячеек рваным(зубчатым) массивом. Здесь мы можем видеть, что типизация теряется и передавая массив object мы ожидаем что EPPlus в конечном итоге использует ToString, чтобы записать переданное в ячейки.

Стилизация

Если вы прямо сейчас откроете документ, то вы возможно увидите не то, что хотелось бы отдать в продакшн в пятницу вечером.

Как это выглядит

Во-первых, шапка никак не выделяется, во-вторых таблица не имеет границ… выравнивание пляшет, даты отображаются магическими числами, а капитализация «уходит в какую-то математику» — как это прокомментировал аналитик.

Да, на все эти красивости у нас уйдет больше года кода, чем на сам вывод данных, и, в конечном тоге, получившаяся каша из логики вывода данных и разметки заставит некоторых усомниться в их компетентности… но, мы же backend разработчики, так давайте сверстаем Excel Sheet!

Размер ячеек

Из коробки у нас есть возможность сделать автофит а так же вручную выставить ширину в соответствии с нашей ситуацией. А ситуация у нас не самая хорошая — по задумке аналитика в шапке у ячеек должен быть автофит, а у ячеек таблицы — тоже автофит. Так в чем же подвох?

Если вы когда-нибудь до этого открывали Excel, то возможно знаете, что ширина ячеек не может отличаться в рамках столбца и автофит будет по самому широкому контенту ячейки. Однако, простые вещи бывает нетак то просто объяснить… Но если вы справитесь, то вот как это будет выглядеть в коде:

sheet.Cells[1, 1, row, column + 2].AutoFitColumns();
sheet.Column(2).Width = 14;
sheet.Column(3).Width = 12;

Формат данных

Как и большая часть стиля ячейки, он задается через одноименное свойство Style. Обратите внимание на вычисление 3-го аргумента индексатора. Это звоночек некачественного кода, но к этому мы вернемся в позже…

sheet.Cells[9, 4, 9 + report.History.Length, 4].Style.Numberformat.Format = "yyyy";
sheet.Cells[9, 2, 9 + report.History.Length, 2].Style.Numberformat.Format =  "### ### ### ##0";

Выравнивание

Его можно задать как на ячейке, так и на диапазоне. На самом деле, для EPPlus, это одна и та же сущность — некий ExcelRange, описывающий диапазон ячеек, в том числе и со всего 1 ячейкой.

sheet.Column(2).Style.HorizontalAlignment = ExcelHorizontalAlignment.Left;
sheet.Cells[8, 3, 8 + report.History.Length, 3].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;

Стиль текста

Также легко задается, используя Style.Font, кстати, здесь, на 2-й строчке, мы впервые указываем диапазон так, как привыкли его видеть пользователи Excel:

sheet.Cells[8, 2, 8, 4].Style.Font.Bold = true;
sheet.Cells["B2:C4"].Style.Font.Bold = true;

Границы

Задаем стиль линии, а также ее толщину. К этому моменту от кол-ва магических чисел-параметров индексатора уже рябит в глазах, но мы уже на финишной прямой… не так ли?

sheet.Cells[8, 2, 8 + report.History.Length, 4].Style.Border.BorderAround(ExcelBorderStyle.Double);
sheet.Cells[8, 2, 8, 4].Style.Border.Bottom.Style = ExcelBorderStyle.Thin;

График

«Ну что за отчет без графиков, верно, Карл?» — ловко подметит специалист по тестированию, и не важно, что этого не было в ТЗ а на часах уже половина 9-го…

Хотя график как сущность сам по себе сложнее таблиц и с графиками мы не работаем каждый день, EPPlus предоставляет довольно понятный API. Давайте добавим простейший график, отражающий рост капитализации:

var capitalizationChart = sheet.Drawings.AddChart("FindingsChart", OfficeOpenXml.Drawing.Chart.eChartType.Line);
capitalizationChart.Title.Text = "Capitalization";
capitalizationChart.SetPosition(7, 0, 5, 0);
capitalizationChart.SetSize(800, 400);
var capitalizationData = (ExcelChartSerie)(capitalizationChart.Series.Add(sheet.Cells["B9:B28"], sheet.Cells["D9:D28"]));
capitalizationData.Header = report.Company.Currency;

Еще, может понадобиться защитить страницу от редактирования:

sheet.Protection.IsProtected = true;

На этом все, репозиторий с рабочим приложением находится здесь.

Заключение

О чем говорит финальная версия метода Generate?

public byte[] Generate(MarketReport report)
{    
  var package = new ExcelPackage();    
  
  var sheet = package.Workbook.Worksheets        
    	.Add("Market Report");      
  
  sheet.Cells["B2"].Value = "Company:";    
  sheet.Cells[2, 3].Value = report.Company.Name;    
  sheet.Cells["B3"].Value = "Location:";    
  sheet.Cells["C3"].Value = $"{report.Company.Address}, " +
    												$"{report.Company.City}, " +                             
    												$"{report.Company.Country}";    
  sheet.Cells["B4"].Value = "Sector:";    
  sheet.Cells["C4"].Value = report.Company.Sector;    
  sheet.Cells["B5"].Value = report.Company.Description;    
  
  sheet.Cells[8, 2, 8, 4].LoadFromArrays(new object[][]{ new []{"Capitalization", "SharePrice", "Date"} });    
  var row = 9;    
  var column = 2;    
  foreach (var item in report.History)    
  {        
    	sheet.Cells[row, column].Value = item.Capitalization;        
   		sheet.Cells[row, column + 1].Value = item.SharePrice;        
   		sheet.Cells[row, column + 2].Value = item.Date;        
    	row++;    
  }    
  
  sheet.Cells[1, 1, row, column + 2].AutoFitColumns();    
  sheet.Column(2).Width = 14;    
  sheet.Column(3).Width = 12;        
  
  sheet.Cells[9, 4, 9+ report.History.Length, 4].Style.Numberformat.Format = "yyyy";    
  sheet.Cells[9, 2, 9+ report.History.Length, 2].Style.Numberformat.Format =  "### ### ### ##0";    
  
  sheet.Column(2).Style.HorizontalAlignment = ExcelHorizontalAlignment.Left;    
  sheet.Cells[8, 3, 8 + report.History.Length, 3].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;    
  sheet.Column(4).Style.HorizontalAlignment = ExcelHorizontalAlignment.Right;    
  
  sheet.Cells[8, 2, 8, 4].Style.Font.Bold = true;    
  sheet.Cells["B2:C4"].Style.Font.Bold = true;
  
  sheet.Cells[8, 2, 8 + report.History.Length, 4].Style.Border.BorderAround(ExcelBorderStyle.Double);    
  sheet.Cells[8, 2, 8, 4].Style.Border.Bottom.Style = ExcelBorderStyle.Thin;     
  
  var capitalizationChart = sheet.Drawings.AddChart("FindingsChart", OfficeOpenXml.Drawing.Chart.eChartType.Line);    
  capitalizationChart.Title.Text = "Capitalization";    
  capitalizationChart.SetPosition(7, 0, 5, 0);    
  capitalizationChart.SetSize(800, 400);    
  var capitalizationData = (ExcelChartSerie)(capitalizationChart.Series.Add(sheet.Cells["B9:B28"], sheet.Cells["D9:D28"]));    
  capitalizationData.Header = report.Company.Currency;       
  
  sheet.Protection.IsProtected = true;    
  
  return package.GetAsByteArray();
}

Во-первых, прежде всего, о том, что мы успешно справились с задачей, а именно, сгенерировали свой первый Excel отчет, поработали со стилями и даже решили пару попутных проблем.

Во-вторых, возможно имеет смысл искать новою работу, но, забегая вперед, я бы с этим не спешил… Если данная публикация наберет 1+ просмотров, то во второй части мы поговорим о том, как можно отделить стилизацию от логики заполнения данными, упростить манипуляции над ячейками и в целом сделаем код боле поддерживаемым.

Все привет, в этой статье опишу исчерпывающие примеры работы с excel на языке C#.

Для начала работы нам необходимо подключить библиотеку COM как на рисунке ниже:

Для этого добавляем ссылку в проект, надеюсь вы знаете как это делается) Выбираем пункт COM ищем библиотеку Microsoft Excel 16.0 Object Library ставим галочку и жмем Ок.

Далее нам не обходимо для сокращения записи и удобства создать алиас.

using Excel = Microsoft.Office.Interop.Excel;

Теперь нам нужно объявить объект Excel задать параметры и приступать к работе.

//Объявляем приложение

            Excel.Application app = new Excel.Application

            {

                //Отобразить Excel

                Visible = true,

                //Количество листов в рабочей книге

                SheetsInNewWorkbook = 2

            };

            //Добавить рабочую книгу

            Excel.Workbook workBook = app.Workbooks.Add(Type.Missing);

            //Отключить отображение окон с сообщениями

            app.DisplayAlerts = false;

            //Получаем первый лист документа (счет начинается с 1)

            Excel.Worksheet sheet = (Excel.Worksheet)app.Worksheets.get_Item(1);

            //Название листа (вкладки снизу)

            sheet.Name = «Имя должно быть не больше 32сим»;

Пример заполнения ячейки:

           //Пример заполнения ячеек №1

            for (int i = 1; i <= 9; i++)

            {

                for (int j = 1; j < 9; j++)

                    sheet.Cells[i, j] = String.Format(«nookery {0} {1}», i, j);

            }

            //Пример №2

            sheet.Range[«A1»].Value = «Пример №2»;

            //Пример №3

            sheet.get_Range(«A2»).Value2 = «Пример №3»;

Захват диапазона ячеек:

            //Захватываем диапазон ячеек Вариант №1

            Excel.Range r1 = sheet.Cells[1, 1];

            Excel.Range r2 = sheet.Cells[9, 9];

            Excel.Range range1 = sheet.get_Range(r1, r2);

            //Захватываем диапазон ячеек Вариант №2

            Excel.Range range2 = sheet.get_Range(«A1»,«H9» );

Оформление, шрифт, размер, цвет, толщина.

            //Шрифт для диапазона

              range.Cells.Font.Name = «Tahoma»;

              range2.Cells.Font.Name = «Times New Roman»;

            //Размер шрифта для диапазона

              range.Cells.Font.Size = 10;

            //Жирный текст

              range.Font.Bold = true;

            //Цвет текста

              range.Font.Color = ColorTranslator.ToOle(Color.Blue);

Объединение ячеек в одну

  //Объединение ячеек с F2 по K2

    Excel.Range range3 = sheet.get_Range(«F2», «K2»);

    range3.Merge(Type.Missing);

Изменяем размеры ячеек по ширине и высоте

//увеличиваем размер по ширине диапазон ячеек

   Excel.Range range2 = sheet.get_Range(«D1», «S1»);

   range2.EntireColumn.ColumnWidth = 10;

//увеличиваем размер по высоте диапазон ячеек

   Excel.Range rowHeight = sheet.get_Range(«A4», «S4»);

   rowHeight.EntireRow.RowHeight = 50;range.EntireColumn.AutoFit();range.EntireColumn.AutoFit(); //авторазмер

Создаем обводку диапазона ячеек

Excel.Range r1 = sheet.Cells[countRow, 2];

Excel.Range r2 = sheet.Cells[countRow, 19];

Excel.Range rangeColor = sheet.get_Range(r1, r2);

rangeColor.Borders.Color = ColorTranslator.ToOle(Color.Black);

Производим выравнивания содержимого диапазона ячеек.

  Excel.Range r = sheet.get_Range(«A1», «S40»);

  //Оформления

  r.Font.Name = «Calibri»;

  r.Cells.Font.Size = 10;

  r.VerticalAlignment = Excel.XlVAlign.xlVAlignCenter;

  r.HorizontalAlignment = Excel.XlHAlign.xlHAlignCenter;

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

sheet.Cells[countRow, countColumn] = $«=G{countRow}-F{countRow}»;

sheet.Cells[countRow, countColumn].FormulaLocal = $«=ЕСЛИ((H{countRow}*O{countRow})+(I{countRow}*P{countRow})/100<=0;J{countRow}*O{countRow}/100;((H{countRow}*O{countRow})+(I{countRow}*P{countRow}))/100)»;

sheet.Cells[countRow, countColumn] = $«=K{countRow}+N{countRow}-R{countRow}»;

sheet.Cells[33, 22].FormulaLocal = «=СУММ(V3:V32)»;

Добавляем разрыв страницы.

//Ячейка, с которой будет разрыв

Excel.Range razr = sheet.Cells&#91;n, m] as Excel.Range;

//Добавить горизонтальный разрыв (sheet — текущий лист)

sheet.HPageBreaks.Add(razr);

//VPageBreaks — Добавить вертикальный разрыв

Как открыть фаил Excel

app.Workbooks.Open(@»C:UsersUserDocumentsExcel.xlsx»,

  Type.Missing, Type.Missing, Type.Missing, Type.Missing,

  Type.Missing, Type.Missing, Type.Missing, Type.Missing,

  Type.Missing, Type.Missing, Type.Missing, Type.Missing,

  Type.Missing, Type.Missing);

Сохраняем документ Excel

app.Application.ActiveWorkbook.SaveAs(«MyFile.xlsx», Type.Missing,

  Type.Missing, Type.Missing, Type.Missing, Type.Missing, Excel.XlSaveAsAccessMode.xlNoChange,

  Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

Завершение работы с объектом Excel.Application

app.Quit();

System.Runtime.InteropServices.Marshal.ReleaseComObject(app);

Пример как выбрать фаил и загрузив его и узнать количество заполненных строк и колонок в одном конкретном листе по имени.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

//поиск файла Excel

            OpenFileDialog ofd = new OpenFileDialog();

            ofd.Multiselect =false;

            ofd.DefaultExt = «*.xls;*.xlsx»;

            ofd.Filter = «Microsoft Excel (*.xls*)|*.xls*»;

            ofd.Title = «Выберите документ Excel»;

            if (ofd.ShowDialog() != DialogResult.OK)

            {

                MessageBox.Show(«Вы не выбрали файл для открытия», «Внимание», MessageBoxButtons.OK, MessageBoxIcon.Information);

                return;

            }

            string xlFileName = ofd.FileName; //имя нашего Excel файла

            //рабоата с Excel

            Excel.Range Rng;            

            Excel.Workbook xlWB;

            Excel.Worksheet xlSht;

            int iLastRow, iLastCol;

            Excel.Application xlApp = new Excel.Application(); //создаём приложение Excel

            xlWB = xlApp.Workbooks.Open(xlFileName); //открываем наш файл          

            xlSht = xlWB.Worksheets[«Лист1»]; //или так xlSht = xlWB.ActiveSheet //активный лист

            iLastRow = xlSht.Cells[xlSht.Rows.Count, «A»].End[Excel.XlDirection.xlUp].Row; //последняя заполненная строка в столбце А

            iLastCol = xlSht.Cells[1, xlSht.Columns.Count].End[Excel.XlDirection.xlToLeft].Column; //последний заполненный столбец в 1-й строке

Получаем список всех загруженных книг «листов» из файла

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

  //поиск файла Excel

            OpenFileDialog ofd = new OpenFileDialog();

            ofd.Multiselect = false;

            ofd.DefaultExt = «*.xls;*.xlsx»;

            ofd.Filter = «Microsoft Excel (*.xls*)|*.xls*»;

            ofd.Title = «Выберите документ Excel»;

            if (ofd.ShowDialog() != DialogResult.OK)

            {

                MessageBox.Show(«Вы не выбрали файл для открытия», «Внимание», MessageBoxButtons.OK, MessageBoxIcon.Information);

                return;

            }

            string xlFileName = ofd.FileName; //имя нашего Excel файла

Excel.Workbook xlWB = ex.Workbooks.Open(xlFileName);

///загружаем список всех книг

            foreach (object item in xlWB.Sheets)

            {

                Excel.Worksheet sheet = (Excel.Worksheet)item;

            }

Содержание

  • Введение
  • Добавление необходимых ссылок в решение
  • Работа с объектом Application (Excel)
    • Запуск и остановка Microsoft Excel в C#
    • Создание рабочей книги Excel в C#
    • Работа с листами Excel в C#. Hello Excel
  • Итого

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

При разработки различных программ для анализа данных нередко требуется не только обрабатывать большие массивы данных, но и предоставить конечному пользователю удобно оформленный отчёт о работе программного обеспечения. В сегодняшних реалиях экономия килобайт оперативной памяти при разработке программного обеспечения уже не так важна по сравнению с удобством работы программного продукта. Не секрет, что при работе в Windows для наиболее удобного представления табличных данных является Microsoft Excel. В этой и других статьях, посвященных работе с Excel в .NET Core и C# я постараюсь рассмотреть наиболее частые задачи по импорту и экспорту данных между приложением и Microsoft Excel. Сегодня мы рассмотрим как использовать MS Excel в .NET Core, используя технологию COM.

Введение

Разработка программ, взаимодействующих с пакетом Microsoft Office, вне зависимости от среды разработки, будь то Visual Studio С# или Delphi, основана на применении так называемой объектной модели Microsoft Office. Взаимодействие с объектами такой модели базируется на технологии OLE (Object Linking and Embedding, связывание и внедрение объектов), которая, в свою очередь, использует технологический стандарт COM (Component Object Model — модель компонентного объекта).

Существует достаточно много решений, позволяющих работать с Microsoft Excel в Visual Studio .NET о которых мы, вполне возможно, поговорим позднее. Сейчас же мы будем учиться использовать возможности Microsoft Excel, опираясь исключительно на объектную модель Microsoft Office и те возможности, которые она предоставляет разработчикам, так сказать, «из коробки».

Добавление необходимых ссылок в решение

Для подключения в решение библиотек для взаимодействия с Microsoft Excel необходимо добавить добавьте следующие ссылки:

  • Microsoft Excel 16.0 Object Library
  • Microsoft Office 16.0 Object Library

В Visual Studio это делается следующим образом:

1. В «Обозревателе решений» кликаем правой кнопкой мыши по строке «Зависимости» и выбираем пункт «Добавить ссылку на модель COM»

2. В открывшемся окне в строке поиска набираем «Microsoft», выбираем необходимые ссылки, то есть Microsoft Excel 16.0 Object Library и Microsoft Office 16.0 Object Library и жмем «Ok»:

После этого, в обозревателе решений для нашего проекта в зависимостях появятся следующие ссылки:

Зависимости
   COM
      Interop.Microsoft.Office.Core
      Interop.Microsoft.Office.Interop.Excel
   Платформы
      Microsoft.NetCore.App

Теперь, чтобы наше решение для .NET Core могло полноценно использовать возможности Microsoft Excel необходимо указать в решении, что все типы, используемые в объектной модели Microsoft Office будут использоваться в нашей сборке. Чтобы это сделать выбираем в зависимостях нашего решения каждую ссылку из раздела COM и в разделе «Свойства» указываем напротив пункта «Внедрить типы взаимодействия» значение «Да». На рисунке ниже представлен пример для ссылки Interop.Microsoft.Office.Core

Если не выполнить эти действия, то при разработки приложения .NET Core при попытке работы с Excel вы получите следующую ошибку:

System.IO.FileNotFoundException HResult=0x80070002
Сообщение = Could not load file or assembly ‘office, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c’. Не удается найти указанный файл.
Источник = <Не удается определить источник исключения>
Трассировка стека: <Не удается определить трассировку стека исключения>

Работа с объектом Application (Excel)

Объект Application представляет всё приложение Microsoft Excel. С помощью этого объекта в C# мы можем запускать и останавливать работу Excel, получать доступ к рабочим книгам Excel и так далее. Посмотрим, как можно взаимодействовать с Excel в C#, используя объект Application.

Запуск и остановка Microsoft Excel в C#

Чтобы продемонстрировать запуск и остановку Excel в C# подключим в наше решение пространство имен Microsoft.Office.Interop.Excel, используя псевдоним:

using Excel = Microsoft.Office.Interop.Excel;

Теперь напишем следующий код для метода Main():

using System;
using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;

namespace ExcelApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Excel.Application application = new Excel.Application();
            Console.WriteLine("Объект Excel.Application создан");
            Console.ReadKey();
            application.Visible = true;
            Console.WriteLine("Окно Excel видно пользователю");
                        
            Console.ReadKey();
            Console.WriteLine("Закрываем Excel");
           
            application.Quit();
            Marshal.ReleaseComObject(application);
        }
    }
}

Разберемся с тем, что происходит в нашем приложении при запуске. Вначале мы создаем объект application типа Excel.Application (Excel — псевдоним пространства имен Microsoft.Office.Interop.Excel). После выполнения этой строки кода происходит запуск процесса Microsoft Excel. Убедиться в этом можно, открыв диспетчер задач Windows и заглянув в раздел «Фоновые процессы»:

Чтобы показать главное окно Excel пользователю, мы устанавливаем значение true свойству Visible:

application.Visible = true;

И, наконец, для того, чтобы закрыть Excel и освободить память, занятую процессом Excel мы вызываем метод Quit().

application.Quit()

Таким образом, всё, что пока делает наше приложение .NET Core — это запускает процесс Microsoft Excel, показывает окно Excel и закрывает приложение. При этом, стоит обратить внимание на следующий момент: так как чаще всего мы будем взаимодействовать с Excel в C# в фоновом режиме без показа главного окна, то по окончанию взаимодействия необходимо обязательно вызывать метод Quit() объекта Application и вызвать метод объекта Marshal.ReleaseComObject(application) из пространства имен System.Runtime.InteropServices. Если этого не сделать, то после N запусков нашего приложения мы рискуем «забить» оперативную память компьютера неиспользуемыми процессами Excel, так как после остановки нашего приложения и вызова Quit() внешний процесс (Excel) автоматически не завершается. Продемонстрировать это можно, написав всего две строки кода:

static void Main(string[] args)
  {
      Excel.Application application = new Excel.Application();
      application.Quit();
  }

и запустить приложение несколько раз. Например, на рисунке ниже показан диспетчер задач после трех запусков нашего приложения:

Создание рабочей книги Excel в C#

Следующий пример демонстрирует создание новой рабочей книги Excel в C#:

using System;
using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;

namespace ExcelApp
{
    class Program
    {
        static void Main(string[] args)
        {
            //Объявляем переменную приложения Excel 
            Excel.Application application = null;
            //объявляем переменные для коллекции рабочих книг 
            //и одной рабочей книги
            Excel.Workbooks workbooks = null;
            Excel.Workbook workbook = null;
            try
            {
                //создаем объект Application
                application = new Excel.Application();
                Console.WriteLine("Объект Excel.Application создан");
                Console.ReadKey();
                //показываем окно Excel пользователю
                application.Visible = true;
                Console.WriteLine("Окно Excel видно пользователю");
                //получаем ссылку на коллекцию рабочих книг
                workbooks = application.Workbooks;
                //добавляем новую рабочую книгу в коллекцию
                workbook = workbooks.Add();
                Console.ReadKey();
                Console.WriteLine("Закрываем Excel");
                application.Quit();
            }
            finally
            {
                //освобождаем память, занятую объектами
                Marshal.ReleaseComObject(workbook);
                Marshal.ReleaseComObject(workbooks);
                Marshal.ReleaseComObject(application);

            }
        }
    }
}

На первый взгляд, может показаться, что этот код вполне можно упростить, например, избавившись от «лишних» переменной workbooks,  например так:

workbook = application.Workbooks.Add();

Такой код, безусловно, будет короче и рабочая книга также будет создана, НО при этом, память выделенная для процесса Excel не будет освобождена по причине того, что в приведенной выше строке кода мы на самом деле создаем не один, а два объекта: объект типа Excel.Workbooks и объект типа Excel.Workbook и, соответственно, оба эти объекта нам необходимо освободить после работы с ними.

Исходя из вышеизложенного, можно определить для себя два следующих правила работы с COM в C#:

Никогда не использовать две и более точек при работе с COM

О том, из-за чего процесс Excel может зависать — смотрите в этой статье.

Работа с листами Excel в C#. Hello Excel

В следующем примере демонстрируется то, как получить доступ к первому листу рабочей книги и записи в ячейку A1 строки «Hello Excel«:

using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;

namespace ExcelApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Excel.Application application = null;
            Excel.Workbooks workbooks = null;
            Excel.Workbook workbook = null;
            Excel.Sheets worksheets = null;
            Excel.Worksheet worksheet = null;
            //переменная для хранения диапазона ячеек
            //в нашем случае - это будет одна ячейка
            Excel.Range cell = null;
            try
            {
                application = new Excel.Application
                {
                    Visible = true
                };
                workbooks = application.Workbooks;
                workbook = workbooks.Add();
                worksheets = workbook.Worksheets; //получаем доступ к коллекции рабочих листов
                worksheet = worksheets.Item[1];//получаем доступ к первому листу
                cell = worksheet.Cells[1, 1];//получаем доступ к ячейке
                cell.Value = "Hello Excel";//пишем строку в ячейку A1
                application.Quit();
            }
            finally
            {
                //освобождаем память, занятую объектами
                Marshal.ReleaseComObject(cell);
                Marshal.ReleaseComObject(worksheet);
                Marshal.ReleaseComObject(worksheets);
                Marshal.ReleaseComObject(workbook);
                Marshal.ReleaseComObject(workbooks);
                Marshal.ReleaseComObject(application);

            }
        }
    }
}

После запуска этого приложения откроется Excel, создастся рабочая книга, на первом листе рабочей книги в ячейке А1 появится строка «Hello Excel» после чего Excel попросит сохранить книгу. После отказа сохранения Excel закроется и память процесса Microsoft Excel будет освобождена.

Итого

Итак, сегодня мы рассмотрели основные моменты по работе с Microsoft Excel в C# и .NET Core: научились создавать процесс Microsoft Excel, создавать рабочую книгу, записывать текст в ячейки листа Excel и корректно освобождать память после работы с Excel в C#. В следующий раз мы продолжим разбираться с темой использования возможностей Excel в .NET Core и более детально познакомимся с объектной моделью Microsoft Office.

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Оставляю заметку по работе с Excel с помощью C#.

Привожу фрагменты кода, которые искал когда-то сам для работы с Excel документами.

Наработки очень пригодились в работе для формирования отчетности.

Прежде всего нужно подключить библиотеку Microsoft.Office.Interop.Excel.

Подключение Microsoft.Office.Interop.Excel

Visual Studio здесь довольно старой версии. Если у вас версия новая, отличаться будет только вид окна.

Далее создаем псевдоним для работы с Excel:

using Excel = Microsoft.Office.Interop.Excel;

//Объявляем приложение
Excel.Application ex = new Microsoft.Office.Interop.Excel.Application();

//Отобразить Excel
ex.Visible = true;

//Количество листов в рабочей книге
ex.SheetsInNewWorkbook = 2;

//Добавить рабочую книгу
Excel.Workbook workBook = ex.Workbooks.Add(Type.Missing);

//Отключить отображение окон с сообщениями
ex.DisplayAlerts = false;                                       

//Получаем первый лист документа (счет начинается с 1)
Excel.Worksheet sheet = (Excel.Worksheet)ex.Worksheets.get_Item(1);

//Название листа (вкладки снизу)
sheet.Name = "Отчет за 13.12.2017";

//Пример заполнения ячеек
for (int i = 1; i <= 9; i++)
{
  for (int j = 1; j < 9; j++)
  sheet.Cells[i, j] = String.Format("Boom {0} {1}", i, j);
}

//Захватываем диапазон ячеек
Excel.Range range1 = sheet.get_Range(sheet.Cells[1, 1], sheet.Cells[9, 9]);

//Шрифт для диапазона
range1.Cells.Font.Name = "Tahoma";
//Размер шрифта для диапазона
range1.Cells.Font.Size = 10;

//Захватываем другой диапазон ячеек
Excel.Range range2 = sheet.get_Range(sheet.Cells[1, 1], sheet.Cells[9, 2]);
range2.Cells.Font.Name = "Times New Roman";

//Задаем цвет этого диапазона. Необходимо подключить System.Drawing
range2.Cells.Font.Color = ColorTranslator.ToOle(Color.Green);
//Фоновый цвет
range2.Interior.Color = ColorTranslator.ToOle(Color.FromArgb(0xFF, 0xFF, 0xCC));

Расстановка рамок.

Расставляем рамки со всех сторон:

range2.Borders.get_Item(Excel.XlBordersIndex.xlEdgeBottom).LineStyle = Excel.XlLineStyle.xlContinuous;
range2.Borders.get_Item(Excel.XlBordersIndex.xlEdgeRight).LineStyle = Excel.XlLineStyle.xlContinuous;
range2.Borders.get_Item(Excel.XlBordersIndex.xlInsideHorizontal).LineStyle = Excel.XlLineStyle.xlContinuous;
range2.Borders.get_Item(Excel.XlBordersIndex.xlInsideVertical).LineStyle = Excel.XlLineStyle.xlContinuous;
range2.Borders.get_Item(Excel.XlBordersIndex.xlEdgeTop).LineStyle = Excel.XlLineStyle.xlContinuous;

Цвет рамки можно установить так:

range2.Borders.Color = ColorTranslator.ToOle(Color.Red);

Выравнивания в диапазоне задаются так:

rangeDate.VerticalAlignment = Excel.XlVAlign.xlVAlignCenter;
rangeDate.HorizontalAlignment = Excel.XlHAlign.xlHAlignLeft;

Формулы

Определим задачу: получить сумму диапазона ячеек A4:A10.

Для начала снова получим диапазон ячеек:

Excel.Range formulaRange = sheet.get_Range(sheet.Cells[4, 1], sheet.Cells[9, 1]);

Далее получим диапазон вида A4:A10 по адресу ячейки ( [4,1]; [9;1] ) описанному выше:

string adder = formulaRange.get_Address(1, 1, Excel.XlReferenceStyle.xlA1, Type.Missing, Type.Missing);

Теперь в переменной adder у нас хранится строковое значение диапазона ( [4,1]; [9;1] ), то есть A4:A10.

Вычисляем формулу:

//Одна ячейка как диапазон
Excel.Range r = sheet.Cells[10, 1] as Excel.Range;
//Оформления
r.Font.Name = "Times New Roman";
r.Font.Bold = true;
r.Font.Color = ColorTranslator.ToOle(Color.Blue);
//Задаем формулу суммы
r.Formula = String.Format("=СУММ({0}", adder);

Выделение ячейки или диапазона ячеек

Так же можно выделить ячейку или диапазон, как если бы мы выделили их мышкой:

sheet.get_Range("J3", "J8").Activate();
//или
sheet.get_Range("J3", "J8").Select();
//Можно вписать одну и ту же ячейку, тогда будет выделена одна ячейка.
sheet.get_Range("J3", "J3").Activate();
sheet.get_Range("J3", "J3").Select();

Авто ширина и авто высота

Чтобы настроить авто ширину и высоту для диапазона, используем такие команды:

range.EntireColumn.AutoFit(); 
range.EntireRow.AutoFit();

Получаем значения из ячеек

Чтобы получить значение из ячейки, используем такой код:

//Получение одной ячейки как ранга
Excel.Range forYach = sheet.Cells[ob + 1, 1] as Excel.Range;
//Получаем значение из ячейки и преобразуем в строку
string yach = forYach.Value2.ToString();

Добавляем лист в рабочую книгу

Чтобы добавить лист и дать ему заголовок, используем следующее:

var sh = workBook.Sheets;
Excel.Worksheet sheetPivot = (Excel.Worksheet)sh.Add(Type.Missing, sh[1], Type.Missing, Type.Missing);
sheetPivot.Name = "Сводная таблица";

Добавление разрыва страницы

//Ячейка, с которой будет разрыв
Excel.Range razr = sheet.Cells[n, m] as Excel.Range;
//Добавить горизонтальный разрыв (sheet - текущий лист)
sheet.HPageBreaks.Add(razr); 
//VPageBreaks - Добавить вертикальный разрыв

Сохраняем документ

ex.Application.ActiveWorkbook.SaveAs("doc.xlsx", Type.Missing,
  Type.Missing, Type.Missing, Type.Missing, Type.Missing, Excel.XlSaveAsAccessMode.xlNoChange,
  Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

Как открыть существующий документ Excel

ex.Workbooks.Open(@"C:UsersMyuserDocumentsExcel.xlsx",
  Type.Missing, Type.Missing, Type.Missing, Type.Missing,
  Type.Missing, Type.Missing, Type.Missing, Type.Missing,
  Type.Missing, Type.Missing, Type.Missing, Type.Missing,
  Type.Missing, Type.Missing);

Комментарии

При работе с Excel с помощью C# большую помощь может оказать редактор Visual Basic, встроенный в Excel.

Для этого в настройках ленты надо добавить пункт «Разработчик». Далее начинаем запись макроса, производим действия и останавливаем запись.

Далее заходим в редактор Visual Basic и смотрим код, который туда записался:

Vusial Basic (VBA)

Например:

Sub Макрос1()
'
' Макрос1 Макрос
'

'
    Range("E88").Select
    ActiveSheet.ListObjects.Add(xlSrcRange, Range("$A$1:$F$118"), , xlYes).Name = _
        "Таблица1"
    Range("A1:F118").Select
    ActiveSheet.ListObjects("Таблица1").TableStyle = "TableStyleLight9"
    Range("E18").Select
    ActiveWindow.SmallScroll Down:=84
End Sub

В данном макросе записаны все действия, которые мы выполнили во время его записи. Эти методы и свойства можно использовать в C# коде.

Данный метод так же может оказать помощь в формировании относительных формул, например, выполнить сложение чисел, находящиеся слева от текущей ячейки на 4 столбца, и т.п. Пример:

//Складываем значения предыдущих 12 ячеек слева
rang.Formula = "=СУММ(RC[-12]:RC[-1])";

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

Чтобы выбрать лист, выполните sheetData.Select(Type.Missing); где sheetData это нужный лист.


Просмотрено:
81 944

Overview

Creating report files in Excel format is a feature that many users need out of their software applications.  

In this article, we’ll cover creating an Excel spreadsheet in C# using a third-party library called EPPlus.

EPPlus is a .NET library that reads and writes Excel files using the Open Office XML format (*.xlsx).  It is licensed under GNU Library General Public License (LGPL) so it can be freely used in commercial software.

Installation

In Visual Studio 2017, go to Tools -> NuGet Package Manager -> Package Manager Console

In the console window that appears, type the following at the prompt to install it into the current project:

PM > Install-Package EPPlus

After it is finished, check the References under Solution Explorer and verify that the EPPlus assembly is listed.

Add to Project

We add line 7 below so we can use the types inside the namespace:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using OfficeOpenXml;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}

Create a Workbook

We call the ExcelPackage class constructor to create a workbook.   In the below code, we do this within a using block so we don’t have to explicitly dispose of the object.

We add 3 blank worksheets to the workbook by using the Add() method in the Worksheets class.   Finally, we save the file to a path using the SaveAs() method.

using (ExcelPackage excel = new ExcelPackage())
{
  excel.Workbook.Worksheets.Add("Worksheet1");
  excel.Workbook.Worksheets.Add("Worksheet2");
  excel.Workbook.Worksheets.Add("Worksheet3");

  FileInfo excelFile = new FileInfo(@"C:UsersamirDesktoptest.xlsx");
  excel.SaveAs(excelFile);
}

The SaveAs method will throw an exception if there are no worksheets defined. 

Select a Worksheet

Let’s say we want to add some data to Worksheet1.   How can we target it?

var excelWorksheet = excel.Workbook.Worksheets["Worksheet1"];

Add a Row

Spreadsheets typically have header rows.  Let’s add a header row to Worksheet1.  

For this, we create a List containing an array of strings:

List<string[]> headerRow = new List<string[]>()
{
  new string[] { "ID", "First Name", "Last Name", "DOB" }
};

Before we can pass this object into our worksheet, we must figure out the cell range for the header row:

// Determine the header range (e.g. A1:E1)
string headerRange = "A1:" + Char.ConvertFromUtf32(headerRow[0].Length + 64) + "1";

Here’s how we load the data into the worksheet:

// Popular header row data
excelWorksheet.Cells[headerRange].LoadFromArrays(headerRow);

Putting it all together here is the code block:

using (ExcelPackage excel = new ExcelPackage())
{
  excel.Workbook.Worksheets.Add("Worksheet1");
  excel.Workbook.Worksheets.Add("Worksheet2");
  excel.Workbook.Worksheets.Add("Worksheet3");
  
  var headerRow = new List<string[]>()
  {
    new string[] { "ID", "First Name", "Last Name", "DOB" }
  };
  
  // Determine the header range (e.g. A1:D1)
  string headerRange = "A1:" + Char.ConvertFromUtf32(headerRow[0].Length + 64) + "1";

  // Target a worksheet
  var worksheet = excel.Workbook.Worksheets["Worksheet1"];
  
  // Popular header row data
  worksheet.Cells[headerRange].LoadFromArrays(headerRow);
  
  FileInfo excelFile = new FileInfo(@"C:UsersamirDesktoptest.xlsx");
  excel.SaveAs(excelFile);
}

If we open the file in Excel, we see that the header row appears in the first worksheet:

Row Styling

We can easily change the font size, color, and weight of any row.

worksheet.Cells[headerRange].Style.Font.Bold = true;
worksheet.Cells[headerRange].Style.Font.Size = 14;
worksheet.Cells[headerRange].Style.Font.Color.SetColor(System.Drawing.Color.Blue);

Add Data to Specific Cell

We can easily add data to a cell by setting the Value property of the Cell element.

worksheet.Cells["A1"].Value = "Hello World!";

Add Data to Multiple Cells

Inserting data into multiple cells is also straightforward.   We can populate a list containing an array that corresponds to each row.    

var cellData= new List()
{
  new object[] {0, 0, 0, 1},
  new object[] {10,7.32,7.42,1},
  new object[] {20,5.01,5.24,1},
  new object[] {30,3.97,4.16,1},
  new object[] {40,3.97,4.16,1},
  new object[] {50,3.97,4.16,1},
  new object[] {60,3.97,4.16,1},
  new object[] {70,3.97,4.16,1},
  new object[] {80,3.97,4.16,1},
  new object[] {90,3.97,4.16,1},
  new object[] {100,3.97,4.16,1}
};

excelWorksheet.Cells[2, 1].LoadFromArrays(cellData);

Here we add the data from cellData into the worksheet starting on row 2, column 1.   Remember that we already inserted a header row on row 1.

Check if Excel is installed

We can use this line of code to determine if Excel is installed:

bool isExcelInstalled = Type.GetTypeFromProgID("Excel.Application") != null ? true : false;

If the expression evaluates to true, then we can safely open the file using the below code:

if (isExcelInstalled) 
{
  System.Diagnostics.Process.Start(excelFile.ToString());
}

What about Microsoft.Office.Interop.Excel?

If the user has Excel installed on their machine, we could tap into the Microsoft.Office.Interop.Excel assembly which instructs .NET on how to call the Excel COM libraries.

This approach has a couple flaws though.   Excel file generation would fail if the user didn’t have Excel installed.  Also, what happens when the version of the assembly we’re using is incompatible with the latest version of Excel?

A software application shouldn’t rely on an Excel installation in order to create a spreadsheet which is why I discourage using Microsoft.Office.Interop.Excel.

Понравилась статья? Поделить с друзьями:
  • С чего следует начинать построение формулы в текстовом документе word
  • С чего начинать работу в excel
  • С чего начинать изучать excel
  • С чего начинается функция в excel
  • С чего начинается формулы microsoft excel