From df2b03ae4fda1b6f72919777c1f6aa8ec3223818 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 27 Nov 2024 12:05:02 +0100 Subject: [PATCH 1/3] Include header scroller in test template. Requires another workaround for https://github.com/AvaloniaUI/Avalonia/issues/15075 as the header presenter needs a height of 0 in the tests. --- .../Primitives/TreeDataGridPresenterBase.cs | 5 ++--- .../TestTemplates.cs | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridPresenterBase.cs b/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridPresenterBase.cs index 7cb9e7b0..11e88c52 100644 --- a/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridPresenterBase.cs +++ b/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridPresenterBase.cs @@ -639,9 +639,8 @@ private Rect EstimateViewport(Size availableSize) { if (!c.Bounds.Equals(default) && c.TransformToVisual(this) is Matrix transform) { - return new Rect(0, 0, c.Bounds.Width, c.Bounds.Height) - .TransformToAABB(transform) - .Intersect(new(0, 0, double.PositiveInfinity, double.PositiveInfinity)); + var r = new Rect(0, 0, c.Bounds.Width, c.Bounds.Height).TransformToAABB(transform); + return Intersect(r, new(0, 0, double.PositiveInfinity, double.PositiveInfinity)); } c = c?.GetVisualParent(); diff --git a/tests/Avalonia.Controls.TreeDataGrid.Tests/TestTemplates.cs b/tests/Avalonia.Controls.TreeDataGrid.Tests/TestTemplates.cs index ab8d7be7..c01d8cce 100644 --- a/tests/Avalonia.Controls.TreeDataGrid.Tests/TestTemplates.cs +++ b/tests/Avalonia.Controls.TreeDataGrid.Tests/TestTemplates.cs @@ -61,12 +61,20 @@ public static IControlTemplate TreeDataGridTemplate() { Children = { - new TreeDataGridColumnHeadersPresenter + new ScrollViewer { - Name = "PART_ColumnHeadersPresenter", + Name = "PART_HeaderScrollViewer", + Template = ScrollViewerTemplate(), + Height = 0, + HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden, + VerticalScrollBarVisibility = ScrollBarVisibility.Disabled, [DockPanel.DockProperty] = Dock.Top, - [!TreeDataGridColumnHeadersPresenter.ElementFactoryProperty] = x[!TreeDataGrid.ElementFactoryProperty], - [!TreeDataGridColumnHeadersPresenter.ItemsProperty] = x[!TreeDataGrid.ColumnsProperty], + Content = new TreeDataGridColumnHeadersPresenter + { + Name = "PART_ColumnHeadersPresenter", + [!TreeDataGridColumnHeadersPresenter.ElementFactoryProperty] = x[!TreeDataGrid.ElementFactoryProperty], + [!TreeDataGridColumnHeadersPresenter.ItemsProperty] = x[!TreeDataGrid.ColumnsProperty], + }.RegisterInNameScope(ns), }.RegisterInNameScope(ns), new ScrollViewer { From 4d3933b4606e57bf093ab0f3bbf799bc018e109a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 3 Dec 2024 11:28:53 +0100 Subject: [PATCH 2/3] Add failing test for horizontal scrollbar. When items are removed, the horizontal scrollbar is reset even if the column headers do not fit in the viewport. --- .../TreeDataGridTests_Flat.cs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/tests/Avalonia.Controls.TreeDataGrid.Tests/TreeDataGridTests_Flat.cs b/tests/Avalonia.Controls.TreeDataGrid.Tests/TreeDataGridTests_Flat.cs index 4f7cbd67..5afa60f7 100644 --- a/tests/Avalonia.Controls.TreeDataGrid.Tests/TreeDataGridTests_Flat.cs +++ b/tests/Avalonia.Controls.TreeDataGrid.Tests/TreeDataGridTests_Flat.cs @@ -560,6 +560,69 @@ public void Can_Remove_Selected_Item_Sorted() } } + [AvaloniaFact(Timeout = 10000)] + public void Should_Show_Horizontal_ScrollBar() + { + var (target, items) = CreateTarget(columns: + [ + new TextColumn("ID", x => x.Id, width: new GridLength(100, GridUnitType.Pixel)), + new TextColumn("Title1", x => x.Title, width: new GridLength(100, GridUnitType.Pixel)), + ]); + var scroll = Assert.IsType(target.Scroll); + var headerScroll = Assert.IsType( + target.GetVisualDescendants().Single(x => x.Name == "PART_HeaderScrollViewer")); + + Assert.Equal(new(100, 100), scroll.Viewport); + Assert.Equal(new(200, 1000), scroll.Extent); + Assert.Equal(new(100, 0), headerScroll.Viewport); + Assert.Equal(new(200, 0), headerScroll.Extent); + } + + [AvaloniaFact(Timeout = 10000)] + public void Should_Show_Horizontal_ScrollBar_With_No_Initial_Rows() + { + var (target, items) = CreateTarget(columns: + [ + new TextColumn("ID", x => x.Id, width: new GridLength(100, GridUnitType.Pixel)), + new TextColumn("Title1", x => x.Title, width: new GridLength(100, GridUnitType.Pixel)), + ], itemCount: 0); + var scroll = Assert.IsType(target.Scroll); + var headerScroll = Assert.IsType( + target.GetVisualDescendants().Single(x => x.Name == "PART_HeaderScrollViewer")); + + Assert.Equal(new(100, 100), scroll.Viewport); + Assert.Equal(new(200, 100), scroll.Extent); + Assert.Equal(new(100, 0), headerScroll.Viewport); + Assert.Equal(new(200, 0), headerScroll.Extent); + } + + [AvaloniaFact(Timeout = 10000)] + public void Should_Preserve_Horizontal_ScrollBar_When_Rows_Removed() + { + var (target, items) = CreateTarget(columns: + [ + new TextColumn("ID", x => x.Id, width: new GridLength(100, GridUnitType.Pixel)), + new TextColumn("Title1", x => x.Title, width: new GridLength(100, GridUnitType.Pixel)), + ]); + var scroll = Assert.IsType(target.Scroll); + var headerScroll = Assert.IsType( + target.GetVisualDescendants().Single(x => x.Name == "PART_HeaderScrollViewer")); + + scroll.PropertyChanged += (s, e) => + { + if (e.Property == ScrollViewer.ExtentProperty) + { + } + }; + items.Clear(); + target.UpdateLayout(); + + Assert.Equal(new(100, 100), scroll.Viewport); + Assert.Equal(new(200, 100), scroll.Extent); + Assert.Equal(new(100, 0), headerScroll.Viewport); + Assert.Equal(new(200, 0), headerScroll.Extent); + } + private static void AssertRowIndexes(TreeDataGrid target, int firstRowIndex, int rowCount) { var presenter = target.RowsPresenter; From 56eff203e2d8b5250bdc55d559e1399a061dad43 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 3 Dec 2024 11:30:05 +0100 Subject: [PATCH 3/3] If we have no rows, get width from the columns. Fixes horizontal scrollbar disappearing when all rows removed. --- .../Primitives/TreeDataGridRowsPresenter.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridRowsPresenter.cs b/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridRowsPresenter.cs index fb371722..e946bc09 100644 --- a/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridRowsPresenter.cs +++ b/src/Avalonia.Controls.TreeDataGrid/Primitives/TreeDataGridRowsPresenter.cs @@ -57,6 +57,17 @@ protected override void UnrealizeElementOnItemRemoved(Control element) ChildIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(element, ((TreeDataGridRow)element).RowIndex)); } + protected override Size MeasureOverride(Size availableSize) + { + var result = base.MeasureOverride(availableSize); + + // If we have no rows, then get the width from the columns. + if (Columns is not null && (Items is null || Items.Count == 0)) + result = result.WithWidth(Columns.GetEstimatedWidth(availableSize.Width)); + + return result; + } + protected override Size ArrangeOverride(Size finalSize) { Columns?.CommitActualWidths();