diff --git a/src/MainDemo.Wpf/App.xaml b/src/MainDemo.Wpf/App.xaml
index 78e99066d6..a5c2537ae3 100644
--- a/src/MainDemo.Wpf/App.xaml
+++ b/src/MainDemo.Wpf/App.xaml
@@ -22,7 +22,7 @@
If you would prefer to use your own colors, there is an option for that as well:
PrimaryColor and SecondaryColor also support the constant string "Inherit" to specify the color should use the system theme accent color
-->
-
+
+
+
+
+
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
SelectedTreeItems { get; } = [];
+
public TestItem? SelectedTreeItem
{
get => _selectedTreeItem;
@@ -210,6 +214,14 @@ static TestItem CreateTestItem(Random random, int depth)
}
},
_ => SelectedItem != null);
+
+ RemoveSelectedListTreeItemCommand = new(item =>
+ {
+ if (item is TestItem treeItem)
+ {
+ SelectedTreeItems.Remove(treeItem);
+ }
+ });
}
private static string GenerateString(int length)
diff --git a/src/MaterialDesign3.Demo.Wpf/Trees.xaml b/src/MaterialDesign3.Demo.Wpf/Trees.xaml
index ad926dee62..93c69c231d 100644
--- a/src/MaterialDesign3.Demo.Wpf/Trees.xaml
+++ b/src/MaterialDesign3.Demo.Wpf/Trees.xaml
@@ -175,35 +175,69 @@
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
(IList)GetValue(SelectedItemsProperty);
+ set => SetValue(SelectedItemsProperty, value);
+ }
+ public static new readonly DependencyProperty SelectedItemsProperty =
+ DependencyProperty.Register(
+ nameof(SelectedItems),
+ typeof(IList),
+ typeof(TreeListView),
+ new FrameworkPropertyMetadata(default, OnSelectedItemsPropertyChanged));
+
+ private static void OnSelectedItemsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is TreeListView tree)
+ {
+ // Unsubscribe old collection changed
+ if (e.OldValue is INotifyCollectionChanged oldCollection)
+ {
+ oldCollection.CollectionChanged -= tree.SelectedItems_CollectionChanged;
+ }
+
+ // Subscribe new collection changed
+ if (e.NewValue is INotifyCollectionChanged newCollection)
+ {
+ newCollection.CollectionChanged += tree.SelectedItems_CollectionChanged;
+ }
+
+ tree.SyncUISelectionWithSelectedItems();
+ }
+ }
+
+
+
static TreeListView()
{
ItemsSourceProperty.OverrideMetadata(typeof(TreeListView), new FrameworkPropertyMetadata()
@@ -30,6 +65,90 @@ static TreeListView()
public TreeListView()
{
+ SelectionChanged += TreeListView_SelectionChanged;
+ }
+
+ private void TreeListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ // Keep the DP collection in sync when UI selection changes
+ if (SelectedItems is null)
+ {
+ return;
+ }
+
+ bool isSingleSelection = Keyboard.Modifiers == ModifierKeys.None && e.AddedItems.Count == 1;
+ // If no modifier keys, treat as single selection (like normal ListView)
+ if (isSingleSelection)
+ {
+ // Remove all except the newly selected item
+ var selected = e.AddedItems[0];
+ SelectedItems.Clear();
+ SelectedItems.Add(selected);
+ return;
+ }
+
+ // Remove unselected
+ if (!IsCollapsingItem)
+ {
+ foreach (var item in e.RemovedItems)
+ {
+ SelectedItems.Remove(item);
+ }
+ }
+
+ // Add newly selected
+ foreach (var item in e.AddedItems)
+ {
+ SelectedItems.Add(item);
+ }
+ }
+
+ private void SelectedItems_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
+ {
+ // NB: Every time we modify the base.SelectedItems we have to unsubscribe from the SelectionChanged event
+ // The base.SelectedItems only contains the visually selected items,
+ // while the SelectedItems contains all selected items, even ones which are not "visible"
+ this.SelectionChanged -= TreeListView_SelectionChanged;
+
+ if (e.Action == NotifyCollectionChangedAction.Reset)
+ {
+ base.SelectedItems.Clear();
+ foreach (var item in SelectedItems)
+ {
+ base.SelectedItems.Add(item);
+ }
+ }
+ else
+ {
+ foreach (var item in e.OldItems ?? Array.Empty