diff --git a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs new file mode 100644 index 0000000000..c1cae862a9 --- /dev/null +++ b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; + +namespace Avalonia.Controls.Generators +{ + public class TreeItemContainerGenerator : ItemContainerGenerator + { + internal TreeItemContainerGenerator(TreeView owner) + : base(owner) + { + Index = new TreeContainerIndex(owner); + } + + public TreeContainerIndex Index { get; } + } + + public class TreeContainerIndex + { + private readonly TreeView _owner; + + internal TreeContainerIndex(TreeView owner) => _owner = owner; + + [Obsolete("Use TreeView.GetRealizedTreeContainers")] + public IEnumerable Containers => _owner.GetRealizedTreeContainers(); + + [Obsolete("Use TreeView.TreeContainerFromItem")] + public Control? ContainerFromItem(object item) => _owner.TreeContainerFromItem(item); + + [Obsolete("Use TreeView.TreeItemFromContainer")] + public object? ItemFromContainer(Control container) => _owner.TreeItemFromContainer(container); + } +} diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index e07eea57ef..645abf50b3 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -107,7 +107,12 @@ namespace Avalonia.Controls /// /// Gets the for the control. /// - public ItemContainerGenerator ItemContainerGenerator => _itemContainerGenerator ??= new(this); + public ItemContainerGenerator ItemContainerGenerator + { +#pragma warning disable CS0612 // Type or member is obsolete + get => _itemContainerGenerator ??= CreateItemContainerGenerator(); +#pragma warning restore CS0612 // Type or member is obsolete + } /// /// Gets or sets the items to display. @@ -188,7 +193,8 @@ namespace Avalonia.Controls /// public Control? ContainerFromItem(object item) { - throw new NotImplementedException(); + var index = Items?.IndexOf(item) ?? -1; + return index >= 0 ? ContainerFromIndex(index) : null; } /// @@ -210,8 +216,8 @@ namespace Avalonia.Controls /// public object? ItemFromContainer(Control container) { - // TODO: Should this throw or return null of container isn't a container? - throw new NotImplementedException(); + var index = IndexFromContainer(container); + return index >= 0 && index < ItemCount ? Items!.ElementAt(index) : null; } /// @@ -273,6 +279,8 @@ namespace Avalonia.Controls if (container == item) return; + var itemTemplate = GetEffectiveItemTemplate(); + if (container is HeaderedContentControl hcc) { hcc.Content = item; @@ -282,20 +290,43 @@ namespace Avalonia.Controls else if (item is not Visual) hcc.Header = item; - if (GetEffectiveItemTemplate() is { } it) - hcc.HeaderTemplate = it; + if (itemTemplate is not null) + hcc.HeaderTemplate = itemTemplate; } else if (container is ContentControl cc) { cc.Content = item; - if (GetEffectiveItemTemplate() is { } it) - cc.ContentTemplate = it; + if (itemTemplate is not null) + cc.ContentTemplate = itemTemplate; } else if (container is ContentPresenter p) { p.Content = item; - if (GetEffectiveItemTemplate() is { } it) - p.ContentTemplate = it; + if (itemTemplate is not null) + p.ContentTemplate = itemTemplate; + } + else if (container is ItemsControl ic) + { + if (itemTemplate is not null) + ic.ItemTemplate = itemTemplate; + if (ItemContainerTheme is { } ict) + ic.ItemContainerTheme = ict; + } + + // This condition is separate because HeaderedItemsControl needs to also run the + // ItemsControl preparation. + if (container is HeaderedItemsControl hic) + { + hic.Header = item; + hic.HeaderTemplate = itemTemplate; + + var treeTemplate = (itemTemplate ?? hic.FindDataTemplate(item)) as ITreeDataTemplate; + + if (treeTemplate is not null) + { + if (item is not null && treeTemplate.ItemsSelector(item) is { } itemsBinding) + BindingOperations.Apply(hic, ItemsProperty, itemsBinding, null); + } } } @@ -316,6 +347,7 @@ namespace Avalonia.Controls /// The container element. protected internal virtual void ClearContainerForItemOverride(Control container) { + // TODO: Remove HeaderedItemsControl.Items binding. } /// @@ -486,6 +518,20 @@ namespace Avalonia.Controls } } + /// + /// Creates the + /// + /// + /// This method is only present for backwards compatibility with 0.10.x in order for + /// TreeView to be able to create a . Can be + /// removed in 12.0. + /// + [Obsolete] + private protected virtual ItemContainerGenerator CreateItemContainerGenerator() + { + return new ItemContainerGenerator(this); + } + internal void AddLogicalChild(Control c) => LogicalChildren.Add(c); internal void RemoveLogicalChild(Control c) => LogicalChildren.Remove(c); diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenter.cs b/src/Avalonia.Controls/Presenters/ItemsPresenter.cs index f11d803fc5..0091bbd48d 100644 --- a/src/Avalonia.Controls/Presenters/ItemsPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ItemsPresenter.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; +using Avalonia.Input; namespace Avalonia.Controls.Presenters { @@ -19,6 +20,13 @@ namespace Avalonia.Controls.Presenters private PanelContainerGenerator? _generator; + static ItemsPresenter() + { + KeyboardNavigation.TabNavigationProperty.OverrideDefaultValue( + typeof(ItemsPresenter), + KeyboardNavigationMode.Once); + } + /// /// Gets or sets a template which creates the used to display the items. /// diff --git a/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs b/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs index 2f051c4fda..71ae7a5bf6 100644 --- a/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs @@ -1,5 +1,6 @@ using Avalonia.Collections; using Avalonia.Controls.Presenters; +using Avalonia.Controls.Templates; using Avalonia.LogicalTree; namespace Avalonia.Controls.Primitives @@ -15,6 +16,12 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty HeaderProperty = HeaderedContentControl.HeaderProperty.AddOwner(); + /// + /// Defines the property. + /// + public static readonly StyledProperty HeaderTemplateProperty = + AvaloniaProperty.Register(nameof(HeaderTemplate)); + /// /// Initializes static members of the class. /// @@ -32,6 +39,15 @@ namespace Avalonia.Controls.Primitives set { SetValue(HeaderProperty, value); } } + /// + /// Gets or sets the data template used to display the header content of the control. + /// + public IDataTemplate? HeaderTemplate + { + get => GetValue(HeaderTemplateProperty); + set => SetValue(HeaderTemplateProperty, value); + } + /// /// Gets the header presenter from the control's template. /// diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index 6336219fb6..c3e891149a 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -75,6 +75,12 @@ namespace Avalonia.Controls remove => RemoveHandler(SelectingItemsControl.SelectionChangedEvent, value); } + /// + /// Gets the for the tree view. + /// + public new TreeItemContainerGenerator ItemContainerGenerator => + (TreeItemContainerGenerator)base.ItemContainerGenerator; + /// /// Gets or sets a value indicating whether to automatically scroll to newly selected items. /// @@ -182,8 +188,25 @@ namespace Avalonia.Controls /// public void SelectAll() { - throw new NotImplementedException(); - ////SynchronizeItems(SelectedItems, ItemContainerGenerator.Index!.Items); + var allItems = new List(); + + void AddItems(ItemsControl itemsControl) + { + if (itemsControl.Items is { } items) + { + foreach (var item in items) + allItems.Add(item); + } + + foreach (var child in itemsControl.GetRealizedContainers()) + { + if (child is ItemsControl childItemsControl) + AddItems(childItemsControl); + } + } + + AddItems(this); + SynchronizeItems(SelectedItems, allItems); } /// @@ -194,6 +217,62 @@ namespace Avalonia.Controls SelectedItems.Clear(); } + public IEnumerable GetRealizedTreeContainers() + { + static IEnumerable GetRealizedContainers(ItemsControl itemsControl) + { + foreach (var container in itemsControl.GetRealizedContainers()) + { + yield return container; + if (container is ItemsControl itemsControlContainer) + foreach (var child in GetRealizedContainers(itemsControlContainer)) + yield return child; + } + } + + return GetRealizedContainers(this); + } + + public Control? TreeContainerFromItem(object item) + { + static Control? TreeContainerFromItem(ItemsControl itemsControl, object item) + { + if (itemsControl.ContainerFromItem(item) is { } container) + return container; + + foreach (var child in itemsControl.GetRealizedContainers()) + { + if (child is ItemsControl childItemsControl && + TreeContainerFromItem(childItemsControl, item) is { } childContainer) + return childContainer; + } + + return null; + } + + return TreeContainerFromItem(this, item); + } + + public object? TreeItemFromContainer(Control container) + { + static object? TreeItemFromContainer(ItemsControl itemsControl, Control container) + { + if (itemsControl.ItemFromContainer(container) is { } item) + return item; + + foreach (var child in itemsControl.GetRealizedContainers()) + { + if (child is ItemsControl childItemsControl && + TreeItemFromContainer(childItemsControl, container) is { } childContainer) + return childContainer; + } + + return null; + } + + return TreeItemFromContainer(this, container); + } + /// /// Subscribes to the CollectionChanged event, if any. /// @@ -277,10 +356,9 @@ namespace Avalonia.Controls break; case NotifyCollectionChangedAction.Reset: - foreach (var child in LogicalChildren) + foreach (var container in GetRealizedTreeContainers()) { - if (child is Control container && IndexFromContainer(container) != -1) - MarkContainerSelected(container, false); + MarkContainerSelected(container, false); } if (SelectedItems.Count > 0) @@ -333,7 +411,7 @@ namespace Avalonia.Controls private void MarkItemSelected(object item, bool selected) { - var container = ContainerFromItem(item)!; + var container = TreeContainerFromItem(item)!; MarkContainerSelected(container, selected); } @@ -366,6 +444,7 @@ namespace Avalonia.Controls incc.CollectionChanged -= SelectedItemsCollectionChanged; } } + (bool handled, IInputElement? next) ICustomKeyboardNavigation.GetNext(IInputElement element, NavigationDirection direction) { @@ -374,7 +453,7 @@ namespace Avalonia.Controls if (!this.IsVisualAncestorOf((Visual)element)) { var result = _selectedItem != null ? - ContainerFromItem(_selectedItem) : + TreeContainerFromItem(_selectedItem) : ContainerFromIndex(0); return (result != null, result); // SelectedItem may not be in the treeview. @@ -386,6 +465,9 @@ namespace Avalonia.Controls return (false, null); } + protected internal override Control CreateContainerOverride() => new TreeViewItem(); + protected internal override bool IsItemItsOwnContainerOverride(Control item) => item is TreeViewItem; + /// protected override void OnGotFocus(GotFocusEventArgs e) { @@ -441,54 +523,58 @@ namespace Avalonia.Controls NavigationDirection direction, bool intoChildren) { - throw new NotImplementedException(); - ////IItemContainerGenerator? parentGenerator = GetParentContainerGenerator(from); - - ////if (parentGenerator == null) - ////{ - //// return null; - ////} - - ////var index = from is not null ? parentGenerator.IndexFromContainer(from) : -1; - ////var parent = from?.Parent as ItemsControl; - ////TreeViewItem? result = null; - - ////switch (direction) - ////{ - //// case NavigationDirection.Up: - //// if (index > 0) - //// { - //// var previous = (TreeViewItem)parentGenerator.ContainerFromIndex(index - 1)!; - //// result = previous.IsExpanded && previous.ItemCount > 0 ? - //// (TreeViewItem)previous.ItemContainerGenerator.ContainerFromIndex(previous.ItemCount - 1)! : - //// previous; - //// } - //// else - //// { - //// result = from?.Parent as TreeViewItem; - //// } - - //// break; - - //// case NavigationDirection.Down: - //// case NavigationDirection.Right: - //// if (from?.IsExpanded == true && intoChildren && from.ItemCount > 0) - //// { - //// result = (TreeViewItem)from.ItemContainerGenerator.ContainerFromIndex(0)!; - //// } - //// else if (index < parent?.ItemCount - 1) - //// { - //// result = (TreeViewItem)parentGenerator.ContainerFromIndex(index + 1)!; - //// } - //// else if (parent is TreeViewItem parentItem) - //// { - //// return GetContainerInDirection(parentItem, direction, false); - //// } - - //// break; - ////} - - ////return result; + var parentItemsControl = from?.Parent switch + { + TreeView tv => (ItemsControl)tv, + TreeViewItem i => i, + _ => null + }; + + if (parentItemsControl == null) + { + return null; + } + + var index = from is not null ? parentItemsControl.IndexFromContainer(from) : -1; + var parent = from?.Parent as ItemsControl; + TreeViewItem? result = null; + + switch (direction) + { + case NavigationDirection.Up: + if (index > 0) + { + var previous = (TreeViewItem)parentItemsControl.ContainerFromIndex(index - 1)!; + result = previous.IsExpanded && previous.ItemCount > 0 ? + (TreeViewItem)previous.ItemContainerGenerator.ContainerFromIndex(previous.ItemCount - 1)! : + previous; + } + else + { + result = from?.Parent as TreeViewItem; + } + + break; + + case NavigationDirection.Down: + case NavigationDirection.Right: + if (from?.IsExpanded == true && intoChildren && from.ItemCount > 0) + { + result = (TreeViewItem)from.ItemContainerGenerator.ContainerFromIndex(0)!; + } + else if (index < parent?.ItemCount - 1) + { + result = (TreeViewItem)parentItemsControl.ContainerFromIndex(index + 1)!; + } + else if (parent is TreeViewItem parentItem) + { + return GetContainerInDirection(parentItem, direction, false); + } + + break; + } + + return result; } /// @@ -527,63 +613,68 @@ namespace Avalonia.Controls bool toggleModifier = false, bool rightButton = false) { - throw new NotImplementedException(); - ////var item = ItemContainerGenerator.Index!.ItemFromContainer(container); - - ////if (item == null) - ////{ - //// return; - ////} - - ////Control? selectedContainer = null; - - ////if (SelectedItem != null) - ////{ - //// selectedContainer = ItemContainerGenerator.Index!.ContainerFromItem(SelectedItem); - ////} - - ////var mode = SelectionMode; - ////var toggle = toggleModifier || mode.HasAllFlags(SelectionMode.Toggle); - ////var multi = mode.HasAllFlags(SelectionMode.Multiple); - ////var range = multi && rangeModifier && selectedContainer != null; - - ////if (rightButton) - ////{ - //// if (!SelectedItems.Contains(item)) - //// { - //// SelectSingleItem(item); - //// } - ////} - ////else if (!toggle && !range) - ////{ - //// SelectSingleItem(item); - ////} - ////else if (multi && range) - ////{ - //// SynchronizeItems( - //// SelectedItems, - //// GetItemsInRange(selectedContainer as TreeViewItem, container as TreeViewItem)); - ////} - ////else - ////{ - //// var i = SelectedItems.IndexOf(item); - - //// if (i != -1) - //// { - //// SelectedItems.Remove(item); - //// } - //// else - //// { - //// if (multi) - //// { - //// SelectedItems.Add(item); - //// } - //// else - //// { - //// SelectedItem = item; - //// } - //// } - ////} + var item = TreeItemFromContainer(container); + + if (item == null) + { + return; + } + + Control? selectedContainer = null; + + if (SelectedItem != null) + { + selectedContainer = TreeContainerFromItem(SelectedItem); + } + + var mode = SelectionMode; + var toggle = toggleModifier || mode.HasAllFlags(SelectionMode.Toggle); + var multi = mode.HasAllFlags(SelectionMode.Multiple); + var range = multi && rangeModifier && selectedContainer != null; + + if (rightButton) + { + if (!SelectedItems.Contains(item)) + { + SelectSingleItem(item); + } + } + else if (!toggle && !range) + { + SelectSingleItem(item); + } + else if (multi && range) + { + SynchronizeItems( + SelectedItems, + GetItemsInRange(selectedContainer as TreeViewItem, container as TreeViewItem)); + } + else + { + var i = SelectedItems.IndexOf(item); + + if (i != -1) + { + SelectedItems.Remove(item); + } + else + { + if (multi) + { + SelectedItems.Add(item); + } + else + { + SelectedItem = item; + } + } + } + } + + [Obsolete] + private protected override ItemContainerGenerator CreateItemContainerGenerator() + { + return new TreeItemContainerGenerator(this); } /// @@ -595,27 +686,24 @@ namespace Avalonia.Controls /// Found first node. private static TreeViewItem? FindFirstNode(TreeView treeView, TreeViewItem nodeA, TreeViewItem nodeB) { - return FindInContainers(treeView.ItemContainerGenerator, nodeA, nodeB); + return FindInContainers(treeView, nodeA, nodeB); } - private static TreeViewItem? FindInContainers(ItemContainerGenerator containerGenerator, + private static TreeViewItem? FindInContainers(ItemsControl itemsControl, TreeViewItem nodeA, TreeViewItem nodeB) { - throw new NotImplementedException(); - ////IEnumerable containers = containerGenerator.Containers; - - ////foreach (ItemContainerInfo container in containers) - ////{ - //// TreeViewItem? node = FindFirstNode(container.ContainerControl as TreeViewItem, nodeA, nodeB); + foreach (var container in itemsControl.GetRealizedContainers()) + { + TreeViewItem? node = FindFirstNode(container as TreeViewItem, nodeA, nodeB); - //// if (node != null) - //// { - //// return node; - //// } - ////} + if (node != null) + { + return node; + } + } - ////return null; + return null; } private static TreeViewItem? FindFirstNode(TreeViewItem? node, TreeViewItem nodeA, TreeViewItem nodeB) @@ -632,7 +720,7 @@ namespace Avalonia.Controls return match; } - return FindInContainers(node.ItemContainerGenerator, nodeA, nodeB); + return FindInContainers(node, nodeA, nodeB); } /// @@ -643,60 +731,59 @@ namespace Avalonia.Controls /// To container. private List GetItemsInRange(TreeViewItem? from, TreeViewItem? to) { - throw new NotImplementedException(); - ////var items = new List(); + var items = new List(); - ////if (from == null || to == null) - ////{ - //// return items; - ////} + if (from == null || to == null) + { + return items; + } - ////TreeViewItem? firstItem = FindFirstNode(this, from, to); + TreeViewItem? firstItem = FindFirstNode(this, from, to); - ////if (firstItem == null) - ////{ - //// return items; - ////} + if (firstItem == null) + { + return items; + } - ////bool wasReversed = false; + bool wasReversed = false; - ////if (firstItem == to) - ////{ - //// var temp = from; + if (firstItem == to) + { + var temp = from; - //// from = to; - //// to = temp; + from = to; + to = temp; - //// wasReversed = true; - ////} + wasReversed = true; + } - ////TreeViewItem? node = from; + TreeViewItem? node = from; - ////while (node != to) - ////{ - //// var item = ItemContainerGenerator.Index!.ItemFromContainer(node); + while (node is not null && node != to) + { + var item = TreeItemFromContainer(node); - //// if (item != null) - //// { - //// items.Add(item); - //// } + if (item != null) + { + items.Add(item); + } - //// node = GetContainerInDirection(node, NavigationDirection.Down, true); - ////} + node = GetContainerInDirection(node, NavigationDirection.Down, true); + } - ////var toItem = ItemContainerGenerator.Index!.ItemFromContainer(to); + var toItem = TreeItemFromContainer(to); - ////if (toItem != null) - ////{ - //// items.Add(toItem); - ////} + if (toItem != null) + { + items.Add(toItem); + } - ////if (wasReversed) - ////{ - //// items.Reverse(); - ////} + if (wasReversed) + { + items.Reverse(); + } - ////return items; + return items; } /// @@ -737,20 +824,11 @@ namespace Avalonia.Controls /// The container or null if the event did not originate in a container. protected TreeViewItem? GetContainerFromEventSource(object eventSource) { - throw new NotImplementedException(); - ////var item = ((Visual)eventSource).GetSelfAndVisualAncestors() - //// .OfType() - //// .FirstOrDefault(); - - ////if (item != null) - ////{ - //// if (item.ItemContainerGenerator.Index == ItemContainerGenerator.Index) - //// { - //// return item; - //// } - ////} + var item = ((Visual)eventSource).GetSelfAndVisualAncestors() + .OfType() + .FirstOrDefault(); - ////return null; + return item?.TreeViewOwner == this ? item : null; } /// @@ -801,7 +879,7 @@ namespace Avalonia.Controls } else { - container.Classes.Set(":selected", selected); + ((IPseudoClasses)container.Classes).Set(":selected", selected); } } diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs index 097fe54f42..3ff34a14b1 100644 --- a/src/Avalonia.Controls/TreeViewItem.cs +++ b/src/Avalonia.Controls/TreeViewItem.cs @@ -88,6 +88,11 @@ namespace Avalonia.Controls private set { SetAndRaise(LevelProperty, ref _level, value); } } + internal TreeView? TreeViewOwner => _treeView; + + protected internal override Control CreateContainerOverride() => new TreeViewItem(); + protected internal override bool IsItemItsOwnContainerOverride(Control item) => item is TreeViewItem; + /// protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) { diff --git a/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml index 834c2245a8..b2dc8615a9 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml @@ -88,6 +88,7 @@ Grid.Column="1" Focusable="False" Content="{TemplateBinding Header}" + ContentTemplate="{TemplateBinding HeaderTemplate}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}" Margin="{TemplateBinding Padding}" /> diff --git a/src/Avalonia.Themes.Simple/Controls/TreeViewItem.xaml b/src/Avalonia.Themes.Simple/Controls/TreeViewItem.xaml index c36bf4c673..b75da2dd7b 100644 --- a/src/Avalonia.Themes.Simple/Controls/TreeViewItem.xaml +++ b/src/Avalonia.Themes.Simple/Controls/TreeViewItem.xaml @@ -58,6 +58,7 @@ Padding="{TemplateBinding Padding}" HorizontalContentAlignment="{TemplateBinding HorizontalAlignment}" Content="{TemplateBinding Header}" + ContentTemplate="{TemplateBinding HeaderTemplate}" Focusable="False" /> diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index 8ded57956b..b9a6566116 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -1,1475 +1,1441 @@ -////using System; -////using System.Collections; -////using System.Collections.Generic; -////using System.Collections.ObjectModel; -////using System.ComponentModel; -////using System.Linq; -////using System.Runtime.CompilerServices; -////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.LogicalTree; -////using Avalonia.Styling; -////using Avalonia.UnitTests; -////using JetBrains.Annotations; -////using Moq; -////using Xunit; - -////namespace Avalonia.Controls.UnitTests -////{ -//// public class TreeViewTests -//// { -//// MouseTestHelper _mouse = new MouseTestHelper(); - -//// [Fact] -//// public void Items_Should_Be_Created() -//// { -//// var target = new TreeView -//// { -//// Template = CreateTreeViewTemplate(), -//// Items = CreateTestTreeData(), -//// }; - -//// var root = new TestRoot(target); - -//// 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( -//// (_, __) => new Canvas(), -//// x => x.Children), -//// } -//// }; - -//// ApplyTemplates(target); - -//// var items = target.ItemContainerGenerator.Index.Containers -//// .OfType() -//// .ToList(); - -//// Assert.Equal(5, items.Count); -//// 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(), -//// Items = CreateTestTreeData(), -//// ItemContainerTheme = theme, -//// ItemTemplate = new FuncTreeDataTemplate( -//// (_, __) => new Canvas(), -//// x => x.Children), -//// } -//// }; - -//// ApplyTemplates(target); - -//// var items = target.ItemContainerGenerator.Index.Containers -//// .OfType() -//// .ToList(); - -//// Assert.Equal(5, items.Count); -//// Assert.All(items, x => Assert.Same(theme, x.ItemContainerTheme)); -//// } - -//// [Fact] -//// public void Root_ItemContainerGenerator_Containers_Should_Be_Root_Containers() -//// { -//// var target = new TreeView -//// { -//// Template = CreateTreeViewTemplate(), -//// Items = CreateTestTreeData(), -//// }; - -//// var root = new TestRoot(target); - -//// 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() -//// { -//// using (Application()) -//// { -//// var tree = CreateTestTreeData(); -//// var target = new TreeView -//// { -//// Template = CreateTreeViewTemplate(), -//// Items = 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.ItemContainerGenerator.Index.ContainerFromItem(item); - -//// Assert.NotNull(container); - -//// _mouse.Click(container); - -//// 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(), -//// Items = 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.ItemContainerGenerator.Index.ContainerFromItem(item); - -//// Assert.NotNull(container); - -//// target.SelectedItem = item; - -//// Assert.True(container.IsSelected); - -//// _mouse.Click(container, modifiers: KeyModifiers.Control); - -//// 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(), -//// Items = tree -//// }; +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.LogicalTree; +using Avalonia.Styling; +using Avalonia.UnitTests; +using Moq; +using Xunit; + +namespace Avalonia.Controls.UnitTests +{ + public class TreeViewTests + { + MouseTestHelper _mouse = new MouseTestHelper(); + + [Fact] + public void Items_Should_Be_Created() + { + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = CreateTestTreeData(), + }; + + var root = new TestRoot(target); + + 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( + (_, __) => new Canvas(), + x => x.Children), + } + }; + + ApplyTemplates(target); + + var items = target.GetRealizedTreeContainers() + .OfType() + .ToList(); + + Assert.Equal(5, items.Count); + 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(), + Items = CreateTestTreeData(), + ItemContainerTheme = theme, + ItemTemplate = new FuncTreeDataTemplate( + (_, __) => new Canvas(), + x => x.Children), + } + }; + + ApplyTemplates(target); + + var items = target.GetRealizedTreeContainers() + .OfType() + .ToList(); + + Assert.Equal(5, items.Count); + Assert.All(items, x => Assert.Same(theme, x.ItemContainerTheme)); + } + + [Fact] + public void Root_ItemContainerGenerator_Containers_Should_Be_Root_Containers() + { + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = CreateTestTreeData(), + }; + + var root = new TestRoot(target); + + CreateNodeDataTemplate(target); + ApplyTemplates(target); + + var container = (TreeViewItem)target.GetRealizedContainers().Single(); + var header = (TextBlock)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(), + 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.TreeContainerFromItem(tree[0].Children[1].Children[0]); + + Assert.NotNull(container); + + var header = ((TreeViewItem)container).HeaderPresenter; + var headerContent = ((TextBlock)header.Child).Text; + + Assert.Equal("Grandchild2a", headerContent); + } + + [Fact] + public void Clicking_Item_Should_Select_It() + { + using (Application()) + { + var tree = CreateTestTreeData(); + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = 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); + + Assert.NotNull(container); + + _mouse.Click(container); + + 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(), + Items = tree + }; -//// var visualRoot = new TestRoot(); -//// visualRoot.Child = target; + 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); + + Assert.NotNull(container); + + target.SelectedItem = item; + + Assert.True(container.IsSelected); + + _mouse.Click(container, modifiers: KeyModifiers.Control); + + 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(), + Items = tree + }; -//// CreateNodeDataTemplate(target); -//// ApplyTemplates(target); -//// ExpandAll(target); + var visualRoot = new TestRoot(); + visualRoot.Child = target; -//// var item1 = tree[0].Children[1].Children[0]; -//// var container1 = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item1); + CreateNodeDataTemplate(target); + ApplyTemplates(target); + ExpandAll(target); -//// var item2 = tree[0].Children[1]; -//// var container2 = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item2); + var item1 = tree[0].Children[1].Children[0]; + var container1 = (TreeViewItem)target.TreeContainerFromItem(item1); -//// Assert.NotNull(container1); -//// Assert.NotNull(container2); - -//// target.SelectedItem = item1; - -//// Assert.True(container1.IsSelected); - -//// _mouse.Click(container2, modifiers: KeyModifiers.Control); - -//// 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(), -//// Items = tree, -//// SelectionMode = SelectionMode.Multiple -//// }; + var item2 = tree[0].Children[1]; + var container2 = (TreeViewItem)target.TreeContainerFromItem(item2); -//// var visualRoot = new TestRoot(); -//// visualRoot.Child = target; + Assert.NotNull(container1); + Assert.NotNull(container2); + + target.SelectedItem = item1; + + Assert.True(container1.IsSelected); + + _mouse.Click(container2, modifiers: KeyModifiers.Control); + + 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(), + Items = tree, + SelectionMode = SelectionMode.Multiple + }; -//// CreateNodeDataTemplate(target); -//// ApplyTemplates(target); -//// ExpandAll(target); + var visualRoot = new TestRoot(); + visualRoot.Child = target; -//// var rootNode = tree[0]; - -//// var item1 = rootNode.Children[0]; -//// var item2 = rootNode.Children.Last(); - -//// var item1Container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item1); -//// var item2Container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item2); - -//// ClickContainer(item1Container, KeyModifiers.Control); -//// Assert.True(item1Container.IsSelected); - -//// ClickContainer(item2Container, KeyModifiers.Control); -//// Assert.True(item2Container.IsSelected); + CreateNodeDataTemplate(target); + ApplyTemplates(target); + ExpandAll(target); -//// Assert.Equal(new[] { item1, item2 }, target.SelectedItems.OfType()); + 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); + + ClickContainer(item1Container, KeyModifiers.Control); + Assert.True(item1Container.IsSelected); + + ClickContainer(item2Container, KeyModifiers.Control); + Assert.True(item2Container.IsSelected); -//// ClickContainer(item1Container, KeyModifiers.Control); -//// Assert.False(item1Container.IsSelected); + Assert.Equal(new[] { item1, item2 }, target.SelectedItems.OfType()); -//// Assert.DoesNotContain(item1, target.SelectedItems.OfType()); -//// } -//// } + ClickContainer(item1Container, KeyModifiers.Control); + Assert.False(item1Container.IsSelected); -//// [Fact] -//// public void Clicking_WithShiftModifier_DownDirection_Should_Select_Range_Of_Items() -//// { -//// using (Application()) -//// { -//// var tree = CreateTestTreeData(); -//// var target = new TreeView -//// { -//// Template = CreateTreeViewTemplate(), -//// Items = tree, -//// SelectionMode = SelectionMode.Multiple -//// }; + Assert.DoesNotContain(item1, target.SelectedItems.OfType()); + } + } -//// var visualRoot = new TestRoot(); -//// visualRoot.Child = target; + [Fact] + public void Clicking_WithShiftModifier_DownDirection_Should_Select_Range_Of_Items() + { + using (Application()) + { + var tree = CreateTestTreeData(); + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = tree, + SelectionMode = SelectionMode.Multiple + }; -//// CreateNodeDataTemplate(target); -//// ApplyTemplates(target); -//// ExpandAll(target); + var visualRoot = new TestRoot(); + visualRoot.Child = target; -//// var rootNode = tree[0]; + CreateNodeDataTemplate(target); + ApplyTemplates(target); + ExpandAll(target); -//// var from = rootNode.Children[0]; -//// var to = rootNode.Children.Last(); + var rootNode = tree[0]; -//// var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from); -//// var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to); + var from = rootNode.Children[0]; + var to = rootNode.Children.Last(); -//// ClickContainer(fromContainer, KeyModifiers.None); + var fromContainer = (TreeViewItem)target.TreeContainerFromItem(from); + var toContainer = (TreeViewItem)target.TreeContainerFromItem(to); -//// Assert.True(fromContainer.IsSelected); + ClickContainer(fromContainer, KeyModifiers.None); -//// ClickContainer(toContainer, KeyModifiers.Shift); -//// AssertChildrenSelected(target, rootNode); -//// } -//// } + Assert.True(fromContainer.IsSelected); -//// [Fact] -//// public void Clicking_WithShiftModifier_UpDirection_Should_Select_Range_Of_Items() -//// { -//// using (Application()) -//// { -//// var tree = CreateTestTreeData(); -//// var target = new TreeView -//// { -//// Template = CreateTreeViewTemplate(), -//// Items = tree, -//// SelectionMode = SelectionMode.Multiple -//// }; - -//// var visualRoot = new TestRoot(); -//// visualRoot.Child = target; - -//// CreateNodeDataTemplate(target); -//// ApplyTemplates(target); -//// ExpandAll(target); + ClickContainer(toContainer, KeyModifiers.Shift); + AssertChildrenSelected(target, rootNode); + } + } -//// var rootNode = tree[0]; - -//// var from = rootNode.Children.Last(); -//// var to = rootNode.Children[0]; - -//// var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from); -//// var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to); - -//// ClickContainer(fromContainer, KeyModifiers.None); + [Fact] + public void Clicking_WithShiftModifier_UpDirection_Should_Select_Range_Of_Items() + { + using (Application()) + { + var tree = CreateTestTreeData(); + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = tree, + SelectionMode = SelectionMode.Multiple + }; + + var visualRoot = new TestRoot(); + visualRoot.Child = target; + + CreateNodeDataTemplate(target); + ApplyTemplates(target); + ExpandAll(target); -//// Assert.True(fromContainer.IsSelected); + 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); -//// } -//// } + Assert.True(fromContainer.IsSelected); -//// [Fact] -//// public void Clicking_First_Item_Of_SelectedItems_Should_Select_Only_It() -//// { -//// using (Application()) -//// { -//// var tree = CreateTestTreeData(); -//// var target = new TreeView -//// { -//// Template = CreateTreeViewTemplate(), -//// Items = 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.ItemContainerGenerator.Index.ContainerFromItem(from); -//// var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to); - -//// ClickContainer(fromContainer, KeyModifiers.None); - -//// ClickContainer(toContainer, KeyModifiers.Shift); -//// AssertChildrenSelected(target, rootNode); - -//// ClickContainer(fromContainer, KeyModifiers.None); - -//// Assert.True(fromContainer.IsSelected); - -//// foreach (var child in rootNode.Children) -//// { -//// if (child == from) -//// { -//// continue; -//// } - -//// var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(child); - -//// Assert.False(container.IsSelected); -//// } -//// } -//// } - -//// [Fact] -//// public void Setting_SelectedItem_Should_Set_Container_Selected() -//// { -//// using (Application()) -//// { -//// var tree = CreateTestTreeData(); -//// var target = new TreeView -//// { -//// Template = CreateTreeViewTemplate(), -//// Items = 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.ItemContainerGenerator.Index.ContainerFromItem(item); - -//// Assert.NotNull(container); - -//// target.SelectedItem = item; - -//// Assert.True(container.IsSelected); -//// } -//// } - -//// [Fact] -//// public void Setting_SelectedItem_Should_Raise_SelectedItemChanged_Event() -//// { -//// using (Application()) -//// { -//// var tree = CreateTestTreeData(); -//// var target = new TreeView -//// { -//// Template = CreateTreeViewTemplate(), -//// Items = tree, -//// }; - -//// var visualRoot = new TestRoot(); -//// visualRoot.Child = target; - -//// CreateNodeDataTemplate(target); -//// ApplyTemplates(target); -//// ExpandAll(target); - -//// 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; -//// }; - -//// target.SelectedItem = item; -//// Assert.True(called); -//// } -//// } - -//// [Fact] -//// public void Bound_SelectedItem_Should_Not_Be_Cleared_when_Changing_Selection() -//// { -//// using (Application()) -//// { -//// var dataContext = new TestDataContext(); - -//// var target = new TreeView -//// { -//// Template = CreateTreeViewTemplate(), -//// DataContext = dataContext -//// }; - -//// target.Bind(TreeView.ItemsProperty, new Binding("Items")); -//// target.Bind(TreeView.SelectedItemProperty, new Binding("SelectedItem")); - -//// var visualRoot = new TestRoot(); -//// visualRoot.Child = target; - -//// CreateNodeDataTemplate(target); -//// ApplyTemplates(target); - -//// var selectedValues = new List(); - -//// dataContext.PropertyChanged += (_, e) => -//// { -//// if (e.PropertyName == nameof(TestDataContext.SelectedItem)) -//// selectedValues.Add(dataContext.SelectedItem); -//// }; -//// selectedValues.Add(dataContext.SelectedItem); - -//// _mouse.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Left); -//// _mouse.Click((Interactive)target.Presenter.Panel.Children[2], MouseButton.Left); - -//// Assert.Equal(3, selectedValues.Count); -//// Assert.Equal(new[] { null, "Item 0", "Item 2" }, selectedValues.ToArray()); -//// } -//// } - -//// [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() -//// .Select(x => x.Header) -//// .OfType() -//// .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.Containers.Count()); - -//// tree[0].Children.RemoveAt(1); - -//// Assert.Equal(3, target.ItemContainerGenerator.Index.Containers.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((x, _) => new Button { Content = x }) -//// }, -//// Items = items, -//// }; - -//// ApplyTemplates(target); - -//// var dataContexts = target.Presenter.Panel.Children -//// .Cast() -//// .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 Should_React_To_Children_Changing() -//// { -//// var data = CreateTestTreeData(); - -//// var target = new TreeView -//// { -//// Template = CreateTreeViewTemplate(), -//// Items = data, -//// }; - -//// var root = new TestRoot(target); - -//// 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 -//// { -//// 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 (Application()) -//// { -//// var focus = FocusManager.Instance; -//// var navigation = AvaloniaLocator.Current.GetService(); -//// 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); -//// ExpandAll(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); -//// } -//// } - -//// [Fact] -//// public void Keyboard_Navigation_Should_Not_Crash_If_Selected_Item_Is_not_In_Tree() -//// { -//// using (Application()) -//// { -//// var focus = FocusManager.Instance; -//// var navigation = AvaloniaLocator.Current.GetService(); -//// var data = CreateTestTreeData(); - -//// var selectedNode = new Node { Value = "Out of Tree Selected Item" }; - -//// var target = new TreeView -//// { -//// Template = CreateTreeViewTemplate(), -//// Items = data, -//// SelectedItem = selectedNode -//// }; - -//// var button = new Button(); - -//// var root = new TestRoot -//// { -//// Child = new StackPanel -//// { -//// Children = { target, button }, -//// } -//// }; - -//// CreateNodeDataTemplate(target); -//// ApplyTemplates(target); -//// ExpandAll(target); - -//// var item = data[0].Children[0]; -//// var node = target.ItemContainerGenerator.Index.ContainerFromItem(item); -//// Assert.NotNull(node); - -//// target.SelectedItem = selectedNode; -//// node.Focus(); -//// Assert.Same(node, focus.Current); - -//// var next = KeyboardNavigationHandler.GetNext(node, NavigationDirection.Previous); -//// } -//// } - -//// [Fact] -//// public void Pressing_SelectAll_Gesture_Should_Select_All_Nodes() -//// { -//// using (UnitTestApplication.Start()) -//// { -//// var tree = CreateTestTreeData(); -//// var target = new TreeView -//// { -//// Template = CreateTreeViewTemplate(), -//// Items = 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.GetService(); -//// var selectAllGesture = keymap.SelectAll.First(); - -//// var keyEvent = new KeyEventArgs -//// { -//// RoutedEvent = InputElement.KeyDownEvent, -//// Key = selectAllGesture.Key, -//// KeyModifiers = selectAllGesture.KeyModifiers -//// }; - -//// target.RaiseEvent(keyEvent); - -//// 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(), -//// Items = 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.ItemContainerGenerator.Index.ContainerFromItem(from); -//// var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to); - -//// ClickContainer(fromContainer, KeyModifiers.None); -//// ClickContainer(toContainer, KeyModifiers.Shift); - -//// var keymap = AvaloniaLocator.Current.GetService(); -//// var selectAllGesture = keymap.SelectAll.First(); - -//// var keyEvent = new KeyEventArgs -//// { -//// RoutedEvent = InputElement.KeyDownEvent, -//// Key = selectAllGesture.Key, -//// KeyModifiers = selectAllGesture.KeyModifiers -//// }; - -//// target.RaiseEvent(keyEvent); - -//// 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(), -//// Items = 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.ItemContainerGenerator.Index.ContainerFromItem(from); -//// var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to); - -//// ClickContainer(fromContainer, KeyModifiers.None); -//// ClickContainer(toContainer, KeyModifiers.Shift); - -//// var keymap = AvaloniaLocator.Current.GetService(); -//// var selectAllGesture = keymap.SelectAll.First(); - -//// var keyEvent = new KeyEventArgs -//// { -//// RoutedEvent = InputElement.KeyDownEvent, -//// Key = selectAllGesture.Key, -//// KeyModifiers = selectAllGesture.KeyModifiers -//// }; - -//// target.RaiseEvent(keyEvent); - -//// 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(), -//// Items = tree, -//// SelectionMode = SelectionMode.Multiple, -//// }; -//// AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); -//// var visualRoot = new TestRoot(); -//// visualRoot.Child = target; - -//// CreateNodeDataTemplate(target); -//// ApplyTemplates(target); -//// target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]); -//// target.SelectAll(); - -//// AssertChildrenSelected(target, tree[0]); -//// Assert.Equal(5, target.SelectedItems.Count); - -//// _mouse.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Right); - -//// 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(), -//// Items = 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.ItemContainerGenerator.Index.ContainerFromItem(rootNode); -//// var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to); -//// var thenContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(then); - -//// ClickContainer(fromContainer, KeyModifiers.None); -//// ClickContainer(toContainer, KeyModifiers.Shift); - -//// Assert.Equal(2, target.SelectedItems.Count); - -//// _mouse.Click(thenContainer, MouseButton.Right); - -//// 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(), -//// Items = 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.ItemContainerGenerator.Index.ContainerFromItem(from); -//// var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to); - -//// _mouse.Click(fromContainer); -//// _mouse.Click(toContainer, MouseButton.Right, modifiers: KeyModifiers.Shift); - -//// 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(), -//// Items = 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.ItemContainerGenerator.Index.ContainerFromItem(from); -//// var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to); - -//// _mouse.Click(fromContainer); -//// _mouse.Click(toContainer, MouseButton.Right, modifiers: KeyModifiers.Control); - -//// Assert.Equal(1, target.SelectedItems.Count); -//// } -//// } - -//// [Fact] -//// public void TreeViewItems_Level_Should_Be_Set() -//// { -//// var tree = CreateTestTreeData(); -//// var target = new TreeView -//// { -//// Template = CreateTreeViewTemplate(), -//// Items = tree, -//// }; - -//// var visualRoot = new TestRoot(); -//// visualRoot.Child = target; - -//// CreateNodeDataTemplate(target); -//// ApplyTemplates(target); -//// ExpandAll(target); - -//// Assert.Equal(0, GetItem(target, 0).Level); -//// Assert.Equal(1, GetItem(target, 0, 0).Level); -//// Assert.Equal(1, GetItem(target, 0, 1).Level); -//// Assert.Equal(1, GetItem(target, 0, 2).Level); -//// Assert.Equal(2, GetItem(target, 0, 1, 0).Level); -//// } - -//// [Fact] -//// public void TreeViewItems_Level_Should_Be_Set_For_Derived_TreeView() -//// { -//// var tree = CreateTestTreeData(); -//// var target = new DerivedTreeView -//// { -//// Template = CreateTreeViewTemplate(), -//// Items = tree, -//// }; - -//// var visualRoot = new TestRoot(); -//// visualRoot.Child = target; - -//// CreateNodeDataTemplate(target); -//// ApplyTemplates(target); -//// ExpandAll(target); - -//// Assert.Equal(0, GetItem(target, 0).Level); -//// Assert.Equal(1, GetItem(target, 0, 0).Level); -//// Assert.Equal(1, GetItem(target, 0, 1).Level); -//// Assert.Equal(1, GetItem(target, 0, 2).Level); -//// Assert.Equal(2, GetItem(target, 0, 1, 0).Level); -//// } - -//// [Fact] -//// public void Adding_Node_To_Removed_And_ReAdded_Parent_Should_Not_Crash() -//// { -//// // Issue #2985 -//// var tree = CreateTestTreeData(); -//// var target = new TreeView -//// { -//// Template = CreateTreeViewTemplate(), -//// Items = tree, -//// }; - -//// var visualRoot = new TestRoot(); -//// visualRoot.Child = target; - -//// CreateNodeDataTemplate(target); -//// ApplyTemplates(target); -//// ExpandAll(target); - -//// var parent = tree[0]; -//// var node = parent.Children[1]; - -//// parent.Children.Remove(node); -//// parent.Children.Add(node); - -//// var item = target.ItemContainerGenerator.Index.ContainerFromItem(node); -//// ApplyTemplates(new[] { item }); - -//// // #2985 causes ArgumentException here. -//// node.Children.Add(new Node()); -//// } - -//// [Fact] -//// public void Auto_Expanding_In_Style_Should_Not_Break_Range_Selection() -//// { -//// // Issue #2980. -//// using (Application()) -//// { -//// var target = new DerivedTreeView -//// { -//// Template = CreateTreeViewTemplate(), -//// SelectionMode = SelectionMode.Multiple, -//// Items = new List -//// { -//// new Node { Value = "Root1", }, -//// new Node { Value = "Root2", }, -//// }, -//// }; - -//// var visualRoot = new TestRoot -//// { -//// Styles = -//// { -//// new Style(x => x.OfType()) -//// { -//// Setters = -//// { -//// new Setter(TreeViewItem.IsExpandedProperty, true), -//// }, -//// }, -//// }, -//// Child = target, -//// }; - -//// CreateNodeDataTemplate(target); -//// ApplyTemplates(target); - -//// _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(), -//// Items = tree, -//// }; - -//// var root = new TestRoot(); -//// root.Child = target; - -//// CreateNodeDataTemplate(target); -//// ApplyTemplates(target); -//// ExpandAll(target); - -//// Assert.Equal(5, target.ItemContainerGenerator.Index.Containers.Count()); - -//// root.Child = null; - -//// Assert.Equal(5, target.ItemContainerGenerator.Index.Containers.Count()); -//// Assert.Equal(1, target.Presenter.Panel.Children.Count); - -//// var rootNode = Assert.IsType(target.Presenter.Panel.Children[0]); -//// Assert.Equal(3, rootNode.ItemContainerGenerator.Containers.Count()); -//// Assert.Equal(3, rootNode.Presenter.Panel.Children.Count); - -//// var child2Node = Assert.IsType(rootNode.Presenter.Panel.Children[1]); -//// Assert.Equal(1, child2Node.ItemContainerGenerator.Containers.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(), -//// Items = tree, -//// }; - -//// var root = new TestRoot(); -//// root.Child = target; - -//// CreateNodeDataTemplate(target); -//// ApplyTemplates(target); - -//// var rootNode = tree[0]; -//// var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(rootNode); - -//// Assert.NotNull(container); - -//// root.Child = null; - -//// tree.Clear(); - -//// Assert.Empty(target.ItemContainerGenerator.Index.Containers); -//// } - -//// [Fact] -//// public void Can_Use_Derived_TreeViewItem() -//// { -//// var tree = CreateTestTreeData(); -//// var target = new DerivedTreeViewWithDerivedTreeViewItems -//// { -//// Template = CreateTreeViewTemplate(), -//// Items = tree, -//// }; - -//// ApplyTemplates(target); - -//// // Verify that all items are DerivedTreeViewItem -//// VerifyItemType(target.ItemContainerGenerator); - -//// void VerifyItemType(ITreeItemContainerGenerator containerGenerator) -//// { -//// foreach (var container in containerGenerator.Index.Containers) -//// { -//// var item = Assert.IsType(container); -//// if (item.ItemCount > 0) -//// { -//// VerifyItemType(item.ItemContainerGenerator); -//// } -//// } -//// } -//// } - -//// private void ApplyTemplates(TreeView tree) -//// { -//// tree.ApplyTemplate(); -//// tree.Presenter.ApplyTemplate(); -//// ApplyTemplates(tree.Presenter.Panel.Children); -//// } - -//// private void ApplyTemplates(IEnumerable controls) -//// { -//// foreach (TreeViewItem control in controls) -//// { -//// control.Template = CreateTreeViewItemTemplate(); -//// control.ApplyTemplate(); -//// control.Presenter.ApplyTemplate(); -//// control.HeaderPresenter.ApplyTemplate(); -//// ApplyTemplates(control.Presenter.Panel.Children); -//// } -//// } - -//// private TreeViewItem GetItem(TreeView target, params int[] indexes) -//// { -//// var c = (ItemsControl)target; - -//// foreach (var index in indexes) -//// { -//// var item = ((IList)c.Items)[index]; -//// c = (ItemsControl)target.ItemContainerGenerator.Index.ContainerFromItem(item); -//// } - -//// return (TreeViewItem)c; -//// } - -//// private IList CreateTestTreeData() -//// { -//// return new AvaloniaList -//// { -//// new Node -//// { -//// Value = "Root", -//// Children = new AvaloniaList -//// { -//// new Node -//// { -//// Value = "Child1", -//// }, -//// new Node -//// { -//// Value = "Child2", -//// Children = new AvaloniaList -//// { -//// new Node -//// { -//// Value = "Grandchild2a", -//// }, -//// }, -//// }, -//// new Node -//// { -//// Value = "Child3", -//// } -//// } -//// } -//// }; -//// } - -//// private void CreateNodeDataTemplate(Control control) -//// { -//// control.DataTemplates.Add(new TestTreeDataTemplate()); -//// } - -//// private IControlTemplate CreateTreeViewTemplate() -//// { -//// return new FuncControlTemplate((parent, scope) => new ItemsPresenter -//// { -//// Name = "PART_ItemsPresenter", -//// }.RegisterInNameScope(scope)); -//// } - -//// private IControlTemplate CreateTreeViewItemTemplate() -//// { -//// return new FuncControlTemplate((parent, scope) => new Panel -//// { -//// Children = -//// { -//// new ContentPresenter -//// { -//// Name = "PART_HeaderPresenter", -//// [~ContentPresenter.ContentProperty] = parent[~TreeViewItem.HeaderProperty], -//// }.RegisterInNameScope(scope), -//// new ItemsPresenter -//// { -//// Name = "PART_ItemsPresenter", -//// }.RegisterInNameScope(scope) -//// } -//// }); -//// } - -//// private void ExpandAll(TreeView tree) -//// { -//// foreach (var i in tree.ItemContainerGenerator.Containers) -//// { -//// tree.ExpandSubTree((TreeViewItem)i.ContainerControl); -//// } -//// } - -//// private List ExtractItemHeader(TreeView tree, int level) -//// { -//// return ExtractItemContent(tree.Presenter.Panel, 0, level) -//// .Select(x => x.Header) -//// .OfType() -//// .Select(x => x.Text) -//// .ToList(); -//// } - -//// private IEnumerable ExtractItemContent(Panel 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 void ClickContainer(Control container, KeyModifiers modifiers) -//// { -//// _mouse.Click(container, modifiers: modifiers); -//// } - -//// private void AssertChildrenSelected(TreeView treeView, Node rootNode) -//// { -//// foreach (var child in rootNode.Children) -//// { -//// var container = (TreeViewItem)treeView.ItemContainerGenerator.Index.ContainerFromItem(child); - -//// Assert.True(container.IsSelected); -//// } -//// } - -//// private IDisposable Application() -//// { -//// return UnitTestApplication.Start( -//// TestServices.MockThreadingInterface.With( -//// focusManager: new FocusManager(), -//// keyboardDevice: () => new KeyboardDevice(), -//// keyboardNavigation: new KeyboardNavigationHandler(), -//// inputManager: new InputManager())); -//// } - -//// private class Node : NotifyingBase -//// { -//// private IAvaloniaList _children; - -//// public string Value { get; set; } - -//// public IAvaloniaList Children -//// { -//// get -//// { -//// return _children; -//// } - -//// set -//// { -//// _children = value; -//// RaisePropertyChanged(nameof(Children)); -//// } -//// } -//// } - -//// private class TestTreeDataTemplate : ITreeDataTemplate -//// { -//// public Control Build(object param) -//// { -//// var node = (Node)param; -//// return new TextBlock { Text = node.Value }; -//// } - -//// 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; -//// } -//// } - -//// private class DerivedTreeView : TreeView -//// { -//// } - -//// private class DerivedTreeViewWithDerivedTreeViewItems : TreeView -//// { -//// protected override ITreeItemContainerGenerator CreateTreeItemContainerGenerator() => -//// CreateTreeItemContainerGenerator(); -//// } - -//// private class DerivedTreeViewItem : TreeViewItem -//// { -//// protected override IItemContainerGenerator CreateItemContainerGenerator() => CreateTreeItemContainerGenerator(); -//// } - -//// private class TestDataContext : INotifyPropertyChanged -//// { -//// private string _selectedItem; - -//// public TestDataContext() -//// { -//// Items = new ObservableCollection(Enumerable.Range(0, 5).Select(i => $"Item {i}")); -//// } - -//// public ObservableCollection Items { get; } - -//// public string SelectedItem -//// { -//// get { return _selectedItem; } -//// set -//// { -//// _selectedItem = value; -//// PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItem))); -//// } -//// } - -//// public event PropertyChangedEventHandler PropertyChanged; - -//// } -//// } -////} + 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(), + Items = 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); + + ClickContainer(fromContainer, KeyModifiers.None); + + Assert.True(fromContainer.IsSelected); + + foreach (var child in rootNode.Children) + { + if (child == from) + { + continue; + } + + var container = (TreeViewItem)target.TreeContainerFromItem(child); + + Assert.False(container.IsSelected); + } + } + } + + [Fact] + public void Setting_SelectedItem_Should_Set_Container_Selected() + { + using (Application()) + { + var tree = CreateTestTreeData(); + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = 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); + + Assert.NotNull(container); + + target.SelectedItem = item; + + Assert.True(container.IsSelected); + } + } + + [Fact] + public void Setting_SelectedItem_Should_Raise_SelectedItemChanged_Event() + { + using (Application()) + { + var tree = CreateTestTreeData(); + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = tree, + }; + + var visualRoot = new TestRoot(); + visualRoot.Child = target; + + CreateNodeDataTemplate(target); + ApplyTemplates(target); + ExpandAll(target); + + 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; + }; + + target.SelectedItem = item; + Assert.True(called); + } + } + + [Fact] + public void Bound_SelectedItem_Should_Not_Be_Cleared_when_Changing_Selection() + { + using (Application()) + { + var dataContext = new TestDataContext(); + + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + DataContext = dataContext + }; + + target.Bind(TreeView.ItemsProperty, new Binding("Items")); + target.Bind(TreeView.SelectedItemProperty, new Binding("SelectedItem")); + + var visualRoot = new TestRoot(); + visualRoot.Child = target; + + CreateNodeDataTemplate(target); + ApplyTemplates(target); + + var selectedValues = new List(); + + dataContext.PropertyChanged += (_, e) => + { + if (e.PropertyName == nameof(TestDataContext.SelectedItem)) + selectedValues.Add(dataContext.SelectedItem); + }; + selectedValues.Add(dataContext.SelectedItem); + + _mouse.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Left); + _mouse.Click((Interactive)target.Presenter.Panel.Children[2], MouseButton.Left); + + Assert.Equal(3, selectedValues.Count); + Assert.Equal(new[] { null, "Item 0", "Item 2" }, selectedValues.ToArray()); + } + } + + [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() + .Select(x => x.HeaderPresenter.Child) + .OfType() + .Select(x => x.Text) + .ToList(); + + Assert.Equal(new[] { "Foo", "Bar", "Baz " }, result); + } + + [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((x, _) => new Button { Content = x }) + }, + Items = items, + }; + + ApplyTemplates(target); + + var dataContexts = target.Presenter.Panel.Children + .Cast() + .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 Should_React_To_Children_Changing() + { + var data = CreateTestTreeData(); + + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = data, + }; + + var root = new TestRoot(target); + + 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 + { + new Node + { + Value = "NewChild1", + } + }; + + ApplyTemplates(target); + + 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 (Application()) + { + var focus = FocusManager.Instance; + var navigation = AvaloniaLocator.Current.GetService(); + 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); + ExpandAll(target); + + var item = data[0].Children[0]; + var node = target.TreeContainerFromItem(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); + } + } + + [Fact] + public void Keyboard_Navigation_Should_Not_Crash_If_Selected_Item_Is_not_In_Tree() + { + using (Application()) + { + var focus = FocusManager.Instance; + var navigation = AvaloniaLocator.Current.GetService(); + var data = CreateTestTreeData(); + + var selectedNode = new Node { Value = "Out of Tree Selected Item" }; + + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = data, + SelectedItem = selectedNode + }; + + var button = new Button(); + + var root = new TestRoot + { + Child = new StackPanel + { + Children = { target, button }, + } + }; + + CreateNodeDataTemplate(target); + ApplyTemplates(target); + ExpandAll(target); + + var item = data[0].Children[0]; + var node = target.TreeContainerFromItem(item); + Assert.NotNull(node); + + target.SelectedItem = selectedNode; + node.Focus(); + Assert.Same(node, focus.Current); + + var next = KeyboardNavigationHandler.GetNext(node, NavigationDirection.Previous); + } + } + + [Fact] + public void Pressing_SelectAll_Gesture_Should_Select_All_Nodes() + { + using (UnitTestApplication.Start()) + { + var tree = CreateTestTreeData(); + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = 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.GetService(); + var selectAllGesture = keymap.SelectAll.First(); + + var keyEvent = new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = selectAllGesture.Key, + KeyModifiers = selectAllGesture.KeyModifiers + }; + + target.RaiseEvent(keyEvent); + + 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(), + Items = 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); + ClickContainer(toContainer, KeyModifiers.Shift); + + var keymap = AvaloniaLocator.Current.GetService(); + var selectAllGesture = keymap.SelectAll.First(); + + var keyEvent = new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = selectAllGesture.Key, + KeyModifiers = selectAllGesture.KeyModifiers + }; + + target.RaiseEvent(keyEvent); + + 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(), + Items = 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); + + var keymap = AvaloniaLocator.Current.GetService(); + var selectAllGesture = keymap.SelectAll.First(); + + var keyEvent = new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = selectAllGesture.Key, + KeyModifiers = selectAllGesture.KeyModifiers + }; + + target.RaiseEvent(keyEvent); + + 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(), + Items = tree, + SelectionMode = SelectionMode.Multiple, + }; + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new Mock().Object); + var visualRoot = new TestRoot(); + visualRoot.Child = target; + + CreateNodeDataTemplate(target); + ApplyTemplates(target); + target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]); + target.SelectAll(); + + AssertChildrenSelected(target, tree[0]); + Assert.Equal(5, target.SelectedItems.Count); + + _mouse.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Right); + + 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(), + Items = 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); + + ClickContainer(fromContainer, KeyModifiers.None); + ClickContainer(toContainer, KeyModifiers.Shift); + + Assert.Equal(2, target.SelectedItems.Count); + + _mouse.Click(thenContainer, MouseButton.Right); + + 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(), + Items = 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); + + _mouse.Click(fromContainer); + _mouse.Click(toContainer, MouseButton.Right, modifiers: KeyModifiers.Shift); + + 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(), + Items = 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); + + _mouse.Click(fromContainer); + _mouse.Click(toContainer, MouseButton.Right, modifiers: KeyModifiers.Control); + + Assert.Equal(1, target.SelectedItems.Count); + } + } + + [Fact] + public void TreeViewItems_Level_Should_Be_Set() + { + var tree = CreateTestTreeData(); + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = tree, + }; + + var visualRoot = new TestRoot(); + visualRoot.Child = target; + + CreateNodeDataTemplate(target); + ApplyTemplates(target); + ExpandAll(target); + + Assert.Equal(0, GetItem(target, 0).Level); + Assert.Equal(1, GetItem(target, 0, 0).Level); + Assert.Equal(1, GetItem(target, 0, 1).Level); + Assert.Equal(1, GetItem(target, 0, 2).Level); + Assert.Equal(2, GetItem(target, 0, 1, 0).Level); + } + + [Fact] + public void TreeViewItems_Level_Should_Be_Set_For_Derived_TreeView() + { + var tree = CreateTestTreeData(); + var target = new DerivedTreeView + { + Template = CreateTreeViewTemplate(), + Items = tree, + }; + + var visualRoot = new TestRoot(); + visualRoot.Child = target; + + CreateNodeDataTemplate(target); + ApplyTemplates(target); + ExpandAll(target); + + Assert.Equal(0, GetItem(target, 0).Level); + Assert.Equal(1, GetItem(target, 0, 0).Level); + Assert.Equal(1, GetItem(target, 0, 1).Level); + Assert.Equal(1, GetItem(target, 0, 2).Level); + Assert.Equal(2, GetItem(target, 0, 1, 0).Level); + } + + [Fact] + public void Adding_Node_To_Removed_And_ReAdded_Parent_Should_Not_Crash() + { + // Issue #2985 + var tree = CreateTestTreeData(); + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = tree, + }; + + var visualRoot = new TestRoot(); + visualRoot.Child = target; + + CreateNodeDataTemplate(target); + ApplyTemplates(target); + ExpandAll(target); + + var parent = tree[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()); + } + + [Fact] + public void Auto_Expanding_In_Style_Should_Not_Break_Range_Selection() + { + // Issue #2980. + using (Application()) + { + var target = new DerivedTreeView + { + Template = CreateTreeViewTemplate(), + SelectionMode = SelectionMode.Multiple, + Items = new List + { + new Node { Value = "Root1", }, + new Node { Value = "Root2", }, + }, + }; + + var visualRoot = new TestRoot + { + Styles = + { + new Style(x => x.OfType()) + { + Setters = + { + new Setter(TreeViewItem.IsExpandedProperty, true), + }, + }, + }, + Child = target, + }; + + CreateNodeDataTemplate(target); + ApplyTemplates(target); + + _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(), + Items = tree, + }; + + var root = new TestRoot(); + root.Child = target; + + CreateNodeDataTemplate(target); + ApplyTemplates(target); + ExpandAll(target); + + Assert.Equal(5, target.GetRealizedTreeContainers().Count()); + + root.Child = null; + + Assert.Equal(5, target.GetRealizedTreeContainers().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); + + var child2Node = Assert.IsType(rootNode.Presenter.Panel.Children[1]); + Assert.Equal(1, child2Node.GetRealizedContainers().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(), + Items = tree, + }; + + var root = new TestRoot(); + root.Child = target; + + CreateNodeDataTemplate(target); + ApplyTemplates(target); + + var rootNode = tree[0]; + var container = (TreeViewItem)target.TreeContainerFromItem(rootNode); + + Assert.NotNull(container); + + root.Child = null; + + tree.Clear(); + + Assert.Empty(target.GetRealizedContainers()); + } + + [Fact] + public void Can_Use_Derived_TreeViewItem() + { + var tree = CreateTestTreeData(); + var target = new DerivedTreeViewWithDerivedTreeViewItems + { + Template = CreateTreeViewTemplate(), + Items = tree, + }; + + ApplyTemplates(target); + + // Verify that all items are DerivedTreeViewItem + foreach (var container in target.GetRealizedTreeContainers()) + { + Assert.IsType(container); + } + } + + private void ApplyTemplates(TreeView tree) + { + tree.ApplyTemplate(); + tree.Presenter.ApplyTemplate(); + ApplyTemplates(tree.Presenter.Panel.Children); + } + + private void ApplyTemplates(IEnumerable controls) + { + foreach (TreeViewItem control in controls) + { + control.Template = CreateTreeViewItemTemplate(); + control.ApplyTemplate(); + control.Presenter.ApplyTemplate(); + ((ContentPresenter)control.HeaderPresenter).UpdateChild(); + ApplyTemplates(control.Presenter.Panel.Children); + } + } + + private TreeViewItem GetItem(TreeView target, params int[] indexes) + { + var c = (ItemsControl)target; + + foreach (var index in indexes) + { + var item = ((IList)c.Items)[index]; + c = (ItemsControl)target.TreeContainerFromItem(item); + } + + return (TreeViewItem)c; + } + + private IList CreateTestTreeData() + { + return new AvaloniaList + { + new Node + { + Value = "Root", + Children = new AvaloniaList + { + new Node + { + Value = "Child1", + }, + new Node + { + Value = "Child2", + Children = new AvaloniaList + { + new Node + { + Value = "Grandchild2a", + }, + }, + }, + new Node + { + Value = "Child3", + } + } + } + }; + } + + private void CreateNodeDataTemplate(Control control) + { + control.DataTemplates.Add(new TestTreeDataTemplate()); + } + + private IControlTemplate CreateTreeViewTemplate() + { + return new FuncControlTemplate((parent, scope) => new ItemsPresenter + { + Name = "PART_ItemsPresenter", + }.RegisterInNameScope(scope)); + } + + private IControlTemplate CreateTreeViewItemTemplate() + { + return new FuncControlTemplate((parent, scope) => new Panel + { + Children = + { + new ContentPresenter + { + Name = "PART_HeaderPresenter", + [~ContentPresenter.ContentProperty] = parent[~TreeViewItem.HeaderProperty], + [~ContentPresenter.ContentTemplateProperty] = parent[~TreeViewItem.HeaderTemplateProperty], + }.RegisterInNameScope(scope), + new ItemsPresenter + { + Name = "PART_ItemsPresenter", + }.RegisterInNameScope(scope) + } + }); + } + + private void ExpandAll(TreeView tree) + { + foreach (var i in tree.GetRealizedContainers()) + { + tree.ExpandSubTree((TreeViewItem)i); + } + } + + private List ExtractItemHeader(TreeView tree, int level) + { + return ExtractItemContent(tree.Presenter.Panel, 0, level) + .Select(x => x.HeaderPresenter.Child) + .OfType() + .Select(x => x.Text) + .ToList(); + } + + private IEnumerable ExtractItemContent(Panel 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 void ClickContainer(Control container, KeyModifiers modifiers) + { + _mouse.Click(container, modifiers: modifiers); + } + + private void AssertChildrenSelected(TreeView treeView, Node rootNode) + { + foreach (var child in rootNode.Children) + { + var container = (TreeViewItem)treeView.TreeContainerFromItem(child); + + Assert.True(container.IsSelected); + } + } + + private IDisposable Application() + { + return UnitTestApplication.Start( + TestServices.MockThreadingInterface.With( + focusManager: new FocusManager(), + keyboardDevice: () => new KeyboardDevice(), + keyboardNavigation: new KeyboardNavigationHandler(), + inputManager: new InputManager())); + } + + private class Node : NotifyingBase + { + private IAvaloniaList _children; + + public string Value { get; set; } + + public IAvaloniaList Children + { + get + { + return _children; + } + + set + { + _children = value; + RaisePropertyChanged(nameof(Children)); + } + } + } + + private class TestTreeDataTemplate : ITreeDataTemplate + { + public Control Build(object param) + { + var node = (Node)param; + return new TextBlock { Text = node.Value }; + } + + 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; + } + } + + private class DerivedTreeView : TreeView + { + } + + private class DerivedTreeViewWithDerivedTreeViewItems : TreeView + { + protected internal override Control CreateContainerOverride() => new DerivedTreeViewItem(); + } + + private class DerivedTreeViewItem : TreeViewItem + { + } + + private class TestDataContext : INotifyPropertyChanged + { + private string _selectedItem; + + public TestDataContext() + { + Items = new ObservableCollection(Enumerable.Range(0, 5).Select(i => $"Item {i}")); + } + + public ObservableCollection Items { get; } + + public string SelectedItem + { + get { return _selectedItem; } + set + { + _selectedItem = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItem))); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + } + } +}