Список работ

C#: вывод отчета в Excel

Содержание

Введение

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

<?xml version="1.0" encoding="utf-8"?>
<traffic>
<!-- Описание порции данных -->
<!-- processName - имя процесса -->
<!-- type - вид порции: 0 - входящие данные; 1 - исходящие -->
<!-- sTime - текущее время -->
<!-- cTime - время передачи данных -->
<!-- size - размер порции данных в битах -->
<portion>
<processName>AAM Updates Notifier</processName>
<type>1</type>
<sTime>23.11.2016 3:15:24.824</sTime>
<cTime>23.11.2016 3:15:24.825</cTime>
<size>157798,765625 B</size>
</portion>
<portion>
<processName>conhost</processName>
<type>0</type>
<sTime>23.11.2016 3:15:27.90</sTime>
<cTime>23.11.2016 3:15:27.90</cTime>
<size>323631,9375 B</size>
</portion>

</traffic>

В файле фиксируются данные интернет-трафика, наблюдаемого в подключенном к интернет компьютере.
В отчете отображаются сводные данные об обнаруженном и сохраненном в xml-файле трафике (рис. 1).

Интернет-трафик

Рис. 1. Сводный интернет-трафик

Программа реализована как Windows Forms Application.
Файл с данными явно указывается в программе, поэтому форма содержит только дну кнопку (рис. 2).

Форма с кнопкой Go

Рис. 2. Форма приложения

Подключение Excel

Создав проект, в окне Solution Explorer выполнить:
правая кнопка мышки на References - Add Reference - .в окне Reference Manager выбрать Assemblies - Extensions - отметить флажком Microsoft.Office.Interop.Excel - OK (рис. 3. и рис. 4).

References - Add Reference

Рис. 3. Solution Explorer - References - Add Reference

Reference Manager - Assemblies - Extensions - Microsoft.Office.Interop.Excel

Рис. 4. Reference Manager - Assemblies - Extensions - Microsoft.Office.Interop.Excel

В use-раздел формы приложения добавить:

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

Порядок формирования отчета

  1. Загрузить xml-файл в DataSet portionsDS.
  2. Сформировать по данным portionsDS список prtnsList. Элементом списка является структура prtnCrrnt (тип prtn).
  3. Сформировать по списку prtnsList три выборки из списка:
  4. Сформировать, используя полученные выборки (группировки), массивы имен
  5. Сформировать, используя полученные выборки (группировки), массивы с итоговыми объемом и числом порций для входящих и исходящих данных - соответственном массивы prtnInTotal, mntInTotal и prtnOutTotal, mntOutTotal.
  6. Вывести, обращаясь к сформированным массивам, отчет с итогами - сводный интернет-трафик (см. рис. 1)

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

Используемые объекты, методы и свойства

Загрузка xml-файла выполняется методом ReadXml объекта типа DataSet (portionsDS.ReadXml(fileName);).
Результат portionsDS содержит коллекцию таблиц Tables (DataTableCollection tbls = portionsDS.Tables;), обнаруженных в xml-файле.
Нас интересует первая таблица коллекции (DataTable tbl = tbls[0];), из которой извлекаем коллекцию ее строк:

DataRowCollection portions = tbl.Rows;

После завершения обработки коллекции строк таблицы ресурсы, выделенные под portionsDS, освобождаются (portionsDS.Dispose();).
Выборки из списка prtnsList, полученного по данным, загруженным в portionsDS, осуществляются с использованием методов Where, GroupBy, Select и OrderBy.
Данные массивов с именами процессов и итогами выбираются из списка prtnsList методами Select и ToArray.
Очистка списка prtnsList выполняется методом Clear (prtnsList.Clear();).
Созданный объект xlApp типа Excel.Application позволяет плучить получить книгу Excel (Excel.Workbook xlWorkBook;), которая используется для получения первого листа этой книги:

Excel.Worksheet xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);

Для работы с ячейками листа создаются объекты типа Excel.Range:

Excel.Range Cells = xlWorkSheet.Cells;
Excel.Range range = xlWorkSheet.Range["B1:B1", System.Type.Missing];
и другие.

При заполнении ячеек листа Excel употреблены следущие свойства и методы:

