diff --git a/dev/NavigationView/NavigationView.cpp b/dev/NavigationView/NavigationView.cpp index 3cafecb6b4..966cc20ecf 100644 --- a/dev/NavigationView/NavigationView.cpp +++ b/dev/NavigationView/NavigationView.cpp @@ -3418,19 +3418,39 @@ void NavigationView::SetNavigationViewItemRevokers(const winrt::NavigationViewIt void NavigationView::ClearNavigationViewItemRevokers(const winrt::NavigationViewItem& nvi) { + RevokeNavigationViewItemRevokers(nvi); nvi.SetValue(s_NavigationViewItemRevokersProperty, nullptr); m_itemsWithRevokerObjects.erase(nvi); } -void NavigationView::ClearAllNavigationViewItemRevokers() +void NavigationView::ClearAllNavigationViewItemRevokers() noexcept { for (const auto& nvi : m_itemsWithRevokerObjects) { - nvi.SetValue(s_NavigationViewItemRevokersProperty, nullptr); + // ClearAllNavigationViewItemRevokers is only called in the destructor, where exceptions cannot be thrown. + // If the associated NV has not yet been cleaned up, we must detach these revokers or risk a call into freed + // memory being made. However if they have been cleaned up these calls will throw. In this case we can ignore + // those exceptions. + try + { + RevokeNavigationViewItemRevokers(nvi); + nvi.SetValue(s_NavigationViewItemRevokersProperty, nullptr); + } + catch (...) {} } m_itemsWithRevokerObjects.clear(); } +void NavigationView::RevokeNavigationViewItemRevokers(const winrt::NavigationViewItem& nvi) +{ + if (auto const revokers = nvi.GetValue(s_NavigationViewItemRevokersProperty)) + { + if (auto const revokersAsNVIR = revokers.try_as()) { + revokersAsNVIR->RevokeAll(); + } + } +} + void NavigationView::InvalidateTopNavPrimaryLayout() { if (m_appliedTemplate && IsTopNavigationView()) diff --git a/dev/NavigationView/NavigationView.h b/dev/NavigationView/NavigationView.h index bd8213c660..1f7085f495 100644 --- a/dev/NavigationView/NavigationView.h +++ b/dev/NavigationView/NavigationView.h @@ -187,7 +187,8 @@ class NavigationView : inline static GlobalDependencyProperty s_NavigationViewItemRevokersProperty{ nullptr }; void SetNavigationViewItemRevokers(const winrt::NavigationViewItem& nvi); void ClearNavigationViewItemRevokers(const winrt::NavigationViewItem& nvi); - void ClearAllNavigationViewItemRevokers(); + void ClearAllNavigationViewItemRevokers() noexcept; + void RevokeNavigationViewItemRevokers(const winrt::NavigationViewItem& nvi); std::set m_itemsWithRevokerObjects; void InvalidateTopNavPrimaryLayout(); diff --git a/dev/NavigationView/NavigationViewItemRevokers.h b/dev/NavigationView/NavigationViewItemRevokers.h index e7592189f9..82c7d04c6c 100644 --- a/dev/NavigationView/NavigationViewItemRevokers.h +++ b/dev/NavigationView/NavigationViewItemRevokers.h @@ -11,4 +11,12 @@ class NavigationViewItemRevokers : public winrt::implements + { + var navView = new NavigationView(); + menuItem1 = new NavigationViewItem(); + + navView.MenuItems.Add(menuItem1); + Content = navView; + Content.UpdateLayout(); + + navView.MenuItems.Clear(); + Content = menuItem1; + Content.UpdateLayout(); + }); + + IdleSynchronizer.Wait(); + + RunOnUIThread.Execute(() => + { + GC.Collect(); + }); + + IdleSynchronizer.Wait(); + + RunOnUIThread.Execute(() => + { + // NavigationView has a handler on NVI's IsSelected DependencyPropertyChangedEvent. + menuItem1.IsSelected = !menuItem1.IsSelected; + }); + } } } \ No newline at end of file