Skip to content

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 #148

Description

@matt-edmondson

What's wrong

An invalidation travels up the tree by two routes at once:

  • UIElementBase.Invalidate() (TUI/Elements/UIElementBase.cs) calls Parent?.Invalidate() and raises Invalidated.
  • UIContainerBase subscribes child.Invalidated += OnChildInvalidated for every child it adds, and OnChildInvalidated calls Invalidate() again (TUI/Elements/UIContainerBase.cs:28, :153).

So every parent's Invalidate() runs twice for each child invalidation, and the count doubles at each level.

Repro

StackPanel root = new(); StackPanel cur = root;
for (int i = 0; i < 15; i++) { StackPanel n = new(); cur.AddChild(n); cur = n; }
TextElement leaf = new("x"); cur.AddChild(leaf);

int count = 0;
root.Invalidated += (_, _) => count++;
leaf.Text = "y";
// count == 65536 (2^16); expected 1

Why it matters

Every setter that invalidates, including Text, Position, Dimensions and IsVisible, goes through this. During arrange, every node in the tree has its Position/Dimensions set, so the cost of a relayout grows exponentially with depth. A typical 3–4-level UI already invalidates the root 8–16 times per change, and any handler on Invalidated fires that many times.

Suggested fix

Keep one propagation path:

  • drop Parent?.Invalidate() from UIElementBase.Invalidate() and rely on the container's event subscription, or
  • drop the OnChildInvalidated subscription and its unsubscribes in RemoveChild/ClearChildren.

Acceptance: a test that sets Text on a leaf nested N levels deep sees the root's Invalidated fire exactly once.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't workingreadyFully specified; implement as written

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions