@@ -913,10 +913,19 @@ internal sealed class UiRenderer(Action requestRender)
913913 private readonly Dictionary < string , Component > _components = new ( ) ;
914914 private readonly Dictionary < string , Flyout > _contentFlyouts = new ( ) ;
915915 private readonly HashSet < string > _mountedPaths = new ( ) ;
916+ private readonly HashSet < string > _visitedControlPaths = new ( ) ;
917+ private readonly HashSet < string > _visitedComponentKeys = new ( ) ;
918+ private readonly HashSet < string > _visitedContentFlyoutPaths = new ( ) ;
916919
917920 public UIElement Render ( Element element , string path , List < Action > effects )
918921 {
919- return RenderElement ( element , path , effects ) ;
922+ _visitedControlPaths . Clear ( ) ;
923+ _visitedComponentKeys . Clear ( ) ;
924+ _visitedContentFlyoutPaths . Clear ( ) ;
925+
926+ var rendered = RenderElement ( element , path , effects ) ;
927+ PruneUnvisitedPaths ( ) ;
928+ return rendered ;
920929 }
921930
922931 public void Dispose ( )
@@ -993,6 +1002,8 @@ private UIElement RenderNavigationHost(INavigationHostElement element, string pa
9931002
9941003 private T GetOrCreate < T > ( string path ) where T : UIElement , new ( )
9951004 {
1005+ _visitedControlPaths . Add ( path ) ;
1006+
9961007 if ( _controls . TryGetValue ( path , out var existing ) && existing is T typed )
9971008 return typed ;
9981009
@@ -1020,6 +1031,8 @@ private UIElement RenderComponent(ComponentElement element, string path, List<Ac
10201031 {
10211032 var componentKey = GetComponentKey ( element . ComponentType ) ;
10221033 var key = path + ":" + componentKey ;
1034+ _visitedComponentKeys . Add ( key ) ;
1035+
10231036 if ( ! _components . TryGetValue ( key , out var component ) )
10241037 {
10251038 component = ( Component ) Activator . CreateInstance ( element . ComponentType ) ! ;
@@ -1378,6 +1391,8 @@ private FlyoutBase CreateFlyout(FlyoutElement element, string path, List<Action>
13781391
13791392 private Flyout CreateContentFlyout ( ContentFlyoutElement element , string path , List < Action > effects )
13801393 {
1394+ _visitedContentFlyoutPaths . Add ( path ) ;
1395+
13811396 // Cache the Flyout instance per path so its identity is STABLE across
13821397 // re-renders. ConfigureButton reassigns control.Flyout on every render,
13831398 // and a full-root re-render fires on every state change (including the
@@ -1403,6 +1418,39 @@ private Flyout CreateContentFlyout(ContentFlyoutElement element, string path, Li
14031418 return flyout ;
14041419 }
14051420
1421+ private void PruneUnvisitedPaths ( )
1422+ {
1423+ foreach ( var ( key , component ) in _components . ToArray ( ) )
1424+ {
1425+ if ( _visitedComponentKeys . Contains ( key ) )
1426+ continue ;
1427+
1428+ component . Context . RunEffectCleanups ( ) ;
1429+ _components . Remove ( key ) ;
1430+ }
1431+
1432+ foreach ( var ( path , flyout ) in _contentFlyouts . ToArray ( ) )
1433+ {
1434+ if ( _visitedContentFlyoutPaths . Contains ( path ) )
1435+ continue ;
1436+
1437+ flyout . Hide ( ) ;
1438+ flyout . Content = null ;
1439+ _contentFlyouts . Remove ( path ) ;
1440+ }
1441+
1442+ foreach ( var ( path , control ) in _controls . ToArray ( ) )
1443+ {
1444+ if ( _visitedControlPaths . Contains ( path ) )
1445+ continue ;
1446+
1447+ _mountedPaths . Remove ( path ) ;
1448+ DetachChildren ( control ) ;
1449+ RemoveFromParent ( control ) ;
1450+ _controls . Remove ( path ) ;
1451+ }
1452+ }
1453+
14061454 private static MenuFlyout CreateMenuFlyout ( MenuFlyoutContentElement element )
14071455 {
14081456 var flyout = new MenuFlyout { Placement = element . Placement } ;
0 commit comments