Skip to content

Commit b1878e2

Browse files
japfJeremy Alles
and
Jeremy Alles
authored
add SelectionEvent to the TreeView control (#2063)
Co-authored-by: Jeremy Alles <[email protected]>
1 parent f305413 commit b1878e2

12 files changed

+387
-39
lines changed

dev/Generated/TreeView.properties.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ TreeViewProperties::TreeViewProperties()
2929
, m_dragItemsStartingEventSource{static_cast<TreeView*>(this)}
3030
, m_expandingEventSource{static_cast<TreeView*>(this)}
3131
, m_itemInvokedEventSource{static_cast<TreeView*>(this)}
32+
, m_selectionChangedEventSource{static_cast<TreeView*>(this)}
3233
{
3334
EnsureProperties();
3435
}
@@ -304,3 +305,13 @@ void TreeViewProperties::ItemInvoked(winrt::event_token const& token)
304305
{
305306
m_itemInvokedEventSource.remove(token);
306307
}
308+
309+
winrt::event_token TreeViewProperties::SelectionChanged(winrt::TypedEventHandler<winrt::TreeView, winrt::TreeViewSelectionChangedEventArgs> const& value)
310+
{
311+
return m_selectionChangedEventSource.add(value);
312+
}
313+
314+
void TreeViewProperties::SelectionChanged(winrt::event_token const& token)
315+
{
316+
m_selectionChangedEventSource.remove(token);
317+
}

dev/Generated/TreeView.properties.h

+3
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,15 @@ class TreeViewProperties
6666
void Expanding(winrt::event_token const& token);
6767
winrt::event_token ItemInvoked(winrt::TypedEventHandler<winrt::TreeView, winrt::TreeViewItemInvokedEventArgs> const& value);
6868
void ItemInvoked(winrt::event_token const& token);
69+
winrt::event_token SelectionChanged(winrt::TypedEventHandler<winrt::TreeView, winrt::TreeViewSelectionChangedEventArgs> const& value);
70+
void SelectionChanged(winrt::event_token const& token);
6971

7072
event_source<winrt::TypedEventHandler<winrt::TreeView, winrt::TreeViewCollapsedEventArgs>> m_collapsedEventSource;
7173
event_source<winrt::TypedEventHandler<winrt::TreeView, winrt::TreeViewDragItemsCompletedEventArgs>> m_dragItemsCompletedEventSource;
7274
event_source<winrt::TypedEventHandler<winrt::TreeView, winrt::TreeViewDragItemsStartingEventArgs>> m_dragItemsStartingEventSource;
7375
event_source<winrt::TypedEventHandler<winrt::TreeView, winrt::TreeViewExpandingEventArgs>> m_expandingEventSource;
7476
event_source<winrt::TypedEventHandler<winrt::TreeView, winrt::TreeViewItemInvokedEventArgs>> m_itemInvokedEventSource;
77+
event_source<winrt::TypedEventHandler<winrt::TreeView, winrt::TreeViewSelectionChangedEventArgs>> m_selectionChangedEventSource;
7578

7679
static void EnsureProperties();
7780
static void ClearProperties();

dev/TreeView/APITests/TreeViewTests.cs

+166
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
using TreeViewList = Microsoft.UI.Xaml.Controls.TreeViewList;
3131
using TreeViewNode = Microsoft.UI.Xaml.Controls.TreeViewNode;
3232
using TreeViewSelectionMode = Microsoft.UI.Xaml.Controls.TreeViewSelectionMode;
33+
using TreeViewSelectionChangedEventArgs = Microsoft.UI.Xaml.Controls.TreeViewSelectionChangedEventArgs;
3334

3435
namespace Windows.UI.Xaml.Tests.MUXControls.ApiTests
3536
{
@@ -501,6 +502,171 @@ public void VerifyVisualTree()
501502
VisualTreeTestHelper.VerifyVisualTree(root: treeView, masterFilePrefix: "TreeView");
502503
}
503504

505+
[TestMethod]
506+
public void TreeViewSelectionChangedSingleMode()
507+
{
508+
RunOnUIThread.Execute(() =>
509+
{
510+
// input data:
511+
// - 1
512+
// - 2
513+
// - 3
514+
TreeViewSelectionChangedEventArgs selectionChangedEventArgs = null;
515+
516+
var treeView = new TreeView { SelectionMode = TreeViewSelectionMode.Single };
517+
treeView.SelectionChanged += (s, e1) => selectionChangedEventArgs = e1;
518+
519+
var collection = new ObservableCollection<int> { 1, 2, 3 };
520+
treeView.ItemsSource = collection;
521+
Content = treeView;
522+
Content.UpdateLayout();
523+
var tvi1 = (TreeViewItem)treeView.ContainerFromItem(1);
524+
var tvi2 = (TreeViewItem)treeView.ContainerFromItem(2);
525+
526+
tvi1.IsSelected = true;
527+
528+
Verify.IsNotNull(selectionChangedEventArgs);
529+
Verify.AreEqual(1, selectionChangedEventArgs.AddedItems.Count);
530+
Verify.IsTrue(selectionChangedEventArgs.AddedItems.Contains(1));
531+
Verify.AreEqual(0, selectionChangedEventArgs.RemovedItems.Count);
532+
selectionChangedEventArgs = null;
533+
534+
tvi2.IsSelected = true;
535+
536+
Verify.IsNotNull(selectionChangedEventArgs);
537+
Verify.AreEqual(1, selectionChangedEventArgs.AddedItems.Count);
538+
Verify.IsTrue(selectionChangedEventArgs.AddedItems.Contains(2));
539+
Verify.AreEqual(1, selectionChangedEventArgs.RemovedItems.Count);
540+
Verify.IsTrue(selectionChangedEventArgs.RemovedItems.Contains(1));
541+
selectionChangedEventArgs = null;
542+
543+
tvi2.IsSelected = false;
544+
545+
Verify.IsNotNull(selectionChangedEventArgs);
546+
Verify.AreEqual(0, selectionChangedEventArgs.AddedItems.Count);
547+
Verify.AreEqual(1, selectionChangedEventArgs.RemovedItems.Count);
548+
Verify.IsTrue(selectionChangedEventArgs.RemovedItems.Contains(2));
549+
selectionChangedEventArgs = null;
550+
551+
tvi1.IsSelected = true;
552+
553+
Verify.IsNotNull(selectionChangedEventArgs);
554+
Verify.AreEqual(1, selectionChangedEventArgs.AddedItems.Count);
555+
Verify.IsTrue(selectionChangedEventArgs.AddedItems.Contains(1));
556+
Verify.AreEqual(0, selectionChangedEventArgs.RemovedItems.Count);
557+
selectionChangedEventArgs = null;
558+
559+
treeView.ItemsSource = null;
560+
561+
Verify.IsNotNull(selectionChangedEventArgs);
562+
Verify.AreEqual(0, selectionChangedEventArgs.AddedItems.Count);
563+
Verify.AreEqual(1, selectionChangedEventArgs.RemovedItems.Count);
564+
Verify.IsTrue(selectionChangedEventArgs.RemovedItems.Contains(1));
565+
});
566+
}
567+
568+
[TestMethod]
569+
public void TreeViewSelectionChangedMultipleMode()
570+
{
571+
RunOnUIThread.Execute(() =>
572+
{
573+
// input data:
574+
// - 1
575+
// - 11
576+
// - 12
577+
// - 13
578+
// - 2
579+
// - 21
580+
// - 3
581+
TreeViewSelectionChangedEventArgs selectionChangedEventArgs = null;
582+
583+
var treeView = new TreeView { SelectionMode = TreeViewSelectionMode.Multiple };
584+
treeView.SelectionChanged += (s, e) => selectionChangedEventArgs = e;
585+
586+
var node1 = new TreeViewNode { Content = "1", IsExpanded = true };
587+
var node11 = new TreeViewNode { Content = "11" };
588+
var node12 = new TreeViewNode { Content = "12" };
589+
var node13 = new TreeViewNode { Content = "13" };
590+
node1.Children.Add(node11);
591+
node1.Children.Add(node12);
592+
node1.Children.Add(node13);
593+
594+
var node2 = new TreeViewNode { Content = "2", IsExpanded = true };
595+
var node21 = new TreeViewNode { Content = "21" };
596+
node2.Children.Add(node21);
597+
598+
var node3 = new TreeViewNode { Content = "3" };
599+
600+
treeView.RootNodes.Add(node1);
601+
treeView.RootNodes.Add(node2);
602+
treeView.RootNodes.Add(node3);
603+
Content = treeView;
604+
Content.UpdateLayout();
605+
606+
var tvi1 = (TreeViewItem)treeView.ContainerFromItem(node1);
607+
var tvi11 = (TreeViewItem)treeView.ContainerFromItem(node11);
608+
var tvi12 = (TreeViewItem)treeView.ContainerFromItem(node12);
609+
var tvi13 = (TreeViewItem)treeView.ContainerFromItem(node13);
610+
var tvi2 = (TreeViewItem)treeView.ContainerFromItem(node2);
611+
var tvi21 = (TreeViewItem)treeView.ContainerFromItem(node21);
612+
var tvi3 = (TreeViewItem)treeView.ContainerFromItem(node3);
613+
614+
// - 1 selected
615+
// - 11 selected
616+
// - 12 selected
617+
// - 13 selected
618+
// - 2
619+
// - 21
620+
// - 3
621+
tvi1.IsSelected = true;
622+
623+
Verify.IsNotNull(selectionChangedEventArgs);
624+
Verify.AreEqual(4, selectionChangedEventArgs.AddedItems.Count);
625+
Verify.IsTrue(selectionChangedEventArgs.AddedItems.Contains(node1));
626+
Verify.IsTrue(selectionChangedEventArgs.AddedItems.Contains(node11));
627+
Verify.IsTrue(selectionChangedEventArgs.AddedItems.Contains(node12));
628+
Verify.IsTrue(selectionChangedEventArgs.AddedItems.Contains(node13));
629+
Verify.AreEqual(0, selectionChangedEventArgs.RemovedItems.Count);
630+
selectionChangedEventArgs = null;
631+
632+
// - 1 selected
633+
// - 11
634+
// - 12 selected
635+
// - 13 selected
636+
// - 2
637+
// - 21
638+
// - 3
639+
tvi11.IsSelected = true;
640+
tvi11.IsSelected = false;
641+
642+
Verify.IsNotNull(selectionChangedEventArgs);
643+
Verify.AreEqual(0, selectionChangedEventArgs.AddedItems.Count);
644+
Verify.AreEqual(1, selectionChangedEventArgs.RemovedItems.Count);
645+
Verify.IsTrue(selectionChangedEventArgs.RemovedItems.Contains(node11));
646+
selectionChangedEventArgs = null;
647+
648+
// - 1 selected
649+
// - 11 selected
650+
// - 12 selected
651+
// - 13 selected
652+
// - 2 selected
653+
// - 21 selected
654+
// - 3 selected
655+
treeView.SelectAll();
656+
Verify.IsNotNull(selectionChangedEventArgs);
657+
var items = selectionChangedEventArgs.AddedItems.ToList();
658+
Verify.AreEqual(7, selectionChangedEventArgs.AddedItems.Count);
659+
Verify.IsTrue(selectionChangedEventArgs.AddedItems.Contains(node1));
660+
Verify.IsTrue(selectionChangedEventArgs.AddedItems.Contains(node11));
661+
Verify.IsTrue(selectionChangedEventArgs.AddedItems.Contains(node12));
662+
Verify.IsTrue(selectionChangedEventArgs.AddedItems.Contains(node13));
663+
Verify.IsTrue(selectionChangedEventArgs.AddedItems.Contains(node2));
664+
Verify.IsTrue(selectionChangedEventArgs.AddedItems.Contains(node21));
665+
Verify.IsTrue(selectionChangedEventArgs.AddedItems.Contains(node3));
666+
Verify.AreEqual(0, selectionChangedEventArgs.RemovedItems.Count);
667+
});
668+
}
669+
504670
private bool IsMultiSelectCheckBoxChecked(TreeView tree, TreeViewNode node)
505671
{
506672
var treeViewItem = tree.ContainerFromNode(node) as TreeViewItem;

dev/TreeView/TreeView.cpp

+22-25
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,12 @@ winrt::IVector<winrt::TreeViewNode> TreeView::SelectedNodes()
9292

9393
void TreeView::SelectedItem(winrt::IInspectable const& item)
9494
{
95-
auto selectedItems = SelectedItems();
96-
if (selectedItems.Size() > 0)
97-
{
98-
selectedItems.Clear();
99-
}
100-
if (item)
95+
if (auto listControl = ListControl())
10196
{
102-
selectedItems.Append(item);
97+
if (auto viewModel = listControl->ListViewModel())
98+
{
99+
viewModel->SelectSingleItem(item);
100+
}
103101
}
104102
}
105103

@@ -130,23 +128,7 @@ void TreeView::UpdateSelection(winrt::TreeViewNode const& node, bool isSelected)
130128
{
131129
if (isSelected != viewModel->IsNodeSelected(node))
132130
{
133-
auto selectedNodes = viewModel->GetSelectedNodes();
134-
if (isSelected)
135-
{
136-
if (SelectionMode() == winrt::TreeViewSelectionMode::Single && selectedNodes.Size() > 0)
137-
{
138-
selectedNodes.Clear();
139-
}
140-
selectedNodes.Append(node);
141-
}
142-
else
143-
{
144-
unsigned int index;
145-
if (selectedNodes.IndexOf(node, index))
146-
{
147-
selectedNodes.RemoveAt(index);
148-
}
149-
}
131+
viewModel->SelectNode(node, isSelected);
150132
}
151133
}
152134
}
@@ -305,6 +287,14 @@ void TreeView::OnListControlDragItemsCompleted(const winrt::IInspectable& sender
305287
m_dragItemsCompletedEventSource(*this, *treeViewArgs);
306288
}
307289

290+
void TreeView::OnListControlSelectionChanged(const winrt::IInspectable& sender, const winrt::SelectionChangedEventArgs& args)
291+
{
292+
if (SelectionMode() == winrt::TreeViewSelectionMode::Single)
293+
{
294+
RaiseSelectionChanged(args.AddedItems(), args.RemovedItems());
295+
}
296+
}
297+
308298
void TreeView::UpdateItemsSelectionMode(bool isMultiSelect)
309299
{
310300
auto listControl = ListControl();
@@ -340,6 +330,12 @@ void TreeView::UpdateItemsSelectionMode(bool isMultiSelect)
340330
}
341331
}
342332

