В прошлой части было составлено техническое задание для требуемой программы, а также был разработан внешний интерфейс. Теперь пришло время заняться непосредственно написанием кода. В этой части мы напишем код для автоматического открытия базы ОГРН (CSV) и открытия выгрузки (XLSX) в dataGridView.
Для всей дальнейшей работы с кодом следует добавить следующие директивы using в код MainForm.cs:
using System;
using System.Text;
using System.IO;
using System.Data;
using ExcelDataReader;
using ExcelLibrary;
using System.Windows.Forms;
Также следует скачать библиотеки ExcelDataReader и ExcelLibrary и добавить их в свой проект.
Метод загрузки CSV
Создадим метод для чтения CSV файла:
public DataTable CsvFile() // Метод загрузки CSV-файла
{
DataTable dt = new DataTable("Base");
DataColumn colOgrn = new DataColumn("ОГРН");
DataColumn colAteId = new DataColumn("Код АТЕ");
DataColumn colAteName = new DataColumn("Наименование АТЕ");
// Добавляем столбцы в таблицу
dt.Columns.AddRange(new DataColumn[] { colOgrn, colAteId, colAteName });
try
{
DataRow dr = null; // Создаем пустую строку
string[] ogrnValues = null; // пустой массив типа string
// Создаем массив и заполняем его содержимым csv-файла.
// Один элемент массива = одна строка из файла
string[] ogrn = File.ReadAllLines(Application.StartupPath + "\\Base\\OgrnBase.csv", Encoding.Default);
for (int i = 0; i < ogrn.Length; i++) // Проходим по массиву с базой
{
if (!String.IsNullOrEmpty(ogrn[i])) // Если элемент массива НЕ пустой
{
ogrnValues = ogrn[i].Split(';'); // Указываем разделитель "точка с запятой" и, полученные между разделителями элементы строки помещаем в массив ogrnValues
dr = dt.NewRow(); // Создаем новую строку по шаблону dt
dr["ОГРН"] = ogrnValues[0]; // Ищем столбец с именем ОГРН, добавляем туда значение 0 позиции ogrnValues
dr["Код АТЕ"] = ogrnValues[1];
dr["Наименование АТЕ"] = ogrnValues[2]; // Таким образом получаем строку для dt
dt.Rows.Add(dr); // Добавляем полученную строку в DataTable dt
}
}
butBrowseSelectionFile.Enabled = true;
tbLogs.Text += Environment.NewLine + Environment.NewLine + DateTime.Now + ": Успешная загрузка базы ОГРН."; // Отчитываемся об успешной загрузке базы
}
catch (Exception ex) // Если базу загрузить не удалось
{
MessageBox.Show(ex.Message); // Выводим сообщение об ошибке
// Отчитываемся о полученной ошибке в логах
tbLogs.Text += Environment.NewLine + Environment.NewLine + DateTime.Now + ": Не удалось загрузить базу ОГРН. Проверьте наличие базы (Base\\OgrnBase.csv) в корневой папке программы. Также проверьте её состав (Шаблон: 'ОГРН; КОД АТЕ; НАИМЕНОВАНИЕ АТЕ;').";
butBrowseSelectionFile.Enabled = false;
}
return dt; // Возвращаем DataTable
}
Метод CsvFile вызывается без аргументов и возвращает таблицу типа DataTable. Алгоритм метода:
- Инициализируем переменную dt типа DataTable.
- Инициализируем переменные colOgrn, colAteId, colAteName типа DataColumn.
- Добавляем полученные DataColumn в dt при помощи метода AddRange. Таким образом получается "заготовка" таблицы с данными по ОГРН.
- Инициализируем переменную dr типа DataRow и присваиваем ей значение null. В неё мы будем помещать строки из CSV-файла.
- Инициализируем массив ogrnValues типа string и значением null для значений строки между разделителями.
- Инициализируем массив ogrn типа string, в который помещаем значения строк в CSV-файле при помощи метода File.ReadAllLines. В аргументах метода мы указываем корневую папку приложения (Application.StartupPath) и добавляем к ней путь, по которому будет находиться база ОГРН, в моем случае это \Base\OgrnBase.csv.
- Начинаем цикл for с 0 и до конца массива ogrn.
- Если значение текущего элемента массива не пустое, то при помощи метода Split(';') мы получаем несколько элементов, разделенных точкой с запятой, из текущего элемента (по циклу) массива ogrn и записываем эти значения в массив ogrnValues. Присваиваем переменной dr схему новой строки dt с помощью метода dt.NewRow(). Записываем в dr значения из ogrnValues согласно имени столбца. После этого добавляем полученную строку в нашу DataTable.
- Цикл повторяется пока не будет достигнут конец массива ogrn.
- После цикла делаем активной кнопку выбора файла выгрузки (butBrowseSelectionFile) и с помощью tbLogs оповещаем пользователя об успешной загрузке базы.
- В случае форс-мажорных ситуаций (удаление, изменение файла базы и др.) приложение должно запретить пользователю дальнейшие операции с программой до исправления ошибки. Поэтому весь код от инициализации dr до оповещения об успешной загрузки базы мы помещаем в тело блока try.
- В блоке catch мы выводим диалоговое окно с сообщением о возникшей ошибке, также оповещаем пользователя об этом через логи и деактивируем кнопку выбора файла выгрузки.
- На этом код завершается и можно вернуть полученное значение dt при помощи команды return dt.
Для удобства использования программы (чтобы не приходилось при каждом запуске выбирать одну и ту же базу ОГРН) было решено загружать её автоматически при запуске из корневой папки. Для этого изменим конструктор формы MainForm следующим образом:
public MainForm()
{
InitializeComponent();
tbLogs.Text = "Отчет о работе 'Генератор отчетов ФИС ФРДО'" + Environment.NewLine + Environment.NewLine + "Запуск программы: " + DateTime.Now;
dgvBase.DataSource = CsvFile();
}
После инициализации компонентов формы происходит запись в tbLogs приветственного сообщения с текущим временем. Источником данных для dgvBase назначается результат выполнения метода CsvFile().
Метод XlsFile
Для открытия XLSX-файлов я решил воспользоваться OpenSource-библиотекой ExcelDataReader. Таким образом, не имеет значения, какой офис установлен на машине пользователя и установлен ли он вообще.
public DataTable XlsFile(string pathToExcelFile) // Метод загрузки Excel-файла
{
progressBar.Value = 0;
progressBar.Minimum = 0;
progressBar.Maximum = 100;
FileStream stream = File.Open(pathToExcelFile, FileMode.Open, FileAccess.Read);
IExcelDataReader excelReader; // присваиваем ExcelDataDeader переменной
excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
progressBar.Value = 30;
DataSet ds = excelReader.AsDataSet();
ds.Tables[0].Rows[0].Delete();
DataTable dt = new DataTable();
dt = ds.Tables[0];
progressBar.Value = 60;
dt.Columns[0].ColumnName = "Название организации";
dt.Columns[1].ColumnName = "ОГРН";
int year = 2000;
for (int i = 2; i < dt.Columns.Count - 1; i++)
{
dt.Columns[i].ColumnName = year.ToString();
year++;
}
dt.Columns[dt.Columns.Count - 1].ColumnName = "Итого по ОО";
progressBar.Value = 100;
return dt;
}
Метод XlsFile принимает один аргумент типа string. В нем должен содержаться путь к XLSX-файлу, который требуется прочитать. Алгоритм метода:
- Устанавливаем стандартные значения свойств элемента progressBar (Value, Minimum, Maximum).
- Инициализируем файловый поток stream с методом File.Open(). В аргументах метода указываем путь к файлу, операцию открытия и доступ для чтения.
- Инициализируем библиотеку IExcelDataReader в переменной excelReader.
- Используем метод CreadeOpenXmlReader сторонней библиотеки для чтения данных из stream.
- Изменяем значение свойства Value у элемента progressBar на 30.
- Инициализируем переменную ds типа DataSet, присваиваем ей значение, полученное методом excelReader.AsDataSet(). Метод автоматически преобразует все данные файла Excel в формат DataSet.
- Удаляем первую строку первой таблицы DataSet, так как в ней находятся заголовки столбцов.
- Инициализируем переменную dt со значениями первой таблицы ds.
- Изменяем значение свойства Value у элемента progressBar на 60.
- Теперь мы подпишем столбцы в dt. Первые два и последний столбцы мы подписываем вручную. Остальные (данные с 2000 по 2018 года) подпишем в автоматическом режиме с помощью цикла for. Такое решение было принято чтобы сократить код и сделать программу более универсальной. Теперь даже если появится новый столбец (допустим, в следующем году появятся данные за 2019 год), то программа безошибочно определит его, и правильно подпишет заголовок.
- Написание метода завершается. Изменяем значение свойства Value у элемента progressBar на 100 и возвращаем полученную переменную dt.
Написанный метод мы будем применять в событии изменения значения Text элемента tbSelectionFilePath:
private void tbSelectionFilePath_TextChanged(object sender, EventArgs e) // Загрузка выборки в таблицу, форматирование таблицы
{
// Проверяем, заполнен ли путь к файлу, существует ли файл по указанному пути
if (File.Exists(tbSelectionFilePath.Text) != false)
{
butSaveReport.Enabled = false;
tabControlPreview.SelectTab(1);
dgvInitialData.DataSource = "";
dgvWorkTable.Rows.Clear();
dgvWorkTable.Columns.Clear();
WriteToSummary(); // Создаем шаблон свода
dgvInitialData.DataSource = XlsFile(tbSelectionFilePath.Text);
tbLogs.Text += Environment.NewLine + Environment.NewLine + DateTime.Now + ": Успешная загрузка файла выгрузки (" + Path.GetFileName(tbSelectionFilePath.Text) + ")";
butCreateReport.Enabled = true;
}
}
Алгоритм работы кода:
- Кнопка сохранения отчета деактивируется (защита от ошибочных нажатий при последовательном создании нескольких отчетов)
- В элементе TabControl активируется вкладка "Файл выгрузки"
- Источник данных таблицы с выгрузкой очищается (защита от ошибочных нажатий при последовательном создании нескольких отчетов)
- Очищаются строки и столбцы таблицы для обработанной выгрузки.
- Таблица со сводными данными заполняется шаблоном с помощью метода WriteToSummary(), который мы напишем позднее.
- Источником данных таблицы файла выгрузки указывается результат выполнения метода XlsFile().
- В tbLogs добавляется запись об успешной загрузке с адресом файла и текущем времени.
- Активируется кнопка создания отчета.
Изменяться значение tbSelectionFilePath.Text будет после клика по кнопке открытия файла выгрузки:
private void butBrowseSelectionFile_Click(object sender, EventArgs e) // Кнопка выбора файла выгрузки
{
OpenFileDialog openSelection = new OpenFileDialog();
openSelection.Filter = "Exel-файлы (*.xls; *.xlsx) |*.xls;*.xlsx";
if (!String.IsNullOrEmpty(tbSelectionFilePath.Text))
{ openSelection.InitialDirectory = Path.GetDirectoryName(tbSelectionFilePath.Text); }
if (openSelection.ShowDialog() == DialogResult.OK)
{ tbSelectionFilePath.Text = openSelection.FileName;}
}
Алгоритм работы:
- Инициализируем экземпляр класса OpenFileDialog. В свойстве Filter указываем расширения, которые должны отображаться пользователю.
- Если во время использования программы уже был открыт другой файл выгрузки, исходной директорией диалогового окна назначается путь к прошлому файлу.
- Если файл успешно выбран, значение tbSelectionFilePath.Text будет изменено на адрес путь к выбранному файлу.
На этом я хочу завершить статью. Надеюсь она была вам полезна. В дальнейшем мы разберем код создания отчета и сохранения его в XLS-файл с помощью библиотеки ExcelLibrary. Спасибо за внимание!