Допустим поставлена задача: отсортировать матрицу на первой странице в нужном порядке. А ещё запомнить этот порядок и применить его для сортировки подобных матриц на других страницах.
Это может понадобиться, когда у вас есть несколько страниц в отчёте, которые выводят одинаковые по заголовкам матрицы, различающиеся лишь данными. Например, первая матрица выводит количество проданных товаров, а вторая - суммы от продаж по товарам. Необходимо отсортировать по количеству или суммам, а затем, применить такой же порядок для второй матрицы. Такой кейс довольно часто встречается в аналитических отчетах.
Давайте рассмотрим его на практике. Возьмем совершенно гипотетическую статистику продаж фруктов. Однако мало только видов фруктов, пусть будет список стран-импортеров фруктов. Количество проданных товаров будет выводиться по трём годам.
Структура таблицы:
Стандартные механизмы сортировки нам тут не помогут. Поэтому займемся реализацией сортировки количества проданных фруктов по каждой стране. Давайте наметим себе последовательность шагов:
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.
В результате запуска отчета мы увидим, что порядок видов фруктов на двух страницах совпадает. А это означает, что сортировка, полученная на первой странице применена и на второй.
Таким образом, мы с помощью скрипта отчета можем манипулировать данными в матрицах как нам угодно. А самое главное - применять один порядок сортировки на разных страницах отчета.