Заметим, что изменение Style.Font.Size, например Cells[1, 2].Style.Font.Size = 14;, отразится на всех ячейках с текстом стиля Style.
Готовый отчет наблюдаем, обращаясь к свойству Visible объекта xlApp:

xlApp.Visible = true;

Реализация

Чтение xml-файла, формирование вспомогательных объектов и отчета обеспечивает следующий код:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

namespace WindowsFormsApplication3
{
 public partial class Form1 : Form
 {
  // Порция для отчетов История и Итоги
  public struct prtn
  {
   public string pNm;
   public int pTp;
   public string sTime, cTime;
   public Int64 size;
   public int mnt;
   public prtn(string pNm2, int pTp2, string sTime2, string cTime2, Int64 size2, int mnt2)
   {
    pNm = pNm2;
    pTp = pTp2;
    sTime = sTime2;
    cTime = cTime2;
    size = size2;
    mnt = mnt2;
   }
  }
  // Список порций для отчета Итоги. Формируется в LoadXML()
  public List<prtn> prtnsList = new List<prtn>();
  // Период наблюдения
  public DateTime sTimeMax, sTimeMin;
  public Form1()
  {
   InitializeComponent();
  }
  // Загружаем xml-файл в DataSet portionsDS, а затем формируем список prtnsList
  private bool LoadXML()
  {
   // Файл с трафиком
   string fileName = "C:/AM/2018/outputTrafficXML.xml";
   DataSet portionsDS = new DataSet();
   try
   {
    prtnsList.Clear();
    prtn prtnCrrnt = new prtn();
    string processName; // Имя процесса
    int pTp; // 0 - входящие данные; 1 - исходящие
    // Текущее время и время порции
    DateTime sTime, cTime;
    // Символьное представление текущего времени и времени порции с миллисекундами
    string sTime2, cTime2;
    Int64 size; // Размер порции в битах
    string size2; // Символьное представление размера порции
    portionsDS.ReadXml(fileName); // Формируем DataSet portionsDS
    DataTableCollection tbls = portionsDS.Tables; // Коллекция таблиц (у нас одна таблица traffic)
    DataTable tbl = tbls[0]; // Получаем таблицу traffic
    DataRowCollection portions = tbl.Rows; // Коллекция строк таблицы
    // Период наблюдения
    sTimeMax = DateTime.MinValue;
    sTimeMin = DateTime.MaxValue;
    foreach (DataRow portion in portions)
    {
     processName = Convert.ToString(portion[0]); // portion["processName"];
     pTp = Convert.ToInt32(portion["type"]);
     sTime = Convert.ToDateTime(portion["sTime"]);
     // Корректируем период наблюдения
     if (sTime > sTimeMax) sTimeMax = sTime;
     if (sTime < sTimeMin) sTimeMin = sTime;
     cTime = Convert.ToDateTime(portion["cTime"]);
     sTime2 = Convert.ToString(portion["sTime"]);
     cTime2 = Convert.ToString(portion["cTime"]);
     size2 = Convert.ToString(portion["size"]);
     // Заменяем, например, 157798,765625 B на 157798765625 и преобразовываем затем в Int64
     size2 = size2.Replace("B", "");
     size2 = size2.Replace(",", "").Trim();
     size = Convert.ToInt64(size2);
     prtnCrrnt.pNm = processName;
     prtnCrrnt.pTp = pTp;
     prtnCrrnt.sTime = sTime2;
     prtnCrrnt.cTime = cTime2;
     prtnCrrnt.size = size;
     prtnCrrnt.mnt = 1;
     prtnsList.Add(prtnCrrnt);
    }
   }
   catch (System.IO.IOException e)
   {
    MessageBox.Show("Плохой файл " + fileName + ". Сообщение " + e.Message);
    return false;
   }
   finally
   {
    portionsDS.Dispose();
   }
   return true;
  }
  // Вывод итогов
  private void buttonTotals_Click(object sender, EventArgs e)
  {
   // Формируем по xml-файлу список prtnsList
   if (!LoadXML()) return;
   // Имена всех процессы без повторений в алфавитном порядке
   var prtnsListGrouped = prtnsList.GroupBy(prtn => prtn.pNm).Select(
    grp => new prtn { pNm = grp.Key }).OrderBy(prtn => prtn.pNm);
   // Итоги по входящим данным
   var prtnsListGrouped_0 = prtnsList.Where(prtn => prtn.pTp == 0).GroupBy(prtn => prtn.pNm).Select(
    grp => new prtn { pNm = grp.Key, size = grp.Sum(prtn => prtn.size), mnt = grp.Sum(prtn => prtn.mnt) }).OrderBy(prtn => prtn.pNm);
   // Итоги по исходящим данным
   var prtnsListGrouped_1 = prtnsList.Where(prtn => prtn.pTp == 1).GroupBy(prtn => prtn.pNm).Select(
    grp => new prtn { pNm = grp.Key, size = grp.Sum(prtn => prtn.size), mnt = grp.Sum(prtn => prtn.mnt) }).OrderBy(prtn => prtn.pNm);
   // Имена всех процессов
   string[] arrPrcAll = prtnsListGrouped.Select(prtn => prtn.pNm).ToArray();
   // Входящие порции
   // Имена процессов
   string[] arrPrcIn = prtnsListGrouped_0.Select(prtn => prtn.pNm).ToArray();
   // Суммарные порции входящих данных соответственно процессов
   Int64[] prtnInTotal = prtnsListGrouped_0.Select(prtn => prtn.size).ToArray();
   // Число порций входящих данных
   int[] mntInTotal = prtnsListGrouped_0.Select(prtn => prtn.mnt).ToArray();
   // Исходящие порции
   // Имена процессов
   string[] arrPrcOut = prtnsListGrouped_1.Select(prtn => prtn.pNm).ToArray();
   // Суммарные порции исходящих данных
   Int64[] prtnOutTotal = prtnsListGrouped_1.Select(prtn => prtn.size).ToArray();
   // Число порций исходящих данных соответственно процессов
   int[] mntOutTotal = prtnsListGrouped_1.Select(prtn => prtn.mnt).ToArray();
   // Вывод отчета в Excel
   Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
   if (xlApp == null)
   {
    MessageBox.Show("Excel не найден");
    return;
   }
   Excel.Workbook xlWorkBook;
   xlWorkBook = xlApp.Workbooks.Add();
   Excel.Worksheet xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
   // Все ячейки листа xlWorkSheet
   Excel.Range Cells = xlWorkSheet.Cells;
   // Высота текста заголовка отчета
   Cells[1, 2].Font.Size = 14;
   // Заголовки
   Cells[1, 2] = "Интернет-трафик за период с " + sTimeMin + " по " + sTimeMax;
   Cells[2, 2] = "№";
   Cells[2, 3] = "Процесс";
   Cells[2, 4] = "Получено";
   Cells[2, 6] = "Передано";
   Cells[3, 4] = "Бит";
   Cells[3, 5] = "Число порций";
   Cells[3, 6] = "Бит";
   Cells[3, 7] = "Число порций";
   int lstClmn = 8; // Следующий после крайнего справа столбца
   // Выравнивание
   for (int i = 2; i < 4; i++)
    for (int j = 2; j < lstClmn; j++)
     Cells[i, j].HorizontalAlignment = Microsoft.Office.Interop.Excel.Constants.xlCenter;
   // Ширина столбцов
   Excel.Range range = xlWorkSheet.Range["B1:B1", System.Type.Missing];
   range.EntireColumn.ColumnWidth = 7;
   range = xlWorkSheet.Range["C1:C1", System.Type.Missing];
   range.EntireColumn.ColumnWidth = 30;
   range = xlWorkSheet.Range["D1:G1", System.Type.Missing];
   range.EntireColumn.ColumnWidth = 15;
   // Границы ячеек с заголовками
   Cells[2, 2].Borders(1).ColorIndex = 1; // Граница слева
   Cells[2, 3].Borders(1).ColorIndex = 1; // Граница слева
   Cells[2, 3].Borders(2).ColorIndex = 1; // Граница справа
   Cells[2, 5].Borders(2).ColorIndex = 1; // Граница справа
   Cells[2, 7].Borders(2).ColorIndex = 1; // Граница справа
   for (int i = 2; i < lstClmn; i++) Cells[2, i].Borders(3).ColorIndex = 1; // Граница сверху
   for (int i = 4; i < lstClmn; i++) Cells[3, i].Borders(3).ColorIndex = 1; // Граница сверху
   for (int i = 2; i < lstClmn; i++) Cells[3, i].Borders(4).ColorIndex = 1; // Граница снизу
   for (int i = 2; i < lstClmn; i++)
    for (int k = 1; k < 3; k++)
     Cells[3, i].Borders(k).ColorIndex = 1; // Границы слева и справа
   // Объединение ячеек
   range = xlWorkSheet.get_Range("D2:E2");
   range.Merge(Type.Missing);
   range = xlWorkSheet.get_Range("F2:G2");
   range.Merge(Type.Missing);
   // Всего имен процессов
   int prcMnt = arrPrcAll.Length;
   // Границы ячеек с данными
   for (int i = 4; i < 4 + prcMnt; i++)
    for (int j = 2; j < lstClmn; j++)
     for (int k = 1; k < 5; k++)
      Cells[i, j].Borders(k).ColorIndex = 1; // Границы слева, справа, сверху и снизу
   // Выводим номера строк и имена процессов
   int n = 0;
   for (int i = 0; i < prcMnt; i++)
   {
    n++;
    Cells[i + 4, 2] = n;
    Cells[i + 4, 3] = arrPrcAll[i];
    // Перенос текста
    Cells[i + 4, 3].WrapText = true;
   }
   // Число входящих и исходящих данных в итоговых массивах
   int inMnt = arrPrcIn.Length;
   int outMnt = arrPrcOut.Length;
   // Получено
   for (int i = 0; i < prcMnt; i++)
   {
    string prcNm = arrPrcAll[i];
    for (int j = 0; j < inMnt; j++)
    {
     string prcNm2 = arrPrcIn[j];
     if (prcNm2 == prcNm)
     {
      Cells[i + 4, 4] = prtnInTotal[j];
      Cells[i + 4, 5] = mntInTotal[j];
      break;
     }
    }
   }
   // Передано
   for (int i = 0; i < prcMnt; i++)
   {
    string prcNm = arrPrcAll[i];
    for (int j = 0; j < outMnt; j++)
    {
     string prcNm2 = arrPrcOut[j];
     if (prcNm2 == prcNm)
     {
      Cells[i + 4, 6] = prtnOutTotal[j];
      Cells[i + 4, 7] = mntOutTotal[j];
      break;
     }
    }
   }
   // Показываем отчет Итоги, выведенный в Excel
   xlApp.Visible = true;
  }
 }
}