333+
void TreeView::RaiseSelectionChanged(const winrt::IVector<winrt::IInspectable> addedItems, const winrt::IVector<winrt::IInspectable> removedItems)
334+
{
335+
const auto treeViewArgs = winrt::make_self<TreeViewSelectionChangedEventArgs>(addedItems, removedItems);
336+
m_selectionChangedEventSource(*this, *treeViewArgs);
337+
}
338+
343339
void TreeView::OnApplyTemplate()
344340
{
345341
winrt::IControlProtected controlProtected = *this;
@@ -359,7 +355,7 @@ void TreeView::OnApplyTemplate()
359355
viewModel->IsContentMode(true);
360356
}
361357
viewModel->PrepareView(m_rootNode.get());
362-
viewModel->SetOwningList(listControl);
358+
viewModel->SetOwners(listControl, *this);
363359
viewModel->NodeExpanding({ this, &TreeView::OnNodeExpanding });
364360
viewModel->NodeCollapsed({ this, &TreeView::OnNodeCollapsed });
365361

@@ -381,6 +377,7 @@ void TreeView::OnApplyTemplate()
381377
m_containerContentChangingRevoker = listControl.ContainerContentChanging(winrt::auto_revoke, { this, &TreeView::OnContainerContentChanging });
382378
m_dragItemsStartingRevoker = listControl.DragItemsStarting(winrt::auto_revoke, { this, &TreeView::OnListControlDragItemsStarting });
383379
m_dragItemsCompletedRevoker = listControl.DragItemsCompleted(winrt::auto_revoke, { this, &TreeView::OnListControlDragItemsCompleted });
380+
m_selectionChangedRevoker = listControl.SelectionChanged(winrt::auto_revoke, { this, &TreeView::OnListControlSelectionChanged });
384381

