From b577eb734ffe035ee7ee83f9f4c9cd0cf8568fc4 Mon Sep 17 00:00:00 2001 From: CHINBOY Date: Fri, 30 May 2025 11:23:22 +0800 Subject: [PATCH] Add SizeType Support for DataGrid --- .../Cell/DataGridCell.cs | 9 +++ .../Column/DataGridColumn.Privates.cs | 8 +-- .../Column/DataGridColumnHeader.cs | 9 +++ .../DataGrid.Privates.cs | 11 +++- src/AtomUI.Controls.DataGrid/DataGrid.cs | 65 +++++++++++++++---- .../Row/DataGridRow.Privates.cs | 3 +- .../Theme/DataGridCellTheme.cs | 10 +++ .../Theme/DataGridColumnHeaderTheme.cs | 37 +++++++---- .../Theme/DataGridTheme.cs | 14 +++- .../RenderScaleAwareThicknessConfigure.cs | 2 +- .../IControlSharedTokenResourcesHost.cs | 2 +- ...ntrolSharedTokenResourcesHostExtensions.cs | 2 +- 12 files changed, 133 insertions(+), 39 deletions(-) diff --git a/src/AtomUI.Controls.DataGrid/Cell/DataGridCell.cs b/src/AtomUI.Controls.DataGrid/Cell/DataGridCell.cs index 598895db3..f4f8a80e7 100644 --- a/src/AtomUI.Controls.DataGrid/Cell/DataGridCell.cs +++ b/src/AtomUI.Controls.DataGrid/Cell/DataGridCell.cs @@ -34,6 +34,15 @@ public class DataGridCell : ContentControl #endregion #region 内部属性定义 + + internal static readonly StyledProperty SizeTypeProperty = + SizeTypeAwareControlProperty.SizeTypeProperty.AddOwner(); + + internal SizeType SizeType + { + get => GetValue(SizeTypeProperty); + set => SetValue(SizeTypeProperty, value); + } internal DataGridRow? OwningRow { get; set; } diff --git a/src/AtomUI.Controls.DataGrid/Column/DataGridColumn.Privates.cs b/src/AtomUI.Controls.DataGrid/Column/DataGridColumn.Privates.cs index 7ac922ba7..9c49feaea 100644 --- a/src/AtomUI.Controls.DataGrid/Column/DataGridColumn.Privates.cs +++ b/src/AtomUI.Controls.DataGrid/Column/DataGridColumn.Privates.cs @@ -66,10 +66,7 @@ public abstract partial class DataGridColumn { get { - if (_headerCell == null) - { - _headerCell = CreateHeader(); - } + _headerCell ??= CreateHeader();; return _headerCell; } } @@ -239,9 +236,10 @@ public abstract partial class DataGridColumn { OwningColumn = this }; + Debug.Assert(OwningGrid != null); result[!ContentControl.ContentProperty] = this[!HeaderProperty]; result[!ContentControl.ContentTemplateProperty] = this[!HeaderTemplateProperty]; - Debug.Assert(OwningGrid != null); + result[!DataGridColumnHeader.SizeTypeProperty] = OwningGrid[!DataGrid.SizeTypeProperty]; // TODO 需要审查,原则上我们 AtomUI 是不允许整体自定义的 if (OwningGrid.ColumnHeaderTheme is { } columnTheme) { diff --git a/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeader.cs b/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeader.cs index eb97db34e..67226cdff 100644 --- a/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeader.cs +++ b/src/AtomUI.Controls.DataGrid/Column/DataGridColumnHeader.cs @@ -64,6 +64,15 @@ public class DataGridColumnHeader : ContentControl #endregion #region 内部属性定义 + + internal static readonly StyledProperty SizeTypeProperty = + SizeTypeAwareControlProperty.SizeTypeProperty.AddOwner(); + + internal SizeType SizeType + { + get => GetValue(SizeTypeProperty); + set => SetValue(SizeTypeProperty, value); + } internal DataGridColumn? OwningColumn { get; set; } diff --git a/src/AtomUI.Controls.DataGrid/DataGrid.Privates.cs b/src/AtomUI.Controls.DataGrid/DataGrid.Privates.cs index 27ddabf9c..8655c10b2 100644 --- a/src/AtomUI.Controls.DataGrid/DataGrid.Privates.cs +++ b/src/AtomUI.Controls.DataGrid/DataGrid.Privates.cs @@ -8,8 +8,10 @@ using System.Collections.Specialized; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Diagnostics; +using System.Reactive.Disposables; using AtomUI.Controls.Data; using AtomUI.Controls.Utils; +using AtomUI.Theme; using AtomUI.Utils; using Avalonia; using Avalonia.Controls; @@ -161,6 +163,12 @@ public partial class DataGrid internal ScrollBar? VerticalScrollBar => _vScrollBar; internal ScrollBar? HorizontalScrollBar => _hScrollBar; + + Control IMotionAwareControl.PropertyBindTarget => this; + Control IControlSharedTokenResourcesHost.HostControl => this; + string IControlSharedTokenResourcesHost.TokenId => DataGridToken.ID; + CompositeDisposable? IResourceBindingManager.ResourceBindingsDisposable => _resourceBindingsDisposable; + private CompositeDisposable? _resourceBindingsDisposable; #endregion @@ -173,8 +181,7 @@ public partial class DataGrid /// some properties to be ordered at the beginning and some at the end. /// private const int DefaultColumnDisplayOrder = 10000; - - 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 dd5d28792..9314b3aaa 100644 --- a/src/AtomUI.Controls.DataGrid/DataGrid.cs +++ b/src/AtomUI.Controls.DataGrid/DataGrid.cs @@ -6,7 +6,12 @@ using System.Collections; using System.Collections.ObjectModel; using System.Diagnostics; +using System.Reactive.Disposables; using AtomUI.Controls.Data; +using AtomUI.Theme; +using AtomUI.Theme.Data; +using AtomUI.Theme.Styling; +using AtomUI.Theme.Utils; using AtomUI.Utils; using Avalonia; using Avalonia.Controls; @@ -32,10 +37,17 @@ namespace AtomUI.Controls; [TemplatePart(DataGridTheme.TopRightCornerPart, typeof(ContentControl))] [TemplatePart(DataGridTheme.VerticalScrollbarPart, typeof(ScrollBar))] [PseudoClasses(StdPseudoClass.Invalid, DataGridPseudoClass.EmptyRows, DataGridPseudoClass.EmptyColumns)] -public partial class DataGrid : TemplatedControl +public partial class DataGrid : TemplatedControl, + ISizeTypeAware, + IMotionAwareControl, + IControlSharedTokenResourcesHost, + IResourceBindingManager { #region 公共属性定义 + public static readonly StyledProperty SizeTypeProperty = + SizeTypeAwareControlProperty.SizeTypeProperty.AddOwner(); + /// /// Identifies the CanUserReorderColumns dependency property. /// @@ -107,6 +119,9 @@ public partial class DataGrid : TemplatedControl public static readonly StyledProperty HorizontalGridLinesBrushProperty = AvaloniaProperty.Register(nameof(HorizontalGridLinesBrush)); + public static readonly StyledProperty VerticalGridLinesBrushProperty = + AvaloniaProperty.Register(nameof(VerticalGridLinesBrush)); + public static readonly StyledProperty HorizontalScrollBarVisibilityProperty = AvaloniaProperty.Register(nameof(HorizontalScrollBarVisibility)); @@ -159,9 +174,6 @@ public partial class DataGrid : TemplatedControl public static readonly StyledProperty SelectionModeProperty = AvaloniaProperty.Register(nameof(SelectionMode)); - public static readonly StyledProperty VerticalGridLinesBrushProperty = - AvaloniaProperty.Register(nameof(VerticalGridLinesBrush)); - public static readonly StyledProperty VerticalScrollBarVisibilityProperty = AvaloniaProperty.Register(nameof(VerticalScrollBarVisibility)); @@ -209,6 +221,15 @@ public partial class DataGrid : TemplatedControl AvaloniaProperty.RegisterDirect(nameof(CollectionView), o => o.CollectionView); + public static readonly StyledProperty IsMotionEnabledProperty + = WaveSpiritAwareControlProperty.IsMotionEnabledProperty.AddOwner(); + + public SizeType SizeType + { + get => GetValue(SizeTypeProperty); + set => SetValue(SizeTypeProperty, value); + } + /// /// Gets or sets a value that indicates whether the user can change /// the column display order by dragging column headers with the mouse. @@ -327,6 +348,15 @@ public partial class DataGrid : TemplatedControl set => SetValue(HorizontalGridLinesBrushProperty, value); } + /// + /// Gets or sets the that is used to paint grid lines separating columns. + /// + public IBrush? VerticalGridLinesBrush + { + get => GetValue(VerticalGridLinesBrushProperty); + set => SetValue(VerticalGridLinesBrushProperty, value); + } + /// /// Gets or sets a value that indicates how the horizontal scroll bar is displayed. /// @@ -437,15 +467,6 @@ public partial class DataGrid : TemplatedControl get => GetValue(SelectionModeProperty); set => SetValue(SelectionModeProperty, value); } - - /// - /// Gets or sets the that is used to paint grid lines separating columns. - /// - public IBrush? VerticalGridLinesBrush - { - get => GetValue(VerticalGridLinesBrushProperty); - set => SetValue(VerticalGridLinesBrushProperty, value); - } /// /// Gets or sets the template that is used when rendering the column headers. @@ -617,7 +638,13 @@ public partial class DataGrid : TemplatedControl /// /// Gets a list that contains the data items corresponding to the selected rows. /// - public IList SelectedItems => _selectedItems as IList; + public IList SelectedItems => _selectedItems; + + public bool IsMotionEnabled + { + get => GetValue(IsMotionEnabledProperty); + set => SetValue(IsMotionEnabledProperty, value); + } #endregion @@ -814,6 +841,8 @@ public partial class DataGrid : TemplatedControl HorizontalScrollBarVisibilityProperty, VerticalScrollBarVisibilityProperty); + SizeTypeProperty.OverrideDefaultValue(SizeType.Large); + ItemsSourceProperty.Changed.AddClassHandler((x, e) => x.HandleItemsSourcePropertyChanged(e)); CanUserResizeColumnsProperty.Changed.AddClassHandler((x, e) => x.HandleCanUserResizeColumnsChanged(e)); ColumnWidthProperty.Changed.AddClassHandler((x, e) => x.HandleColumnWidthChanged(e)); @@ -841,6 +870,8 @@ public partial class DataGrid : TemplatedControl public DataGrid() { + this.RegisterResources(); + this.BindMotionProperties(); KeyDown += HandleKeyDown; KeyUp += HandleKeyUp; @@ -875,6 +906,7 @@ public partial class DataGrid : TemplatedControl RowGroupHeaderHeightEstimate = DefaultRowHeight; RowGroupSublevelIndents = []; _rowGroupHeightsByLevel = []; + _resourceBindingsDisposable = new CompositeDisposable(); UpdatePseudoClasses(); } @@ -1030,6 +1062,10 @@ public partial class DataGrid : TemplatedControl protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); + this.AddResourceBindingDisposable(TokenResourceBinder.CreateTokenBinding(this, BorderThicknessProperty, + SharedTokenKey.BorderThickness, + BindingPriority.Template, + new RenderScaleAwareThicknessConfigure(this))); if (DataConnection.DataSource != null && !DataConnection.EventsWired) { DataConnection.WireEvents(DataConnection.DataSource); @@ -1045,6 +1081,7 @@ public partial class DataGrid : TemplatedControl { DataConnection.UnWireEvents(DataConnection.DataSource); } + this.DisposeTokenBindings(); } /// diff --git a/src/AtomUI.Controls.DataGrid/Row/DataGridRow.Privates.cs b/src/AtomUI.Controls.DataGrid/Row/DataGridRow.Privates.cs index 6c8741bc6..319dca07e 100644 --- a/src/AtomUI.Controls.DataGrid/Row/DataGridRow.Privates.cs +++ b/src/AtomUI.Controls.DataGrid/Row/DataGridRow.Privates.cs @@ -48,7 +48,8 @@ public partial class DataGridRow if (_bottomGridLine != null && OwningGrid != null && OwningGrid.AreRowBottomGridLinesRequired) { // Unfortunately, _bottomGridLine has no size yet so we can't get its actualheight - return DataGrid.HorizontalGridLinesThickness; + // TODO 需要审查这里不是常量可以吗?是不是会导致高度计算错误 + return OwningGrid.BorderThickness.Left; } return 0; } diff --git a/src/AtomUI.Controls.DataGrid/Theme/DataGridCellTheme.cs b/src/AtomUI.Controls.DataGrid/Theme/DataGridCellTheme.cs index d6375a18b..0feb3140f 100644 --- a/src/AtomUI.Controls.DataGrid/Theme/DataGridCellTheme.cs +++ b/src/AtomUI.Controls.DataGrid/Theme/DataGridCellTheme.cs @@ -5,6 +5,7 @@ using Avalonia.Controls.Presenters; using Avalonia.Controls.Shapes; using Avalonia.Controls.Templates; using Avalonia.Media; +using Avalonia.Styling; using HorizontalAlignment = Avalonia.Layout.HorizontalAlignment; using VerticalAlignment = Avalonia.Layout.VerticalAlignment; @@ -131,4 +132,13 @@ internal class DataGridCellTheme : BaseControlTheme Grid.SetColumn(focusVisualLayout, 0); rootLayout.Children.Add(focusVisualLayout); } + + protected override void BuildStyles() + { + var commonStyle = new Style(selector => selector.Nesting()); + commonStyle.Add(DataGridCell.HorizontalAlignmentProperty, HorizontalAlignment.Stretch); + commonStyle.Add(DataGridCell.VerticalAlignmentProperty, VerticalAlignment.Stretch); + commonStyle.Add(DataGridCell.FocusableProperty, false); + Add(commonStyle); + } } \ No newline at end of file diff --git a/src/AtomUI.Controls.DataGrid/Theme/DataGridColumnHeaderTheme.cs b/src/AtomUI.Controls.DataGrid/Theme/DataGridColumnHeaderTheme.cs index 73cf9579e..3da52ce25 100644 --- a/src/AtomUI.Controls.DataGrid/Theme/DataGridColumnHeaderTheme.cs +++ b/src/AtomUI.Controls.DataGrid/Theme/DataGridColumnHeaderTheme.cs @@ -1,3 +1,4 @@ +using AtomUI.IconPkg; using AtomUI.Theme; using AtomUI.Theme.Styling; using Avalonia.Controls; @@ -6,6 +7,7 @@ using Avalonia.Controls.Shapes; using Avalonia.Controls.Templates; using Avalonia.Layout; using Avalonia.Media; +using Avalonia.Styling; namespace AtomUI.Controls; @@ -39,6 +41,7 @@ internal class DataGridColumnHeaderTheme : BaseControlTheme { Name = HeaderFramePart }; + CreateTemplateParentBinding(headerFrame, Border.BackgroundProperty, DataGridColumnHeader.BackgroundProperty); var headerRootLayout = new Grid { @@ -70,7 +73,6 @@ internal class DataGridColumnHeaderTheme : BaseControlTheme private void BuildContentPresenter(Grid rootLayout) { - var contentPresenterLayout = new Panel(); var gridLayout = new Grid() { ColumnDefinitions = @@ -79,7 +81,8 @@ internal class DataGridColumnHeaderTheme : BaseControlTheme new ColumnDefinition(GridLength.Auto), ] }; - + CreateTemplateParentBinding(gridLayout, Grid.HorizontalAlignmentProperty, DataGridColumnHeader.HorizontalAlignmentProperty); + CreateTemplateParentBinding(gridLayout, Grid.VerticalAlignmentProperty, DataGridColumnHeader.VerticalAlignmentProperty); var contentPresenter = new ContentPresenter() { Name = ContentPresenterPart, @@ -89,27 +92,24 @@ internal class DataGridColumnHeaderTheme : BaseControlTheme Grid.SetColumn(contentPresenter, 0); gridLayout.Children.Add(contentPresenter); - var iconPresenter = new ContentPresenter() + var iconPresenter = new IconPresenter() { Name = SortIconPresenterPart, - IsVisible = false, - HorizontalAlignment = HorizontalAlignment.Center, - VerticalAlignment = VerticalAlignment.Center, + IsVisible = false }; Grid.SetColumn(iconPresenter, 1); gridLayout.Children.Add(iconPresenter); - - contentPresenterLayout.Children.Add(gridLayout); - Grid.SetColumn(rootLayout, 0); - rootLayout.Children.Add(contentPresenterLayout); + + Grid.SetColumn(gridLayout, 0); + rootLayout.Children.Add(gridLayout); } private void BuildFocusVisual(Grid rootLayout) { - var focusVisualLayout = new Grid() + var focusVisualLayout = new Panel() { - IsVisible = false, - Name = FocusVisualPart, + Name = FocusVisualPart, + IsVisible = false, IsHitTestVisible = false, }; @@ -138,4 +138,15 @@ internal class DataGridColumnHeaderTheme : BaseControlTheme Grid.SetColumn(focusVisualLayout, 0); rootLayout.Children.Add(focusVisualLayout); } + + protected override void BuildStyles() + { + var commonStyle = new Style(selector => selector.Nesting()); + commonStyle.Add(DataGridColumnHeader.ForegroundProperty, DataGridTokenKey.TableHeaderTextColor); + commonStyle.Add(DataGridColumnHeader.BackgroundProperty, DataGridTokenKey.TableHeaderBg); + commonStyle.Add(DataGridColumnHeader.FontWeightProperty, SharedTokenKey.FontWeightStrong); + commonStyle.Add(DataGridColumnHeader.HorizontalContentAlignmentProperty, HorizontalAlignment.Left); + commonStyle.Add(DataGridColumnHeader.VerticalContentAlignmentProperty, VerticalAlignment.Center); + Add(commonStyle); + } } \ No newline at end of file diff --git a/src/AtomUI.Controls.DataGrid/Theme/DataGridTheme.cs b/src/AtomUI.Controls.DataGrid/Theme/DataGridTheme.cs index c7b0060ce..c4c3364e3 100644 --- a/src/AtomUI.Controls.DataGrid/Theme/DataGridTheme.cs +++ b/src/AtomUI.Controls.DataGrid/Theme/DataGridTheme.cs @@ -6,6 +6,8 @@ using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Controls.Shapes; using Avalonia.Controls.Templates; +using Avalonia.Data; +using Avalonia.Data.Converters; using Avalonia.Input.GestureRecognizers; using Avalonia.Layout; using Avalonia.Styling; @@ -82,8 +84,11 @@ internal class DataGridTheme : BaseControlTheme { Name = ColumnHeadersAndRowsSeparatorPart, VerticalAlignment = VerticalAlignment.Bottom, - Height = 1 }; + + CreateTemplateParentBinding(columnHeadersAndRowsSeparator, Rectangle.HeightProperty, DataGrid.BorderThicknessProperty, + BindingMode.Default, new FuncValueConverter(thickness => thickness.Left)); + Grid.SetRow(columnHeadersAndRowsSeparator, 0); Grid.SetColumn(columnHeadersAndRowsSeparator, 0); Grid.SetColumnSpan(columnHeadersAndRowsSeparator, 3); @@ -188,6 +193,13 @@ internal class DataGridTheme : BaseControlTheme commonStyle.Add(DataGrid.VerticalScrollBarVisibilityProperty, ScrollBarVisibility.Auto); commonStyle.Add(DataGrid.SelectionModeProperty, DataGridSelectionMode.Extended); commonStyle.Add(DataGrid.FocusAdornerProperty, null); + commonStyle.Add(DataGrid.HorizontalGridLinesBrushProperty, DataGridTokenKey.TableBorderColor); + commonStyle.Add(DataGrid.VerticalGridLinesBrushProperty, DataGridTokenKey.TableBorderColor); + + var columnHeadersAndRowsSeparatorStyle = new Style(selector => selector.Nesting().Template().Name(ColumnHeadersAndRowsSeparatorPart)); + columnHeadersAndRowsSeparatorStyle.Add(Rectangle.FillProperty, DataGridTokenKey.TableBorderColor); + commonStyle.Add(columnHeadersAndRowsSeparatorStyle); + Add(commonStyle); } } \ No newline at end of file diff --git a/src/AtomUI.Theme/Data/RenderScaleAwareThicknessConfigure.cs b/src/AtomUI.Theme/Data/RenderScaleAwareThicknessConfigure.cs index 6af8d0851..9b8d0c7fa 100644 --- a/src/AtomUI.Theme/Data/RenderScaleAwareThicknessConfigure.cs +++ b/src/AtomUI.Theme/Data/RenderScaleAwareThicknessConfigure.cs @@ -6,7 +6,7 @@ using Avalonia.VisualTree; namespace AtomUI.Theme.Data; -internal class RenderScaleAwareThicknessConfigure +public class RenderScaleAwareThicknessConfigure { private readonly WeakReference _control; private readonly Func? _postProcessor; diff --git a/src/AtomUI.Theme/IControlSharedTokenResourcesHost.cs b/src/AtomUI.Theme/IControlSharedTokenResourcesHost.cs index 777bddc19..28a67e33c 100644 --- a/src/AtomUI.Theme/IControlSharedTokenResourcesHost.cs +++ b/src/AtomUI.Theme/IControlSharedTokenResourcesHost.cs @@ -2,7 +2,7 @@ using Avalonia.Controls; namespace AtomUI.Theme; -internal interface IControlSharedTokenResourcesHost +public interface IControlSharedTokenResourcesHost { Control HostControl { get; } string TokenId { get; } diff --git a/src/AtomUI.Theme/Utils/ControlSharedTokenResourcesHostExtensions.cs b/src/AtomUI.Theme/Utils/ControlSharedTokenResourcesHostExtensions.cs index 1fe31b47d..daa1eb707 100644 --- a/src/AtomUI.Theme/Utils/ControlSharedTokenResourcesHostExtensions.cs +++ b/src/AtomUI.Theme/Utils/ControlSharedTokenResourcesHostExtensions.cs @@ -5,7 +5,7 @@ using Avalonia.LogicalTree; namespace AtomUI.Theme.Utils; -internal static class ControlSharedTokenResourcesHostExtensions +public static class ControlSharedTokenResourcesHostExtensions { public static void RegisterResources(this IControlSharedTokenResourcesHost host) {