I have a problem that I can't handle for 3 days. It sounds very simple, but in fact it turned out to be much more complicated. I have an application that takes data from an Excel file and stores it in a database, then this application forms its own table with this data. And everything would be fine, but one field in the table is not manageable. The structure itself is as follows: 4 main columns. The first column stores account numbers, groups of accounts with the total amount of a group of accounts, classes with the total amount of groups of accounts by class. All fields are arranged properly, except for one field with the name of the class, it should combine all columns. Next, I will attach screenshots of how it should be and how it really is.
Next, I will provide the code with which I am trying to do this, I will say right away, I am a novice developer, do not judge strictly:
{
public CollectionViewSource AccountsViewSource { get; set; }
public SfDataGrid DataGrid { get; set; }
public DisplayDataService(int fileId, SfDataGrid dataGrid)
{
AccountsViewSource = new CollectionViewSource();
DataGrid = dataGrid;
LoadDataFromDatabase(fileId);
}
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);
}
DataGrid.QueryCoveredRange += sfDataGrid_QueryCoveredRange;
AccountsViewSource.Source = displayData;
}
}
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();
}
}
}
The model to display:
{
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; }
}
window generation:
{
public DataDisplayWindow(int fileId)
{
InitializeComponent();
DataContext = new DisplayDataService(fileId, sfDataGrid);
}
}
XAML code:
<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>
class selector:
{
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;
}
}
}
The application uses the following libraries: EF core , Plus, Syncfusion.Grid.WPF
when an object has the property Is Class Header = true, then all the cells of the field for this object should be combined into one. But objects of this type with the property Is Class Header = true are stored only in 1 column, respectively, the first column for such an object should be one for the entire width of the table, as shown in the screenshots
I have already tried many options, used logging for each line of code, debug, and different approaches to updating the visual component... Nothing helped.
Based on the reported scenario, with the mentioned code snippet it is found that there are some misalignments in the parameter parsing of the sfDatagrid_QueryCoveredRange method in CoveredCellInfo.
Please note that the parsing parameter of CoveredCellInfo should contain four parameters. The first two parameters(left and right) should consider the column values (startcolumnindex and endcolumnindex), while the other two parameters(top and bottom) should consider the row values (startrowindex and endrowindex). Based on these parameters, the cell can be merged.
Please see the attached tested sample. Please take a look at this.
Please find the below attached UG link for reference for handling "Merge Cells.".
UG link-> enter link description here
Please refer the output: enter image description here