diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs
index 888f4a2013..e186f08561 100644
--- a/src/Avalonia.Controls/TreeView.cs
+++ b/src/Avalonia.Controls/TreeView.cs
@@ -164,6 +164,48 @@ namespace Avalonia.Controls
}
}
+ ///
+ /// Expands the specified all descendent s.
+ ///
+ /// The item to expand.
+ public void ExpandSubTree(TreeViewItem item)
+ {
+ item.IsExpanded = true;
+
+ var panel = item.Presenter.Panel;
+
+ if (panel != null)
+ {
+ foreach (var child in panel.Children)
+ {
+ if (child is TreeViewItem treeViewItem)
+ {
+ ExpandSubTree(treeViewItem);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Selects all items in the .
+ ///
+ ///
+ /// Note that this method only selects nodes currently visible due to their parent nodes
+ /// being expanded: it does not expand nodes.
+ ///
+ public void SelectAll()
+ {
+ SynchronizeItems(SelectedItems, ItemContainerGenerator.Index.Items);
+ }
+
+ ///
+ /// Deselects all items in the .
+ ///
+ public void UnselectAll()
+ {
+ SelectedItems.Clear();
+ }
+
///
/// Subscribes to the CollectionChanged event, if any.
///
@@ -409,7 +451,7 @@ namespace Avalonia.Controls
if (this.SelectionMode == SelectionMode.Multiple && Match(keymap.SelectAll))
{
- SynchronizeItems(SelectedItems, ItemContainerGenerator.Index.Items);
+ SelectAll();
e.Handled = true;
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
index b66d6ed11c..8defb353b3 100644
--- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
@@ -11,6 +11,7 @@ using Avalonia.Data;
using Avalonia.Data.Core;
using Avalonia.Input;
using Avalonia.Input.Platform;
+using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.UnitTests;
using Xunit;
@@ -740,6 +741,71 @@ namespace Avalonia.Controls.UnitTests
}
}
+ [Fact]
+ public void Right_Click_On_SelectedItem_Should_Not_Clear_Existing_Selection()
+ {
+ var tree = CreateTestTreeData();
+ var target = new TreeView
+ {
+ Template = CreateTreeViewTemplate(),
+ Items = tree,
+ ItemTemplate = new FuncDataTemplate(x => new TextBlock { Width = 20, Height = 10 }),
+ SelectionMode = SelectionMode.Multiple,
+ };
+
+ 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()
+ {
+ var tree = CreateTestTreeData();
+ var target = new TreeView
+ {
+ Template = CreateTreeViewTemplate(),
+ Items = tree,
+ ItemTemplate = new FuncDataTemplate(x => new TextBlock { Width = 20, Height = 10 }),
+ 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, InputModifiers.None);
+ ClickContainer(toContainer, InputModifiers.Shift);
+
+ Assert.Equal(2, target.SelectedItems.Count);
+
+ _mouse.Click(thenContainer, MouseButton.Right);
+
+ Assert.Equal(1, target.SelectedItems.Count);
+ }
+
private void ApplyTemplates(TreeView tree)
{
tree.ApplyTemplate();
@@ -874,7 +940,6 @@ namespace Avalonia.Controls.UnitTests
}
}
-
private class Node : NotifyingBase
{
private IAvaloniaList _children;