diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index 8f2636a783..1a9042a25b 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -10,6 +10,7 @@ using Avalonia.Controls.Generators; using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Input.Platform; +using Avalonia.Layout; using Avalonia.Threading; using Avalonia.VisualTree; @@ -163,6 +164,9 @@ namespace Avalonia.Controls { item.IsExpanded = true; + if (item.Presenter?.Panel is null) + (this.GetVisualRoot() as ILayoutRoot)?.LayoutManager.ExecuteLayoutPass(); + if (item.Presenter?.Panel is { } panel) { foreach (var child in panel.Children) diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs index cf83ba8253..5dbfe49533 100644 --- a/src/Avalonia.Controls/TreeViewItem.cs +++ b/src/Avalonia.Controls/TreeViewItem.cs @@ -90,8 +90,20 @@ namespace Avalonia.Controls internal TreeView? TreeViewOwner => _treeView; - protected internal override Control CreateContainerForItemOverride() => new TreeViewItem(); - protected internal override bool IsItemItsOwnContainerOverride(Control item) => item is TreeViewItem; + protected internal override Control CreateContainerForItemOverride() + { + return EnsureTreeView().CreateContainerForItemOverride(); + } + + protected internal override bool IsItemItsOwnContainerOverride(Control item) + { + return EnsureTreeView().IsItemItsOwnContainerOverride(item); + } + + protected internal override void PrepareContainerForItemOverride(Control container, object? item, int index) + { + EnsureTreeView().PrepareContainerForItemOverride(container, item, index); + } /// protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) @@ -283,6 +295,9 @@ namespace Avalonia.Controls return logical != null ? result : @default; } + private TreeView EnsureTreeView() => _treeView ?? + throw new InvalidOperationException("The TreeViewItem is not part of a TreeView."); + private void HeaderDoubleTapped(object? sender, TappedEventArgs e) { OnHeaderDoubleTapped(e); diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index 4f533c2f78..129720d00a 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -1,43 +1,35 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using Avalonia.Collections; -using Avalonia.Controls.Generators; using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.Data.Core; using Avalonia.Input; using Avalonia.Input.Platform; -using Avalonia.Interactivity; +using Avalonia.Layout; using Avalonia.LogicalTree; using Avalonia.Styling; using Avalonia.UnitTests; -using Moq; +using Avalonia.VisualTree; using Xunit; +#nullable enable + namespace Avalonia.Controls.UnitTests { public class TreeViewTests { - MouseTestHelper _mouse = new MouseTestHelper(); + private readonly MouseTestHelper _mouse = new(); [Fact] public void Items_Should_Be_Created() { - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = CreateTestTreeData(), - }; - - var root = new TestRoot(target); - - CreateNodeDataTemplate(target); - ApplyTemplates(target); + using var app = Start(); + var target = CreateTarget(); Assert.Equal(new[] { "Root" }, ExtractItemHeader(target, 0)); Assert.Equal(new[] { "Child1", "Child2", "Child3" }, ExtractItemHeader(target, 1)); @@ -47,50 +39,31 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Items_Should_Be_Created_Using_ItemTemplate_If_Present() { - TreeView target; - - var root = new TestRoot - { - Child = target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = CreateTestTreeData(), - ItemTemplate = new FuncTreeDataTemplate( - (_, __) => new Canvas(), - x => x.Children), - } - }; - - ApplyTemplates(target); + using var app = Start(); + var itemTemplate = new FuncTreeDataTemplate( + (_, _) => new Canvas(), + x => x.Children); + var target = CreateTarget(itemTemplate: itemTemplate); var items = target.GetRealizedTreeContainers() .OfType() .ToList(); Assert.Equal(5, items.Count); - Assert.All(items, x => Assert.IsType(x.HeaderPresenter.Child)); + Assert.All(items, x => Assert.IsType(x.HeaderPresenter?.Child)); } [Fact] public void Items_Should_Be_Created_Using_ItemConatinerTheme_If_Present() { - TreeView target; - var theme = new ControlTheme(typeof(TreeViewItem)); - - var root = new TestRoot - { - Child = target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = CreateTestTreeData(), - ItemContainerTheme = theme, - ItemTemplate = new FuncTreeDataTemplate( - (_, __) => new Canvas(), - x => x.Children), - } - }; + var theme = CreateTreeViewItemControlTheme(); + var itemTemplate = new FuncTreeDataTemplate( + (_, _) => new Canvas(), + x => x.Children); - ApplyTemplates(target); + var target = CreateTarget( + itemContainerTheme: theme, + itemTemplate: itemTemplate); var items = target.GetRealizedTreeContainers() .OfType() @@ -104,21 +77,12 @@ namespace Avalonia.Controls.UnitTests public void Finds_Correct_DataTemplate_When_Application_DataTemplate_Is_Present() { // #10398 - using var app = UnitTestApplication.Start(); - - Avalonia.Application.Current.DataTemplates.Add(new FuncDataTemplate((x, _) => new Canvas())); - AvaloniaLocator.CurrentMutable.Bind().ToConstant(Avalonia.Application.Current); - - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = CreateTestTreeData(), - }; + using var app = Start(); - var root = new TestRoot(target); + Avalonia.Application.Current!.DataTemplates.Add(new FuncDataTemplate((x, _) => new Canvas())); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(Avalonia.Application.Current); - CreateNodeDataTemplate(target); - ApplyTemplates(target); + var target = CreateTarget(); Assert.Equal(new[] { "Root" }, ExtractItemHeader(target, 0)); Assert.Equal(new[] { "Child1", "Child2", "Child3" }, ExtractItemHeader(target, 1)); @@ -128,46 +92,27 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Root_ItemContainerGenerator_Containers_Should_Be_Root_Containers() { - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = CreateTestTreeData(), - }; - - var root = new TestRoot(target); - - CreateNodeDataTemplate(target); - ApplyTemplates(target); + using var app = Start(); + var target = CreateTarget(); var container = (TreeViewItem)target.GetRealizedContainers().Single(); - var header = (TextBlock)container.HeaderPresenter.Child; - Assert.Equal("Root", header.Text); + var header = Assert.IsType(container.HeaderPresenter?.Child); + Assert.Equal("Root", header?.Text); } [Fact] public void Root_TreeContainerFromItem_Should_Return_Descendant_Item() { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - // For TreeViewItem to find its parent TreeView, OnAttachedToLogicalTree needs - // to be called, which requires an IStyleRoot. - var root = new TestRoot(); - root.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); - var container = target.TreeContainerFromItem(tree[0].Children[1].Children[0]); + var container = target.TreeContainerFromItem(data[0].Children[1].Children[0]); Assert.NotNull(container); - var header = ((TreeViewItem)container).HeaderPresenter; - var headerContent = ((TextBlock)header.Child).Text; + var header = ((TreeViewItem)container).HeaderPresenter!; + var headerContent = ((TextBlock)header.Child!).Text; Assert.Equal("Grandchild2a", headerContent); } @@ -175,335 +120,192 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Clicking_Item_Should_Select_It() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); - - var item = tree[0].Children[1].Children[0]; - var container = (TreeViewItem)target.TreeContainerFromItem(item); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); - Assert.NotNull(container); + var item = data[0].Children[1].Children[0]; + var container = Assert.IsType(target.TreeContainerFromItem(item)); - _mouse.Click(container); + _mouse.Click(container); - Assert.Equal(item, target.SelectedItem); - Assert.True(container.IsSelected); - } + Assert.Equal(item, target.SelectedItem); + Assert.True(container.IsSelected); } [Fact] public void Clicking_WithControlModifier_Selected_Item_Should_Deselect_It() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); + var item = data[0].Children[1].Children[0]; + var container = Assert.IsType(target.TreeContainerFromItem(item)); - var item = tree[0].Children[1].Children[0]; - var container = (TreeViewItem)target.TreeContainerFromItem(item); + target.SelectedItem = item; - Assert.NotNull(container); + Assert.True(container.IsSelected); - target.SelectedItem = item; + _mouse.Click(container, modifiers: KeyModifiers.Control); - Assert.True(container.IsSelected); - - _mouse.Click(container, modifiers: KeyModifiers.Control); - - Assert.Null(target.SelectedItem); - Assert.False(container.IsSelected); - } + Assert.Null(target.SelectedItem); + Assert.False(container.IsSelected); } [Fact] public void Clicking_WithControlModifier_Not_Selected_Item_Should_Select_It() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); - - var item1 = tree[0].Children[1].Children[0]; - var container1 = (TreeViewItem)target.TreeContainerFromItem(item1); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); - var item2 = tree[0].Children[1]; - var container2 = (TreeViewItem)target.TreeContainerFromItem(item2); + var item1 = data[0].Children[1].Children[0]; + var container1 = Assert.IsType(target.TreeContainerFromItem(item1)); - Assert.NotNull(container1); - Assert.NotNull(container2); + var item2 = data[0].Children[1]; + var container2 = Assert.IsType(target.TreeContainerFromItem(item2)); - target.SelectedItem = item1; + target.SelectedItem = item1; - Assert.True(container1.IsSelected); + Assert.True(container1.IsSelected); - _mouse.Click(container2, modifiers: KeyModifiers.Control); + _mouse.Click(container2, modifiers: KeyModifiers.Control); - Assert.Equal(item2, target.SelectedItem); - Assert.False(container1.IsSelected); - Assert.True(container2.IsSelected); - } + Assert.Equal(item2, target.SelectedItem); + Assert.False(container1.IsSelected); + Assert.True(container2.IsSelected); } [Fact] public void Clicking_WithControlModifier_Selected_Item_Should_Deselect_And_Remove_From_SelectedItems() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - SelectionMode = SelectionMode.Multiple - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); - - var rootNode = tree[0]; - - var item1 = rootNode.Children[0]; - var item2 = rootNode.Children.Last(); - - var item1Container = (TreeViewItem)target.TreeContainerFromItem(item1); - var item2Container = (TreeViewItem)target.TreeContainerFromItem(item2); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data, multiSelect: true); + var rootNode = data[0]; + var item1 = rootNode.Children[0]; + var item2 = rootNode.Children.Last(); + var item1Container = Assert.IsType(target.TreeContainerFromItem(item1)); + var item2Container = Assert.IsType(target.TreeContainerFromItem(item2)); - ClickContainer(item1Container, KeyModifiers.Control); - Assert.True(item1Container.IsSelected); + ClickContainer(item1Container, KeyModifiers.Control); + Assert.True(item1Container.IsSelected); - ClickContainer(item2Container, KeyModifiers.Control); - Assert.True(item2Container.IsSelected); + ClickContainer(item2Container, KeyModifiers.Control); + Assert.True(item2Container.IsSelected); - Assert.Equal(new[] { item1, item2 }, target.SelectedItems.OfType()); + Assert.Equal(new[] { item1, item2 }, target.SelectedItems.OfType()); - ClickContainer(item1Container, KeyModifiers.Control); - Assert.False(item1Container.IsSelected); + ClickContainer(item1Container, KeyModifiers.Control); + Assert.False(item1Container.IsSelected); - Assert.DoesNotContain(item1, target.SelectedItems.OfType()); - } + Assert.DoesNotContain(item1, target.SelectedItems.OfType()); } [Fact] public void Clicking_WithShiftModifier_DownDirection_Should_Select_Range_Of_Items() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - SelectionMode = SelectionMode.Multiple - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); - - var rootNode = tree[0]; - - var from = rootNode.Children[0]; - var to = rootNode.Children.Last(); - - var fromContainer = (TreeViewItem)target.TreeContainerFromItem(from); - var toContainer = (TreeViewItem)target.TreeContainerFromItem(to); - - ClickContainer(fromContainer, KeyModifiers.None); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data, multiSelect: true); + var rootNode = data[0]; + var from = rootNode.Children[0]; + var to = rootNode.Children.Last(); + var fromContainer = Assert.IsType(target.TreeContainerFromItem(from)); + var toContainer = Assert.IsType(target.TreeContainerFromItem(to)); - Assert.True(fromContainer.IsSelected); + ClickContainer(fromContainer, KeyModifiers.None); + Assert.True(fromContainer.IsSelected); - ClickContainer(toContainer, KeyModifiers.Shift); - AssertChildrenSelected(target, rootNode); - } + ClickContainer(toContainer, KeyModifiers.Shift); + AssertChildrenSelected(target, rootNode); } [Fact] public void Clicking_WithShiftModifier_UpDirection_Should_Select_Range_Of_Items() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - SelectionMode = SelectionMode.Multiple - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); - - var rootNode = tree[0]; - - var from = rootNode.Children.Last(); - var to = rootNode.Children[0]; - - var fromContainer = (TreeViewItem)target.TreeContainerFromItem(from); - var toContainer = (TreeViewItem)target.TreeContainerFromItem(to); - - ClickContainer(fromContainer, KeyModifiers.None); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data, multiSelect: true); + var rootNode = data[0]; + var from = rootNode.Children.Last(); + var to = rootNode.Children[0]; + var fromContainer = Assert.IsType(target.TreeContainerFromItem(from)); + var toContainer = Assert.IsType(target.TreeContainerFromItem(to)); - Assert.True(fromContainer.IsSelected); + ClickContainer(fromContainer, KeyModifiers.None); + Assert.True(fromContainer.IsSelected); - ClickContainer(toContainer, KeyModifiers.Shift); - AssertChildrenSelected(target, rootNode); - } + ClickContainer(toContainer, KeyModifiers.Shift); + AssertChildrenSelected(target, rootNode); } [Fact] public void Clicking_First_Item_Of_SelectedItems_Should_Select_Only_It() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - SelectionMode = SelectionMode.Multiple - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); - - var rootNode = tree[0]; - - var from = rootNode.Children.Last(); - var to = rootNode.Children[0]; - - var fromContainer = (TreeViewItem)target.TreeContainerFromItem(from); - var toContainer = (TreeViewItem)target.TreeContainerFromItem(to); - - ClickContainer(fromContainer, KeyModifiers.None); - - ClickContainer(toContainer, KeyModifiers.Shift); - AssertChildrenSelected(target, rootNode); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data, multiSelect: true); + var rootNode = data[0]; + var from = rootNode.Children.Last(); + var to = rootNode.Children[0]; + var fromContainer = Assert.IsType(target.TreeContainerFromItem(from)); + var toContainer = Assert.IsType(target.TreeContainerFromItem(to)); - ClickContainer(fromContainer, KeyModifiers.None); + ClickContainer(fromContainer, KeyModifiers.None); + ClickContainer(toContainer, KeyModifiers.Shift); + AssertChildrenSelected(target, rootNode); - Assert.True(fromContainer.IsSelected); + ClickContainer(fromContainer, KeyModifiers.None); + Assert.True(fromContainer.IsSelected); - foreach (var child in rootNode.Children) - { - if (child == from) - { - continue; - } - - var container = (TreeViewItem)target.TreeContainerFromItem(child); + foreach (var child in rootNode.Children) + { + if (child == from) + continue; - Assert.False(container.IsSelected); - } + var container = Assert.IsType(target.TreeContainerFromItem(child)); + Assert.False(container.IsSelected); } } [Fact] public void Double_Clicking_Item_Header_Should_Expand_It() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); - CreateNodeDataTemplate(target); - ApplyTemplates(target); - CollapseAll(target); + CollapseAll(target); - var item = tree[0].Children[1]; - var container = (TreeViewItem)target.TreeContainerFromItem(item); + var item = data[0].Children[1]; + var container = Assert.IsType(target.TreeContainerFromItem(item)); + var header = container.HeaderPresenter?.Child; - Assert.NotNull(container); - Assert.False(container.IsExpanded); - var header = container.HeaderPresenter.Child; - Assert.NotNull(header); + Assert.False(container.IsExpanded); + Assert.NotNull(header); - _mouse.DoubleClick(header); + _mouse.DoubleClick(header); - Assert.True(container.IsExpanded); - } + Assert.True(container.IsExpanded); } [Fact] public void Double_Clicking_Item_Header_With_No_Children_Does_Not_Expand_It() { - using (Application()) + using var app = Start(); { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); - CreateNodeDataTemplate(target); - ApplyTemplates(target); CollapseAll(target); - var item = tree[0].Children[1].Children[0]; - var container = (TreeViewItem)target.TreeContainerFromItem(item); + var item = data[0].Children[1].Children[0]; + var container = Assert.IsType(target.TreeContainerFromItem(item)); + var header = container.HeaderPresenter?.Child; - Assert.NotNull(container); Assert.False(container.IsExpanded); - var header = container.HeaderPresenter.Child; Assert.NotNull(header); _mouse.DoubleClick(header); @@ -515,346 +317,238 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Double_Clicking_Item_Header_Should_Collapse_It() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); - - var item = tree[0].Children[1]; - var container = (TreeViewItem)target.TreeContainerFromItem(item); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); + var item = data[0].Children[1]; + var container = Assert.IsType(target.TreeContainerFromItem(item)); + var header = container.HeaderPresenter?.Child; - Assert.NotNull(container); - Assert.True(container.IsExpanded); - var header = container.HeaderPresenter.Child; - Assert.NotNull(header); + Assert.True(container.IsExpanded); + Assert.NotNull(header); - _mouse.DoubleClick(header); + _mouse.DoubleClick(header); - Assert.False(container.IsExpanded); - } + Assert.False(container.IsExpanded); } [Fact] public void Enter_Key_Should_Collapse_TreeViewItem() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); // NOTE this line - - var item = tree[0]; - var container = (TreeViewItem)target.TreeContainerFromItem(item); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); + var item = data[0]; + var container = Assert.IsType(target.TreeContainerFromItem(item)); + var header = container.HeaderPresenter?.Child; - Assert.NotNull(container); - Assert.True(container.IsExpanded); - var header = container.HeaderPresenter.Child; - Assert.NotNull(header); + Assert.True(container.IsExpanded); + Assert.NotNull(header); - container.RaiseEvent(new KeyEventArgs - { - RoutedEvent = InputElement.KeyDownEvent, - Key = Key.Enter, - }); + container.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Enter, + }); - Assert.False(container.IsExpanded); - } + Assert.False(container.IsExpanded); } [Fact] - public void Enter_plus_Ctrl_Key_Should_Collapse_TreeViewItem_Recursively() + public void Enter_Plus_Ctrl_Key_Should_Collapse_TreeViewItem_Recursively() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); - var visualRoot = new TestRoot(); - visualRoot.Child = target; + var item = data[0]; + var container = Assert.IsType(target.TreeContainerFromItem(item)); + var header = container.HeaderPresenter?.Child; - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); // NOTE this line + Assert.True(container.IsExpanded); + Assert.NotNull(header); - var item = tree[0]; - var container = (TreeViewItem)target.TreeContainerFromItem(item); + container.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Enter, + KeyModifiers = KeyModifiers.Control, + }); - Assert.NotNull(container); - Assert.True(container.IsExpanded); - var header = container.HeaderPresenter.Child; - Assert.NotNull(header); + Assert.False(container.IsExpanded); - container.RaiseEvent(new KeyEventArgs - { - RoutedEvent = InputElement.KeyDownEvent, - Key = Key.Enter, - KeyModifiers = KeyModifiers.Control, - }); + AssertEachItemWithChildrenIsCollapsed(item); - Assert.False(container.IsExpanded); - - AssertEachItemWithChildrenIsCollapsed(item); + void AssertEachItemWithChildrenIsCollapsed(Node node) + { + var container = Assert.IsType(target.TreeContainerFromItem(node)); - void AssertEachItemWithChildrenIsCollapsed(Node node) + if (node.Children?.Count > 0) { - var container = (TreeViewItem)target.TreeContainerFromItem(node); - Assert.NotNull(container); - if (node.Children?.Count > 0) - { - Assert.False(container.IsExpanded); - foreach (var c in node.Children) - { - AssertEachItemWithChildrenIsCollapsed(c); - } - } - else + Assert.False(container.IsExpanded); + foreach (var c in node.Children) { - Assert.True(container.IsExpanded); + AssertEachItemWithChildrenIsCollapsed(c); } } + else + { + Assert.True(container.IsExpanded); + } } } [Fact] public void Enter_Key_Should_Expand_TreeViewItem() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); - CreateNodeDataTemplate(target); - ApplyTemplates(target); - CollapseAll(target); // NOTE this line + CollapseAll(target); - var item = tree[0]; - var container = (TreeViewItem)target.TreeContainerFromItem(item); + var item = data[0]; + var container = Assert.IsType(target.TreeContainerFromItem(item)); + var header = container.HeaderPresenter?.Child; - Assert.NotNull(container); - Assert.False(container.IsExpanded); - var header = container.HeaderPresenter.Child; - Assert.NotNull(header); + Assert.False(container.IsExpanded); + Assert.NotNull(header); - container.RaiseEvent(new KeyEventArgs - { - RoutedEvent = InputElement.KeyDownEvent, - Key = Key.Enter, - }); + container.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Enter, + }); - Assert.True(container.IsExpanded); - } + Assert.True(container.IsExpanded); } [Fact] - public void Enter_plus_Ctrl_Key_Should_Expand_TreeViewItem_Recursively() + public void Enter_Plus_Ctrl_Key_Should_Expand_TreeViewItem_Recursively() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); - var visualRoot = new TestRoot(); - visualRoot.Child = target; + CollapseAll(target); - CreateNodeDataTemplate(target); - ApplyTemplates(target); - CollapseAll(target); // NOTE this line + var item = data[0]; + var container = Assert.IsType(target.TreeContainerFromItem(item)); + var header = container.HeaderPresenter?.Child; - var item = tree[0]; - var container = (TreeViewItem)target.TreeContainerFromItem(item); + Assert.False(container.IsExpanded); + Assert.NotNull(header); - Assert.NotNull(container); - Assert.False(container.IsExpanded); - var header = container.HeaderPresenter.Child; - Assert.NotNull(header); + container.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Enter, + KeyModifiers = KeyModifiers.Control, + }); - container.RaiseEvent(new KeyEventArgs - { - RoutedEvent = InputElement.KeyDownEvent, - Key = Key.Enter, - KeyModifiers = KeyModifiers.Control, - }); + Assert.True(container.IsExpanded); - Assert.True(container.IsExpanded); + AssertEachItemWithChildrenIsExpanded(item); - AssertEachItemWithChildrenIsExpanded(item); + void AssertEachItemWithChildrenIsExpanded(Node node) + { + var container = Assert.IsType(target.TreeContainerFromItem(node)); - void AssertEachItemWithChildrenIsExpanded(Node node) + if (node.Children?.Count > 0) { - var container = (TreeViewItem)target.TreeContainerFromItem(node); - Assert.NotNull(container); - if (node.Children?.Count > 0) + Assert.True(container.IsExpanded); + foreach (var c in node.Children) { - Assert.True(container.IsExpanded); - foreach (var c in node.Children) - { - AssertEachItemWithChildrenIsExpanded(c); - } - } - else - { - Assert.False(container.IsExpanded); + AssertEachItemWithChildrenIsExpanded(c); } } + else + { + Assert.False(container.IsExpanded); + } } } [Fact] public void Space_Key_Should_Collapse_TreeViewItem() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); // NOTE this line - - var item = tree[0]; - var container = (TreeViewItem)target.TreeContainerFromItem(item); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); + var item = data[0]; + var container = Assert.IsType(target.TreeContainerFromItem(item)); + var header = container.HeaderPresenter?.Child; - Assert.NotNull(container); - Assert.True(container.IsExpanded); - var header = container.HeaderPresenter.Child; - Assert.NotNull(header); + Assert.True(container.IsExpanded); + Assert.NotNull(header); - container.RaiseEvent(new KeyEventArgs - { - RoutedEvent = InputElement.KeyDownEvent, - Key = Key.Enter, - }); + container.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Enter, + }); - Assert.False(container.IsExpanded); - } + Assert.False(container.IsExpanded); } [Fact] - public void Space_plus_Ctrl_Key_Should_Collapse_TreeViewItem_Recursively() + public void Space_Plus_Ctrl_Key_Should_Collapse_TreeViewItem_Recursively() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); // NOTE this line - - var item = tree[0]; - var container = (TreeViewItem)target.TreeContainerFromItem(item); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); + var item = data[0]; + var container = Assert.IsType(target.TreeContainerFromItem(item)); + var header = container.HeaderPresenter?.Child; - Assert.NotNull(container); - Assert.True(container.IsExpanded); - var header = container.HeaderPresenter.Child; - Assert.NotNull(header); + Assert.True(container.IsExpanded); + Assert.NotNull(header); - container.RaiseEvent(new KeyEventArgs - { - RoutedEvent = InputElement.KeyDownEvent, - Key = Key.Enter, - KeyModifiers = KeyModifiers.Control, - }); + container.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Enter, + KeyModifiers = KeyModifiers.Control, + }); - Assert.False(container.IsExpanded); + Assert.False(container.IsExpanded); - AssertEachItemWithChildrenIsCollapsed(item); + AssertEachItemWithChildrenIsCollapsed(item); - void AssertEachItemWithChildrenIsCollapsed(Node node) + void AssertEachItemWithChildrenIsCollapsed(Node node) + { + var container = Assert.IsType(target.TreeContainerFromItem(node)); + Assert.NotNull(container); + if (node.Children?.Count > 0) { - var container = (TreeViewItem)target.TreeContainerFromItem(node); - Assert.NotNull(container); - if (node.Children?.Count > 0) + Assert.False(container.IsExpanded); + foreach (var c in node.Children) { - Assert.False(container.IsExpanded); - foreach (var c in node.Children) - { - AssertEachItemWithChildrenIsCollapsed(c); - } - } - else - { - Assert.True(container.IsExpanded); + AssertEachItemWithChildrenIsCollapsed(c); } } + else + { + Assert.True(container.IsExpanded); + } } } [Fact] public void Space_Key_Should_Expand_TreeViewItem() { - using (Application()) + using var app = Start(); { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); - CreateNodeDataTemplate(target); - ApplyTemplates(target); - CollapseAll(target); // NOTE this line + CollapseAll(target); - var item = tree[0]; - var container = (TreeViewItem)target.TreeContainerFromItem(item); + var item = data[0]; + var container = Assert.IsType(target.TreeContainerFromItem(item)); + var header = container.HeaderPresenter?.Child; - Assert.NotNull(container); Assert.False(container.IsExpanded); - var header = container.HeaderPresenter.Child; Assert.NotNull(header); container.RaiseEvent(new KeyEventArgs @@ -870,281 +564,221 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Space_plus_Ctrl_Key_Should_Expand_TreeViewItem_Recursively() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); - var visualRoot = new TestRoot(); - visualRoot.Child = target; + CollapseAll(target); - CreateNodeDataTemplate(target); - ApplyTemplates(target); - CollapseAll(target); // NOTE this line + var item = data[0]; + var container = Assert.IsType(target.TreeContainerFromItem(item)); + var header = container.HeaderPresenter?.Child; - var item = tree[0]; - var container = (TreeViewItem)target.TreeContainerFromItem(item); + Assert.False(container.IsExpanded); + Assert.NotNull(header); - Assert.NotNull(container); - Assert.False(container.IsExpanded); - var header = container.HeaderPresenter.Child; - Assert.NotNull(header); - - container.RaiseEvent(new KeyEventArgs - { - RoutedEvent = InputElement.KeyDownEvent, - Key = Key.Enter, - KeyModifiers = KeyModifiers.Control, - }); + container.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Enter, + KeyModifiers = KeyModifiers.Control, + }); - Assert.True(container.IsExpanded); + Assert.True(container.IsExpanded); - AssertEachItemWithChildrenIsExpanded(item); + AssertEachItemWithChildrenIsExpanded(item); - void AssertEachItemWithChildrenIsExpanded(Node node) + void AssertEachItemWithChildrenIsExpanded(Node node) + { + var container = Assert.IsType(target.TreeContainerFromItem(node)); + Assert.NotNull(container); + if (node.Children?.Count > 0) { - var container = (TreeViewItem)target.TreeContainerFromItem(node); - Assert.NotNull(container); - if (node.Children?.Count > 0) + Assert.True(container.IsExpanded); + foreach (var c in node.Children) { - Assert.True(container.IsExpanded); - foreach (var c in node.Children) - { - AssertEachItemWithChildrenIsExpanded(c); - } - } - else - { - Assert.False(container.IsExpanded); + AssertEachItemWithChildrenIsExpanded(c); } } + else + { + Assert.False(container.IsExpanded); + } } } [Fact] public void Numpad_Star_Should_Expand_All_Children_Recursively() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); - CreateNodeDataTemplate(target); - ApplyTemplates(target); - CollapseAll(target); + CollapseAll(target); - var item = tree[0]; - var container = (TreeViewItem)target.TreeContainerFromItem(item); + var item = data[0]; + var container = Assert.IsType(target.TreeContainerFromItem(item)); - Assert.NotNull(container); - container.RaiseEvent(new KeyEventArgs - { - RoutedEvent = InputElement.KeyDownEvent, - Key = Key.Multiply, - }); + Assert.NotNull(container); + container.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Multiply, + }); - AssertEachItemWithChildrenIsExpanded(item); + AssertEachItemWithChildrenIsExpanded(item); - void AssertEachItemWithChildrenIsExpanded(Node node) + void AssertEachItemWithChildrenIsExpanded(Node node) + { + var container = Assert.IsType(target.TreeContainerFromItem(node)); + Assert.NotNull(container); + if (node.Children?.Count > 0) { - var container = (TreeViewItem)target.TreeContainerFromItem(node); - Assert.NotNull(container); - if (node.Children?.Count > 0) + Assert.True(container.IsExpanded); + foreach (var c in node.Children) { - Assert.True(container.IsExpanded); - foreach (var c in node.Children) - { - AssertEachItemWithChildrenIsExpanded(c); - } - } - else - { - Assert.False(container.IsExpanded); + AssertEachItemWithChildrenIsExpanded(c); } } + else + { + Assert.False(container.IsExpanded); + } } } [Fact] public void Numpad_Slash_Should_Collapse_All_Children_Recursively() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); + var item = data[0]; + var container = Assert.IsType(target.TreeContainerFromItem(item)); - var visualRoot = new TestRoot(); - visualRoot.Child = target; + Assert.NotNull(container); - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); + container.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Divide, + }); - var item = tree[0]; - var container = (TreeViewItem)target.TreeContainerFromItem(item); + AssertEachItemWithChildrenIsCollapsed(item); + void AssertEachItemWithChildrenIsCollapsed(Node node) + { + var container = Assert.IsType(target.TreeContainerFromItem(node)); Assert.NotNull(container); - container.RaiseEvent(new KeyEventArgs + if (node.Children?.Count > 0) { - RoutedEvent = InputElement.KeyDownEvent, - Key = Key.Divide, - }); - - AssertEachItemWithChildrenIsCollapsed(item); - - void AssertEachItemWithChildrenIsCollapsed(Node node) - { - var container = (TreeViewItem)target.TreeContainerFromItem(node); - Assert.NotNull(container); - if (node.Children?.Count > 0) + Assert.False(container.IsExpanded); + foreach (var c in node.Children) { - Assert.False(container.IsExpanded); - foreach (var c in node.Children) - { - AssertEachItemWithChildrenIsCollapsed(c); - } - } - else - { - Assert.True(container.IsExpanded); + AssertEachItemWithChildrenIsCollapsed(c); } } + else + { + Assert.True(container.IsExpanded); + } } } [Fact] public void Setting_SelectedItem_Should_Set_Container_Selected() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); - - var item = tree[0].Children[1].Children[0]; - var container = (TreeViewItem)target.TreeContainerFromItem(item); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); + var item = data[0].Children[1].Children[0]; + var container = Assert.IsType(target.TreeContainerFromItem(item)); - Assert.NotNull(container); + Assert.NotNull(container); - target.SelectedItem = item; + target.SelectedItem = item; - Assert.True(container.IsSelected); - } + Assert.True(container.IsSelected); } [Fact] public void Setting_SelectedItem_Should_Raise_SelectedItemChanged_Event() - { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); + { + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); + var item = data[0].Children[1].Children[0]; - var item = tree[0].Children[1].Children[0]; + var called = false; + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.RemovedItems); + Assert.Equal(1, e.AddedItems.Count); + Assert.Same(item, e.AddedItems[0]); + called = true; + }; - var called = false; - target.SelectionChanged += (s, e) => - { - Assert.Empty(e.RemovedItems); - Assert.Equal(1, e.AddedItems.Count); - Assert.Same(item, e.AddedItems[0]); - called = true; - }; - - target.SelectedItem = item; - Assert.True(called); - } + target.SelectedItem = item; + Assert.True(called); } [Fact] public void Bound_SelectedItem_Should_Not_Be_Cleared_when_Changing_Selection() { - using (Application()) - { - var dataContext = new TestDataContext(); + using var app = Start(); + var dataContext = new TestDataContext(); + var target = CreateTarget(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - DataContext = dataContext - }; + target.DataContext = dataContext; + target.Bind(TreeView.ItemsSourceProperty, new Binding("Items")); + target.Bind(TreeView.SelectedItemProperty, new Binding("SelectedItem")); - target.Bind(TreeView.ItemsSourceProperty, new Binding("Items")); - target.Bind(TreeView.SelectedItemProperty, new Binding("SelectedItem")); + var selectedValues = new List(); - var visualRoot = new TestRoot(); - visualRoot.Child = target; + dataContext.PropertyChanged += (_, e) => + { + if (e.PropertyName == nameof(TestDataContext.SelectedItem)) + selectedValues.Add(dataContext.SelectedItem); + }; - CreateNodeDataTemplate(target); - ApplyTemplates(target); + selectedValues.Add(dataContext.SelectedItem); - var selectedValues = new List(); + _mouse.Click(target.Presenter!.Panel!.Children[0], MouseButton.Left); + _mouse.Click(target.Presenter!.Panel!.Children[2], MouseButton.Left); - dataContext.PropertyChanged += (_, e) => - { - if (e.PropertyName == nameof(TestDataContext.SelectedItem)) - selectedValues.Add(dataContext.SelectedItem); - }; - selectedValues.Add(dataContext.SelectedItem); + Assert.Equal(3, selectedValues.Count); + Assert.Equal(new[] { null, "Item 0", "Item 2" }, selectedValues.ToArray()); + } + + [Fact] + public void Expanding_SelectedItem_To_Be_Visible_Should_Result_In_Selected_Container() + { + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data, expandAll: false); + + target.SelectedItem = data[0].Children[1]; - _mouse.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Left); - _mouse.Click((Interactive)target.Presenter.Panel.Children[2], MouseButton.Left); + var rootItem = Assert.IsType(target.ContainerFromIndex(0)); + rootItem.IsExpanded = true; + Layout(target); - Assert.Equal(3, selectedValues.Count); - Assert.Equal(new[] { null, "Item 0", "Item 2" }, selectedValues.ToArray()); - } + var container = Assert.IsType(rootItem.ContainerFromIndex(1)); + Assert.True(container.IsSelected); } [Fact] public void LogicalChildren_Should_Be_Set() { - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = new[] { "Foo", "Bar", "Baz " }, - }; + using var app = Start(); + var target = CreateTarget(data: null); - ApplyTemplates(target); + target.ItemsSource = new[] { "Foo", "Bar", "Baz " }; + Layout(target); var result = target.GetLogicalChildren() .OfType() - .Select(x => x.HeaderPresenter.Child) + .Select(x => x.HeaderPresenter?.Child) .OfType() .Select(x => x.Text) .ToList(); @@ -1155,6 +789,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void DataContexts_Should_Be_Correctly_Set() { + using var app = Start(); var items = new object[] { "Foo", @@ -1163,20 +798,14 @@ namespace Avalonia.Controls.UnitTests new TreeViewItem { Header = "Qux" }, }; - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - DataContext = "Base", - DataTemplates = - { - new FuncDataTemplate((x, _) => new Button { Content = x }) - }, - ItemsSource = items, - }; + var target = CreateTarget(); + var root = Assert.IsType(target.GetVisualRoot()); - ApplyTemplates(target); + root.DataTemplates.Add(new FuncDataTemplate((x, _) => new Button { Content = x })); + target.DataContext = "Base"; + target.ItemsSource = items; - var dataContexts = target.Presenter.Panel.Children + var dataContexts = target.Presenter!.Panel!.Children .Cast() .Select(x => x.DataContext) .ToList(); @@ -1189,37 +818,22 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Control_Item_Should_Not_Be_NameScope() { - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - Items = - { - new TreeViewItem(), - } - }; + using var app = Start(); + var target = CreateTarget(data: null); + var item = new TreeViewItem(); - target.ApplyTemplate(); - target.Presenter.ApplyTemplate(); + target.Items!.Add(item); - var item = target.LogicalChildren[0]; + Assert.Same(item, target.LogicalChildren[0]); Assert.Null(NameScope.GetNameScope((TreeViewItem)item)); } [Fact] public void Should_React_To_Children_Changing() { + using var app = Start(); var data = CreateTestTreeData(); - - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = data, - }; - - var root = new TestRoot(target); - - CreateNodeDataTemplate(target); - ApplyTemplates(target); + var target = CreateTarget(data: data); Assert.Equal(new[] { "Root" }, ExtractItemHeader(target, 0)); Assert.Equal(new[] { "Child1", "Child2", "Child3" }, ExtractItemHeader(target, 1)); @@ -1236,7 +850,7 @@ namespace Avalonia.Controls.UnitTests } }; - ApplyTemplates(target); + Layout(target); Assert.Equal(new[] { "Root" }, ExtractItemHeader(target, 0)); Assert.Equal(new[] { "NewChild1" }, ExtractItemHeader(target, 1)); @@ -1245,374 +859,240 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Keyboard_Navigation_Should_Move_To_Last_Selected_Node() { - using (Application()) - { - var focus = FocusManager.Instance; - var navigation = AvaloniaLocator.Current.GetRequiredService(); - var data = CreateTestTreeData(); + using var app = Start(); + var focus = FocusManager.Instance!; + var navigation = AvaloniaLocator.Current.GetRequiredService(); + var data = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = data, - }; + var target = new TreeView + { + ItemsSource = data, + }; - var button = new Button(); + var button = new Button(); - var root = new TestRoot - { - Child = new StackPanel - { - Children = { target, button }, - } - }; + var root = CreateRoot(new StackPanel + { + Children = { target, button }, + }); - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); + root.LayoutManager.ExecuteInitialLayoutPass(); + ExpandAll(target); - var item = data[0].Children[0]; - var node = target.TreeContainerFromItem(item); - Assert.NotNull(node); + var item = data[0].Children[0]; + var node = target.TreeContainerFromItem(item); + Assert.NotNull(node); - target.SelectedItem = item; - node.Focus(); - Assert.Same(node, focus.Current); + target.SelectedItem = item; + node.Focus(); + Assert.Same(node, focus.Current); - navigation.Move(focus.Current, NavigationDirection.Next); - Assert.Same(button, focus.Current); + navigation.Move(focus.Current!, NavigationDirection.Next); + Assert.Same(button, focus.Current); - navigation.Move(focus.Current, NavigationDirection.Next); - Assert.Same(node, focus.Current); - } + navigation.Move(focus.Current!, NavigationDirection.Next); + Assert.Same(node, focus.Current); } [Fact] public void Keyboard_Navigation_Should_Not_Crash_If_Selected_Item_Is_not_In_Tree() { - using (Application()) - { - var focus = FocusManager.Instance; - var data = CreateTestTreeData(); - - var selectedNode = new Node { Value = "Out of Tree Selected Item" }; + using var app = Start(); + var focus = FocusManager.Instance!; + var data = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = data, - SelectedItem = selectedNode - }; + var selectedNode = new Node { Value = "Out of Tree Selected Item" }; - var button = new Button(); + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + ItemsSource = data, + SelectedItem = selectedNode + }; - var root = new TestRoot - { - Child = new StackPanel - { - Children = { target, button }, - } - }; + var button = new Button(); - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); + var root = CreateRoot(new StackPanel + { + Children = { target, button }, + }); - var item = data[0].Children[0]; - var node = target.TreeContainerFromItem(item); - Assert.NotNull(node); + root.LayoutManager.ExecuteInitialLayoutPass(); + ExpandAll(target); - target.SelectedItem = selectedNode; - node.Focus(); - Assert.Same(node, focus.Current); + var item = data[0].Children[0]; + var node = target.TreeContainerFromItem(item); + Assert.NotNull(node); - var next = KeyboardNavigationHandler.GetNext(node, NavigationDirection.Previous); - } + target.SelectedItem = selectedNode; + node.Focus(); + Assert.Same(node, focus.Current); } [Fact] public void Pressing_SelectAll_Gesture_Should_Select_All_Nodes() { - using (UnitTestApplication.Start()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - SelectionMode = SelectionMode.Multiple - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); - - var rootNode = tree[0]; - - var keymap = AvaloniaLocator.Current.GetRequiredService(); - var selectAllGesture = keymap.SelectAll.First(); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data, multiSelect: true); + var rootNode = data[0]; + var keymap = AvaloniaLocator.Current.GetRequiredService(); + var selectAllGesture = keymap.SelectAll.First(); - var keyEvent = new KeyEventArgs - { - RoutedEvent = InputElement.KeyDownEvent, - Key = selectAllGesture.Key, - KeyModifiers = selectAllGesture.KeyModifiers - }; + var keyEvent = new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = selectAllGesture.Key, + KeyModifiers = selectAllGesture.KeyModifiers + }; - target.RaiseEvent(keyEvent); + target.RaiseEvent(keyEvent); - AssertChildrenSelected(target, rootNode); - } + AssertChildrenSelected(target, rootNode); } [Fact] public void Pressing_SelectAll_Gesture_With_Downward_Range_Selected_Should_Select_All_Nodes() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - SelectionMode = SelectionMode.Multiple - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); - - var rootNode = tree[0]; - - var from = rootNode.Children[0]; - var to = rootNode.Children.Last(); - - var fromContainer = (TreeViewItem)target.TreeContainerFromItem(from); - var toContainer = (TreeViewItem)target.TreeContainerFromItem(to); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data, multiSelect: true); + var rootNode = data[0]; + var from = rootNode.Children[0]; + var to = rootNode.Children.Last(); + var fromContainer = Assert.IsType(target.TreeContainerFromItem(from)); + var toContainer = Assert.IsType(target.TreeContainerFromItem(to)); - ClickContainer(fromContainer, KeyModifiers.None); - ClickContainer(toContainer, KeyModifiers.Shift); + ClickContainer(fromContainer, KeyModifiers.None); + ClickContainer(toContainer, KeyModifiers.Shift); - var keymap = AvaloniaLocator.Current.GetRequiredService(); - var selectAllGesture = keymap.SelectAll.First(); + var keymap = AvaloniaLocator.Current.GetRequiredService(); + var selectAllGesture = keymap.SelectAll.First(); - var keyEvent = new KeyEventArgs - { - RoutedEvent = InputElement.KeyDownEvent, - Key = selectAllGesture.Key, - KeyModifiers = selectAllGesture.KeyModifiers - }; + var keyEvent = new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = selectAllGesture.Key, + KeyModifiers = selectAllGesture.KeyModifiers + }; - target.RaiseEvent(keyEvent); + target.RaiseEvent(keyEvent); - AssertChildrenSelected(target, rootNode); - } + AssertChildrenSelected(target, rootNode); } [Fact] public void Pressing_SelectAll_Gesture_With_Upward_Range_Selected_Should_Select_All_Nodes() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - SelectionMode = SelectionMode.Multiple - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); - - var rootNode = tree[0]; - - var from = rootNode.Children.Last(); - var to = rootNode.Children[0]; - - var fromContainer = (TreeViewItem)target.TreeContainerFromItem(from); - var toContainer = (TreeViewItem)target.TreeContainerFromItem(to); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data, multiSelect: true); + var rootNode = data[0]; + var from = rootNode.Children.Last(); + var to = rootNode.Children[0]; + var fromContainer = Assert.IsType(target.TreeContainerFromItem(from)); + var toContainer = Assert.IsType(target.TreeContainerFromItem(to)); - ClickContainer(fromContainer, KeyModifiers.None); - ClickContainer(toContainer, KeyModifiers.Shift); + ClickContainer(fromContainer, KeyModifiers.None); + ClickContainer(toContainer, KeyModifiers.Shift); - var keymap = AvaloniaLocator.Current.GetRequiredService(); - var selectAllGesture = keymap.SelectAll.First(); + var keymap = AvaloniaLocator.Current.GetRequiredService(); + var selectAllGesture = keymap.SelectAll.First(); - var keyEvent = new KeyEventArgs - { - RoutedEvent = InputElement.KeyDownEvent, - Key = selectAllGesture.Key, - KeyModifiers = selectAllGesture.KeyModifiers - }; + var keyEvent = new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = selectAllGesture.Key, + KeyModifiers = selectAllGesture.KeyModifiers + }; - target.RaiseEvent(keyEvent); + target.RaiseEvent(keyEvent); - AssertChildrenSelected(target, rootNode); - } + AssertChildrenSelected(target, rootNode); } [Fact] public void Right_Click_On_SelectedItem_Should_Not_Clear_Existing_Selection() { - using (UnitTestApplication.Start()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - SelectionMode = SelectionMode.Multiple, - }; - AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); - var visualRoot = new TestRoot(); - visualRoot.Child = target; + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data, multiSelect: true); - CreateNodeDataTemplate(target); - ApplyTemplates(target); - target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]); - target.SelectAll(); + target.SelectAll(); - AssertChildrenSelected(target, tree[0]); - Assert.Equal(5, target.SelectedItems.Count); + AssertChildrenSelected(target, data[0]); + Assert.Equal(5, target.SelectedItems.Count); - _mouse.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Right); + _mouse.Click(target.Presenter!.Panel!.Children[0], MouseButton.Right); - Assert.Equal(5, target.SelectedItems.Count); - } + Assert.Equal(5, target.SelectedItems.Count); } [Fact] public void Right_Click_On_UnselectedItem_Should_Clear_Existing_Selection() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - SelectionMode = SelectionMode.Multiple, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]); - - var rootNode = tree[0]; - var to = rootNode.Children[0]; - var then = rootNode.Children[1]; - - var fromContainer = (TreeViewItem)target.TreeContainerFromItem(rootNode); - var toContainer = (TreeViewItem)target.TreeContainerFromItem(to); - var thenContainer = (TreeViewItem)target.TreeContainerFromItem(then); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data, multiSelect: true); + var rootNode = data[0]; + var to = rootNode.Children[0]; + var then = rootNode.Children[1]; + var fromContainer = Assert.IsType(target.TreeContainerFromItem(rootNode)); + var toContainer = Assert.IsType(target.TreeContainerFromItem(to)); + var thenContainer = Assert.IsType(target.TreeContainerFromItem(then)); - ClickContainer(fromContainer, KeyModifiers.None); - ClickContainer(toContainer, KeyModifiers.Shift); + ClickContainer(fromContainer, KeyModifiers.None); + ClickContainer(toContainer, KeyModifiers.Shift); - Assert.Equal(2, target.SelectedItems.Count); + Assert.Equal(2, target.SelectedItems.Count); - _mouse.Click(thenContainer, MouseButton.Right); + _mouse.Click(thenContainer, MouseButton.Right); - Assert.Equal(1, target.SelectedItems.Count); - } + Assert.Equal(1, target.SelectedItems.Count); } [Fact] public void Shift_Right_Click_Should_Not_Select_Multiple() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - SelectionMode = SelectionMode.Multiple, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]); - - var rootNode = tree[0]; - var from = rootNode.Children[0]; - var to = rootNode.Children[1]; - var fromContainer = (TreeViewItem)target.TreeContainerFromItem(from); - var toContainer = (TreeViewItem)target.TreeContainerFromItem(to); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data, multiSelect: true); + var rootNode = data[0]; + var from = rootNode.Children[0]; + var to = rootNode.Children[1]; + var fromContainer = Assert.IsType(target.TreeContainerFromItem(from)); + var toContainer = Assert.IsType(target.TreeContainerFromItem(to)); - _mouse.Click(fromContainer); - _mouse.Click(toContainer, MouseButton.Right, modifiers: KeyModifiers.Shift); + _mouse.Click(fromContainer); + _mouse.Click(toContainer, MouseButton.Right, modifiers: KeyModifiers.Shift); - Assert.Equal(1, target.SelectedItems.Count); - } + Assert.Equal(1, target.SelectedItems.Count); } [Fact] public void Ctrl_Right_Click_Should_Not_Select_Multiple() { - using (Application()) - { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - SelectionMode = SelectionMode.Multiple, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]); - - var rootNode = tree[0]; - var from = rootNode.Children[0]; - var to = rootNode.Children[1]; - var fromContainer = (TreeViewItem)target.TreeContainerFromItem(from); - var toContainer = (TreeViewItem)target.TreeContainerFromItem(to); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data, multiSelect: true); + var rootNode = data[0]; + var from = rootNode.Children[0]; + var to = rootNode.Children[1]; + var fromContainer = Assert.IsType(target.TreeContainerFromItem(from)); + var toContainer = Assert.IsType(target.TreeContainerFromItem(to)); - _mouse.Click(fromContainer); - _mouse.Click(toContainer, MouseButton.Right, modifiers: KeyModifiers.Control); + _mouse.Click(fromContainer); + _mouse.Click(toContainer, MouseButton.Right, modifiers: KeyModifiers.Control); - Assert.Equal(1, target.SelectedItems.Count); - } + Assert.Equal(1, target.SelectedItems.Count); } [Fact] public void TreeViewItems_Level_Should_Be_Set() { - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); Assert.Equal(0, GetItem(target, 0).Level); Assert.Equal(1, GetItem(target, 0, 0).Level); @@ -1624,6 +1104,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void TreeViewItems_Level_Should_Be_Set_For_Derived_TreeView() { + using var app = Start(); var tree = CreateTestTreeData(); var target = new DerivedTreeView { @@ -1631,11 +1112,8 @@ namespace Avalonia.Controls.UnitTests ItemsSource = tree, }; - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); + var root = CreateRoot(target); + root.LayoutManager.ExecuteInitialLayoutPass(); ExpandAll(target); Assert.Equal(0, GetItem(target, 0).Level); @@ -1649,29 +1127,15 @@ namespace Avalonia.Controls.UnitTests public void Adding_Node_To_Removed_And_ReAdded_Parent_Should_Not_Crash() { // Issue #2985 - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - var visualRoot = new TestRoot(); - visualRoot.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); - - var parent = tree[0]; + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); + var parent = data[0]; var node = parent.Children[1]; parent.Children.Remove(node); parent.Children.Add(node); - var item = target.TreeContainerFromItem(node); - ApplyTemplates(new[] { item }); - // #2985 causes ArgumentException here. node.Children.Add(new Node()); } @@ -1680,101 +1144,69 @@ namespace Avalonia.Controls.UnitTests public void Auto_Expanding_In_Style_Should_Not_Break_Range_Selection() { // Issue #2980. - using (Application()) + using var app = Start(); + + var data = new List { - var target = new DerivedTreeView - { - Template = CreateTreeViewTemplate(), - SelectionMode = SelectionMode.Multiple, - ItemsSource = new List - { - new Node { Value = "Root1", }, - new Node { Value = "Root2", }, - }, - }; + new Node { Value = "Root1", }, + new Node { Value = "Root2", }, + }; - var visualRoot = new TestRoot + var style = new Style(x => x.OfType()) + { + Setters = { - Styles = - { - new Style(x => x.OfType()) - { - Setters = - { - new Setter(TreeViewItem.IsExpandedProperty, true), - }, - }, - }, - Child = target, - }; + new Setter(TreeViewItem.IsExpandedProperty, true), + }, + }; - CreateNodeDataTemplate(target); - ApplyTemplates(target); + var target = CreateTarget(data: data, styles: new[] { style }, multiSelect: true); - _mouse.Click(GetItem(target, 0)); - _mouse.Click(GetItem(target, 1), modifiers: KeyModifiers.Shift); - } + _mouse.Click(GetItem(target, 0)); + _mouse.Click(GetItem(target, 1), modifiers: KeyModifiers.Shift); } [Fact] public void Removing_TreeView_From_Root_Should_Preserve_TreeViewItems() { // Issue #3328 - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - var root = new TestRoot(); - root.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - ExpandAll(target); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); + var root = Assert.IsType(target.GetVisualRoot()); Assert.Equal(5, target.GetRealizedTreeContainers().Count()); root.Child = null; Assert.Equal(5, target.GetRealizedTreeContainers().Count()); - Assert.Equal(1, target.Presenter.Panel.Children.Count); + Assert.Equal(1, target.Presenter!.Panel!.Children.Count); var rootNode = Assert.IsType(target.Presenter.Panel.Children[0]); Assert.Equal(3, rootNode.GetRealizedContainers().Count()); - Assert.Equal(3, rootNode.Presenter.Panel.Children.Count); + Assert.Equal(3, rootNode.Presenter!.Panel!.Children.Count); var child2Node = Assert.IsType(rootNode.Presenter.Panel.Children[1]); Assert.Equal(1, child2Node.GetRealizedContainers().Count()); - Assert.Equal(1, child2Node.Presenter.Panel.Children.Count); + Assert.Equal(1, child2Node.Presenter!.Panel!.Children.Count); } [Fact] public void Clearing_TreeView_Items_Clears_Index() { // Issue #3551 - var tree = CreateTestTreeData(); - var target = new TreeView - { - Template = CreateTreeViewTemplate(), - ItemsSource = tree, - }; - - var root = new TestRoot(); - root.Child = target; - - CreateNodeDataTemplate(target); - ApplyTemplates(target); - - var rootNode = tree[0]; - var container = (TreeViewItem)target.TreeContainerFromItem(rootNode); + using var app = Start(); + var data = CreateTestTreeData(); + var target = CreateTarget(data: data); + var root = Assert.IsType(target.GetVisualRoot()); + var rootNode = data[0]; + var container = Assert.IsType(target.TreeContainerFromItem(rootNode)); Assert.NotNull(container); root.Child = null; - tree.Clear(); + data.Clear(); Assert.Empty(target.GetRealizedContainers()); } @@ -1789,7 +1221,9 @@ namespace Avalonia.Controls.UnitTests ItemsSource = tree, }; - ApplyTemplates(target); + var root = CreateRoot(target); + root.LayoutManager.ExecuteInitialLayoutPass(); + ExpandAll(target); // Verify that all items are DerivedTreeViewItem foreach (var container in target.GetRealizedTreeContainers()) @@ -1798,39 +1232,54 @@ namespace Avalonia.Controls.UnitTests } } - private void ApplyTemplates(TreeView tree) + private static TreeView CreateTarget(Optional?> data = default, + bool expandAll = true, + ControlTheme? itemContainerTheme = null, + IDataTemplate? itemTemplate = null, + bool multiSelect = false, + IEnumerable