diff --git a/src/AtomUI.Controls.DataGrid/Cell/DataGridCellsPresenter.cs b/src/AtomUI.Controls.DataGrid/Cell/DataGridCellsPresenter.cs index f64c8e8e1..efe1a0899 100644 --- a/src/AtomUI.Controls.DataGrid/Cell/DataGridCellsPresenter.cs +++ b/src/AtomUI.Controls.DataGrid/Cell/DataGridCellsPresenter.cs @@ -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? IChildIndexProvider.ChildIndexChanged { @@ -56,7 +57,7 @@ public sealed class DataGridCellsPresenter : Panel, IChildIndexProvider /// 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; diff --git a/src/AtomUI.Controls.DataGrid/Column/DataGridColumn.cs b/src/AtomUI.Controls.DataGrid/Column/DataGridColumn.cs index aab4a2f5f..a2a616941 100644 --- a/src/AtomUI.Controls.DataGrid/Column/DataGridColumn.cs +++ b/src/AtomUI.Controls.DataGrid/Column/DataGridColumn.cs @@ -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 diff --git a/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeadersPresenter.cs b/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeadersPresenter.cs index 21504c8c8..d6c7736df 100644 --- a/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeadersPresenter.cs +++ b/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeadersPresenter.cs @@ -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)); diff --git a/src/AtomUI.Controls.DataGrid/DataGrid.Columns.cs b/src/AtomUI.Controls.DataGrid/DataGrid.Columns.cs index 89572f26f..4f5378e68 100644 --- a/src/AtomUI.Controls.DataGrid/DataGrid.Columns.cs +++ b/src/AtomUI.Controls.DataGrid/DataGrid.Columns.cs @@ -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) diff --git a/src/AtomUI.Controls.DataGrid/DataGrid.Privates.cs b/src/AtomUI.Controls.DataGrid/DataGrid.Privates.cs index 8ce803446..2371df053 100644 --- a/src/AtomUI.Controls.DataGrid/DataGrid.Privates.cs +++ b/src/AtomUI.Controls.DataGrid/DataGrid.Privates.cs @@ -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; diff --git a/src/AtomUI.Controls.DataGrid/DataGrid.Rows.cs b/src/AtomUI.Controls.DataGrid/DataGrid.Rows.cs index 13296580a..179b2fb84 100644 --- a/src/AtomUI.Controls.DataGrid/DataGrid.Rows.cs +++ b/src/AtomUI.Controls.DataGrid/DataGrid.Rows.cs @@ -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; } diff --git a/src/AtomUI.Controls.DataGrid/DataGrid.cs b/src/AtomUI.Controls.DataGrid/DataGrid.cs index 0cc9376e7..bdc731beb 100644 --- a/src/AtomUI.Controls.DataGrid/DataGrid.cs +++ b/src/AtomUI.Controls.DataGrid/DataGrid.cs @@ -105,6 +105,11 @@ public partial class DataGrid : TemplatedControl, AvaloniaProperty.Register( nameof(LeftFrozenColumnCount), validate: ValidateFrozenColumnCount); + + public static readonly StyledProperty RightFrozenColumnCountProperty = + AvaloniaProperty.Register( + nameof(RightFrozenColumnCount), + validate: ValidateFrozenColumnCount); public static readonly StyledProperty GridLinesVisibilityProperty = AvaloniaProperty.Register(nameof(GridLinesVisibility), @@ -309,13 +314,22 @@ public partial class DataGrid : TemplatedControl, } /// - /// 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. /// public int LeftFrozenColumnCount { get => GetValue(LeftFrozenColumnCountProperty); set => SetValue(LeftFrozenColumnCountProperty, value); } + + /// + /// Gets or sets the number of right edge columns that the user cannot scroll horizontally. + /// + public int RightFrozenColumnCount + { + get => GetValue(RightFrozenColumnCountProperty); + set => SetValue(RightFrozenColumnCountProperty, value); + } /// /// Gets or sets a value that indicates which grid lines separating inner cells are shown. diff --git a/src/AtomUI.Controls.DataGrid/DataGridColumnCollection.cs b/src/AtomUI.Controls.DataGrid/DataGridColumnCollection.cs index 00b7739c4..0253774f9 100644 --- a/src/AtomUI.Controls.DataGrid/DataGridColumnCollection.cs +++ b/src/AtomUI.Controls.DataGrid/DataGridColumnCollection.cs @@ -250,6 +250,12 @@ internal class DataGridColumnCollection : ObservableCollection } } + internal int GetDisplayedColumnCount() + { + Debug.Assert(ItemsInternal != null); + return DisplayIndexMap.Count; + } + /// /// Returns an enumeration of all columns that meet the criteria of the filter predicate. /// diff --git a/src/AtomUI.Controls.DataGrid/DataGridToken.cs b/src/AtomUI.Controls.DataGrid/DataGridToken.cs index c88ad7cdd..d2211fd83 100644 --- a/src/AtomUI.Controls.DataGrid/DataGridToken.cs +++ b/src/AtomUI.Controls.DataGrid/DataGridToken.cs @@ -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, diff --git a/src/AtomUI.Controls.DataGrid/Row/DataGridRow.cs b/src/AtomUI.Controls.DataGrid/Row/DataGridRow.cs index 950dadf00..9ead504c5 100644 --- a/src/AtomUI.Controls.DataGrid/Row/DataGridRow.cs +++ b/src/AtomUI.Controls.DataGrid/Row/DataGridRow.cs @@ -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) diff --git a/src/AtomUI.Controls.DataGrid/Themes/DataGridCellTheme.axaml b/src/AtomUI.Controls.DataGrid/Themes/DataGridCellTheme.axaml index fe9740a66..faf765c03 100644 --- a/src/AtomUI.Controls.DataGrid/Themes/DataGridCellTheme.axaml +++ b/src/AtomUI.Controls.DataGrid/Themes/DataGridCellTheme.axaml @@ -82,8 +82,16 @@ + + + diff --git a/src/AtomUI.Controls.DataGrid/Themes/DataGridColumnHeaderTheme.axaml b/src/AtomUI.Controls.DataGrid/Themes/DataGridColumnHeaderTheme.axaml index 892c05290..6a8829192 100644 --- a/src/AtomUI.Controls.DataGrid/Themes/DataGridColumnHeaderTheme.axaml +++ b/src/AtomUI.Controls.DataGrid/Themes/DataGridColumnHeaderTheme.axaml @@ -8,7 +8,6 @@ @@ -91,8 +90,7 @@ - - + @@ -163,8 +161,21 @@ + + + + +