Winform在用的DataGridView修改为通过线条和透明的方式实现了合并列的效果。一段时间也在留意Avalonia.Controls.DataGrid是否也有类似的做法。一直没有心思去看那些代码,水平有限,实在搞不懂XML/Content一类如何实现绘制内容的。借着AI的帮助终于能试着去修改了。
让AI从DataGrid的以下方法入手
private void AddNewCellPrivate(DataGridRow row, DataGridColumn column)
{
DataGridCell newCell = new DataGridCell();
PopulateCellContent(
isCellEdited: false,
dataGridColumn: column,
dataGridRow: row,
dataGridCell: newCell);
if (row.OwningGrid != null)
{
newCell.OwningColumn = column;
newCell.IsVisible = column.IsVisible;
if (row.OwningGrid.CellTheme is {} cellTheme)
{
newCell.SetValue(ThemeProperty, cellTheme, BindingPriority.Template);
}
}
row.Cells.Insert(column.Index, newCell);
// 智能AutoMerge逻辑:比较当前行和下一行的数据
//ApplySmartAutoMerge(row, column, newCell);
}
最后发现,DataGrid并没有为每行数据创建DataGridRow/DataGridCell,且还会复用这些创建出来的DataGridCell显其他行的数据。就是说上面的方法只是开始的显示内容是可预测的。
多次尝试后,发现绘制前都会调用DataGridCell的EnsureGridLine方法,于是将合并的代码主要放在其中
internal void EnsureGridLine(DataGridColumn lastVisibleColumn)
{
if (OwningGrid != null && _rightGridLine != null)
{
if (OwningGrid.VerticalGridLinesBrush != null && OwningGrid.VerticalGridLinesBrush != _rightGridLine.Fill)
{
_rightGridLine.Fill = OwningGrid.VerticalGridLinesBrush;
}
//忽略其他代码
// 调用DataGrid的CheckCellToMerge方法
var (isSameAsPrevious, isSameAsNext) = OwningGrid?.CheckCellToMerge(this) ?? (false, false);
// 根据不同的组合情况处理底边框和透明度
HandleMergeResult(isSameAsPrevious, isSameAsNext);
}
至此,DataGrid决定如何合并,返回当前行与上一行和下一行的数据是否可以合并(现在只是简单的比较当前列的内容是否一致)。

有一个小小的问题,在滚动时合并列可能会显示空白。虽说在用的winform版的datagridview也有这个小问题。但新的东西多少有点改进,对吧。于是我向AI发出要求。最大的帮助就是判断是否是当前显示的第一列的方法了。
/// <summary>
/// 判断指定行是否为可视区域的第一行
/// </summary>
/// <param name="row">要检查的行</param>
/// <returns>true表示是可视区域的第一行,false表示不是</returns>
public bool IsFirstVisibleRow(DataGridRow row)
{
if (_rowsPresenter == null || row == null)
return false;
// 遍历可视区域的所有行,找到索引最小的行
int firstVisibleRowIndex = int.MaxValue;
foreach (Control element in _rowsPresenter.Children)
{
if (element is DataGridRow visibleRow && visibleRow.IsVisible)
{
firstVisibleRowIndex = Math.Min(firstVisibleRowIndex, visibleRow.Index);
}
}
// 如果指定行索引等于可视区域最小索引,则为第一行
return row.Index == firstVisibleRowIndex;
}
人工分析后发现,DataGridCell的EnsureGridLine不知为何只是lastVisibleColumn有调用,并不是行中的全部列。滚动的刷新由DataGridCellsPresenter发起,看以下方法
internal void EnsureFillerVisibility()
{
DataGridFillerColumn fillerColumn = OwningGrid.ColumnsInternal.FillerColumn;
//忽略其他代码
// This must be done after the Filler visibility is determined. This also must be done
// regardless of whether or not the filler visibility actually changed values because
// we could scroll in a cell that didn't have EnsureGridLine called yet
DataGridColumn lastVisibleColumn = OwningGrid.ColumnsInternal.LastVisibleColumn;
if (lastVisibleColumn != null)
{
DataGridCell cell = OwningRow.Cells[lastVisibleColumn.Index];
cell.EnsureGridLine(lastVisibleColumn);//不知道为什么只是最后一列需要执行。
}
// 检查是否为首行显示,如果是则清除合并单元格的透明度
bool isFirstVisualRow = (OwningGrid?.IsFirstVisibleRow(OwningRow) == true);
foreach (DataGridCell cell in OwningRow.Cells)
{
cell?.ClearMergeOpacity(isFirstVisualRow);//否的时候,可能需要恢复透明度
}
}
好了,这就是全部修改。代码在gitee
https://gitee.com/kevin2y/Avalonia.Controls.DataGrid/tree/feature/auto-merge-enhancement