385382
if (m_pendingSelectedNodes && m_pendingSelectedNodes.get().Size() > 0)
386383
{

dev/TreeView/TreeView.h

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "TreeViewCollapsedEventArgs.h"
1010
#include "TreeViewDragItemsStartingEventArgs.h"
1111
#include "TreeViewDragItemsCompletedEventArgs.h"
12+
#include "TreeViewSelectionChangedEventArgs.h"
1213

1314
#include "TreeView.g.h"
1415
#include "TreeView.properties.h"
@@ -36,6 +37,7 @@ class TreeView :
3637
winrt::IVector<winrt::IInspectable> SelectedItems();
3738

3839
void UpdateSelection(winrt::TreeViewNode const& node, bool isSelected);
40+
void RaiseSelectionChanged(const winrt::IVector<winrt::IInspectable> addedItems, const winrt::IVector<winrt::IInspectable> removedItems);
3941

4042
void Expand(winrt::TreeViewNode const& value);
4143
void Collapse(winrt::TreeViewNode const& value);
@@ -47,6 +49,7 @@ class TreeView :
4749
void OnNodeCollapsed(const winrt::TreeViewNode& sender, const winrt::IInspectable&);
4850
void OnListControlDragItemsStarting(const winrt::IInspectable& sender, const winrt::DragItemsStartingEventArgs& args);
4951
void OnListControlDragItemsCompleted(const winrt::IInspectable& sender, const winrt::DragItemsCompletedEventArgs& args);
52+
void OnListControlSelectionChanged(const winrt::IInspectable& sender, const winrt::SelectionChangedEventArgs& args);
5053
void OnPropertyChanged(const winrt::DependencyPropertyChangedEventArgs& args);
5154
void UpdateItemsSelectionMode(bool isMultiSelect);
5255

@@ -77,4 +80,5 @@ class TreeView :
7780
winrt::ListViewBase::ContainerContentChanging_revoker m_containerContentChangingRevoker{};
7881
winrt::ListViewBase::DragItemsStarting_revoker m_dragItemsStartingRevoker{};
7982
winrt::ListViewBase::DragItemsCompleted_revoker m_dragItemsCompletedRevoker{};
83+
winrt::ListViewBase::SelectionChanged_revoker m_selectionChangedRevoker{};
8084
};

