Add right frozen support

This commit is contained in:
chinboy
2025-07-13 22:13:41 +08:00
parent 32f01cff48
commit 4e59a71c7d
12 changed files with 181 additions and 40 deletions
@@ -18,6 +18,7 @@ public sealed class DataGridCellsPresenter : Panel, IChildIndexProvider
internal DataGrid? OwningGrid => OwningRow?.OwningGrid;
internal DataGridRow? OwningRow { get; set; }
internal DataGridRowsPresenter? OwningRowsPresenter { get; set; }
event EventHandler<ChildIndexChangedEventArgs>? IChildIndexProvider.ChildIndexChanged
{
@@ -56,7 +57,7 @@ public sealed class DataGridCellsPresenter : Panel, IChildIndexProvider
/// </param>
protected override Size ArrangeOverride(Size finalSize)
{
if (OwningGrid == null)
if (OwningGrid == null || OwningRowsPresenter == null)
{
return base.ArrangeOverride(finalSize);
}
@@ -72,8 +73,34 @@ public sealed class DataGridCellsPresenter : Panel, IChildIndexProvider
return base.ArrangeOverride(finalSize);
}
double frozenLeftEdge = 0;
double frozenLeftEdge = 0;
double frozenRightEdge = OwningRowsPresenter.DesiredSize.Width;
double realFrozenRightEdge = OwningRowsPresenter.DesiredSize.Width;
var rowHeader = OwningRow.HeaderCell;
double rowHeaderWidth = 0.0;
if (rowHeader != null)
{
rowHeaderWidth = rowHeader.DesiredSize.Width;
}
frozenRightEdge -= rowHeaderWidth;
realFrozenRightEdge -= rowHeaderWidth;
double scrollingLeftEdge = -OwningGrid.HorizontalOffset;
var visibleColumnCount = 0;
// 需要先计算出 frozenRightEdge
foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns())
{
if (column.IsRightFrozen)
{
realFrozenRightEdge -= column.ActualWidth;
}
visibleColumnCount++;
}
var maxOffsetX = finalSize.Width - frozenRightEdge;
var visibleColumnIndex = 0;
foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns())
{
@@ -81,15 +108,25 @@ public sealed class DataGridCellsPresenter : Panel, IChildIndexProvider
DataGridCell cell = OwningRow.Cells[column.Index];
Debug.Assert(cell.OwningColumn == column);
Debug.Assert(column.IsVisible);
if (column.IsFrozen)
if (column.IsLeftFrozen)
{
cellLeftEdge = frozenLeftEdge;
// This can happen before or after clipping because frozen cells aren't clipped
frozenLeftEdge += column.ActualWidth;
cell.IsFrozen = true;
cell.FrozenShadowPosition = FrozenColumnShadowPosition.Right; // 目前我们就支持左边冻结
cell.FrozenShadowPosition = FrozenColumnShadowPosition.Right;
cell.IsShowFrozenShadow = (visibleColumnIndex == OwningGrid.LeftFrozenColumnCount - 1) && OwningGrid.HorizontalOffset > 0;
}
else if (column.IsRightFrozen)
{
frozenRightEdge -= column.ActualWidth;
// This can happen before or after clipping because frozen cells aren't clipped
cellLeftEdge = frozenRightEdge;
cell.IsFrozen = true;
cell.FrozenShadowPosition = FrozenColumnShadowPosition.Left;
cell.IsShowFrozenShadow = (visibleColumnIndex == visibleColumnCount - OwningGrid.RightFrozenColumnCount) &&
OwningGrid.HorizontalOffset < maxOffsetX;
}
else
{
cellLeftEdge = scrollingLeftEdge;
@@ -98,7 +135,8 @@ public sealed class DataGridCellsPresenter : Panel, IChildIndexProvider
if (cell.IsVisible)
{
cell.Arrange(new Rect(cellLeftEdge, 0, column.LayoutRoundedWidth, finalSize.Height));
EnsureCellClip(cell, column.ActualWidth, finalSize.Height, frozenLeftEdge, scrollingLeftEdge);
var cellRightEdge = cell.Bounds.Right;
EnsureCellClip(cell, column.ActualWidth, finalSize.Height, frozenLeftEdge, realFrozenRightEdge, scrollingLeftEdge, cellRightEdge);
}
scrollingLeftEdge += column.ActualWidth;
column.IsInitialDesiredWidthDetermined = true;
@@ -112,17 +150,32 @@ public sealed class DataGridCellsPresenter : Panel, IChildIndexProvider
return finalSize;
}
private static void EnsureCellClip(DataGridCell cell, double width, double height, double frozenLeftEdge, double cellLeftEdge)
private static void EnsureCellClip(DataGridCell cell, double width, double height, double frozenLeftEdge, double frozenRightEdge, double cellLeftEdge, double cellRightEdge)
{
// Clip the cell only if it's scrolled under frozen columns. Unfortunately, we need to clip in this case
// because cells could be transparent
if (cell.OwningColumn != null && !cell.OwningColumn.IsFrozen && frozenLeftEdge > cellLeftEdge)
if (cell.OwningColumn != null && !cell.OwningColumn.IsFrozen && frozenLeftEdge > cellLeftEdge &&
cellRightEdge > frozenRightEdge)
{
RectangleGeometry rg = new RectangleGeometry();
double xClip = Math.Round(Math.Min(width, frozenLeftEdge - cellLeftEdge));
rg.Rect = new Rect(xClip, 0, Math.Max(0, width - (frozenLeftEdge - cellLeftEdge) - (cellRightEdge - frozenRightEdge)), height);
cell.Clip = rg;
}
else if (cell.OwningColumn != null && !cell.OwningColumn.IsFrozen && frozenLeftEdge > cellLeftEdge)
{
RectangleGeometry rg = new RectangleGeometry();
double xClip = Math.Round(Math.Min(width, frozenLeftEdge - cellLeftEdge));
rg.Rect = new Rect(xClip, 0, Math.Max(0, width - xClip), height);
cell.Clip = rg;
}
else if (cell.OwningColumn != null && !cell.OwningColumn.IsFrozen && cellRightEdge > frozenRightEdge)
{
RectangleGeometry rg = new RectangleGeometry();
double xClip = Math.Round(Math.Min(width, cellRightEdge - frozenRightEdge));
rg.Rect = new Rect(0, 0, Math.Max(0, width - xClip), height);
cell.Clip = rg;
}
else
{
cell.Clip = null;
@@ -497,8 +497,19 @@ public abstract partial class DataGridColumn : AvaloniaObject
}
public bool IsAutoGenerated { get; internal set; }
public bool IsFrozen
{
get => IsLeftFrozen || IsRightFrozen;
set {
IsLeftFrozen = value;
IsRightFrozen = value;
}
}
public bool IsFrozen { get; internal set; }
public bool IsLeftFrozen { get; internal set; }
public bool IsRightFrozen { get; internal set; }
#endregion
@@ -140,13 +140,15 @@ public sealed class DataGridColumnHeadersPresenter : Panel, IChildIndexProvider
double dragIndicatorLeftEdge = 0;
double frozenLeftEdge = 0;
double frozenRightEdge = finalSize.Width;
double scrollingLeftEdge = -OwningGrid.HorizontalOffset;
var visibleColumnIndex = 0;
var VisibleColumnCount = OwningGrid.ColumnsInternal.GetDisplayedColumnCount();
foreach (DataGridColumn dataGridColumn in OwningGrid.ColumnsInternal.GetVisibleColumns())
{
DataGridColumnHeader columnHeader = dataGridColumn.HeaderCell;
Debug.Assert(columnHeader.OwningColumn == dataGridColumn);
if (dataGridColumn.IsFrozen)
if (dataGridColumn.IsLeftFrozen)
{
columnHeader.Arrange(new Rect(frozenLeftEdge, 0, dataGridColumn.LayoutRoundedWidth, finalSize.Height));
columnHeader.Clip = null; // The layout system could have clipped this because it's not aware of our render transform
@@ -156,9 +158,22 @@ public sealed class DataGridColumnHeadersPresenter : Panel, IChildIndexProvider
}
frozenLeftEdge += dataGridColumn.ActualWidth;
columnHeader.IsFrozen = true;
columnHeader.FrozenShadowPosition = FrozenColumnShadowPosition.Right; // 目前我们就支持左边冻结
columnHeader.FrozenShadowPosition = FrozenColumnShadowPosition.Right;
columnHeader.IsShowFrozenShadow = (visibleColumnIndex == OwningGrid.LeftFrozenColumnCount - 1) && OwningGrid.HorizontalOffset > 0;
}
else if (dataGridColumn.IsRightFrozen)
{
frozenRightEdge -= dataGridColumn.ActualWidth;
columnHeader.Arrange(new Rect(frozenRightEdge, 0, dataGridColumn.LayoutRoundedWidth, finalSize.Height));
columnHeader.Clip = null; // The layout system could have clipped this because it's not aware of our render transform
// if (DragColumn == dataGridColumn && DragIndicator != null)
// {
// dragIndicatorLeftEdge = frozenLeftEdge + DragIndicatorOffset;
// }
columnHeader.IsFrozen = true;
columnHeader.FrozenShadowPosition = FrozenColumnShadowPosition.Left;
columnHeader.IsShowFrozenShadow = (visibleColumnIndex == VisibleColumnCount - OwningGrid.RightFrozenColumnCount) && OwningGrid.HorizontalOffset < OwningGrid.HorizontalMaximizeOffset;
}
else
{
columnHeader.Arrange(new Rect(scrollingLeftEdge, 0, dataGridColumn.LayoutRoundedWidth, finalSize.Height));
@@ -770,14 +770,30 @@ public partial class DataGrid
SetAndSelectCurrentCell(newCurrentCellCoordinates.ColumnIndex,
newCurrentCellCoordinates.Slot,
ColumnsInternal.VisibleColumnCount == 1 /*forceCurrentCellSelection*/);
if (newDisplayIndex < FrozenColumnCountWithFiller)
if (IsFrozenColumnIndex(newDisplayIndex))
{
CorrectColumnFrozenStates();
}
}
}
internal bool IsFrozenColumnIndex(int index)
{
if (index < LeftFrozenColumnCountWithFiller)
{
return true;
}
var displayColumnCount = ColumnsInternal.GetDisplayedColumnCount();
if (index >= displayColumnCount - RightFrozenColumnCount && index < displayColumnCount)
{
return true;
}
return false;
}
internal void HandleInsertedColumnPreNotification(DataGridColumn insertedColumn)
{
// Fix the Index of all following columns
@@ -1512,32 +1528,31 @@ public partial class DataGrid
private void CorrectColumnFrozenStates()
{
int index = 0;
double frozenColumnWidth = 0;
double oldFrozenColumnWidth = 0;
int displayedColumnCount = ColumnsInternal.GetDisplayedColumnCount();
foreach (DataGridColumn column in ColumnsInternal.GetDisplayedColumns())
{
if (column.IsFrozen)
bool isLeftFrozen = index < LeftFrozenColumnCountWithFiller;
bool isRightFrozen = index >= (displayedColumnCount - RightFrozenColumnCount);
if (isLeftFrozen)
{
oldFrozenColumnWidth += column.ActualWidth;
column.IsLeftFrozen = true;
column.IsRightFrozen = false;
}
column.IsFrozen = index < FrozenColumnCountWithFiller;
if (column.IsFrozen)
else if (isRightFrozen)
{
frozenColumnWidth += column.ActualWidth;
column.IsRightFrozen = true;
column.IsLeftFrozen = false;
}
else
{
column.IsFrozen = false;
}
index++;
}
if (HorizontalOffset > Math.Max(0, frozenColumnWidth - oldFrozenColumnWidth))
{
UpdateHorizontalOffset(HorizontalOffset - frozenColumnWidth + oldFrozenColumnWidth);
}
else
{
UpdateHorizontalOffset(0);
}
UpdateHorizontalOffset(0);
}
private void CorrectColumnIndexesAfterDeletion(DataGridColumn deletedColumn)
@@ -110,6 +110,8 @@ public partial class DataGrid
internal double NegVerticalOffset { get; private set; }
internal double HorizontalMaximizeOffset => _hScrollBar?.Maximum ?? 0.0d;
private int NoSelectionChangeCount
{
get => _noSelectionChangeCount;
@@ -100,7 +100,7 @@ public partial class DataGrid
internal int FirstVisibleSlot => (SlotCount > 0) ? GetNextVisibleSlot(-1) : -1;
internal int FrozenColumnCountWithFiller
internal int LeftFrozenColumnCountWithFiller
{
get
{
@@ -118,7 +118,9 @@ public partial class DataGrid
}
}
internal int LastVisibleSlot => (SlotCount > 0) ? GetPreviousVisibleSlot(SlotCount) : -1;
internal int FrozenColumnCountWithFiller => LeftFrozenColumnCountWithFiller + RightFrozenColumnCount;
internal int LastVisibleSlot => SlotCount > 0 ? GetPreviousVisibleSlot(SlotCount) : -1;
internal DataGridRow? EditingRow { get; private set; }
+15 -1
View File
@@ -105,6 +105,11 @@ public partial class DataGrid : TemplatedControl,
AvaloniaProperty.Register<DataGrid, int>(
nameof(LeftFrozenColumnCount),
validate: ValidateFrozenColumnCount);
public static readonly StyledProperty<int> RightFrozenColumnCountProperty =
AvaloniaProperty.Register<DataGrid, int>(
nameof(RightFrozenColumnCount),
validate: ValidateFrozenColumnCount);
public static readonly StyledProperty<DataGridGridLinesVisibility> GridLinesVisibilityProperty =
AvaloniaProperty.Register<DataGrid, DataGridGridLinesVisibility>(nameof(GridLinesVisibility),
@@ -309,13 +314,22 @@ public partial class DataGrid : TemplatedControl,
}
/// <summary>
/// Gets or sets the number of columns that the user cannot scroll horizontally.
/// Gets or sets the number of left edge columns that the user cannot scroll horizontally.
/// </summary>
public int LeftFrozenColumnCount
{
get => GetValue(LeftFrozenColumnCountProperty);
set => SetValue(LeftFrozenColumnCountProperty, value);
}
/// <summary>
/// Gets or sets the number of right edge columns that the user cannot scroll horizontally.
/// </summary>
public int RightFrozenColumnCount
{
get => GetValue(RightFrozenColumnCountProperty);
set => SetValue(RightFrozenColumnCountProperty, value);
}
/// <summary>
/// Gets or sets a value that indicates which grid lines separating inner cells are shown.
@@ -250,6 +250,12 @@ internal class DataGridColumnCollection : ObservableCollection<DataGridColumn>
}
}
internal int GetDisplayedColumnCount()
{
Debug.Assert(ItemsInternal != null);
return DisplayIndexMap.Count;
}
/// <summary>
/// Returns an enumeration of all columns that meet the criteria of the filter predicate.
/// </summary>
@@ -367,7 +367,7 @@ internal class DataGridToken : AbstractControlDesignToken
TableScrollBg = SharedToken.ColorSplit;
LeftFrozenShadows = new BoxShadows(new BoxShadow
{
OffsetX = 10,
OffsetX = -10,
OffsetY = 0,
Blur = 8,
Spread = 0,
@@ -375,7 +375,7 @@ internal class DataGridToken : AbstractControlDesignToken
});
RightFrozenShadows = new BoxShadows(new BoxShadow
{
OffsetX = -10,
OffsetX = 10,
OffsetY = 0,
Blur = 8,
Spread = 0,
@@ -219,7 +219,7 @@ public partial class DataGridRow : TemplatedControl
if (_bottomGridLine != null)
{
RectangleGeometry gridlineClipGeometry = new RectangleGeometry();
gridlineClipGeometry.Rect = new Rect(OwningGrid.HorizontalOffset, 0, Math.Max(0, DesiredSize.Width - OwningGrid.HorizontalOffset), _bottomGridLine.DesiredSize.Height);
gridlineClipGeometry.Rect = new Rect(0, 0, Math.Max(0, DesiredSize.Width - OwningGrid.HorizontalOffset), _bottomGridLine.DesiredSize.Height);
_bottomGridLine.Clip = gridlineClipGeometry;
}
@@ -284,6 +284,10 @@ public partial class DataGridRow : TemplatedControl
if (_cellsElement != null)
{
_cellsElement.OwningRow = this;
if (Parent is DataGridRowsPresenter dataGridRowsPresenter)
{
_cellsElement.OwningRowsPresenter = dataGridRowsPresenter;
}
// Cells that were already added before the Template was applied need to
// be added to the Canvas
if (Cells.Count > 0)
@@ -82,8 +82,16 @@
</Style>
<Style Selector="^[IsShowFrozenShadow=True]">
<Style Selector="^ /template/ Border#PART_CellFrame">
<Setter Property="BoxShadow" Value="{DynamicResource {x:Static atom:DataGridTokenKey.LeftFrozenShadows}}"/>
<Style Selector="^[FrozenShadowPosition=Left]">
<Style Selector="^ /template/ Border#PART_CellFrame">
<Setter Property="BoxShadow" Value="{DynamicResource {x:Static atom:DataGridTokenKey.LeftFrozenShadows}}"/>
</Style>
</Style>
<Style Selector="^[FrozenShadowPosition=Right]">
<Style Selector="^ /template/ Border#PART_CellFrame">
<Setter Property="BoxShadow" Value="{DynamicResource {x:Static atom:DataGridTokenKey.RightFrozenShadows}}"/>
</Style>
</Style>
</Style>
</ControlTheme>
@@ -8,7 +8,6 @@
<Setter Property="Template">
<ControlTemplate TargetType="atom:DataGridColumnHeader">
<Border Name="{x:Static atom:DataGridColumnHeaderThemeConstants.FramePart}"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}">
<Panel Name="{x:Static atom:DataGridColumnHeaderThemeConstants.HeaderRootLayoutPart}">
<Decorator Name="{x:Static atom:DataGridColumnHeaderThemeConstants.ContentDecoratorPart}">
@@ -91,8 +90,7 @@
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="ClipToBounds" Value="False"/>
<Setter Property="Background" Value="Transparent" />
<Setter Property="Background" Value="{DynamicResource {x:Static atom:DataGridTokenKey.TableHeaderBg}}" />
<Style Selector="^[ShowSorterTooltip=True]">
<Setter Property="atom:ToolTip.ServiceEnabled" Value="True" />
</Style>
@@ -163,8 +161,21 @@
<!-- BuildSortableColumnStyle 结束 -->
<Style Selector="^[IsShowFrozenShadow=True]">
<Style Selector="^ /template/ Border#PART_Frame">
<Setter Property="BoxShadow" Value="{DynamicResource {x:Static atom:DataGridTokenKey.LeftFrozenShadows}}"/>
<Setter Property="Background" Value="{DynamicResource {x:Static atom:DataGridTokenKey.TableHeaderBg}}" />
</Style>
<Style Selector="^[FrozenShadowPosition=Left]">
<Style Selector="^ /template/ Border#PART_Frame">
<Setter Property="BoxShadow" Value="{DynamicResource {x:Static atom:DataGridTokenKey.LeftFrozenShadows}}"/>
</Style>
</Style>
<Style Selector="^[FrozenShadowPosition=Right]">
<Style Selector="^ /template/ Border#PART_Frame">
<Setter Property="BoxShadow" Value="{DynamicResource {x:Static atom:DataGridTokenKey.RightFrozenShadows}}"/>
</Style>
</Style>
</Style>