Заключение

Используя те же объекты (структуру prtn, список prtnsList и пр.) по данным xml-файла формируется отчет История процесса (рис. 5).

Отчет История процесса

Рис. 5. Отчет история процесса

Вывод этого отчета обеспечивает следующий код (приводится отладочный вариант, в котором имя процесса задется в теле процедуры):

  // Вывод истории процесса
  private void buttonHistory_Click(object sender, EventArgs e)
  {
   // Формируем по xml-файлу список prtnsList
   if (!LoadXML()) return;
   string pNm = "opera"; // Имя процесса
   // Отбираем входящие порции для процесса с именем pNm
   // Отбор по имени
   var prtnsL = prtnsList.Where(prtn => prtn.pNm == pNm).Select(
    prtn => new { pTp = prtn.pTp, sTime = prtn.sTime, cTime = prtn.cTime, size = prtn.size });
   // Отбор входящих порций процесса pNm
   var prtnsL_0 = prtnsL.Where(prtn => prtn.pTp == 0).Select(prtn => new { sTime = prtn.sTime, cTime = prtn.cTime, size = prtn.size });
   // Число входящих порций процесса pNm
   int inMnt = prtnsL_0.Count();
   // Текущее время для входных данных (время замера входных данных)
   string[] arrTCrrntIn = new string[inMnt];
   // Замеренное время для входных данных
   string[] arrTIn = new string[inMnt];
   // Размеры входящих порций процесса pNm
   Int64[] prtnIn = new Int64[inMnt];
   int k = 0;
   // Массивы с временем и размерами входящих порций
   foreach (var p in prtnsL_0)
   {
    arrTCrrntIn[k] = p.sTime;
    arrTIn[k] = p.cTime;
    prtnIn[k] = p.size;
    k++;
   }
   // Отбор исходящих порций процесса pNm
   var prtnsL_1 = prtnsL.Where(prtn => prtn.pTp == 1).Select(prtn => new { sTime = prtn.sTime, cTime = prtn.cTime, size = prtn.size });
   // Число исходящих порций процесса pNm
   int outMnt = prtnsL_1.Count();
   // Текущее время для исходящих данных (время замера выходных данных)
   string[] arrTCrrntOut = new string[inMnt];
   // Замеренное время для исходящих данных
   string[] arrTOut = new string[outMnt];
   // Размеры исходящих порций процесса pNm
   Int64[] prtnOut = new Int64[outMnt];
   k = 0;
   // Массивы с временем и размерами исходящих порций процесса pNm
   foreach (var p in prtnsL_1)
   {
    arrTCrrntOut[k] = p.sTime;
    arrTOut[k] = p.cTime;
    prtnOut[k] = p.size;
    k++;
   }
   Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
   if (xlApp == null)
   {
    MessageBox.Show("Excel не найден");
    return;
   }
   Excel.Workbook xlWorkBook;
   Excel.Worksheet xlWorkSheet;
   //object misValue = System.Reflection.Missing.Value;
   //xlWorkBook = xlApp.Workbooks.Add(misValue);
   xlWorkBook = xlApp.Workbooks.Add();
   xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
   // Все ячейки листа xlWorkSheet
   Excel.Range Cells = xlWorkSheet.Cells;
   // Высота текста заголовка отчета
   Cells[1, 2].Font.Size = 12;
   // Заголовки
   Cells[1, 2] = "История процесса " + pNm + " за период с " + sTimeMin + " по " + sTimeMax;
   Cells[2, 2] = "№";
   Cells[2, 3] = "Текущее время";
   Cells[2, 4] = "Время порции";
   Cells[2, 5] = "Размер порции, бит";
   Cells[3, 2] = "Входящие данные";
   Cells[inMnt + 4, 2] = "Исходящие данные";
   int lstClmn = 6; // Следующий после крайнего справа столбца
   // Выравнивание
   for (int i = 2; i < lstClmn; i++) xlWorkSheet.Cells[2, i].HorizontalAlignment = Microsoft.Office.Interop.Excel.Constants.xlCenter;
   // Границы ячеек
   for (int i = 2; i < 5 + inMnt + outMnt; i++)
    for (int j = 2; j < lstClmn; j++)
     for (k = 1; k < 5; k++)
     {
      if (i == 3 || i == 4 + inMnt) continue;
      Cells[i, j].Borders(k).ColorIndex = 1; // Границы слева, справа, сверху и снизу
     }
   // Границы ячеек строках 3 и 4 + inMnt с текстом Входящие данные и Исходящие данные
   Cells[3, 2].Borders(1).ColorIndex = 1; // Граница слева
   Cells[3, 5].Borders(2).ColorIndex = 1; // Граница справа
   Cells[4 + inMnt, 2].Borders(1).ColorIndex = 1; // Граница слева
   Cells[4 + inMnt, 5].Borders(2).ColorIndex = 1; // Граница справа
   // Ширина столбцов
   Excel.Range range = xlWorkSheet.Range["B1:B1", System.Type.Missing];
   range.EntireColumn.ColumnWidth = 8;
   range = xlWorkSheet.Range["C1:E1", System.Type.Missing];
   range.EntireColumn.ColumnWidth = 22;
   // Вывод входящих данных процесса pNm
   for(int i = 0; i < inMnt; i++)
   {
    Cells[i + 4, 2] = i + 1;
    Cells[i + 4, 3] = arrTCrrntIn[i];
    Cells[i + 4, 4] = arrTIn[i];
    Cells[i + 4, 5] = prtnIn[i];
   }
   // Вывод исходящих данных процесса pNm
   for(int i = 0; i < outMnt; i++)
   {
    Cells[i + inMnt + 5, 2] = i + 1;
    Cells[i + inMnt + 5, 3] = arrTCrrntOut[i];
    Cells[i + inMnt + 5, 4] = arrTOut[i];
    Cells[i + inMnt + 5, 5] = prtnOut[i];
   }
   // Показываем отчет История процесса, выведенный в Excel
   xlApp.Visible = true;
  }

Список работ

Рейтинг@Mail.ru