Проблема объединения ячеек при генерации таблицы в приложении WPF с использованием бибилотеки Syncfusion
Проблема заключается в том что я генерирую таблицу используя данные из БД. Далее приложу скрины как должно быть и как получается на самом деле.
а вот как у меня получается:
проблема в поле с названием класса, оно не хочет групировать всё поле в одну строку. Я пытался делать и селектор, и через событие но не работает. Взможно гте-то неправильный порядок действий, но не понятно где. Я уже и делал логгирование через консоль. Вроде правлильно определяет в каком поле надо выполнить объединение, и выводит что объединение отработало, но по факту ничего не происходит. Сижу уже 3 дня не могу понять, ниже сккину весь код который этим занимается. класс занимающийся отображением данных и событие на объединение ячеек:
{
public CollectionViewSource AccountsViewSource { get; set; }
public SfDataGrid DataGrid { get; set; }
// Конструктор для DisplayDataService
public DisplayDataService(int fileId, SfDataGrid dataGrid)
{
AccountsViewSource = new CollectionViewSource();
DataGrid = dataGrid;
LoadDataFromDatabase(fileId);
}
// Загрузка данных из базы данных и заполнение SfDataGrid
private void LoadDataFromDatabase(int fileId)
{
using (var context = new AppDbContext())
{
Console.WriteLine("Создание контекста базы данных...");
// Получение информации о файле из базы данных
var fileInDb = context.Files.FirstOrDefault(f => f.Id == fileId);
// Получение данных по счетам с соответствующими связанными сущностями из базы данных
var accounts = context.Accounts
.Include(a => a.AccountDetails)
.Include(a => a.Class)
.Include(a => a.AccountGroups)
.Where(a => a.Class.FileId == fileId)
.ToList();
// Преобразование данных по счетам в модели отображения
var accountDisplayModels = accounts.Select(account => new AccountDisplayModel
{
// Отображение свойств счета на свойства модели отображения
AccountNumber = account.AccountNumber,
ClassName = account.Class.ClassName,
AccountGroup = account.AccountGroups.AccountGroup,
ActiveOpeningBalance = account.AccountDetails.ActiveOpeningBalance,
PassiveOpeningBalance = account.AccountDetails.PassiveOpeningBalance,
DebitTurnover = account.AccountDetails.DebitTurnover,
LoanTurnover = account.AccountDetails.LoanTurnover,
ActiveClosingBalance = account.AccountDetails.ActiveClosingBalance,
PassiveClosingBalance = account.AccountDetails.PassiveClosingBalance,
IsGroupSummary = false,
IsClassSummary = false,
DisplayText = account.AccountNumber.ToString(),
// Дополнительные свойства
// ...
}).ToList();
var displayData = new List<AccountDisplayModel>();
// Группировка и суммирование моделей отображения счетов
foreach (var classGroup in accountDisplayModels.GroupBy(a => a.ClassName).OrderBy(g => g.Key))
{
// Добавление заголовка класса
displayData.Add(new AccountDisplayModel { DisplayText = classGroup.Key, IsClassHeader = true });
// Перебор групп счетов внутри класса
foreach (var group in classGroup.GroupBy(a => a.AccountGroup).OrderBy(g => g.Key))
{
// Добавление отдельных счетов
displayData.AddRange(group.OrderBy(a => a.AccountNumber));
// Добавление суммарной информации по группе счетов
var groupSummary = new AccountDisplayModel
{
// Заполнение свойств суммарной информации по группе
DisplayText = $"{group.Key}",
ActiveOpeningBalance = group.Sum(a => a.ActiveOpeningBalance),
PassiveOpeningBalance = group.Sum(a => a.PassiveOpeningBalance),
DebitTurnover = group.Sum(a => a.DebitTurnover),
LoanTurnover = group.Sum(a => a.LoanTurnover),
ActiveClosingBalance = group.Sum(a => a.ActiveClosingBalance),
PassiveClosingBalance = group.Sum(a => a.PassiveClosingBalance),
IsGroupSummary = true,
// Дополнительные свойства
// ...
};
displayData.Add(groupSummary);
}
// Добавление суммарной информации по классу
var classSummary = new AccountDisplayModel
{
// Заполнение свойств суммарной информации по классу
DisplayText = "ПО КЛАССУ",
ActiveOpeningBalance = classGroup.Sum(a => a.ActiveOpeningBalance),
PassiveOpeningBalance = classGroup.Sum(a => a.PassiveOpeningBalance),
DebitTurnover = classGroup.Sum(a => a.DebitTurnover),
LoanTurnover = classGroup.Sum(a => a.LoanTurnover),
ActiveClosingBalance = classGroup.Sum(a => a.ActiveClosingBalance),
PassiveClosingBalance = classGroup.Sum(a => a.PassiveClosingBalance),
IsClassSummary = true,
// Дополнительные свойства
// ...
};
displayData.Add(classSummary);
}
// Подписка на событие QueryCoveredRange для настройки обработки
DataGrid.QueryCoveredRange += sfDataGrid_QueryCoveredRange;
// Установка данных отображения в качестве источника для CollectionViewSource
AccountsViewSource.Source = displayData;
}
}
// Настройка обработки события QueryCoveredRange в SfDataGrid
private void sfDataGrid_QueryCoveredRange(object sender, GridQueryCoveredRangeEventArgs e)
{
var dataGrid = sender as SfDataGrid;
if (dataGrid == null)
{
Console.WriteLine("SfDataGrid не найден.");
return;
}
var recordIndex = dataGrid.ResolveToRecordIndex(e.RowColumnIndex.RowIndex);
if (recordIndex < 0)
{
Console.WriteLine($"Не удалось разрешить индекс записи для строки: {e.RowColumnIndex.RowIndex}");
return;
}
var record = dataGrid.View.Records[recordIndex].Data as AccountDisplayModel;
if (record != null)
{
// Проверка, является ли запись заголовком класса
if (record.DisplayText != null && (record.DisplayText.StartsWith("КЛАСС") || record.IsClassHeader))
{
// Покрытие всей строки для заголовка класса
int startColumnIndex = 1;
int endColumnIndex = dataGrid.Columns.Count;
e.Range = new CoveredCellInfo(e.RowColumnIndex.RowIndex, startColumnIndex, e.RowColumnIndex.RowIndex, endColumnIndex);
e.Handled = true;
}
else
{
// Обработка других случаев при необходимости
Console.WriteLine();
}
}
else
{
Console.WriteLine();
}
}
}
селектор, который определяет какие ячейки обыные, а какие необходимо объеденять и к каким применить слити:
public class ClassHeaderTemplateSelector : DataTemplateSelector
{
// Шаблон для заголовка класса
public DataTemplate ClassHeaderTemplate { get; set; }
// Обычный шаблон для отображения данных
public DataTemplate NormalTemplate { get; set; }
// Переопределение метода выбора шаблона для элемента
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
// Преобразование объекта в модель отображения счета
var record = item as AccountDisplayModel;
// Проверка, является ли элемент заголовком класса
if (record != null && record.IsClassHeader)
{
// Возврат шаблона для заголовка класса
return ClassHeaderTemplate;
}
else
{
// Возврат обычного шаблона для отображения данных
return NormalTemplate;
}
}
}
модель данных для генерации таблицы:
public class AccountDisplayModel
{
public int AccountNumber { get; set; }
public int AccountGroup { get; set; }
public string ClassName { get; set; }
public decimal ActiveOpeningBalance { get; set; }
public decimal PassiveOpeningBalance { get; set; }
public decimal DebitTurnover { get; set; }
public decimal LoanTurnover { get; set; }
public decimal ActiveClosingBalance { get; set; }
public decimal PassiveClosingBalance { get; set; }
public bool IsGroupSummary { get; set; }
public bool IsClassSummary { get; set; }
public bool IsClassHeader { get; set; }
public string DisplayText { get; set; }
}
XAML код этого окна:
<Window.Resources>
<DataTemplate x:Key="ClassHeaderCellTemplate">
<TextBlock Text="{Binding DisplayText}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="LightGray" />
</DataTemplate>
<DataTemplate x:Key="NormalCellTemplate">
<TextBlock Text="{Binding DisplayText}" />
</DataTemplate>
<local1:ClassHeaderTemplateSelector x:Key="classHeaderTemplateSelector"
ClassHeaderTemplate="{StaticResource ClassHeaderCellTemplate}"
NormalTemplate="{StaticResource NormalCellTemplate}" />
</Window.Resources>
<Grid>
<syncfusion:SfDataGrid x:Name="sfDataGrid"
AutoGenerateColumns="False"
ItemsSource="{Binding AccountsViewSource.View}"
SelectionUnit="Cell"
NavigationMode="Cell"
AllowEditing="False"
AllowDeleting="False"
AllowDraggingColumns="False"
AllowResizingColumns="True"
AllowSorting="True"
ShowGroupDropArea="False">
<syncfusion:SfDataGrid.StackedHeaderRows>
<syncfusion:StackedHeaderRow>
<syncfusion:StackedHeaderRow.StackedColumns>
<syncfusion:StackedColumn ChildColumns="ActiveOpeningBalance,PassiveOpeningBalance" HeaderText="ВХОДЯЩЕЕ САЛЬДО"/>
<syncfusion:StackedColumn ChildColumns="DebitTurnover,LoanTurnover" HeaderText="ОБОРОТЫ"/>
<syncfusion:StackedColumn ChildColumns="ActiveClosingBalance,PassiveClosingBalance" HeaderText="ИСХОДЯЩЕЕ САЛЬДО"/>
</syncfusion:StackedHeaderRow.StackedColumns>
</syncfusion:StackedHeaderRow>
</syncfusion:SfDataGrid.StackedHeaderRows>
<syncfusion:SfDataGrid.Columns>
<syncfusion:GridTemplateColumn MappingName="DisplayText" HeaderText="Б/сч"
CellTemplateSelector="{StaticResource classHeaderTemplateSelector}"/>
<syncfusion:GridTextColumn MappingName="ActiveOpeningBalance" HeaderText="Актив"/>
<syncfusion:GridTextColumn MappingName="PassiveOpeningBalance" HeaderText="Пассив"/>
<syncfusion:GridTextColumn MappingName="DebitTurnover" HeaderText="Дебет"/>
<syncfusion:GridTextColumn MappingName="LoanTurnover" HeaderText="Кредит"/>
<syncfusion:GridTextColumn MappingName="ActiveClosingBalance" HeaderText="Актив"/>
<syncfusion:GridTextColumn MappingName="PassiveClosingBalance" HeaderText="Пассив"/>
</syncfusion:SfDataGrid.Columns>
</syncfusion:SfDataGrid>
</Grid>
конфигурация окна:
public partial class DataDisplayWindow : Window
{
public DataDisplayWindow(int fileId)
{
InitializeComponent();
DataContext = new DisplayDataService(fileId, sfDataGrid);
}
}
в общем вот, весь код скинул. Не могу понять в чем проблема, сам я не эксперт в WPF приложениях так еще и с библиотекой. Если вы знаете как это сделать без использования библиотеки буду так же рад, но вот пришлось это делать

