mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-17 05:31:01 +08:00
Fix DataGrid row reorder bug
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using AtomUI.Controls.Data;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
@@ -121,6 +120,11 @@ public sealed class DataGridRowReorderColumn : DataGridColumn
|
||||
{
|
||||
throw DataGridError.DataGridRow.RowReorderNotAllowedException();
|
||||
}
|
||||
// TODO 需否需要检查每一列的配置
|
||||
if (owningGrid.CanUserFilterColumns || owningGrid.CanUserSortColumns)
|
||||
{
|
||||
throw DataGridError.DataGridRow.InvalidRowReorderPreConditionException();
|
||||
}
|
||||
owningGrid.PropertyChanged += HandleOwningGridItemsSourceChanged;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
|
||||
@@ -26,9 +25,11 @@ internal class DataGridRowReorderHandle : TemplatedControl
|
||||
|
||||
private static Point? _dragStart;
|
||||
private static double? _dragDelta;
|
||||
private static DataGridRow? _dragRow;
|
||||
private static DataGridRow? _prevDraggingOverRow;
|
||||
private static DataGridRow? _currentDraggingOverRow;
|
||||
|
||||
private static Rect? _dragRowBounds;
|
||||
private static int? _dragRowIndex;
|
||||
private static int? _currentDraggingOverRowIndex;
|
||||
|
||||
private static Point? _lastMousePositionInPresenter;
|
||||
private IconButton? _indicatorButton;
|
||||
|
||||
@@ -67,48 +68,45 @@ internal class DataGridRowReorderHandle : TemplatedControl
|
||||
{
|
||||
_indicatorButton.DisableTransitions();
|
||||
}
|
||||
if (_prevDraggingOverRow != null && _prevDraggingOverRow != OwningRow)
|
||||
if (_currentDraggingOverRowIndex != null && _currentDraggingOverRowIndex != _dragRowIndex)
|
||||
{
|
||||
if (OwningGrid.CollectionView is IList collectionView)
|
||||
{
|
||||
var data = collectionView[OwningRow.Index];
|
||||
if (data is not null)
|
||||
if (_dragRowIndex != null)
|
||||
{
|
||||
collectionView.RemoveAt(OwningRow.Index);
|
||||
collectionView.Insert(_prevDraggingOverRow.Index, data);
|
||||
var data = collectionView[_dragRowIndex.Value];
|
||||
if (data is not null)
|
||||
{
|
||||
collectionView.RemoveAt(_dragRowIndex.Value);
|
||||
collectionView.Insert(_currentDraggingOverRowIndex.Value, data);
|
||||
}
|
||||
}
|
||||
|
||||
if (_indicatorButton != null)
|
||||
{
|
||||
_indicatorButton.EnableTransitions();
|
||||
}
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
OwningGrid.CollectionView.Refresh();
|
||||
});
|
||||
OwningGrid.CollectionView.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
OwningRow.Opacity = 1.0;
|
||||
|
||||
_dragDelta = null;
|
||||
_dragRow = null;
|
||||
_dragRowIndex = null;
|
||||
_dragRowBounds = null;
|
||||
_dragStart = null;
|
||||
_lastMousePositionInPresenter = null;
|
||||
_currentDraggingOverRow = null;
|
||||
_prevDraggingOverRow = null;
|
||||
rowsPresenter.DraggedRow = null;
|
||||
_currentDraggingOverRowIndex = null;
|
||||
rowsPresenter.DragRowOffset = 0.0;
|
||||
rowsPresenter.InvalidateArrange();
|
||||
}
|
||||
|
||||
private void HandlePointerMoved(object? sender, PointerEventArgs e)
|
||||
{
|
||||
if (OwningGrid == null || !IsEnabled || OwningGrid.ColumnHeaders == null)
|
||||
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed || OwningGrid == null || !IsEnabled || OwningGrid.ColumnHeaders == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Point mousePositionPresenter = e.GetPosition(OwningGrid.RowsPresenter);
|
||||
|
||||
Debug.Assert(OwningGrid.Parent is InputElement);
|
||||
HandleMouseMoveReorder(mousePositionPresenter);
|
||||
}
|
||||
@@ -122,38 +120,31 @@ internal class DataGridRowReorderHandle : TemplatedControl
|
||||
var rowsPresenter = OwningGrid.RowsPresenter;
|
||||
|
||||
//handle entry into reorder mode
|
||||
if (_dragRow == null && _lastMousePositionInPresenter != null)
|
||||
if (_dragRowIndex == null && _lastMousePositionInPresenter != null)
|
||||
{
|
||||
HandleMouseMoveBeginReorder(mousePositionPresenter);
|
||||
}
|
||||
|
||||
//handle reorder mode (eg, positioning of the popup)
|
||||
if (rowsPresenter.DraggedRow != null)
|
||||
if (rowsPresenter.DraggedRowIndex != null)
|
||||
{
|
||||
Debug.Assert(_dragStart != null);
|
||||
// Find row we're hovering over
|
||||
var targetRow = GetReorderingTargetRow(mousePositionPresenter, true, out double scrollAmount);
|
||||
if (targetRow != _currentDraggingOverRow)
|
||||
{
|
||||
_prevDraggingOverRow = _currentDraggingOverRow;
|
||||
_currentDraggingOverRow = targetRow;
|
||||
if (_currentDraggingOverRow != null && _currentDraggingOverRow != _dragRow)
|
||||
{
|
||||
rowsPresenter.SwapOrderingChildren(OwningRow, _currentDraggingOverRow);
|
||||
}
|
||||
}
|
||||
|
||||
var targetRowIndex = GetReorderingTargetRow(mousePositionPresenter, true, out double scrollAmount);
|
||||
_currentDraggingOverRowIndex = targetRowIndex;
|
||||
|
||||
if (_dragDelta == null)
|
||||
{
|
||||
_dragDelta = _dragRow?.Bounds.Y;
|
||||
_dragDelta = _dragRowBounds?.Y;
|
||||
}
|
||||
var delta = _dragDelta.HasValue ? _dragDelta.Value : 0.0;
|
||||
rowsPresenter.DragRowOffset = delta + mousePositionPresenter.Y - _dragStart.Value.Y + scrollAmount;
|
||||
rowsPresenter.DraggedRowIndex = _dragRowIndex;
|
||||
rowsPresenter.InvalidateArrange();
|
||||
}
|
||||
}
|
||||
|
||||
private DataGridRow? GetReorderingTargetRow(Point mousePositionPresenter, bool scroll, out double scrollAmount)
|
||||
private int? GetReorderingTargetRow(Point mousePositionPresenter, bool scroll, out double scrollAmount)
|
||||
{
|
||||
scrollAmount = 0;
|
||||
Debug.Assert(OwningGrid != null);
|
||||
@@ -163,8 +154,8 @@ internal class DataGridRowReorderHandle : TemplatedControl
|
||||
|
||||
var rowsPresenter = OwningGrid.RowsPresenter;
|
||||
|
||||
double topEdge = rowsPresenter.Bounds.Top;
|
||||
double bottomEdge = rowsPresenter.Bounds.Bottom;
|
||||
double topEdge = 0;
|
||||
double bottomEdge = rowsPresenter.DesiredSize.Height;
|
||||
|
||||
if (mousePositionPresenter.Y < topEdge)
|
||||
{
|
||||
@@ -187,23 +178,26 @@ internal class DataGridRowReorderHandle : TemplatedControl
|
||||
OwningGrid.VerticalScrollBar.IsVisible &&
|
||||
OwningGrid.VerticalScrollBar.Value < OwningGrid.VerticalScrollBar.Maximum)
|
||||
{
|
||||
double newVal = mousePositionPresenter.X - bottomEdge;
|
||||
double newVal = mousePositionPresenter.Y - bottomEdge;
|
||||
scrollAmount = Math.Min(newVal,
|
||||
OwningGrid.VerticalScrollBar.Maximum - OwningGrid.VerticalScrollBar.Value);
|
||||
OwningGrid.ScrollSlotsByHeight(scrollAmount);
|
||||
if (scrollAmount > 0)
|
||||
{
|
||||
OwningGrid.ScrollSlotsByHeight(scrollAmount);
|
||||
}
|
||||
}
|
||||
|
||||
mousePositionPresenter = mousePositionPresenter.WithY(bottomEdge - 1);
|
||||
}
|
||||
|
||||
foreach (Control child in rowsPresenter.Children)
|
||||
foreach (Control child in OwningGrid.DisplayData.GetScrollingElements())
|
||||
{
|
||||
if (child is DataGridRow row)
|
||||
{
|
||||
Point mousePosition = OwningGrid.RowsPresenter.Translate(row, mousePositionPresenter);
|
||||
if (mousePosition.Y >= 0 && mousePosition.Y <= row.Bounds.Height)
|
||||
{
|
||||
return row;
|
||||
return row.Index;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -225,13 +219,13 @@ internal class DataGridRowReorderHandle : TemplatedControl
|
||||
}
|
||||
|
||||
// The user didn't cancel, so prepare for the reorder
|
||||
_dragRow = OwningRow;
|
||||
_dragStart = mousePosition;
|
||||
_dragRow.Opacity = 0.0;
|
||||
_dragRowIndex = OwningRow.Index;
|
||||
_dragRowBounds = OwningRow.Bounds;
|
||||
_dragStart = mousePosition;
|
||||
|
||||
Debug.Assert(OwningGrid.RowsPresenter != null);
|
||||
// Display the reordering thumb
|
||||
OwningGrid.RowsPresenter.DraggedRow = OwningRow;
|
||||
OwningGrid.RowsPresenter.DraggedRowIndex = _dragRowIndex;
|
||||
OwningGrid.RowsPresenter.NotifyAboutToDragging();
|
||||
}
|
||||
|
||||
|
||||
@@ -3863,7 +3863,7 @@ public partial class DataGrid
|
||||
EditingRow = null;
|
||||
}
|
||||
|
||||
private void ResetFocusedRow()
|
||||
internal void ResetFocusedRow()
|
||||
{
|
||||
if (_focusedRow != null
|
||||
&& _focusedRow != EditingRow
|
||||
|
||||
@@ -161,16 +161,24 @@ internal class DataGridDisplayData
|
||||
{
|
||||
DataGridRow row = _recyclableRows.Pop();
|
||||
row.IsVisible = false;
|
||||
Debug.Assert(!_fullyRecycledRows.Contains(row));
|
||||
_fullyRecycledRows.Push(row);
|
||||
// TODO 拖动的时候滚动翻页了会触发这个,暂时改成动态判断
|
||||
// Debug.Assert(!_fullyRecycledRows.Contains(row));
|
||||
if (!_fullyRecycledRows.Contains(row))
|
||||
{
|
||||
_fullyRecycledRows.Push(row);
|
||||
}
|
||||
}
|
||||
// Fully recycle Recyclable GroupHeaders and transfer them to Recycled GroupHeaders
|
||||
while (_recyclableGroupHeaders.Count > 0)
|
||||
{
|
||||
DataGridRowGroupHeader groupHeader = _recyclableGroupHeaders.Pop();
|
||||
groupHeader.IsVisible = false;
|
||||
Debug.Assert(!_fullyRecycledGroupHeaders.Contains(groupHeader));
|
||||
_fullyRecycledGroupHeaders.Push(groupHeader);
|
||||
// TODO 拖动的时候滚动翻页了会触发这个,暂时改成动态判断
|
||||
// Debug.Assert(!_fullyRecycledGroupHeaders.Contains(groupHeader));
|
||||
if (!_fullyRecycledGroupHeaders.Contains(groupHeader))
|
||||
{
|
||||
_fullyRecycledGroupHeaders.Push(groupHeader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -122,14 +122,6 @@ internal static class DataGridError
|
||||
|
||||
}
|
||||
|
||||
public static class DataGridColumnHeader
|
||||
{
|
||||
public static NotSupportedException ContentDoesNotSupportUIElements()
|
||||
{
|
||||
return new NotSupportedException("Content does not support Controls; use ContentTemplate instead.");
|
||||
}
|
||||
}
|
||||
|
||||
public static class DataGridColumn
|
||||
{
|
||||
public static NotSupportedException SelectionModeException()
|
||||
@@ -190,6 +182,11 @@ internal static class DataGridError
|
||||
{
|
||||
return new InvalidOperationException("Only one DataGridRowReorderColumn is allowed.");
|
||||
}
|
||||
|
||||
public static InvalidOperationException InvalidRowReorderPreConditionException()
|
||||
{
|
||||
return new InvalidOperationException("Cannot start sorting or filtering while sorting is in progress.");
|
||||
}
|
||||
}
|
||||
|
||||
public static class DataGridSelectedItemsCollection
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// All other rights reserved.
|
||||
|
||||
using System.Diagnostics;
|
||||
using AtomUI.Controls.Data;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
@@ -21,9 +22,9 @@ namespace AtomUI.Controls;
|
||||
public sealed class DataGridRowsPresenter : Panel, IChildIndexProvider
|
||||
{
|
||||
#region 内部属性定义
|
||||
|
||||
internal DataGridRow? DraggedRow { get; set; }
|
||||
|
||||
|
||||
internal int? DraggedRowIndex { get; set; }
|
||||
|
||||
internal Double DragRowOffset { get; set; }
|
||||
|
||||
internal DataGrid? OwningGrid { get; set; }
|
||||
@@ -31,7 +32,6 @@ public sealed class DataGridRowsPresenter : Panel, IChildIndexProvider
|
||||
|
||||
#endregion
|
||||
|
||||
private List<Control>? _orderingChildren;
|
||||
private DataGridRow? _dragIndicator;
|
||||
|
||||
public DataGridRowsPresenter()
|
||||
@@ -89,7 +89,16 @@ public sealed class DataGridRowsPresenter : Panel, IChildIndexProvider
|
||||
|
||||
double rowDesiredWidth = OwningGrid.RowHeadersDesiredWidth + OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth + OwningGrid.ColumnsInternal.FillerColumn.FillerWidth;
|
||||
double topEdge = -OwningGrid.NegVerticalOffset;
|
||||
foreach (Control element in GetScrollingElements())
|
||||
|
||||
var visibleRows = new List<Control>();
|
||||
visibleRows.AddRange(OwningGrid.DisplayData.GetScrollingElements());
|
||||
|
||||
if (_dragIndicator != null)
|
||||
{
|
||||
visibleRows.Add(_dragIndicator);
|
||||
}
|
||||
|
||||
foreach (Control element in visibleRows)
|
||||
{
|
||||
if (element is DataGridRow row)
|
||||
{
|
||||
@@ -98,15 +107,14 @@ public sealed class DataGridRowsPresenter : Panel, IChildIndexProvider
|
||||
// Visibility for all filler cells needs to be set in one place. Setting it individually in
|
||||
// each CellsPresenter causes an NxN layout cycle (see DevDiv Bugs 211557)
|
||||
row.EnsureFillerVisibility();
|
||||
if (_dragIndicator != row)
|
||||
{
|
||||
row.Arrange(new Rect(0, topEdge, rowDesiredWidth, element.DesiredSize.Height));
|
||||
}
|
||||
else
|
||||
if (_dragIndicator == row)
|
||||
{
|
||||
row.Arrange(new Rect(0, DragRowOffset, rowDesiredWidth, element.DesiredSize.Height));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
row.Arrange(new Rect(0, topEdge, rowDesiredWidth, element.DesiredSize.Height));
|
||||
}
|
||||
}
|
||||
else if (element is DataGridRowGroupHeader groupHeader)
|
||||
{
|
||||
@@ -172,7 +180,14 @@ public sealed class DataGridRowsPresenter : Panel, IChildIndexProvider
|
||||
double totalCellsWidth = OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth;
|
||||
|
||||
double headerWidth = 0;
|
||||
foreach (Control element in GetScrollingElements())
|
||||
|
||||
var visibleRows = OwningGrid.DisplayData.GetScrollingElements().ToList();
|
||||
if (_dragIndicator != null)
|
||||
{
|
||||
visibleRows.Add(_dragIndicator);
|
||||
}
|
||||
|
||||
foreach (Control element in visibleRows)
|
||||
{
|
||||
DataGridRow? row = element as DataGridRow;
|
||||
if (row != null)
|
||||
@@ -213,37 +228,25 @@ public sealed class DataGridRowsPresenter : Panel, IChildIndexProvider
|
||||
{
|
||||
e.Handled = e.Handled || (OwningGrid?.UpdateScroll(-e.Delta) ?? false);
|
||||
}
|
||||
|
||||
IEnumerable<Control> GetScrollingElements()
|
||||
{
|
||||
Debug.Assert(OwningGrid != null);
|
||||
if (DraggedRow != null)
|
||||
{
|
||||
return _orderingChildren!;
|
||||
}
|
||||
|
||||
return OwningGrid.DisplayData.GetScrollingElements();
|
||||
}
|
||||
|
||||
internal void NotifyAboutToDragging()
|
||||
{
|
||||
Debug.Assert(OwningGrid != null);
|
||||
Debug.Assert(DraggedRow != null);
|
||||
_orderingChildren = new List<Control>();
|
||||
_orderingChildren.AddRange(OwningGrid.DisplayData.GetScrollingElements());
|
||||
_dragIndicator = OwningGrid.GetGeneratedGhostRow(DraggedRow.DataContext);
|
||||
_dragIndicator.Index = DraggedRow.Index;
|
||||
Debug.Assert(DraggedRowIndex.HasValue);
|
||||
object? data = null;
|
||||
if (OwningGrid.CollectionView is DataGridCollectionView collectionView)
|
||||
{
|
||||
data = collectionView.GetItemAt(DraggedRowIndex.Value);
|
||||
}
|
||||
_dragIndicator = OwningGrid.GetGeneratedGhostRow(data);
|
||||
_dragIndicator.Index = DraggedRowIndex.Value;
|
||||
_dragIndicator.IsDragging = true;
|
||||
_dragIndicator.ZIndex = 1000;
|
||||
_orderingChildren.Add(_dragIndicator);
|
||||
LogicalChildren.Add(_dragIndicator);
|
||||
VisualChildren.Add(_dragIndicator);
|
||||
}
|
||||
|
||||
internal void NotifyDropped()
|
||||
{
|
||||
_orderingChildren?.Clear();
|
||||
_orderingChildren = null;
|
||||
if (_dragIndicator != null)
|
||||
{
|
||||
LogicalChildren.Remove(_dragIndicator);
|
||||
@@ -253,25 +256,6 @@ public sealed class DataGridRowsPresenter : Panel, IChildIndexProvider
|
||||
_dragIndicator = null;
|
||||
}
|
||||
|
||||
public void SwapOrderingChildren(Control item1, Control item2)
|
||||
{
|
||||
if (_orderingChildren != null)
|
||||
{
|
||||
if (!_orderingChildren.Contains(item1))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(item1));
|
||||
}
|
||||
|
||||
if (!_orderingChildren.Contains(item2))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(item2));
|
||||
}
|
||||
var index1 = _orderingChildren.IndexOf(item1);
|
||||
var index2 = _orderingChildren.IndexOf(item2);
|
||||
(_orderingChildren[index2], _orderingChildren[index1]) = (_orderingChildren[index1], _orderingChildren[index2]);
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
internal void PrintChildren()
|
||||
{
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
<Setter Property="Background"
|
||||
Value="{DynamicResource {x:Static atom:DataGridTokenKey.TableRowHoverBg}}" />
|
||||
</Style>
|
||||
<Setter Property="ZIndex" Value="1000" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="^[IsDragging=False]">
|
||||
|
||||
Reference in New Issue
Block a user