diff --git a/src/Avalonia.Controls/GridSplitter.cs b/src/Avalonia.Controls/GridSplitter.cs
index ee615a26f8..ba857687f6 100644
--- a/src/Avalonia.Controls/GridSplitter.cs
+++ b/src/Avalonia.Controls/GridSplitter.cs
@@ -7,6 +7,7 @@ using System;
using System.Diagnostics;
using Avalonia.Collections;
using Avalonia.Controls.Primitives;
+using Avalonia.Controls.Presenters;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
@@ -211,7 +212,9 @@ namespace Avalonia.Controls
private void InitializeData(bool showsPreview)
{
// If not in a grid or can't resize, do nothing.
- if (Parent is Grid grid)
+ var grid = GetParentGrid();
+
+ if (grid != null)
{
GridResizeDirection resizeDirection = GetEffectiveResizeDirection();
@@ -244,13 +247,15 @@ namespace Avalonia.Controls
///
private bool SetupDefinitionsToResize()
{
- int gridSpan = GetValue(_resizeData!.ResizeDirection == GridResizeDirection.Columns ?
+ // Get properties values from ContentPresenter if Grid it's used in ItemsControl as ItemsPanel otherwise directly from GridSplitter.
+ var sourceControl = GetPropertiesValueSource();
+ int gridSpan = sourceControl.GetValue(_resizeData!.ResizeDirection == GridResizeDirection.Columns ?
Grid.ColumnSpanProperty :
Grid.RowSpanProperty);
if (gridSpan == 1)
{
- var splitterIndex = GetValue(_resizeData.ResizeDirection == GridResizeDirection.Columns ?
+ var splitterIndex = sourceControl.GetValue(_resizeData.ResizeDirection == GridResizeDirection.Columns ?
Grid.ColumnProperty :
Grid.RowProperty);
@@ -351,6 +356,81 @@ namespace Avalonia.Controls
}
}
+ ///
+ /// Retrieves the that ultimately hosts this
+ /// in the visual/logical tree.
+ ///
+ ///
+ /// A splitter can be placed directly inside a or
+ /// indirectly inside an that uses a
+ /// as its .
+ /// In the latter case the first logical parent is usually an
+ /// (or the items control itself),
+ /// so the method walks these intermediate containers to locate the
+ /// underlying grid.
+ ///
+ ///
+ /// The containing if one is found; otherwise
+ /// null.
+ ///
+ protected virtual Grid? GetParentGrid()
+ {
+ // When GridSplitter is used inside an ItemsControl with Grid as
+ // its ItemsPanel, its immediate parent is usually a ItemsControl or ContentPresenter.
+ switch (Parent)
+ {
+ case Grid grid:
+ {
+ return grid;
+ }
+ case ItemsControl itemsControl:
+ {
+ if (itemsControl.ItemsPanelRoot is Grid grid)
+ {
+ return grid;
+ }
+
+ break;
+ }
+ case ContentPresenter { Parent: ItemsControl presenterItemsControl }:
+ {
+ if (presenterItemsControl.ItemsPanelRoot is Grid grid)
+ {
+ return grid;
+ }
+
+ break;
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Returns the element that carries the grid-attached properties
+ /// (, , etc.) relevant
+ /// to this .
+ ///
+ ///
+ /// When the splitter is generated as part of an
+ /// template, the attached properties are set on the surrounding
+ /// rather than on the splitter itself.
+ /// This helper selects that presenter when appropriate so subsequent
+ /// property look-ups read the correct values; otherwise it simply
+ /// returns this.
+ ///
+ ///
+ /// The from which grid-attached properties
+ /// should be read—either the parent or
+ /// the splitter instance.
+ ///
+ protected virtual StyledElement GetPropertiesValueSource()
+ {
+ return Parent is ContentPresenter
+ ? Parent
+ : this;
+ }
+
protected override void OnPointerEntered(PointerEventArgs e)
{
base.OnPointerEntered(e);
diff --git a/tests/Avalonia.Controls.UnitTests/GridSplitterTests.cs b/tests/Avalonia.Controls.UnitTests/GridSplitterTests.cs
index cdb8fb21fe..c0974ea97b 100644
--- a/tests/Avalonia.Controls.UnitTests/GridSplitterTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/GridSplitterTests.cs
@@ -1,5 +1,8 @@
+using System.Collections.Generic;
+using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
+using Avalonia.Markup.Xaml;
using Avalonia.Platform;
using Avalonia.UnitTests;
using Moq;
@@ -380,5 +383,164 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(columnDefinitions[0].Width, new GridLength(1, GridUnitType.Star));
Assert.Equal(columnDefinitions[2].Width, new GridLength(1, GridUnitType.Star));
}
+
+ [Fact]
+ public void Works_In_ItemsControl_ItemsSource()
+ {
+ using var app = UnitTestApplication.Start(TestServices.StyledWindow);
+
+ var xaml = @"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+";
+
+ var itemsControl = AvaloniaRuntimeXamlLoader.Parse(xaml);
+ itemsControl.ItemsSource = new List
+ {
+ new TextItem { Column = 0, Text = "A" },
+ new SplitterItem { Column = 1 },
+ new TextItem { Column = 2, Text = "B" },
+ };
+
+ var root = new TestRoot { Child = itemsControl };
+ root.Measure(new Size(200, 100));
+ root.Arrange(new Rect(0, 0, 200, 100));
+
+ var panel = Assert.IsType(itemsControl.ItemsPanelRoot);
+ var cp = Assert.IsType(panel.Children[1]);
+ cp.UpdateChild();
+ var splitter = Assert.IsType(cp.Child);
+
+ splitter.RaiseEvent(new VectorEventArgs { RoutedEvent = Thumb.DragStartedEvent });
+ splitter.RaiseEvent(new VectorEventArgs { RoutedEvent = Thumb.DragDeltaEvent, Vector = new Vector(-20, 0) });
+ splitter.RaiseEvent(new VectorEventArgs { RoutedEvent = Thumb.DragCompletedEvent });
+
+ Assert.NotEqual(panel.ColumnDefinitions[0].Width, panel.ColumnDefinitions[2].Width);
+ }
+
+ [Fact]
+ public void Works_In_ItemsControl_Items()
+ {
+ using var app = UnitTestApplication.Start(TestServices.StyledWindow);
+
+ var xaml = @"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+";
+
+ var itemsControl = AvaloniaRuntimeXamlLoader.Parse(xaml);
+ var root = new TestRoot { Child = itemsControl };
+ root.Measure(new Size(200, 100));
+ root.Arrange(new Rect(0, 0, 200, 100));
+
+ var panel = Assert.IsType(itemsControl.ItemsPanelRoot);
+ var splitter = Assert.IsType(panel.Children[1]);
+
+ splitter.RaiseEvent(new VectorEventArgs { RoutedEvent = Thumb.DragStartedEvent });
+ splitter.RaiseEvent(new VectorEventArgs { RoutedEvent = Thumb.DragDeltaEvent, Vector = new Vector(-20, 0) });
+ splitter.RaiseEvent(new VectorEventArgs { RoutedEvent = Thumb.DragCompletedEvent });
+
+ Assert.NotEqual(panel.ColumnDefinitions[0].Width, panel.ColumnDefinitions[2].Width);
+ }
+
+ [Fact]
+ public void Works_In_Grid()
+ {
+ using var app = UnitTestApplication.Start(TestServices.StyledWindow);
+
+ var xaml = @"
+
+
+
+";
+
+ var grid = AvaloniaRuntimeXamlLoader.Parse(xaml);
+ var root = new TestRoot { Child = grid };
+ root.Measure(new Size(200, 100));
+ root.Arrange(new Rect(0, 0, 200, 100));
+
+ var splitter = Assert.IsType(grid.Children[1]);
+
+ splitter.RaiseEvent(new VectorEventArgs { RoutedEvent = Thumb.DragStartedEvent });
+ splitter.RaiseEvent(new VectorEventArgs { RoutedEvent = Thumb.DragDeltaEvent, Vector = new Vector(-20, 0) });
+ splitter.RaiseEvent(new VectorEventArgs { RoutedEvent = Thumb.DragCompletedEvent });
+
+ Assert.NotEqual(grid.ColumnDefinitions[0].Width, grid.ColumnDefinitions[2].Width);
+ }
+ }
+
+ public interface IGridItem
+ {
+ int Column { get; set; }
+ }
+
+ public class TextItem : IGridItem
+ {
+ public int Column { get; set; }
+ public string? Text { get; set; }
+ }
+
+ public class SplitterItem : IGridItem
+ {
+ public int Column { get; set; }
}
}