Skip to content

fix: invalidate each ancestor once instead of 2^depth times - #181

Merged
matt-edmondson merged 1 commit into
mainfrom
fix/148-single-invalidation-path
Sep 30, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
fix/148-single-invalidation-path

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #148

What changed

UIElementBase.Invalidate() passed the change upward twice: it called Parent?.Invalidate() directly, and it raised Invalidated, which UIContainerBase subscribes to for every child it adopts. Each level doubled the count, so a leaf 16 levels deep invalidated the root 65,536 times.

This keeps the event subscription as the one propagation path and removes the direct Parent?.Invalidate() call. I kept the subscription rather than the Parent call because it follows actual membership in _children and covers any IUIElement implementation, not only UIElementBase. Every built-in container (StackPanel, BorderElement) adopts children through UIContainerBase.AddChild, so nothing loses notification.

The IUIElement.Invalidate() doc comment now says the parent is notified through the event.

Tests

  • AChangeNestedManyLevelsDeepInvalidatesTheRootOnce: the repro from the issue (a leaf 16 levels deep), asserting the root fires once.
  • AChangeInvalidatesEachAncestorOnce: an intermediate container and the root each fire once.

Both tests fail with the fix reverted (root count 65536, middle count 2) and pass with it. The full TUI.Test suite passes: 219/219.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Jn27sjy5Z1Sbi1aZaAJDgj


Generated by Claude Code

UIElementBase.Invalidate() called Parent.Invalidate() and also raised
Invalidated, which every container subscribes to for its children, so each
level passed the invalidation up twice and the root saw 2^depth calls.
Keep the event subscription as the single propagation path.

Fixes #148

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jn27sjy5Z1Sbi1aZaAJDgj
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 75a6847 into main Sep 30, 2026
14 checks passed
@matt-edmondson
matt-edmondson deleted the fix/148-single-invalidation-path branch September 30, 2026 04:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A change to a nested element invalidates the root 2^depth times, because each level passes Invalidate up both directly and through the Invalidated event

2 participants