| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528 |
- // Copyright (c) The Avalonia Project. All rights reserved.
- // Licensed under the MIT license. See licence.md file in the project root for full license information.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using Avalonia.Collections;
- using Avalonia.Controls.Presenters;
- using Avalonia.Controls.Templates;
- using Avalonia.Data;
- using Avalonia.Data.Core;
- using Avalonia.Input;
- using Avalonia.LogicalTree;
- using Avalonia.Markup.Data;
- using Avalonia.UnitTests;
- using Xunit;
- namespace Avalonia.Controls.UnitTests
- {
- public class TreeViewTests
- {
- [Fact]
- public void Items_Should_Be_Created()
- {
- var target = new TreeView
- {
- Template = CreateTreeViewTemplate(),
- Items = CreateTestTreeData(),
- };
- CreateNodeDataTemplate(target);
- ApplyTemplates(target);
- Assert.Equal(new[] { "Root" }, ExtractItemHeader(target, 0));
- Assert.Equal(new[] { "Child1", "Child2", "Child3" }, ExtractItemHeader(target, 1));
- Assert.Equal(new[] { "Grandchild2a" }, ExtractItemHeader(target, 2));
- }
- [Fact]
- public void Items_Should_Be_Created_Using_ItemTemplate_If_Present()
- {
- TreeView target;
- var root = new TestRoot
- {
- Child = target = new TreeView
- {
- Template = CreateTreeViewTemplate(),
- Items = CreateTestTreeData(),
- ItemTemplate = new FuncTreeDataTemplate<Node>(
- _ => new Canvas(),
- x => x.Children),
- }
- };
- ApplyTemplates(target);
- var items = target.ItemContainerGenerator.Index.Items
- .OfType<TreeViewItem>()
- .ToList();
- Assert.Equal(5, items.Count);
- Assert.All(items, x => Assert.IsType<Canvas>(x.HeaderPresenter.Child));
- }
- [Fact]
- public void Root_ItemContainerGenerator_Containers_Should_Be_Root_Containers()
- {
- var target = new TreeView
- {
- Template = CreateTreeViewTemplate(),
- Items = CreateTestTreeData(),
- };
- CreateNodeDataTemplate(target);
- ApplyTemplates(target);
- var container = (TreeViewItem)target.ItemContainerGenerator.Containers.Single().ContainerControl;
- var header = (TextBlock)container.Header;
- Assert.Equal("Root", header.Text);
- }
- [Fact]
- public void Root_TreeContainerFromItem_Should_Return_Descendant_Item()
- {
- var tree = CreateTestTreeData();
- var target = new TreeView
- {
- Template = CreateTreeViewTemplate(),
- Items = 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);
- var container = target.ItemContainerGenerator.Index.ContainerFromItem(
- tree[0].Children[1].Children[0]);
- Assert.NotNull(container);
- var header = ((TreeViewItem)container).Header;
- var headerContent = ((TextBlock)header).Text;
- Assert.Equal("Grandchild2a", headerContent);
- }
- [Fact]
- public void Clicking_Item_Should_Select_It()
- {
- var tree = CreateTestTreeData();
- var target = new TreeView
- {
- Template = CreateTreeViewTemplate(),
- Items = tree,
- };
- var visualRoot = new TestRoot();
- visualRoot.Child = target;
- CreateNodeDataTemplate(target);
- ApplyTemplates(target);
- var item = tree[0].Children[1].Children[0];
- var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
- Assert.NotNull(container);
- container.RaiseEvent(new PointerPressedEventArgs
- {
- RoutedEvent = InputElement.PointerPressedEvent,
- MouseButton = MouseButton.Left,
- });
- Assert.Equal(item, target.SelectedItem);
- Assert.True(container.IsSelected);
- }
- [Fact]
- public void Setting_SelectedItem_Should_Set_Container_Selected()
- {
- var tree = CreateTestTreeData();
- var target = new TreeView
- {
- Template = CreateTreeViewTemplate(),
- Items = tree,
- };
- var visualRoot = new TestRoot();
- visualRoot.Child = target;
- CreateNodeDataTemplate(target);
- ApplyTemplates(target);
- var item = tree[0].Children[1].Children[0];
- var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
- Assert.NotNull(container);
- target.SelectedItem = item;
- Assert.True(container.IsSelected);
- }
- [Fact]
- public void LogicalChildren_Should_Be_Set()
- {
- var target = new TreeView
- {
- Template = CreateTreeViewTemplate(),
- Items = new[] { "Foo", "Bar", "Baz " },
- };
- ApplyTemplates(target);
- var result = target.GetLogicalChildren()
- .OfType<TreeViewItem>()
- .Select(x => x.Header)
- .OfType<TextBlock>()
- .Select(x => x.Text)
- .ToList();
- Assert.Equal(new[] { "Foo", "Bar", "Baz " }, result);
- }
- [Fact]
- public void Removing_Item_Should_Remove_Itself_And_Children_From_Index()
- {
- var tree = CreateTestTreeData();
- var target = new TreeView
- {
- Template = CreateTreeViewTemplate(),
- Items = tree,
- };
- var root = new TestRoot();
- root.Child = target;
- CreateNodeDataTemplate(target);
- ApplyTemplates(target);
- Assert.Equal(5, target.ItemContainerGenerator.Index.Items.Count());
- tree[0].Children.RemoveAt(1);
- Assert.Equal(3, target.ItemContainerGenerator.Index.Items.Count());
- }
- [Fact]
- public void DataContexts_Should_Be_Correctly_Set()
- {
- var items = new object[]
- {
- "Foo",
- new Node { Value = "Bar" },
- new TextBlock { Text = "Baz" },
- new TreeViewItem { Header = "Qux" },
- };
- var target = new TreeView
- {
- Template = CreateTreeViewTemplate(),
- DataContext = "Base",
- DataTemplates =
- {
- new FuncDataTemplate<Node>(x => new Button { Content = x })
- },
- Items = items,
- };
- ApplyTemplates(target);
- var dataContexts = target.Presenter.Panel.Children
- .Cast<Control>()
- .Select(x => x.DataContext)
- .ToList();
- Assert.Equal(
- new object[] { items[0], items[1], "Base", "Base" },
- dataContexts);
- }
- [Fact]
- public void Control_Item_Should_Not_Be_NameScope()
- {
- var items = new object[]
- {
- new TreeViewItem(),
- };
- var target = new TreeView
- {
- Template = CreateTreeViewTemplate(),
- Items = items,
- };
- target.ApplyTemplate();
- target.Presenter.ApplyTemplate();
- var item = target.Presenter.Panel.LogicalChildren[0];
- Assert.Null(NameScope.GetNameScope((TreeViewItem)item));
- }
- [Fact]
- public void DataTemplate_Created_Item_Should_Be_NameScope()
- {
- var items = new object[]
- {
- "foo",
- };
- var target = new TreeView
- {
- Template = CreateTreeViewTemplate(),
- Items = items,
- };
- target.ApplyTemplate();
- target.Presenter.ApplyTemplate();
- var item = target.Presenter.Panel.LogicalChildren[0];
- Assert.NotNull(NameScope.GetNameScope((TreeViewItem)item));
- }
- [Fact]
- public void Should_React_To_Children_Changing()
- {
- var data = CreateTestTreeData();
- var target = new TreeView
- {
- Template = CreateTreeViewTemplate(),
- Items = data,
- };
- CreateNodeDataTemplate(target);
- ApplyTemplates(target);
- Assert.Equal(new[] { "Root" }, ExtractItemHeader(target, 0));
- Assert.Equal(new[] { "Child1", "Child2", "Child3" }, ExtractItemHeader(target, 1));
- Assert.Equal(new[] { "Grandchild2a" }, ExtractItemHeader(target, 2));
- // Make sure that the binding to Node.Children does not get collected.
- GC.Collect();
- data[0].Children = new AvaloniaList<Node>
- {
- new Node
- {
- Value = "NewChild1",
- }
- };
- Assert.Equal(new[] { "Root" }, ExtractItemHeader(target, 0));
- Assert.Equal(new[] { "NewChild1" }, ExtractItemHeader(target, 1));
- }
- [Fact]
- public void Keyboard_Navigation_Should_Move_To_Last_Selected_Node()
- {
- using (UnitTestApplication.Start(TestServices.RealFocus))
- {
- var focus = FocusManager.Instance;
- var navigation = AvaloniaLocator.Current.GetService<IKeyboardNavigationHandler>();
- var data = CreateTestTreeData();
- var target = new TreeView
- {
- Template = CreateTreeViewTemplate(),
- Items = data,
- };
- var button = new Button();
- var root = new TestRoot
- {
- Child = new StackPanel
- {
- Children = { target, button },
- }
- };
- CreateNodeDataTemplate(target);
- ApplyTemplates(target);
- var item = data[0].Children[0];
- var node = target.ItemContainerGenerator.Index.ContainerFromItem(item);
- Assert.NotNull(node);
- 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(node, focus.Current);
- }
- }
- private void ApplyTemplates(TreeView tree)
- {
- tree.ApplyTemplate();
- tree.Presenter.ApplyTemplate();
- ApplyTemplates(tree.Presenter.Panel.Children);
- }
- private void ApplyTemplates(IEnumerable<IControl> controls)
- {
- foreach (TreeViewItem control in controls)
- {
- control.Template = CreateTreeViewItemTemplate();
- control.ApplyTemplate();
- control.Presenter.ApplyTemplate();
- control.HeaderPresenter.ApplyTemplate();
- ApplyTemplates(control.Presenter.Panel.Children);
- }
- }
- private IList<Node> CreateTestTreeData()
- {
- return new AvaloniaList<Node>
- {
- new Node
- {
- Value = "Root",
- Children = new AvaloniaList<Node>
- {
- new Node
- {
- Value = "Child1",
- },
- new Node
- {
- Value = "Child2",
- Children = new AvaloniaList<Node>
- {
- new Node
- {
- Value = "Grandchild2a",
- },
- },
- },
- new Node
- {
- Value = "Child3",
- },
- }
- }
- };
- }
- private void CreateNodeDataTemplate(IControl control)
- {
- control.DataTemplates.Add(new TestTreeDataTemplate());
- }
- private IControlTemplate CreateTreeViewTemplate()
- {
- return new FuncControlTemplate<TreeView>(parent => new ItemsPresenter
- {
- Name = "PART_ItemsPresenter",
- [~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty],
- });
- }
- private IControlTemplate CreateTreeViewItemTemplate()
- {
- return new FuncControlTemplate<TreeViewItem>(parent => new Panel
- {
- Children =
- {
- new ContentPresenter
- {
- Name = "PART_HeaderPresenter",
- [~ContentPresenter.ContentProperty] = parent[~TreeViewItem.HeaderProperty],
- },
- new ItemsPresenter
- {
- Name = "PART_ItemsPresenter",
- [~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty],
- }
- }
- });
- }
- private List<string> ExtractItemHeader(TreeView tree, int level)
- {
- return ExtractItemContent(tree.Presenter.Panel, 0, level)
- .Select(x => x.Header)
- .OfType<TextBlock>()
- .Select(x => x.Text)
- .ToList();
- }
- private IEnumerable<TreeViewItem> ExtractItemContent(IPanel panel, int currentLevel, int level)
- {
- foreach (TreeViewItem container in panel.Children)
- {
- if (container.Template == null)
- {
- container.Template = CreateTreeViewItemTemplate();
- container.ApplyTemplate();
- }
- if (currentLevel == level)
- {
- yield return container;
- }
- else
- {
- foreach (var child in ExtractItemContent(container.Presenter.Panel, currentLevel + 1, level))
- {
- yield return child;
- }
- }
- }
- }
- private class Node : NotifyingBase
- {
- private IAvaloniaList<Node> _children;
- public string Value { get; set; }
- public IAvaloniaList<Node> Children
- {
- get
- {
- return _children;
- }
- set
- {
- _children = value;
- RaisePropertyChanged(nameof(Children));
- }
- }
- }
- private class TestTreeDataTemplate : ITreeDataTemplate
- {
- public IControl Build(object param)
- {
- var node = (Node)param;
- return new TextBlock { Text = node.Value };
- }
- public bool SupportsRecycling => false;
- public InstancedBinding ItemsSelector(object item)
- {
- var obs = ExpressionObserver.Create(item, o => (o as Node).Children);
- return InstancedBinding.OneWay(obs);
- }
- public bool Match(object data)
- {
- return data is Node;
- }
- }
- }
- }
|