diff --git a/dev/NavigationView/NavigationView_ApiTests/NavigationViewTests.cs b/dev/NavigationView/NavigationView_ApiTests/NavigationViewTests.cs index 65cfb2ff63..e0a1463c80 100644 --- a/dev/NavigationView/NavigationView_ApiTests/NavigationViewTests.cs +++ b/dev/NavigationView/NavigationView_ApiTests/NavigationViewTests.cs @@ -653,5 +653,37 @@ public void VerifyMenuItemAndContainerMappingMenuItems() MUXControlsTestApp.App.TestContentRoot = null; }); } + + [TestMethod] + public void VerifySelectedItemIsNullWhenNoItemIsSelected() + { + RunOnUIThread.Execute(() => + { + var navView = new NavigationView(); + Content = navView; + + var menuItem1 = new NavigationViewItem(); + menuItem1.Content = "Item 1"; + + navView.MenuItems.Add(menuItem1); + navView.Width = 1008; // forces the control into Expanded mode so that the menu renders + Content.UpdateLayout(); + + Verify.IsFalse(menuItem1.IsSelected); + Verify.AreEqual(null, navView.SelectedItem); + + menuItem1.IsSelected = true; + Content.UpdateLayout(); + + Verify.IsTrue(menuItem1.IsSelected); + Verify.AreEqual(menuItem1, navView.SelectedItem); + + menuItem1.IsSelected = false; + Content.UpdateLayout(); + + Verify.IsFalse(menuItem1.IsSelected); + Verify.AreEqual(null, navView.SelectedItem, "SelectedItem should have been [null] as no item is selected"); + }); + } } } \ No newline at end of file diff --git a/dev/Repeater/APITests/SelectionModelTests.cs b/dev/Repeater/APITests/SelectionModelTests.cs index 8a567317f1..5b86b8f31d 100644 --- a/dev/Repeater/APITests/SelectionModelTests.cs +++ b/dev/Repeater/APITests/SelectionModelTests.cs @@ -53,15 +53,56 @@ public void ValidateOneLevelSingleSelection() SelectionModel selectionModel = new SelectionModel() { SingleSelect = true }; Log.Comment("Set the source to 10 items"); selectionModel.Source = Enumerable.Range(0, 10).ToList(); + + // Check index selection Select(selectionModel, 3, true); ValidateSelection(selectionModel, new List() { Path(3) }, new List() { Path() }); Select(selectionModel, 3, false); ValidateSelection(selectionModel, new List() { }); + + // Check index path selection + Select(selectionModel, Path(4), true); + ValidateSelection(selectionModel, new List() { Path(4) }, new List() { Path() }); + Select(selectionModel, Path(4), false); + ValidateSelection(selectionModel, new List() { }); + }); + } + + [TestMethod] + public void ValidateSelectionChangedEventSingleSelection() + { + RunOnUIThread.Execute(() => + { + SelectionModel selectionModel = new SelectionModel() { SingleSelect = true }; + selectionModel.Source = Enumerable.Range(0, 10).ToList(); + + bool select = true; + int selectionChangedFiredCount = 0; + selectionModel.SelectionChanged += delegate (SelectionModel sender, SelectionModelSelectionChangedEventArgs args) { + selectionChangedFiredCount++; + + // Verify SelectionChanged was raised after selection state was changed in the SelectionModel + if (select) + { + ValidateSelection(selectionModel, new List() { Path(4) }, new List() { Path() }); + } + else + { + ValidateSelection(selectionModel, new List() { }); + } + }; + + Select(selectionModel, Path(4), select); + Verify.AreEqual(1, selectionChangedFiredCount); + + select = false; + Select(selectionModel, Path(4), select); + Verify.AreEqual(2, selectionChangedFiredCount); }); } [TestMethod] - public void ValidateSelectionChangedEvent() + public void ValidateSelectionChangedEventMultipleSelection() { RunOnUIThread.Execute(() => { @@ -72,11 +113,12 @@ public void ValidateSelectionChangedEvent() selectionModel.SelectionChanged += delegate (SelectionModel sender, SelectionModelSelectionChangedEventArgs args) { selectionChangedFiredCount++; + + // Verify SelectionChanged was raised after selection state was changed in the SelectionModel ValidateSelection(selectionModel, new List() { Path(4) }, new List() { Path() }); }; Select(selectionModel, 4, true); - ValidateSelection(selectionModel, new List() { Path(4) }, new List() { Path() }); Verify.AreEqual(1, selectionChangedFiredCount); }); } diff --git a/dev/Repeater/SelectionModel.cpp b/dev/Repeater/SelectionModel.cpp index 9f160bea84..9caeefb3ac 100644 --- a/dev/Repeater/SelectionModel.cpp +++ b/dev/Repeater/SelectionModel.cpp @@ -647,7 +647,13 @@ void SelectionModel::SelectWithPathImpl(const winrt::IndexPath& index, bool sele // If we unselect something, raise event any way, otherwise changedSelection is false bool changedSelection = false; - if (m_singleSelect) + // We only need to clear selection by walking the data structure from the beginning when: + // - we are in single selection mode and + // - want to select something. + // + // If we want to unselect something we unselect it directly in TraverseIndexPath below and raise the SelectionChanged event + // if required. + if (m_singleSelect && select) { ClearSelection(true /*resetAnchor*/, false /* raiseSelectionChanged */); }