Как сделать сквозную сортировку подобных матриц на нескольких страницах в FastReport .NET

22.09.2021

Допустим поставлена задача: отсортировать матрицу на первой странице в нужном порядке. А ещё запомнить этот порядок и применить его для сортировки подобных матриц на других страницах.

Это может понадобиться, когда у вас есть несколько страниц в отчёте, которые выводят одинаковые по заголовкам матрицы, различающиеся лишь данными. Например, первая матрица выводит количество проданных товаров, а вторая - суммы от продаж по товарам. Необходимо отсортировать по количеству или суммам, а затем, применить такой же порядок для второй матрицы. Такой кейс довольно часто встречается в аналитических отчетах.

Давайте рассмотрим его на практике. Возьмем совершенно гипотетическую статистику продаж фруктов. Однако мало только видов фруктов, пусть будет список стран-импортеров фруктов. Количество проданных товаров будет выводиться по трём годам.

Шаблон отчета

Структура таблицы:

  • country_name
  • fruit_type
  • year
  • amount
  • price
  • sum

Сортировка

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

1. Получить список стран.

2. Для каждой страны:

2.1. получить значения ячеек с видами фруктов и количеством проданных по каждому году;

2.2. отсортировать значения для нужного года;

2.3. для каждой строки записать ячейки вида фруктов и количества по всем годам согласно индексам строк в отсортированном списке.

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

Приступим. Матрица имеет событие для модификации уже построенного объекта - ModifyResult. Создадим обработчик этого события в скрипте отчета.

 private List<List<int>> sortOrders = new List<List<int>>(); 
