Browse Source

Made TreeView work again and tests pass.

Steven Kirk 2 years ago
parent
commit
6f04196b84

+ 32 - 0
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<Control> 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);
+    }
+}

+ 56 - 10
src/Avalonia.Controls/ItemsControl.cs

@@ -107,7 +107,12 @@ namespace Avalonia.Controls
         /// <summary>
         /// Gets the <see cref="ItemContainerGenerator"/> for the control.
         /// </summary>
-        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
+        }
 
         /// <summary>
         /// Gets or sets the items to display.
@@ -188,7 +193,8 @@ namespace Avalonia.Controls
         /// </returns>
         public Control? ContainerFromItem(object item)
         {
-            throw new NotImplementedException();
+            var index = Items?.IndexOf(item) ?? -1;
+            return index >= 0 ? ContainerFromIndex(index) : null;
         }
 
         /// <summary>
@@ -210,8 +216,8 @@ namespace Avalonia.Controls
         /// </returns>
         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;
         }
 
         /// <summary>
@@ -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
         /// <param name="container">The container element.</param>
         protected internal virtual void ClearContainerForItemOverride(Control container)
         {
+            // TODO: Remove HeaderedItemsControl.Items binding.
         }
 
         /// <summary>
@@ -486,6 +518,20 @@ namespace Avalonia.Controls
             }
         }
 
+        /// <summary>
+        /// Creates the <see cref="ItemContainerGenerator"/>
+        /// </summary>
+        /// <remarks>
+        /// This method is only present for backwards compatibility with 0.10.x in order for
+        /// TreeView to be able to create a <see cref="TreeItemContainerGenerator"/>. Can be
+        /// removed in 12.0.
+        /// </remarks>
+        [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);
 

+ 8 - 0
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);
+        }
+
         /// <summary>
         /// Gets or sets a template which creates the <see cref="Panel"/> used to display the items.
         /// </summary>

+ 16 - 0
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<object?> HeaderProperty =
             HeaderedContentControl.HeaderProperty.AddOwner<HeaderedItemsControl>();
 
+        /// <summary>
+        /// Defines the <see cref="HeaderTemplate"/> property.
+        /// </summary>
+        public static readonly StyledProperty<IDataTemplate?> HeaderTemplateProperty =
+            AvaloniaProperty.Register<HeaderedItemsControl, IDataTemplate?>(nameof(HeaderTemplate));
+
         /// <summary>
         /// Initializes static members of the <see cref="ContentControl"/> class.
         /// </summary>
@@ -32,6 +39,15 @@ namespace Avalonia.Controls.Primitives
             set { SetValue(HeaderProperty, value); }
         }
 
+        /// <summary>
+        /// Gets or sets the data template used to display the header content of the control.
+        /// </summary>
+        public IDataTemplate? HeaderTemplate
+        {
+            get => GetValue(HeaderTemplateProperty);
+            set => SetValue(HeaderTemplateProperty, value);
+        }
+
         /// <summary>
         /// Gets the header presenter from the control's template.
         /// </summary>

+ 258 - 180
src/Avalonia.Controls/TreeView.cs

@@ -75,6 +75,12 @@ namespace Avalonia.Controls
             remove => RemoveHandler(SelectingItemsControl.SelectionChangedEvent, value);
         }
 
+        /// <summary>
+        /// Gets the <see cref="TreeItemContainerGenerator"/> for the tree view.
+        /// </summary>
+        public new TreeItemContainerGenerator ItemContainerGenerator =>
+            (TreeItemContainerGenerator)base.ItemContainerGenerator;
+
         /// <summary>
         /// Gets or sets a value indicating whether to automatically scroll to newly selected items.
         /// </summary>
@@ -182,8 +188,25 @@ namespace Avalonia.Controls
         /// </remarks>
         public void SelectAll()
         {
-            throw new NotImplementedException();
-            ////SynchronizeItems(SelectedItems, ItemContainerGenerator.Index!.Items);
+            var allItems = new List<object>();
+
+            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);
         }
 
         /// <summary>