dev/TreeView/TreeView.idl

+13-1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ runtimeclass TreeViewDragItemsCompletedEventArgs
9494
}
9595
}
9696

97+
[WUXC_VERSION_PREVIEW]
98+
[webhosthidden]
99+
runtimeclass TreeViewSelectionChangedEventArgs
100+
{
101+
Windows.Foundation.Collections.IVector<Object> AddedItems{ get; };
102+
Windows.Foundation.Collections.IVector<Object> RemovedItems{ get; };
103+
}
104+
97105
[WUXC_VERSION_RS4]
98106
[webhosthidden]
99107
[WUXC_INTERFACE_NAME("ITreeViewList", 0f00a54e-099a-47a5-a942-94692b01f452)]
@@ -124,6 +132,10 @@ unsealed runtimeclass TreeView : Windows.UI.Xaml.Controls.Control
124132
event Windows.Foundation.TypedEventHandler<TreeView, TreeViewItemInvokedEventArgs> ItemInvoked;
125133
event Windows.Foundation.TypedEventHandler<TreeView, TreeViewExpandingEventArgs> Expanding;
126134
event Windows.Foundation.TypedEventHandler<TreeView, TreeViewCollapsedEventArgs> Collapsed;
135+
[WUXC_VERSION_PREVIEW]
136+
{
137+
event Windows.Foundation.TypedEventHandler<TreeView, TreeViewSelectionChangedEventArgs> SelectionChanged;
138+
}
127139

