Мы уже научили программу загружать файлы с расширением XLSX и CSV в свои dataGridView. Теперь мы будем работать с загруженными данными. Создадим еще одну таблицу, но уже с подписанными районами (согласно ОГРН), а также создадим и заполним общий свод по всем районам области.
Для начала напишем метод для заполнения свода шаблоном данных:
public void WriteToSummary() // Метод заполнения сводной таблицы шаблоном данных
{
dgvSummary.Rows.Clear();
dgvSummary.Columns.Clear();
dgvSummary.Columns.Add("ateID", "Код АТЕ");
dgvSummary.Columns.Add("ateName", "Наименование АТЕ");
dgvSummary.Columns.Add("not", "Не заполнили совсем");
dgvSummary.Columns.Add("partially", "Заполнили частично");
dgvSummary.Columns.Add("full", "Заполнили полностью");
dgvSummary.Columns.Add("only", "Всего в выборке");
dgvSummary.Rows.Add(510, "Арбажский", 0, 0, 0, 0);
dgvSummary.Rows.Add(520, "Афанасьевский", 0, 0, 0, 0);
dgvSummary.Rows.Add(530, "Белохолуницкий", 0, 0, 0, 0);
dgvSummary.Rows.Add(540, "Богородский", 0, 0, 0, 0);
dgvSummary.Rows.Add(550, "Верхнекамский", 0, 0, 0, 0);
dgvSummary.Rows.Add(560, "Верхошижемский", 0, 0, 0, 0);
dgvSummary.Rows.Add(570, "Вятскополянский", 0, 0, 0, 0);
dgvSummary.Rows.Add(580, "Даровской", 0, 0, 0, 0);
dgvSummary.Rows.Add(590, "Зуевский", 0, 0, 0, 0);
dgvSummary.Rows.Add(600, "Кикнурский", 0, 0, 0, 0);
dgvSummary.Rows.Add(610, "Кильмезский", 0, 0, 0, 0);
dgvSummary.Rows.Add(620, "Кирово-Чепецкий", 0, 0, 0, 0);
dgvSummary.Rows.Add(630, "Котельнический", 0, 0, 0, 0);
dgvSummary.Rows.Add(640, "Куменский", 0, 0, 0, 0);
dgvSummary.Rows.Add(650, "Лебяжский", 0, 0, 0, 0);
dgvSummary.Rows.Add(660, "Лузский", 0, 0, 0, 0);
dgvSummary.Rows.Add(670, "Малмыжский", 0, 0, 0, 0);
dgvSummary.Rows.Add(680, "Мурашинский", 0, 0, 0, 0);
dgvSummary.Rows.Add(690, "Нагорский", 0, 0, 0, 0);
dgvSummary.Rows.Add(700, "Немский", 0, 0, 0, 0);
dgvSummary.Rows.Add(710, "Нолинский", 0, 0, 0, 0);
dgvSummary.Rows.Add(720, "Омутнинский", 0, 0, 0, 0);
dgvSummary.Rows.Add(730, "Опаринский", 0, 0, 0, 0);
dgvSummary.Rows.Add(740, "Оричевский", 0, 0, 0, 0);
dgvSummary.Rows.Add(750, "Орловский", 0, 0, 0, 0);
dgvSummary.Rows.Add(760, "Пижанский", 0, 0, 0, 0);
dgvSummary.Rows.Add(770, "Подосиновский", 0, 0, 0, 0);
dgvSummary.Rows.Add(780, "Санчурский", 0, 0, 0, 0);
dgvSummary.Rows.Add(790, "Свечинский", 0, 0, 0, 0);
dgvSummary.Rows.Add(800, "Слободской", 0, 0, 0, 0);
dgvSummary.Rows.Add(810, "Советский", 0, 0, 0, 0);
dgvSummary.Rows.Add(820, "Сунский", 0, 0, 0, 0);
dgvSummary.Rows.Add(830, "Тужинский", 0, 0, 0, 0);
dgvSummary.Rows.Add(840, "Унинский", 0, 0, 0, 0);
dgvSummary.Rows.Add(850, "Уржумский", 0, 0, 0, 0);
dgvSummary.Rows.Add(860, "Фаленский", 0, 0, 0, 0);
dgvSummary.Rows.Add(870, "Шабалинский", 0, 0, 0, 0);
dgvSummary.Rows.Add(880, "Юрьянский", 0, 0, 0, 0);
dgvSummary.Rows.Add(890, "Яранский", 0, 0, 0, 0);
dgvSummary.Rows.Add(900, "Вятские Поляны (город)", 0, 0, 0, 0);
dgvSummary.Rows.Add(910, "Кирово-Чепецк (город)", 0, 0, 0, 0);
dgvSummary.Rows.Add(920, "Котельнич (город)", 0, 0, 0, 0);
dgvSummary.Rows.Add(930, "Слободской (город)", 0, 0, 0, 0);
dgvSummary.Rows.Add(940, "Киров (город)", 0, 0, 0, 0);
dgvSummary.Rows.Add(950, "ЗАТО Первомайский", 0, 0, 0, 0);
}
Метод WriteToSummary() вызывается без аргументов и не возвращает никаких данных. С его помощью очищаются строки и столбцы сводной таблицы, а затем они заполняются шаблонными данными. Очищать столбцы и строки требуется, чтобы избежать искажения данных в таблице при создании нескольких отчетов за время работы программы.
Теперь напишем метод, который скопирует таблицу выгрузки в таблицу обработки и проставит в ней районы для каждой организации, а также на основе этих данных заполнит сводную таблицу:
public void WriteAteAndSummary() // Метод рабочей и сводной таблиц
{
bool status = false;
progressBar.Value = 0;
progressBar.Minimum = 0;
progressBar.Maximum = dgvInitialData.ColumnCount + (dgvInitialData.RowCount * 4);
// Добавляем столбцы в рабочую таблицу
tbLogs.Text += Environment.NewLine + Environment.NewLine + DateTime.Now + ": Копирование столбцов из выборки в рабочую таблицу...";
for (int i = 0; i < dgvInitialData.Columns.Count; i++)
{
dgvWorkTable.Columns.Add(dgvInitialData.Columns[i].Name, dgvInitialData.Columns[i].HeaderText);
progressBar.Value++;
}
tbLogs.Text += "ОК";
// Копируем данные из выборки в рабочую таблицу
tbLogs.Text += Environment.NewLine + DateTime.Now + ": Копирование строк из выборки в рабочую таблицу...";
for (int i = 0; i < dgvInitialData.RowCount; i++)
{
dgvWorkTable.Rows.Add();
for (int j = 0; j < dgvWorkTable.Columns.Count; j++)
{
dgvWorkTable[j, i].Value = dgvInitialData[j, i].Value;
}
progressBar.Value++;
}
tbLogs.Text += "ОК";
// Добавляем колонки с кодом и наименованием АТЕ, перемещаем их на 2 и 3 позиции
tbLogs.Text += Environment.NewLine + DateTime.Now + ": Присваиваем АТЕ по ОГРН...";
dgvWorkTable.Columns.Add("ooAteId", "Код АТЕ");
dgvWorkTable.Columns.Add("ooAteName", "Наименование АТЕ");
dgvWorkTable.Columns[dgvWorkTable.Columns.Count - 2].DisplayIndex = 2;
dgvWorkTable.Columns[dgvWorkTable.Columns.Count - 1].DisplayIndex = 3;
// Присваиваем код и наименование АТЕ
for (int i = 0; i < dgvWorkTable.RowCount; i++) // Счетчик строк для листа с выгрузкой
{
for (int j = 0; j < dgvBase.RowCount; j++)
{
if (Convert.ToInt64(dgvWorkTable[1, i].Value) == Convert.ToInt64(dgvBase[0, j].Value)) // Если ОГРН совпадают
{
dgvWorkTable[dgvWorkTable.Columns.Count - 2, i].Value = dgvBase[1, j].Value;
dgvWorkTable[dgvWorkTable.Columns.Count - 1, i].Value = dgvBase[2, j].Value;
}
}
progressBar.Value++;
}
tbLogs.Text += "ОК";
tbLogs.Text += Environment.NewLine + DateTime.Now + ": Проверяем целостность...";
// Проверяем целостность данных
for (int i = 0; i < dgvWorkTable.RowCount; i++)
{
if (dgvWorkTable[dgvWorkTable.ColumnCount - 2, i].Value == null)
{
for (int cCol = 0; cCol < dgvWorkTable.ColumnCount; cCol++)
{
dgvWorkTable[cCol, i].Style.BackColor = System.Drawing.Color.Red;
}
status = true;
tabControlPreview.SelectTab(4);
tbLogs.Text += Environment.NewLine + Environment.NewLine + DateTime.Now + ": ОШИБКА! ОГРН (" + dgvWorkTable[1, i].Value + ") не найден в базе. Строка в рабочей таблице выделена красным.";
}
progressBar.Value++;
}
if (status == false)
{
tabControlPreview.SelectTab(3);
tbLogs.Text += "ОК";
}
tbLogs.Text += Environment.NewLine + Environment.NewLine + DateTime.Now + ": Заполняем сводную таблицу...";
for (int i = 0; i < dgvWorkTable.RowCount; i++)
{
for (int j = 0; j < dgvSummary.RowCount; j++)
{
if (Convert.ToInt32(dgvWorkTable[dgvWorkTable.ColumnCount - 2, i].Value) == Convert.ToInt32(dgvSummary[0, j].Value)) // Если код АТЕ совпадает
{
long pr = 1;
for (int colC = 2; colC < (dgvWorkTable.ColumnCount - 3); colC++)
{
pr *= Convert.ToInt32(dgvWorkTable[colC, i].Value);
}
if (pr == 0 && Convert.ToInt32(dgvWorkTable[dgvWorkTable.ColumnCount - 3, i].Value) == 0)
{
dgvSummary[2, j].Value = Convert.ToInt32(dgvSummary[2, j].Value) + 1;
}
else if (pr == 0 && Convert.ToInt32(dgvWorkTable[dgvWorkTable.ColumnCount - 3, i].Value) != 0)
{
dgvSummary[3, j].Value = Convert.ToInt32(dgvSummary[3, j].Value) + 1;
}
else if (pr != 0 && Convert.ToInt32(dgvWorkTable[dgvWorkTable.ColumnCount - 3, i].Value) != 0)
{
dgvSummary[4, j].Value = Convert.ToInt32(dgvSummary[4, j].Value) + 1;
}
// Подсчитываем количество ОО в выборке по данному АТЕ
dgvSummary[5, j].Value = Convert.ToInt32(dgvSummary[5, j].Value) + 1;
}
}
progressBar.Value++;
}
tbLogs.Text += "ОК";
tbLogs.Text += Environment.NewLine + Environment.NewLine + DateTime.Now + ": Отчет сгенерирован.";
}
Алгоритм работы кода:
- Инициализируем переменную status типа bool со значением false. Я решил добавить проверку целостности данных в программу. То есть, если в выгрузке будет присутствовать организация, неизвестная базе ОГРН, то программа об этом сообщит. Для этого нужна переменная status.
- Обнуляем значение поля Value элемента progressBar. Значение Minimum устанавливаем по-умолчанию (0). А также устанавливаем новое значение поля Maximum, равное [количеству столбцов в таблице с исходной выгрузкой] + [количество строк в таблице * 4] - именно столько итераций будет циклов в нашем методе, каждая итерация будет прибавлять единицу к значению Value.
- С помощью цикла for копируем столбцы выгрузки в таблицу обработки. Оповещаем о начале и завершении операции в tbLogs, прибавляем единицу к progressBar.Value после каждой итерации.
- С помощью двух циклов for (по строкам таблицы выгрузки и по столбцам таблицы обработки) копируем все данные в таблицу для обработки. Оповещаем пользователя в tbLogs о начале и завершении операции. Здесь после каждой итерации первого(!) цикла for прибавляем единицу в progressBar.
- Присваиваем название и код района для каждой организации. Для этого сперва добавляем два столбца в таблицу обработки (Код и Наименование), а также для удобства пользователя меняем индекс отображения (Свойство столбца DisplayIndex) этих столбцов на 2 и 3. Таким образом новые столбцы будут отображаться 3 и 4 по счету. Обратите внимание, что индекс столбца меняется только для отображения пользователю, реальный индекс остается прежним! После этого с помощью двух циклов for (первый по строкам таблицы обработки, второй - по строкам таблицы базы ОГРН) в случае совпадения ОГРН в таблице обработки в столбцах для наименования и кода района организации проставляются соответствующие данному ОГРН сведения из базы. Оповещаем о начале и окончании операции в tbLogs.
- Проверяем целостность данных. С помощью цикла for проверяем, есть ли пустые ячейки в столбце с кодом района. Если такие есть, это означает, что в базе данный ОГРН не был найден. В случае обнаружения, меняем значение переменной status на true, с помощью еще одного цикла for проходимся по ячейкам этой строки и меняем их фон на красный, а также оповещаем пользователя через tbLogs о том, что данный ОГРН не был найден, а также сразу делаем активной вкладку с логами, чтобы пользователь сразу мог увидеть, что что-то пошло не так. Если за время прохождения первого цикла переменная статус не изменила своего значения (осталась false), то оповещаем пользователя через tbLogs об успешном завершении операции и делаем активной вкладку со сводной таблицей.
- Заполняем сводную таблицу. Первый цикл for заходит в стркоу i и ищет совпадения по коду района в таблице свода с помощью вложенного в него второго цикла for. Если совпадение найдено, прибавляем единицу в столбце "Всего", инициализируем переменную pr типа long со значением 1 для вычисления произведения значений столбцов с данными за все года с помощью еще одного цикла for (по ячейкам с данными по годам текущей строки в таблице обработки). Если произведение и значение ячейки с суммой по всем годам равны 0, то прибавляем единицу в столбец "Не заполнили". Если произведение равно 0, но сумма не равна 0, значит организация предоставила данные частично и в соответствующем столбце свода прибавляется единица. Если же произведение и сумма НЕ равны 0, это означает что организация полностью предоставила данные за все годы и прибавляем единицу в столбец "Заполнили полностью". Оповещаем пользователя через tbLogs о начале и завершении данной операции, а также о том, что отчет был сгенерирован полностью. При каждой итерации цикла по строкам сводной таблицы прибавляем единицу к progressBar.Value.
Методы успешно написаны. Теперь нужно, чтобы все это добро мог запустить пользователь. Для этого в обработчике клика по кнопке butCreateReport напишем следующие строки:
if (!String.IsNullOrEmpty(tbSelectionFilePath.Text))
{
butCreateReport.Enabled = false;
WriteToSummary(); // Создаем шаблон свода
WriteAteAndSummary(); // Создание отчета
butSaveReport.Enabled = true; // Активируем кнопку сохранения отчета
}
Для защиты от ошибок на всякий случай сделаем код возможным только если заполнен textBox, содержащий путь к файлу выгрузки.
На этом мы закончили писать код для создания отчетов на основе загруженных данных. В следующей части мы научим программу сохранять данные из нескольких dataGridView в файл Excel при помощи сторонней библиотеки ExcelLibrary.