@@ -194,6 +217,62 @@ namespace Avalonia.Controls
             SelectedItems.Clear();
         }
 
+        public IEnumerable<Control> GetRealizedTreeContainers()
+        {
+            static IEnumerable<Control> 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);
+        }
+
         /// <summary>
         /// Subscribes to the <see cref="SelectedItems"/> CollectionChanged event, if any.
         /// </summary>
@@ -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;
+
         /// <inheritdoc/>
         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;
         }
 
         /// <inheritdoc/>
@@ -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);
         }
 
         /// <summary>
@@ -595,27 +686,24 @@ namespace Avalonia.Controls
         /// <returns>Found first node.</returns>
         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<ItemContainerInfo> 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);
         }
 
         /// <summary>
@@ -643,60 +731,59 @@ namespace Avalonia.Controls
         /// <param name="to">To container.</param>
         private List<object> GetItemsInRange(TreeViewItem? from, TreeViewItem? to)
         {
-            throw new NotImplementedException();
-            ////var items = new List<object>();
+            var items = new List<object>();
 
-            ////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;
         }
 
         /// <summary>
@@ -737,20 +824,11 @@ namespace Avalonia.Controls
         /// <returns>The container or null if the event did not originate in a container.</returns>
         protected TreeViewItem? GetContainerFromEventSource(object eventSource)
         {
-            throw new NotImplementedException();
-            ////var item = ((Visual)eventSource).GetSelfAndVisualAncestors()
-            ////    .OfType<TreeViewItem>()
-            ////    .FirstOrDefault();
-
-            ////if (item != null)
-            ////{
-            ////    if (item.ItemContainerGenerator.Index == ItemContainerGenerator.Index)
-            ////    {
-            ////        return item;
-            ////    }
-            ////}
+            var item = ((Visual)eventSource).GetSelfAndVisualAncestors()
+                .OfType<TreeViewItem>()
+                .FirstOrDefault();
 
-            ////return null;
+            return item?.TreeViewOwner == this ? item : null;
         }
 
         /// <summary>
@@ -801,7 +879,7 @@ namespace Avalonia.Controls
             }
             else
             {
-                container.Classes.Set(":selected", selected);
+                ((IPseudoClasses)container.Classes).Set(":selected", selected);
             }
         }
 

+ 5 - 0
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;
+
         /// <inheritdoc/>
         protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
         {

+ 1 - 0
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}" />

+ 1 - 0
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" />
             </Grid>
           </Border>

+ 1416 - 1450
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<Node>(
-////                        (_, __) => new Canvas(),
-////                        x => x.Children),
-////                }
-////            };
-
-////            ApplyTemplates(target);
-
-////            var items = target.ItemContainerGenerator.Index.Containers
-////                .OfType<TreeViewItem>()
-////                .ToList();
-
-////            Assert.Equal(5, items.Count);
-////            Assert.All(items, x => Assert.IsType<Canvas>(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<Node>(
-////                        (_, __) => new Canvas(),
-////                        x => x.Children),
-////                }
-////            };
-
-////            ApplyTemplates(target);
-
-////            var items = target.ItemContainerGenerator.Index.Containers
-////                .OfType<TreeViewItem>()
-////                .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<Node>(
+                        (_, __) => new Canvas(),
+                        x => x.Children),
+                }
+            };
+
+            ApplyTemplates(target);
+
+            var items = target.GetRealizedTreeContainers()
+                .OfType<TreeViewItem>()
+                .ToList();
+
+            Assert.Equal(5, items.Count);
+            Assert.All(items, x => Assert.IsType<Canvas>(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<Node>(
+                        (_, __) => new Canvas(),
+                        x => x.Children),
+                }
+            };
+
+            ApplyTemplates(target);
+
+            var items = target.GetRealizedTreeContainers()
+                .OfType<TreeViewItem>()
+                .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<Node>());
+                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<Node>());
 
