From da976fb80da5abff33059d1cfd369fcbc83ba894 Mon Sep 17 00:00:00 2001 From: CHINBOY Date: Thu, 29 May 2025 00:12:29 +0800 Subject: [PATCH] Add DataGridRowHeader --- .../Cell/CellEditBinding.cs | 2 +- .../Cell/DataGridCellsPresenter.cs | 2 +- .../Cell/ICellEditBinding.cs | 2 +- .../DataGrid.Privates.cs | 41 +++- src/AtomUI.Controls.DataGrid/DataGrid.cs | 6 +- .../Row/DataGridDetailsPresenter.cs | 130 ++++++----- .../Row/DataGridRow.Privates.cs | 205 +++++------------- .../Row/DataGridRow.cs | 21 +- .../Row/DataGridRowGroupHeader.cs | 20 +- .../Row/DataGridRowHeader.cs | 4 +- .../Row/DataGridRowsPresenter.cs | 5 +- 11 files changed, 202 insertions(+), 236 deletions(-) diff --git a/src/AtomUI.Controls.DataGrid/Cell/CellEditBinding.cs b/src/AtomUI.Controls.DataGrid/Cell/CellEditBinding.cs index 70b8344be..dfe3ee1e8 100644 --- a/src/AtomUI.Controls.DataGrid/Cell/CellEditBinding.cs +++ b/src/AtomUI.Controls.DataGrid/Cell/CellEditBinding.cs @@ -2,7 +2,7 @@ using AtomUI.Controls.Utils; using AtomUI.Reactive; using Avalonia.Data; -namespace AtomUI.Controls.Cell; +namespace AtomUI.Controls; internal class CellEditBinding : ICellEditBinding { diff --git a/src/AtomUI.Controls.DataGrid/Cell/DataGridCellsPresenter.cs b/src/AtomUI.Controls.DataGrid/Cell/DataGridCellsPresenter.cs index e831794a3..725c40c3b 100644 --- a/src/AtomUI.Controls.DataGrid/Cell/DataGridCellsPresenter.cs +++ b/src/AtomUI.Controls.DataGrid/Cell/DataGridCellsPresenter.cs @@ -11,7 +11,7 @@ using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Utilities; -namespace AtomUI.Controls.Cell; +namespace AtomUI.Controls; public sealed class DataGridCellsPresenter : Panel, IChildIndexProvider { diff --git a/src/AtomUI.Controls.DataGrid/Cell/ICellEditBinding.cs b/src/AtomUI.Controls.DataGrid/Cell/ICellEditBinding.cs index ebb91acbc..9526d7701 100644 --- a/src/AtomUI.Controls.DataGrid/Cell/ICellEditBinding.cs +++ b/src/AtomUI.Controls.DataGrid/Cell/ICellEditBinding.cs @@ -1,4 +1,4 @@ -namespace AtomUI.Controls.Cell; +namespace AtomUI.Controls; public interface ICellEditBinding { diff --git a/src/AtomUI.Controls.DataGrid/DataGrid.Privates.cs b/src/AtomUI.Controls.DataGrid/DataGrid.Privates.cs index 31bd4c6a9..50730dad7 100644 --- a/src/AtomUI.Controls.DataGrid/DataGrid.Privates.cs +++ b/src/AtomUI.Controls.DataGrid/DataGrid.Privates.cs @@ -132,6 +132,45 @@ public partial class DataGrid } } + internal int? MouseOverRowIndex + { + get => _mouseOverRowIndex; + set + { + if (_mouseOverRowIndex != value) + { + DataGridRow? oldMouseOverRow = null; + if (_mouseOverRowIndex.HasValue) + { + int oldSlot = SlotFromRowIndex(_mouseOverRowIndex.Value); + if (IsSlotVisible(oldSlot)) + { + oldMouseOverRow = DisplayData.GetDisplayedElement(oldSlot) as DataGridRow; + } + } + + _mouseOverRowIndex = value; + + // State for the old row needs to be applied after setting the new value + if (oldMouseOverRow != null) + { + oldMouseOverRow.ApplyState(); + } + + if (_mouseOverRowIndex.HasValue) + { + int newSlot = SlotFromRowIndex(_mouseOverRowIndex.Value); + if (IsSlotVisible(newSlot)) + { + DataGridRow? newMouseOverRow = DisplayData.GetDisplayedElement(newSlot) as DataGridRow; + Debug.Assert(newMouseOverRow != null); + newMouseOverRow.ApplyState(); + } + } + } + } + } + internal ScrollBar? VerticalScrollBar => _vScrollBar; #endregion @@ -146,7 +185,7 @@ public partial class DataGrid /// private const int DefaultColumnDisplayOrder = 10000; - private const double HorizontalGridLinesThickness = 1; + internal const double HorizontalGridLinesThickness = 1; private const double MinimumRowHeaderWidth = 4; private const double MinimumColumnHeaderHeight = 4; internal const double MaximumStarColumnWidth = 10000; diff --git a/src/AtomUI.Controls.DataGrid/DataGrid.cs b/src/AtomUI.Controls.DataGrid/DataGrid.cs index 36f266f1d..0d0451b58 100644 --- a/src/AtomUI.Controls.DataGrid/DataGrid.cs +++ b/src/AtomUI.Controls.DataGrid/DataGrid.cs @@ -7,7 +7,6 @@ using System.Collections; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; -using AtomUI.Controls.Cell; using AtomUI.Controls.Data; using AtomUI.Controls.Utils; using AtomUI.Utils; @@ -1945,6 +1944,7 @@ public partial class DataGrid : TemplatedControl _noCurrentCellChangeCount++; try { + Debug.Assert(ColumnsInternal.RowGroupSpacerColumn != null); if (ColumnsInternal.RowGroupSpacerColumn.IsRepresented && columnIndex == ColumnsInternal.RowGroupSpacerColumn.Index) { @@ -1993,7 +1993,7 @@ public partial class DataGrid : TemplatedControl // The DataGrid's CurrentItem is already up-to-date, so we don't need to do anything return; } - + Debug.Assert(ColumnsInternal.RowGroupSpacerColumn != null); int columnIndex = CurrentColumnIndex; if (columnIndex == -1) { @@ -2124,7 +2124,7 @@ public partial class DataGrid : TemplatedControl } } - internal void OnRowDetailsChanged() + internal void NotifyRowDetailsChanged() { if (!_scrollingByHeight) { diff --git a/src/AtomUI.Controls.DataGrid/Row/DataGridDetailsPresenter.cs b/src/AtomUI.Controls.DataGrid/Row/DataGridDetailsPresenter.cs index 8dc84a5cd..1ad585355 100644 --- a/src/AtomUI.Controls.DataGrid/Row/DataGridDetailsPresenter.cs +++ b/src/AtomUI.Controls.DataGrid/Row/DataGridDetailsPresenter.cs @@ -1,5 +1,5 @@ +using System.Diagnostics; using Avalonia; -using Avalonia.Automation.Peers; using Avalonia.Controls; using Avalonia.Media; @@ -22,12 +22,9 @@ public sealed class DataGridDetailsPresenter : Panel set => SetValue(ContentHeightProperty, value); } - internal DataGridRow? OwningRow - { - get; - set; - } - // private DataGrid? OwningGrid => OwningRow?.OwningGrid; + internal DataGridRow? OwningRow { get; set; } + + private DataGrid? OwningGrid => OwningRow?.OwningGrid; public DataGridDetailsPresenter() { @@ -50,46 +47,47 @@ public sealed class DataGridDetailsPresenter : Panel /// protected override Size ArrangeOverride(Size finalSize) { - // if (OwningGrid == null) - // { - // return base.ArrangeOverride(finalSize); - // } - // double rowGroupSpacerWidth = OwningGrid.ColumnsInternal.RowGroupSpacerColumn.Width.Value; - // double leftEdge = rowGroupSpacerWidth; - // double xClip = OwningGrid.AreRowGroupHeadersFrozen ? rowGroupSpacerWidth : 0; - // double width; - // if (OwningGrid.AreRowDetailsFrozen) - // { - // leftEdge += OwningGrid.HorizontalOffset; - // width = OwningGrid.CellsWidth; - // } - // else - // { - // xClip += OwningGrid.HorizontalOffset; - // width = Math.Max(OwningGrid.CellsWidth, OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth); - // } - // // Details should not extend through the indented area - // width -= rowGroupSpacerWidth; - // double height = Math.Max(0, double.IsNaN(ContentHeight) ? 0 : ContentHeight); - // - // foreach (Control child in Children) - // { - // child.Arrange(new Rect(leftEdge, 0, width, height)); - // } - // - // if (OwningGrid.AreRowDetailsFrozen) - // { - // // Frozen Details should not be clipped, similar to frozen cells - // Clip = null; - // } - // else - // { - // // Clip so Details doesn't obstruct elements to the left (the RowHeader by default) as we scroll to the right - // Clip = new RectangleGeometry - // { - // Rect = new Rect(xClip, 0, Math.Max(0, width - xClip + rowGroupSpacerWidth), height) - // }; - // } + if (OwningGrid == null) + { + return base.ArrangeOverride(finalSize); + } + Debug.Assert(OwningGrid.ColumnsInternal.RowGroupSpacerColumn != null); + double rowGroupSpacerWidth = OwningGrid.ColumnsInternal.RowGroupSpacerColumn.Width.Value; + double leftEdge = rowGroupSpacerWidth; + double xClip = OwningGrid.AreRowGroupHeadersFrozen ? rowGroupSpacerWidth : 0; + double width; + if (OwningGrid.AreRowDetailsFrozen) + { + leftEdge += OwningGrid.HorizontalOffset; + width = OwningGrid.CellsWidth; + } + else + { + xClip += OwningGrid.HorizontalOffset; + width = Math.Max(OwningGrid.CellsWidth, OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth); + } + // Details should not extend through the indented area + width -= rowGroupSpacerWidth; + double height = Math.Max(0, double.IsNaN(ContentHeight) ? 0 : ContentHeight); + + foreach (Control child in Children) + { + child.Arrange(new Rect(leftEdge, 0, width, height)); + } + + if (OwningGrid.AreRowDetailsFrozen) + { + // Frozen Details should not be clipped, similar to frozen cells + Clip = null; + } + else + { + // Clip so Details doesn't obstruct elements to the left (the RowHeader by default) as we scroll to the right + Clip = new RectangleGeometry + { + Rect = new Rect(xClip, 0, Math.Max(0, width - xClip + rowGroupSpacerWidth), height) + }; + } return finalSize; } @@ -106,25 +104,25 @@ public sealed class DataGridDetailsPresenter : Panel /// protected override Size MeasureOverride(Size availableSize) { - return default; - // if (OwningGrid == null || Children.Count == 0) - // { - // return default; - // } + if (OwningGrid == null || Children.Count == 0) + { + return default; + } + Debug.Assert(OwningGrid.ColumnsInternal.RowGroupSpacerColumn != null); - // double desiredWidth = OwningGrid.AreRowDetailsFrozen ? - // OwningGrid.CellsWidth : - // Math.Max(OwningGrid.CellsWidth, OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth); - // - // desiredWidth -= OwningGrid.ColumnsInternal.RowGroupSpacerColumn.Width.Value; - // - // foreach (Control child in Children) - // { - // child.Measure(new Size(desiredWidth, double.PositiveInfinity)); - // } - // - // double desiredHeight = Math.Max(0, double.IsNaN(ContentHeight) ? 0 : ContentHeight); - // - // return new Size(desiredWidth, desiredHeight); + double desiredWidth = OwningGrid.AreRowDetailsFrozen ? + OwningGrid.CellsWidth : + Math.Max(OwningGrid.CellsWidth, OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth); + + desiredWidth -= OwningGrid.ColumnsInternal.RowGroupSpacerColumn.Width.Value; + + foreach (Control child in Children) + { + child.Measure(new Size(desiredWidth, double.PositiveInfinity)); + } + + double desiredHeight = Math.Max(0, double.IsNaN(ContentHeight) ? 0 : ContentHeight); + + return new Size(desiredWidth, desiredHeight); } } \ No newline at end of file diff --git a/src/AtomUI.Controls.DataGrid/Row/DataGridRow.Privates.cs b/src/AtomUI.Controls.DataGrid/Row/DataGridRow.Privates.cs index 172ab8c01..d4581bcc4 100644 --- a/src/AtomUI.Controls.DataGrid/Row/DataGridRow.Privates.cs +++ b/src/AtomUI.Controls.DataGrid/Row/DataGridRow.Privates.cs @@ -3,15 +3,18 @@ // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. -using System.Diagnostics; -using System.Drawing; -using System.Reactive.Disposables; -using AtomUI.Controls.Cell; -using Avalonia; -using Avalonia.Controls; +using Avalonia.Controls.Shapes; using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.Input; +using Avalonia.Utilities; +using System.Diagnostics; +using System.Reactive.Disposables; +using AtomUI.Utils; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Layout; +using Avalonia.Styling; namespace AtomUI.Controls; @@ -29,7 +32,7 @@ public partial class DataGridRow // Returns the actual template that should be sued for Details: either explicity set on this row // or inherited from the DataGrid - private IDataTemplate ActualDetailsTemplate + private IDataTemplate? ActualDetailsTemplate { get { @@ -55,6 +58,7 @@ public partial class DataGridRow { get { + Debug.Assert(OwningGrid != null); if (_fillerCell == null) { _fillerCell = new DataGridCell @@ -105,11 +109,7 @@ public partial class DataGridRow } } - internal bool IsRecycled - { - get; - private set; - } + internal bool IsRecycled { get; private set; } internal bool IsRecyclable { @@ -125,12 +125,10 @@ public partial class DataGridRow internal int? MouseOverColumnIndex { - get - { - return _mouseOverColumnIndex; - } + get => _mouseOverColumnIndex; set { + Debug.Assert(OwningGrid != null); if (_mouseOverColumnIndex != value) { DataGridCell? oldMouseOverCell = null; @@ -157,11 +155,7 @@ public partial class DataGridRow } } - internal Panel? RootElement - { - get; - private set; - } + internal Panel? RootElement { get; private set; } // Height that the row will eventually end up at after a possible details animation has completed internal double TargetHeight @@ -181,17 +175,6 @@ public partial class DataGridRow return DesiredSize.Height; } } - - // Returns the actual template that should be sued for Details: either explicity set on this row - // or inherited from the DataGrid - private IDataTemplate ActualDetailsTemplate - { - get - { - Debug.Assert(OwningGrid != null); - return DetailsTemplate ?? OwningGrid.RowDetailsTemplate; - } - } private bool ActualDetailsVisibility { @@ -264,7 +247,7 @@ public partial class DataGridRow if (!_areHandlersSuspended && OwningGrid != null) { - IDataTemplate actualDetailsTemplate(IDataTemplate? template) => (template ?? OwningGrid.RowDetailsTemplate); + IDataTemplate? actualDetailsTemplate(IDataTemplate? template) => (template ?? OwningGrid.RowDetailsTemplate); // We don't always want to apply the new Template because they might have set the same one // we inherited from the DataGrid @@ -277,9 +260,10 @@ public partial class DataGridRow internal void ApplyDetailsTemplate(bool initializeDetailsPreferredHeight) { + Debug.Assert(OwningGrid != null); if (_detailsElement != null && AreDetailsVisible) { - IDataTemplate oldDetailsTemplate = _appliedDetailsTemplate; + IDataTemplate? oldDetailsTemplate = _appliedDetailsTemplate; if (ActualDetailsTemplate != null && ActualDetailsTemplate != _appliedDetailsTemplate) { if (_detailsContent != null) @@ -288,7 +272,7 @@ public partial class DataGridRow _detailsContentSizeSubscription = null; if (_detailsLoaded) { - OwningGrid.OnUnloadingRowDetails(this, _detailsContent); + OwningGrid.NotifyUnloadingRowDetails(this, _detailsContent); _detailsLoaded = false; } } @@ -299,14 +283,14 @@ public partial class DataGridRow if (_detailsContent != null) { - if (_detailsContent is Layout.Layoutable layoutableContent) + if (_detailsContent is Layoutable layoutableContent) { layoutableContent.LayoutUpdated += HandleLayoutUpdated; _detailsContentSizeSubscription = new CompositeDisposable(2) { Disposable.Create(() => layoutableContent.LayoutUpdated -= HandleLayoutUpdated), - _detailsContent.GetObservable(MarginProperty).Subscribe(HandleMarginChanged) + _detailsContent.GetObservable(MarginProperty).Subscribe(NotifyMarginChanged) }; @@ -315,7 +299,7 @@ public partial class DataGridRow { _detailsContentSizeSubscription = _detailsContent.GetObservable(MarginProperty) - .Subscribe(HandleMarginChanged); + .Subscribe(NotifyMarginChanged); } @@ -327,7 +311,7 @@ public partial class DataGridRow { _detailsLoaded = true; _detailsContent.DataContext = DataContext; - OwningGrid.OnLoadingRowDetails(this, _detailsContent); + OwningGrid.NotifyLoadingRowDetails(this, _detailsContent); } if (initializeDetailsPreferredHeight && double.IsNaN(_detailsDesiredHeight) && _appliedDetailsTemplate != null && _detailsElement.Children.Count > 0) @@ -356,8 +340,8 @@ public partial class DataGridRow throw DataGridError.DataGridRow.InvalidRowIndexCannotCompleteOperation(); } - var newValue = (bool)e.NewValue; - OwningGrid.OnRowDetailsVisibilityPropertyChanged(Index, newValue); + var newValue = (bool)(e.NewValue ?? false); + OwningGrid.NotifyRowDetailsVisibilityPropertyChanged(Index, newValue); SetDetailsVisibilityInternal(newValue, raiseNotification: true, animate: true); } } @@ -385,6 +369,7 @@ public partial class DataGridRow internal void ApplyHeaderStatus() { + Debug.Assert(OwningGrid != null); if (_headerElement != null && OwningGrid.AreRowHeadersVisible) { _headerElement.UpdatePseudoClasses(); @@ -467,7 +452,7 @@ public partial class DataGridRow } } - internal void EnsureHeaderStyleAndVisibility(Styling.Style previousStyle) + internal void EnsureHeaderStyleAndVisibility(Style? previousStyle) { if (_headerElement != null && OwningGrid != null) { @@ -505,43 +490,16 @@ public partial class DataGridRow _bottomGridLine = null; } - private void HandleCellAdded(object sender, DataGridCellEventArgs e) - { - _cellsElement?.Children.Add(e.Cell); - } - - private void HandleCellRemoved(object sender, DataGridCellEventArgs e) - { - _cellsElement?.Children.Remove(e.Cell); - } - - private void HandlePointerPressed(PointerPressedEventArgs e) - { - if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) - { - return; - } - - if (OwningGrid != null) - { - OwningGrid.IsDoubleClickRecordsClickOnCall(this); - } - } - - private void HandleRowDetailsChanged() - { - OwningGrid?.OnRowDetailsChanged(); - } - private void UnloadDetailsTemplate(bool recycle) { + Debug.Assert(OwningGrid != null); if (_detailsElement != null) { if (_detailsContent != null) { if (_detailsLoaded) { - OwningGrid.OnUnloadingRowDetails(this, _detailsContent); + OwningGrid.NotifyUnloadingRowDetails(this, _detailsContent); } _detailsContent.DataContext = null; if (!recycle) @@ -612,17 +570,18 @@ public partial class DataGridRow double? _previousDetailsHeight = null; //TODO Animation - private void HandleHeightChanged(double newValue) + private void NotifyHeightChanged(double newValue) { if (_previousDetailsHeight.HasValue) { var oldValue = _previousDetailsHeight.Value; _previousDetailsHeight = newValue; - if (newValue != oldValue && newValue != _detailsDesiredHeight) + if (!MathUtils.AreClose(newValue, oldValue) && !MathUtils.AreClose(newValue, _detailsDesiredHeight)) { if (AreDetailsVisible && _appliedDetailsTemplate != null) { + Debug.Assert(_detailsElement != null); // Update the new desired height for RowDetails _detailsDesiredHeight = newValue; @@ -630,7 +589,7 @@ public partial class DataGridRow // Calling this when details are not visible invalidates during layout when we have no work // to do. In certain scenarios, this could cause a layout cycle - OnRowDetailsChanged(); + NotifyRowDetailsChanged(); } } } @@ -639,26 +598,34 @@ public partial class DataGridRow _previousDetailsHeight = newValue; } } - - private void HandleSizeChanged(Rect newValue) + + private void NotifyRowDetailsChanged() { - HandleHeightChanged(newValue.Height); + OwningGrid?.NotifyRowDetailsChanged(); } - private void HandleMarginChanged(Thickness newValue) + private void NotifySizeChanged(Rect newValue) + { + NotifyHeightChanged(newValue.Height); + } + + private void NotifyMarginChanged(Thickness newValue) { if (_detailsContent != null) - HandleSizeChanged(_detailsContent.Bounds.Inflate(newValue)); + { + NotifySizeChanged(_detailsContent.Bounds.Inflate(newValue)); + } + } - private void HandleLayoutUpdated(object sender, EventArgs e) + private void HandleLayoutUpdated(object? sender, EventArgs e) { if (_detailsContent != null) { var margin = _detailsContent.Margin; var height = _detailsContent.DesiredSize.Height + margin.Top + margin.Bottom; - HandleHeightChanged(height); + NotifyHeightChanged(height); } } @@ -707,80 +674,26 @@ public partial class DataGridRow _detailsElement.ContentHeight = 0; } - OnRowDetailsChanged(); + NotifyRowDetailsChanged(); if (raiseNotification) { + Debug.Assert(_detailsContent != null); OwningGrid.OnRowDetailsVisibilityChanged(new DataGridRowDetailsEventArgs(this, _detailsContent)); } } } - - internal void ApplyDetailsTemplate(bool initializeDetailsPreferredHeight) + + private void SetValueNoCallback(AvaloniaProperty property, T value, BindingPriority priority = BindingPriority.LocalValue) { - if (_detailsElement != null && AreDetailsVisible) + _areHandlersSuspended = true; + try { - IDataTemplate oldDetailsTemplate = _appliedDetailsTemplate; - if (ActualDetailsTemplate != null && ActualDetailsTemplate != _appliedDetailsTemplate) - { - if (_detailsContent != null) - { - _detailsContentSizeSubscription?.Dispose(); - _detailsContentSizeSubscription = null; - if (_detailsLoaded) - { - OwningGrid.OnUnloadingRowDetails(this, _detailsContent); - _detailsLoaded = false; - } - } - _detailsElement.Children.Clear(); - - _detailsContent = ActualDetailsTemplate.Build(DataContext); - _appliedDetailsTemplate = ActualDetailsTemplate; - - if (_detailsContent != null) - { - if (_detailsContent is Layout.Layoutable layoutableContent) - { - layoutableContent.LayoutUpdated += HandleLayoutUpdated; - - _detailsContentSizeSubscription = new CompositeDisposable(2) - { - Disposable.Create(() => layoutableContent.LayoutUpdated -= HandleLayoutUpdated), - _detailsContent.GetObservable(MarginProperty).Subscribe(HandleMarginChanged) - }; - - - } - else - { - _detailsContentSizeSubscription = - _detailsContent.GetObservable(MarginProperty) - .Subscribe(HandleMarginChanged); - - } - - _detailsElement.Children.Add(_detailsContent); - } - } - - if (_detailsContent != null && !_detailsLoaded) - { - _detailsLoaded = true; - _detailsContent.DataContext = DataContext; - OwningGrid.OnLoadingRowDetails(this, _detailsContent); - } - if (initializeDetailsPreferredHeight && double.IsNaN(_detailsDesiredHeight) && - _appliedDetailsTemplate != null && _detailsElement.Children.Count > 0) - { - EnsureDetailsDesiredHeight(); - } - else if (oldDetailsTemplate == null) - { - _detailsDesiredHeight = double.NaN; - EnsureDetailsDesiredHeight(); - _detailsElement.ContentHeight = _detailsDesiredHeight; - } + SetValue(property, value, priority); + } + finally + { + _areHandlersSuspended = false; } } } \ No newline at end of file diff --git a/src/AtomUI.Controls.DataGrid/Row/DataGridRow.cs b/src/AtomUI.Controls.DataGrid/Row/DataGridRow.cs index 9a18009c5..21a1dce88 100644 --- a/src/AtomUI.Controls.DataGrid/Row/DataGridRow.cs +++ b/src/AtomUI.Controls.DataGrid/Row/DataGridRow.cs @@ -3,10 +3,13 @@ // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. +using AtomUI.Controls.Utils; +using AtomUI.Utils; using Avalonia; using Avalonia.Automation; using Avalonia.Controls; using Avalonia.Controls.Primitives; +using Avalonia.Controls.Shapes; using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Media; @@ -162,7 +165,7 @@ public partial class DataGridRow : TemplatedControl // If the DataGrid was scrolled horizontally after our last Arrange, we need to make sure // the Cells and Details are Arranged again - if (_lastHorizontalOffset != OwningGrid.HorizontalOffset) + if (!MathUtils.AreClose(_lastHorizontalOffset, OwningGrid.HorizontalOffset)) { _lastHorizontalOffset = OwningGrid.HorizontalOffset; InvalidateHorizontalArrange(); @@ -240,7 +243,7 @@ public partial class DataGridRow : TemplatedControl /// protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { - RootElement = e.NameScope.Find(DATAGRIDROW_elementRoot); + RootElement = e.NameScope.Find(DataGridRowTheme.FramePart); if (RootElement != null) { ApplyState(); @@ -254,7 +257,7 @@ public partial class DataGridRow : TemplatedControl updateVerticalScrollBar = true; } - _cellsElement = e.NameScope.Find(DATAGRIDROW_elementCells); + _cellsElement = e.NameScope.Find(DataGridRowTheme.CellsPresenterPart); if (_cellsElement != null) { _cellsElement.OwningRow = this; @@ -269,7 +272,7 @@ public partial class DataGridRow : TemplatedControl } } - _detailsElement = e.NameScope.Find(DATAGRIDROW_elementDetails); + _detailsElement = e.NameScope.Find(DataGridRowTheme.DetailsPresenterPart); if (_detailsElement != null && OwningGrid != null) { _detailsElement.OwningRow = this; @@ -281,10 +284,10 @@ public partial class DataGridRow : TemplatedControl } } - _bottomGridLine = e.NameScope.Find(DATAGRIDROW_elementBottomGridLine); + _bottomGridLine = e.NameScope.Find(DataGridRowTheme.BottomGridLinePart); EnsureGridLines(); - _headerElement = e.NameScope.Find(DATAGRIDROW_elementRowHeader); + _headerElement = e.NameScope.Find(DataGridRowTheme.RowHeaderPart); if (_headerElement != null) { _headerElement.Owner = this; @@ -326,7 +329,11 @@ public partial class DataGridRow : TemplatedControl { if (columns[ci] is DataGridTemplateColumn column) { - column.RefreshCellContent((Control)this.Cells[column.Index].Content, nameof(DataGridTemplateColumn.CellTemplate)); + var content = (Control?)Cells[column.Index].Content; + if (content != null) + { + column.RefreshCellContent(content, nameof(DataGridTemplateColumn.CellTemplate)); + } } } } diff --git a/src/AtomUI.Controls.DataGrid/Row/DataGridRowGroupHeader.cs b/src/AtomUI.Controls.DataGrid/Row/DataGridRowGroupHeader.cs index 717aa7bd2..f56d96281 100644 --- a/src/AtomUI.Controls.DataGrid/Row/DataGridRowGroupHeader.cs +++ b/src/AtomUI.Controls.DataGrid/Row/DataGridRowGroupHeader.cs @@ -28,8 +28,8 @@ public class DataGridRowGroupHeader : TemplatedControl public static readonly StyledProperty ItemCountFormatProperty = AvaloniaProperty.Register(nameof(ItemCountFormat)); - public static readonly StyledProperty PropertyNameProperty = - AvaloniaProperty.Register(nameof(PropertyName)); + public static readonly StyledProperty PropertyNameProperty = + AvaloniaProperty.Register(nameof(PropertyName)); public static readonly StyledProperty IsPropertyNameVisibleProperty = AvaloniaProperty.Register(nameof(IsPropertyNameVisible)); @@ -61,7 +61,7 @@ public class DataGridRowGroupHeader : TemplatedControl /// /// Gets or sets the name of the property that this row is bound to. /// - public string PropertyName + public string? PropertyName { get => GetValue(PropertyNameProperty); set => SetValue(PropertyNameProperty, value); @@ -97,6 +97,7 @@ public class DataGridRowGroupHeader : TemplatedControl get { Debug.Assert(OwningGrid != null); + Debug.Assert(RowGroupInfo != null); return (RowGroupInfo.Slot == OwningGrid.CurrentSlot); } } @@ -151,7 +152,7 @@ public class DataGridRowGroupHeader : TemplatedControl { if (OwningGrid != null) { - OwningGrid.OnSublevelIndentUpdated(this, (double)e.NewValue); + OwningGrid.OnSublevelIndentUpdated(this, (double)(e.NewValue ?? 0)); } } @@ -198,6 +199,7 @@ public class DataGridRowGroupHeader : TemplatedControl internal void ApplyHeaderStatus() { + Debug.Assert(OwningGrid != null); if (_headerElement != null && OwningGrid.AreRowHeadersVisible) { _headerElement.UpdatePseudoClasses(); @@ -278,10 +280,12 @@ public class DataGridRowGroupHeader : TemplatedControl { return; } + Debug.Assert(RowGroupInfo != null); if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) { if (OwningGrid.IsDoubleClickRecordsClickOnCall(this) && !e.Handled) { + ToggleExpandCollapse(!RowGroupInfo.IsVisible, true); e.Handled = true; } @@ -397,6 +401,8 @@ public class DataGridRowGroupHeader : TemplatedControl internal void ToggleExpandCollapse(bool isVisible, bool setCurrent) { + Debug.Assert(RowGroupInfo != null); + Debug.Assert(RowGroupInfo.CollectionViewGroup != null); if (RowGroupInfo.CollectionViewGroup.ItemCount != 0) { if (OwningGrid == null) @@ -426,7 +432,7 @@ public class DataGridRowGroupHeader : TemplatedControl } else { - txt = string.Format("{0}:", PropertyName); + txt = $"{PropertyName}:"; } _propertyNameElement.Text = txt; } @@ -435,11 +441,11 @@ public class DataGridRowGroupHeader : TemplatedControl string formatString; if (RowGroupInfo.CollectionViewGroup.ItemCount == 1) { - formatString = (ItemCountFormat == null ? "({0} Item)" : ItemCountFormat); + formatString = (string.IsNullOrEmpty(ItemCountFormat) ? "({0} Item)" : ItemCountFormat); } else { - formatString = (ItemCountFormat == null ? "({0} Items)" : ItemCountFormat); + formatString = (string.IsNullOrEmpty(ItemCountFormat) ? "({0} Items)" : ItemCountFormat); } _itemCountElement.Text = string.Format(formatString, RowGroupInfo.CollectionViewGroup.ItemCount); } diff --git a/src/AtomUI.Controls.DataGrid/Row/DataGridRowHeader.cs b/src/AtomUI.Controls.DataGrid/Row/DataGridRowHeader.cs index 6b63872e6..d39ea0746 100644 --- a/src/AtomUI.Controls.DataGrid/Row/DataGridRowHeader.cs +++ b/src/AtomUI.Controls.DataGrid/Row/DataGridRowHeader.cs @@ -69,7 +69,7 @@ public class DataGridRowHeader : ContentControl { return OwningRow.Slot; } - if (OwningRowGroupHeader != null) + if (OwningRowGroupHeader != null && OwningRowGroupHeader.RowGroupInfo != null) { return OwningRowGroupHeader.RowGroupInfo.Slot; } @@ -150,7 +150,7 @@ public class DataGridRowHeader : ContentControl PseudoClasses.Set(StdPseudoClass.Current, OwningRow.Slot == OwningGrid.CurrentSlot); } } - else if (OwningRowGroupHeader != null && OwningGrid != null) + else if (OwningRowGroupHeader != null && OwningGrid != null && OwningRowGroupHeader.RowGroupInfo != null) { PseudoClasses.Set(StdPseudoClass.Current, OwningRowGroupHeader.RowGroupInfo.Slot == OwningGrid.CurrentSlot); } diff --git a/src/AtomUI.Controls.DataGrid/Row/DataGridRowsPresenter.cs b/src/AtomUI.Controls.DataGrid/Row/DataGridRowsPresenter.cs index eb236bacf..273e10725 100644 --- a/src/AtomUI.Controls.DataGrid/Row/DataGridRowsPresenter.cs +++ b/src/AtomUI.Controls.DataGrid/Row/DataGridRowsPresenter.cs @@ -72,6 +72,8 @@ public sealed class DataGridRowsPresenter : Panel, IChildIndexProvider /// protected override Size ArrangeOverride(Size finalSize) { + Debug.Assert(OwningGrid is not null); + Debug.Assert(OwningGrid.ColumnsInternal.FillerColumn is not null); if (finalSize.Height == 0 || OwningGrid == null) { return base.ArrangeOverride(finalSize); @@ -79,6 +81,7 @@ public sealed class DataGridRowsPresenter : Panel, IChildIndexProvider if (OwningGrid.RowsPresenterAvailableSize.HasValue) { + // TODO 需要审查,是否可以删除 var availableHeight = OwningGrid.RowsPresenterAvailableSize.Value.Height; } @@ -211,7 +214,7 @@ public sealed class DataGridRowsPresenter : Panel, IChildIndexProvider } else if (element is DataGridRowGroupHeader groupHeader) { - Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Slot: {0} GroupHeader: {1} Visibility: {2}", groupHeader.RowGroupInfo.Slot, groupHeader.RowGroupInfo.CollectionViewGroup.Key, groupHeader.IsVisible)); + Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Slot: {0} GroupHeader: {1} Visibility: {2}", groupHeader.RowGroupInfo?.Slot, groupHeader?.RowGroupInfo?.CollectionViewGroup?.Key, groupHeader?.IsVisible)); } } }