@@ -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 } ;
@@ -1620,20 +1668,37 @@ private static void ApplyModifiers(FrameworkElement control, Element element)
16201668 var m = element . Modifiers ;
16211669 control . Tag = element ;
16221670 if ( m . Margin is { } margin ) control . Margin = margin ;
1671+ else control . ClearValue ( FrameworkElement . MarginProperty ) ;
16231672 if ( m . Width is { } width ) control . Width = width ;
1673+ else control . ClearValue ( FrameworkElement . WidthProperty ) ;
16241674 if ( m . Height is { } height ) control . Height = height ;
1675+ else control . ClearValue ( FrameworkElement . HeightProperty ) ;
16251676 if ( m . MinWidth is { } minWidth ) control . MinWidth = minWidth ;
1677+ else control . ClearValue ( FrameworkElement . MinWidthProperty ) ;
16261678 if ( m . MaxWidth is { } maxWidth ) control . MaxWidth = maxWidth ;
1679+ else control . ClearValue ( FrameworkElement . MaxWidthProperty ) ;
16271680 if ( m . MinHeight is { } minHeight ) control . MinHeight = minHeight ;
1681+ else control . ClearValue ( FrameworkElement . MinHeightProperty ) ;
16281682 if ( m . MaxHeight is { } maxHeight ) control . MaxHeight = maxHeight ;
1683+ else control . ClearValue ( FrameworkElement . MaxHeightProperty ) ;
16291684 if ( m . HorizontalAlignment is { } hAlign ) control . HorizontalAlignment = hAlign ;
1685+ else control . ClearValue ( FrameworkElement . HorizontalAlignmentProperty ) ;
16301686 if ( m . VerticalAlignment is { } vAlign ) control . VerticalAlignment = vAlign ;
1687+ else control . ClearValue ( FrameworkElement . VerticalAlignmentProperty ) ;
16311688 if ( m . Opacity is { } opacity ) control . Opacity = opacity ;
1689+ else control . ClearValue ( UIElement . OpacityProperty ) ;
16321690 if ( m . AutomationName is { } automationName ) AutomationProperties . SetName ( control , automationName ) ;
1691+ else control . ClearValue ( AutomationProperties . NameProperty ) ;
16331692 if ( m . LiveRegion is { } liveRegion ) AutomationProperties . SetLiveSetting ( control , liveRegion ) ;
1693+ else control . ClearValue ( AutomationProperties . LiveSettingProperty ) ;
16341694 ApplyResourceOverrides ( control , m . ResourceOverrides ) ;
1635- if ( m . Disabled is { } disabled && control is Control disabledControl )
1636- disabledControl . IsEnabled = ! disabled ;
1695+ if ( control is Control disabledControl )
1696+ {
1697+ if ( m . Disabled is { } disabled )
1698+ disabledControl . IsEnabled = ! disabled ;
1699+ else
1700+ disabledControl . ClearValue ( Control . IsEnabledProperty ) ;
1701+ }
16371702
16381703 control . KeyDown -= ElementKeyDown ;
16391704 if ( m . KeyDown is not null ) control . KeyDown += ElementKeyDown ;
@@ -1646,37 +1711,59 @@ private static void ApplyModifiers(FrameworkElement control, Element element)
16461711 {
16471712 case TextBlock tb :
16481713 if ( m . FontSize is { } textSize ) tb . FontSize = textSize ;
1714+ else tb . ClearValue ( TextBlock . FontSizeProperty ) ;
16491715 if ( m . FontWeight is { } textWeight ) tb . FontWeight = textWeight ;
1716+ else tb . ClearValue ( TextBlock . FontWeightProperty ) ;
16501717 if ( m . FontFamily is { } textFamily ) tb . FontFamily = textFamily ;
1718+ else tb . ClearValue ( TextBlock . FontFamilyProperty ) ;
16511719 if ( m . TextWrapping is { } wrapping ) tb . TextWrapping = wrapping ;
1720+ else tb . ClearValue ( TextBlock . TextWrappingProperty ) ;
16521721 if ( m . Padding is { } textPadding ) tb . Padding = textPadding ;
1722+ else tb . ClearValue ( TextBlock . PaddingProperty ) ;
16531723 if ( m . ForegroundResourceKey is { } textFgResource ) tb . Foreground = ThemeResources . ResolveBrush ( textFgResource ) ;
16541724 else if ( m . Foreground is { } textFg ) tb . Foreground = textFg ;
1725+ else tb . ClearValue ( TextBlock . ForegroundProperty ) ;
1726+ tb . ClearValue ( TextBlock . TextTrimmingProperty ) ;
1727+ tb . ClearValue ( TextBlock . MaxLinesProperty ) ;
1728+ tb . ClearValue ( TextBlock . LineHeightProperty ) ;
1729+ tb . ClearValue ( TextBlock . CharacterSpacingProperty ) ;
16551730 break ;
16561731 case Control c :
16571732 if ( m . Padding is { } controlPadding ) c . Padding = controlPadding ;
1733+ else c . ClearValue ( Control . PaddingProperty ) ;
16581734 if ( m . FontSize is { } controlSize ) c . FontSize = controlSize ;
1735+ else c . ClearValue ( Control . FontSizeProperty ) ;
16591736 if ( m . FontWeight is { } controlWeight ) c . FontWeight = controlWeight ;
1737+ else c . ClearValue ( Control . FontWeightProperty ) ;
16601738 if ( m . FontFamily is { } controlFamily ) c . FontFamily = controlFamily ;
1739+ else c . ClearValue ( Control . FontFamilyProperty ) ;
16611740 if ( m . ForegroundResourceKey is { } controlFgResource ) c . Foreground = ThemeResources . ResolveBrush ( controlFgResource ) ;
16621741 else if ( m . Foreground is { } controlFg ) c . Foreground = controlFg ;
1742+ else c . ClearValue ( Control . ForegroundProperty ) ;
16631743 if ( m . BorderBrushResourceKey is { } controlBorderResource ) c . BorderBrush = ThemeResources . ResolveBrush ( controlBorderResource ) ;
16641744 else if ( m . BorderBrush is { } controlBorder ) c . BorderBrush = controlBorder ;
1745+ else c . ClearValue ( Control . BorderBrushProperty ) ;
16651746 if ( m . BorderThickness is { } controlThickness ) c . BorderThickness = controlThickness ;
1747+ else c . ClearValue ( Control . BorderThicknessProperty ) ;
16661748 break ;
16671749 case Border b :
16681750 if ( m . Padding is { } borderPadding ) b . Padding = borderPadding ;
1751+ else b . ClearValue ( Border . PaddingProperty ) ;
16691752 if ( m . BackgroundResourceKey is { } backgroundResourceKey )
16701753 b . Background = ThemeResources . ResolveBrush ( backgroundResourceKey ) ;
16711754 else if ( m . Background is { } bg )
16721755 b . Background = bg ;
1756+ else b . ClearValue ( Border . BackgroundProperty ) ;
16731757 if ( m . BorderBrushResourceKey is { } borderResourceKey )
16741758 b . BorderBrush = ThemeResources . ResolveBrush ( borderResourceKey ) ;
16751759 else if ( m . BorderBrush is { } borderBrush )
16761760 b . BorderBrush = borderBrush ;
1761+ else b . ClearValue ( Border . BorderBrushProperty ) ;
16771762 if ( m . BorderThickness is { } borderThickness )
16781763 b . BorderThickness = borderThickness ;
1764+ else b . ClearValue ( Border . BorderThicknessProperty ) ;
16791765 if ( m . CornerRadius is { } radius ) b . CornerRadius = radius ;
1766+ else b . ClearValue ( Border . CornerRadiusProperty ) ;
16801767 break ;
16811768 }
16821769 }
0 commit comments