128140
static Windows.UI.Xaml.DependencyProperty SelectionModeProperty{ get; };
129141

@@ -231,4 +243,4 @@ unsealed runtimeclass TreeViewItem : Windows.UI.Xaml.Controls.ListViewItem
231243
}
232244
}
233245

234-
}
246+
}

dev/TreeView/TreeView.vcxitems

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
<ClInclude Include="$(MSBuildThisFileDirectory)TreeViewItemDataAutomationPeer.h" />
2929
<ClInclude Include="$(MSBuildThisFileDirectory)TreeViewItemInvokedEventArgs.h" />
3030
<ClInclude Include="$(MSBuildThisFileDirectory)TreeViewList.h" />
31+
<ClInclude Include="$(MSBuildThisFileDirectory)TreeViewSelectionChangedEventArgs.h" />
3132
<ClInclude Include="$(MSBuildThisFileDirectory)ViewModel.h" />
3233
</ItemGroup>
3334
<ItemGroup>
@@ -48,13 +49,14 @@
4849
<ClCompile Include="$(MSBuildThisFileDirectory)TreeViewItemDataAutomationPeer.cpp" />
4950
<ClCompile Include="$(MSBuildThisFileDirectory)TreeViewItemInvokedEventArgs.cpp" />
5051
<ClCompile Include="$(MSBuildThisFileDirectory)TreeViewList.cpp" />
52+
<ClCompile Include="$(MSBuildThisFileDirectory)TreeViewSelectionChangedEventArgs.cpp" />
5153
<ClCompile Include="$(MSBuildThisFileDirectory)ViewModel.cpp" />
5254
</ItemGroup>
5355
<ItemGroup>
5456
<Midl Include="$(MSBuildThisFileDirectory)TreeView.idl" />
5557
<Midl Include="$(MSBuildThisFileDirectory)TreeViewAutomationPeers.idl" />
5658
</ItemGroup>
57-
<ItemGroup >
59+
<ItemGroup>
5860
<Page Include="$(MSBuildThisFileDirectory)TreeView.xaml">
5961
<Version>RS1</Version>
6062
<Type>DefaultStyle</Type>

0 commit comments

Comments
 (0)