-////                Assert.DoesNotContain(item1, target.SelectedItems.OfType<Node>());
-////            }
-////        }
+                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<Node>());
+            }
+        }
 
-////                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<object>();
-
-////                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<TreeViewItem>()
-////                .Select(x => x.Header)
-////                .OfType<TextBlock>()
-////                .Select(x => x.Text)
-////                .ToList();
-
-////            Assert.Equal(new[] { "Foo", "Bar", "Baz " }, result);
-////        }
-
-////        [Fact]
-////        public void Removing_Item_Should_Remove_Itself_And_Children_From_Index()
-////        {
-////            var tree = CreateTestTreeData();
-////            var target = new TreeView
-////            {
-////                Template = CreateTreeViewTemplate(),
-////                Items = tree,
-////            };
-
-////            var root = new TestRoot();
-////            root.Child = target;
-
-////            CreateNodeDataTemplate(target);
-////            ApplyTemplates(target);
-
-////            Assert.Equal(5, target.ItemContainerGenerator.Index.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<Node>((x, _) => new Button { Content = x })
-////                },
-////                Items = items,
-////            };
-
-////            ApplyTemplates(target);
-
-////            var dataContexts = target.Presenter.Panel.Children
-////                .Cast<Control>()
-////                .Select(x => x.DataContext)
-////                .ToList();
-
-////            Assert.Equal(
-////                new object[] { items[0], items[1], "Base", "Base" },
-////                dataContexts);
-////        }
-
-////        [Fact]
-////        public void Control_Item_Should_Not_Be_NameScope()
-////        {
-////            var items = new object[]
-////            {
-////                new TreeViewItem(),
-////            };
-
-////            var target = new TreeView
-////            {
-////                Template = CreateTreeViewTemplate(),
-////                Items = items,
-////            };
-
-////            target.ApplyTemplate();
-////            target.Presenter.ApplyTemplate();
-
-////            var item = target.Presenter.Panel.LogicalChildren[0];
-////            Assert.Null(NameScope.GetNameScope((TreeViewItem)item));
-////        }
-
-////        [Fact]
-////        public void 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<Node>
-////            {
-////                new Node
-////                {
-////                    Value = "NewChild1",
-////                }
-////            };
-
-////            Assert.Equal(new[] { "Root" }, ExtractItemHeader(target, 0));
-////            Assert.Equal(new[] { "NewChild1" }, ExtractItemHeader(target, 1));
-////        }
-
-////        [Fact]
-////        public void Keyboard_Navigation_Should_Move_To_Last_Selected_Node()
-////        {
-////            using (Application())
-////            {
-////                var focus = FocusManager.Instance;
-////                var navigation = AvaloniaLocator.Current.GetService<IKeyboardNavigationHandler>();
-////                var data = CreateTestTreeData();
-
-////                var target = new TreeView
-////                {
-////                    Template = CreateTreeViewTemplate(),
-////                    Items = data,
-////                };
-
-////                var button = new Button();
-
-////                var root = new TestRoot
-////                {
-////                    Child = new StackPanel
-////                    {
-////                        Children = { target, button },
-////                    }
-////                };
-
-////                CreateNodeDataTemplate(target);
-////                ApplyTemplates(target);
-////                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<IKeyboardNavigationHandler>();
-////                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<PlatformHotkeyConfiguration>();
-////                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<PlatformHotkeyConfiguration>();
-////                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<PlatformHotkeyConfiguration>();
-////                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<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().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<Node>
-////                {
-////                    new Node { Value = "Root1", },
-////                    new Node { Value = "Root2", },
-////                },
-////                };
-
-////                var visualRoot = new TestRoot
-////                {
-////                    Styles =
-////                    {
-////                        new Style(x => x.OfType<TreeViewItem>())
-////                        {
-////                            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<TreeViewItem>(target.Presenter.Panel.Children[0]);
-////            Assert.Equal(3, rootNode.ItemContainerGenerator.Containers.Count());
-////            Assert.Equal(3, rootNode.Presenter.Panel.Children.Count);
-
-////            var child2Node = Assert.IsType<TreeViewItem>(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<DerivedTreeViewItem>(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<Control> 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<Node> CreateTestTreeData()
-////        {
-////            return new AvaloniaList<Node>
-////            {
-////                new Node
-////                {
-////                    Value = "Root",
-////                    Children = new AvaloniaList<Node>
-////                    {
-////                        new Node
-////                        {
-////                            Value = "Child1",
-////                        },
-////                        new Node
-////                        {
-////                            Value = "Child2",
-////                            Children = new AvaloniaList<Node>
-////                            {
-////                                new Node
-////                                {
-////                                    Value = "Grandchild2a",
-////                                },
-////                            },
-////                        },
-////                        new Node
-////                        {
-////                            Value = "Child3",
-////                        }
-////                    }
-////                }
-////            };
-////        }
-
-////        private void CreateNodeDataTemplate(Control control)
-////        {
-////            control.DataTemplates.Add(new TestTreeDataTemplate());
-////        }
-
-////        private IControlTemplate CreateTreeViewTemplate()
-////        {
-////            return new FuncControlTemplate<TreeView>((parent, scope) => new ItemsPresenter
-////            {
-////                Name = "PART_ItemsPresenter",
-////            }.RegisterInNameScope(scope));
-////        }
-
-////        private IControlTemplate CreateTreeViewItemTemplate()
-////        {
-////            return new FuncControlTemplate<TreeViewItem>((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<string> ExtractItemHeader(TreeView tree, int level)
-////        {
-////            return ExtractItemContent(tree.Presenter.Panel, 0, level)
-////                .Select(x => x.Header)
-////                .OfType<TextBlock>()
-////                .Select(x => x.Text)
-////                .ToList();
-////        }
-
-////        private IEnumerable<TreeViewItem> ExtractItemContent(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<Node> _children;
-
-////            public string Value { get; set; }
-
-////            public IAvaloniaList<Node> Children
-////            {
-////                get
-////                {
-////                    return _children;
-////                }
-
-////                set
-////                {
-////                    _children = value;
-////                    RaisePropertyChanged(nameof(Children));
-////                }
-////            }
-////        }
-
-////        private class TestTreeDataTemplate : ITreeDataTemplate
-////        {
-////            public 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<DerivedTreeViewItem>();
-////        }
-
-////        private class DerivedTreeViewItem : TreeViewItem
-////        {
-////            protected override IItemContainerGenerator CreateItemContainerGenerator() => CreateTreeItemContainerGenerator<DerivedTreeViewItem>();
-////        }
-
-////        private class TestDataContext : INotifyPropertyChanged
-////        {
-////            private string _selectedItem;
-
-////            public TestDataContext()
-////            {
-////                Items = new ObservableCollection<string>(Enumerable.Range(0, 5).Select(i => $"Item {i}"));
-////            }
-
-////            public ObservableCollection<string> 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<object>();
+
+                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<TreeViewItem>()
+                .Select(x => x.HeaderPresenter.Child)
+                .OfType<TextBlock>()
+                .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<Node>((x, _) => new Button { Content = x })
+                },
+                Items = items,
+            };
+
+            ApplyTemplates(target);
+
+            var dataContexts = target.Presenter.Panel.Children
+                .Cast<Control>()
+                .Select(x => x.DataContext)
+                .ToList();
+
+            Assert.Equal(
+                new object[] { items[0], items[1], "Base", "Base" },
+                dataContexts);
+        }
+
+        [Fact]
+        public void Control_Item_Should_Not_Be_NameScope()
+        {
+            var items = new object[]
+            {
+                new TreeViewItem(),
+            };
+
+            var target = new TreeView
+            {
+                Template = CreateTreeViewTemplate(),
+                Items = items,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            var item = target.Presenter.Panel.LogicalChildren[0];
+            Assert.Null(NameScope.GetNameScope((TreeViewItem)item));
+        }
+
+        [Fact]
+        public void 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<Node>
+            {
+                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<IKeyboardNavigationHandler>();
+                var data = CreateTestTreeData();
+
+                var target = new TreeView
+                {
+                    Template = CreateTreeViewTemplate(),
+                    Items = data,
+                };
+
+                var button = new Button();
+
+                var root = new TestRoot
+                {
+                    Child = new StackPanel
+                    {
+                        Children = { target, button },
+                    }
+                };
+
+                CreateNodeDataTemplate(target);
+                ApplyTemplates(target);
+                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<IKeyboardNavigationHandler>();
+                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<PlatformHotkeyConfiguration>();
+                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<PlatformHotkeyConfiguration>();
+                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<PlatformHotkeyConfiguration>();
+                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<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().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<Node>
+                {
+                    new Node { Value = "Root1", },
+                    new Node { Value = "Root2", },
+                },
+                };
+
+                var visualRoot = new TestRoot
+                {
+                    Styles =
+                    {
+                        new Style(x => x.OfType<TreeViewItem>())
+                        {
+                            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<TreeViewItem>(target.Presenter.Panel.Children[0]);
+            Assert.Equal(3, rootNode.GetRealizedContainers().Count());
+            Assert.Equal(3, rootNode.Presenter.Panel.Children.Count);
+
+            var child2Node = Assert.IsType<TreeViewItem>(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<DerivedTreeViewItem>(container);
+            }
+        }
+
+        private void ApplyTemplates(TreeView tree)
+        {
+            tree.ApplyTemplate();
+            tree.Presenter.ApplyTemplate();
+            ApplyTemplates(tree.Presenter.Panel.Children);
+        }
+
+        private void ApplyTemplates(IEnumerable<Control> 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<Node> CreateTestTreeData()
+        {
+            return new AvaloniaList<Node>
+            {
+                new Node
+                {
+                    Value = "Root",
+                    Children = new AvaloniaList<Node>
+                    {
+                        new Node
+                        {
+                            Value = "Child1",
+                        },
+                        new Node
+                        {
+                            Value = "Child2",
+                            Children = new AvaloniaList<Node>
+                            {
+                                new Node
+                                {
+                                    Value = "Grandchild2a",
+                                },
+                            },
+                        },
+                        new Node
+                        {
+                            Value = "Child3",
+                        }
+                    }
+                }
+            };
+        }
+
+        private void CreateNodeDataTemplate(Control control)
+        {
+            control.DataTemplates.Add(new TestTreeDataTemplate());
+        }
+
+        private IControlTemplate CreateTreeViewTemplate()
+        {
+            return new FuncControlTemplate<TreeView>((parent, scope) => new ItemsPresenter
+            {
+                Name = "PART_ItemsPresenter",
+            }.RegisterInNameScope(scope));
+        }
+
+        private IControlTemplate CreateTreeViewItemTemplate()
+        {
+            return new FuncControlTemplate<TreeViewItem>((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<string> ExtractItemHeader(TreeView tree, int level)
+        {
+            return ExtractItemContent(tree.Presenter.Panel, 0, level)
+                .Select(x => x.HeaderPresenter.Child)
+                .OfType<TextBlock>()
+                .Select(x => x.Text)
+                .ToList();
+        }
+
+        private IEnumerable<TreeViewItem> 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<Node> _children;
+
+            public string Value { get; set; }
+
+            public IAvaloniaList<Node> Children
+            {
+                get
+                {
+                    return _children;
+                }
+
+                set
+                {
+                    _children = value;
+                    RaisePropertyChanged(nameof(Children));
+                }
+            }
+        }
+
+        private class TestTreeDataTemplate : ITreeDataTemplate
+        {
+            public 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<string>(Enumerable.Range(0, 5).Select(i => $"Item {i}"));
+            }
+
+            public ObservableCollection<string> Items { get; }
+
+            public string SelectedItem
+            {
+                get { return _selectedItem; }
+                set
+                {
+                    _selectedItem = value;
+                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItem)));
+                }
+            }
+
+            public event PropertyChangedEventHandler PropertyChanged;
+
+        }
+    }
+}