//Список порядков сортировки для каждого забора видов фруктов по странам
 
 private void Matrix1_ModifyResult(object sender, EventArgs e)
 {
//Словари в которых мы будем хранить индекс строки и значение ячейки
 Dictionary<int, double> firstYearCells = new Dictionary<int, double>();
 Dictionary<int, double> secondYearCells = new Dictionary<int, double>();
 Dictionary<int, double> thirdYearCells = new Dictionary<int, double>();
 Dictionary<int, string> typeCells = new Dictionary<int, string>();
 Dictionary<int, double> sortCells = new Dictionary<int, double>();
 
//bool prevYearSortNeeded = false;
 
 var total = false;
 var z = 1;
 var val2 = 0.0;
 var val3 = 0.0;
 
 List<string> countries = new List<string>(); 
 //В этом списке будем хранить список стран
 //Получаем все страны из первой колонки 
 for (int j=2; j<(sender as TableBase).ResultTable.RowCount-1; j++)
 { 
 try
 {
 var val = (sender as TableBase).ResultTable.GetCellData(0,j).Value.ToString();
 if (val.Length > 0) countries.Add(val);
 }
 catch (Exception)
 {}
 }
 
 int columnFirstYearIndex=0;
 int columnSecondYearIndex=0;
 int columnThirdYearIndex=0;
 int columnTypeIndex=0;
 
 //Проходим по всем колонкам матрицы чтобы сохранить ячейки в словари
 for (int t=0; t < (sender as TableBase).ResultTable.ColumnCount; t++)
 {
 
 if ((sender as TableBase).ResultTable.GetCellData(t,0).Text.Contains("2017"))
 {
 columnFirstYearIndex=t;
 }
 if ((sender as TableBase).ResultTable.GetCellData(t,0).Text.Contains("2018"))
 { 
 columnSecondYearIndex=t;
 }
 if ((sender as TableBase).ResultTable.GetCellData(t,0).Text.Contains("2019"))
 { 
 columnThirdYearIndex=t;
 }
 if ((sender as TableBase).ResultTable.GetCellData(t,0).Text.Contains("Fruit"))
 {
 columnTypeIndex=t;
 }
 }
 
 int countryOrder =0;
 
 //Для каждой страны прогоняем цикл, чтобы определить группы фруктов и отсортировать их
 foreach (var country in countries)
 {
 total = false;
 
 sortCells.Clear(); 
 //Очищаем список для сортировки
 
 //Выбираем ячейки из строк пока не встретим Total. Так как Total не должен сортироваться 
 while (!total)
 {
 if ((string)(sender as TableBase).ResultTable.GetCellData(columnTypeIndex,z).Text!="Total")
 {
 //Выбираем ячейки для первого года
 var value = (sender as TableBase).ResultTable.GetCellData(columnFirstYearIndex,z).Value;
 if (value!=null)
 {
 Double.TryParse(value.ToString(),out val3);
 firstYearCells.Add(z,val3);
 }
 else
 firstYearCells.Add(z, 0.0); 
 
//Выбираем ячейки для второго года 
 value = (sender as TableBase).ResultTable.GetCellData(columnSecondYearIndex,z).Value;
 if (value!=null)
 {
 Double.TryParse(value.ToString(),out val3);
 secondYearCells.Add(z,val3);
 }
 else
 secondYearCells.Add(z, 0.0); 
 
 //Выбираем ячейки для третьего года 
 value = (sender as TableBase).ResultTable.GetCellData(columnThirdYearIndex,z).Value;
 if (value!=null)
 {
 Double.TryParse(value.ToString(),out val3);
 thirdYearCells.Add(z,val3);
 }
 else
 thirdYearCells.Add(z, 0.0); 
 
//Выбираем ячейки для типов фруктов 
 value = (sender as TableBase).ResultTable.GetCellData(columnTypeIndex,z).Text;
 typeCells.Add(z,value.ToString()); 
 }
 else
 {
//Условие выхода из цикла
 total = true;
 } 
 z++;
 }
 
 sortCells = firstYearCells;
 //Задаем колонку для сортировки - в данном случае по первому году
 
 List<int> keys = new List<int>(); 
 
//Если имеем заполненный список сортировок для всех стран - значит первая страница отчета построена и можно использовать этот список на второй странице. Именно здесь обеспечивается сквозная сортировка.
 if ( sortOrders.Count == countries.Count )
 {
 keys = sortOrders.ElementAt(countryOrder);
 }
 else 
 keys = sortCells.OrderByDescending(i=>i.Value).Select(key => key.Key).ToList(); 
//Сортируем массив по убыванию используя библиотеку Linq
 
 int k = 0;
//Проходим цикл по всем элементам отсортированного списка
 foreach(var key in keys)
 {
//Выстраиваем значения ячеек для всех колонок в порядке сортировки
 (sender as TableBase).ResultTable.GetCellData(columnFirstYearIndex, firstYearCells.Keys.ElementAt(k)).Text = firstYearCells[key].ToString();
 (sender as TableBase).ResultTable.GetCellData(columnSecondYearIndex, secondYearCells.Keys.ElementAt(k)).Text = secondYearCells[key].ToString();
 (sender as TableBase).ResultTable.GetCellData(columnThirdYearIndex, thirdYearCells.Keys.ElementAt(k)).Text = thirdYearCells[key].ToString();
 (sender as TableBase).ResultTable.GetCellData(columnTypeIndex, typeCells.Keys.ElementAt(k)).Text = typeCells[key].ToString();
 k++; 
 } 
 if (keys.Count>0) sortOrders.Add(new List<int>(keys)); 
//Сохраняем порядок сортировки для текущей страны 
 
 //Важно почистить
 firstYearCells.Clear();
 secondYearCells.Clear();
 thirdYearCells.Clear();
 typeCells.Clear();
 countryOrder++; 
//Переходим к следующей стране
 }
}
}

Теперь копируем страницу отчета с матрицей, но вместо поля amount будем выводить sum.

А в событиях матрицы выберем созданный нами обработчик для ModifyResult.

Создаем событие матрицы ModifyResult

В результате запуска отчета мы увидим, что порядок видов фруктов на двух страницах совпадает. А это означает, что сортировка, полученная на первой странице применена и на второй.

Сравниваем порядок сортировки матриц на разных страницах отчета

Таким образом, мы с помощью скрипта отчета можем манипулировать данными в матрицах как нам угодно. А самое главное - применять один порядок сортировки на разных страницах отчета.

2 сентября 2024

Обзор облачного решения для создания и управления отчетами

МоиОтчеты Облако — это мощное облачное решение для создания и управления отчетами, обеспечивающее широкий спектр возможностей, от создания документов в различных форматах до интеграции с корпоративными системами.
9 ноября 2023

Как сделать отчет из C# проекта в МоиОтчеты Облако

В этой статье разберем пример, как с помощью SDK FastReport создавать отчеты и экспортировать их в любой удобный для вас формат.
4 октября 2023

Как из приложения ASP.NET Core сформировать отчет с использованием FastReport.Core.Skia

Рассказываем как сформировать отчет на Windows и Linux с использованием FastReport.Core.Skia и приватного NuGet сервера.