Skip to content

Fix the expander never collapsing its content - #3329

Merged
FrayxRulez merged 1 commit into
developfrom
settingsexpander-collapse-guard
Aug 10, 2026
Merged

Fix the expander never collapsing its content#3329
FrayxRulez merged 1 commit into
developfrom
settingsexpander-collapse-guard

Conversation

@FrayxRulez

Copy link
Copy Markdown
Collaborator

Problem

SettingsExpander.OnExpandedChanged guards the scoped batch's completion against a newer
toggle:

var tracker = _tracker++;
...
batch.Completed += (s, args) =>
{
    if (_tracker == tracker)
    {
        PopupRoot.Visibility = _expanded ? Visibility.Visible : Visibility.Collapsed;
    }
};

_tracker++ returns the value before the increment, so by the time the handler runs the
comparison is (n + 1) == n. It is never true, and the body never executes — on any toggle,
not just a superseded one.

That handler is the only thing that applies Visibility.Collapsed, and PopupRoot.Visibility
is set to Visible unconditionally a few lines above:

PopupHost.Height = newValue ? double.NaN : 0;
PopupRoot.Margin = new Thickness(0, 0, 0, newValue ? 0 : -PopupRoot.ActualHeight);
PopupRoot.Visibility = Visibility.Visible;

So a collapsed expander keeps its content Visible in the tree, still measured and arranged
on every pass, hidden only by Height = 0 on the host and a negative bottom margin.

Fix

Pre-increment, so tracker identifies this run:

var tracker = ++_tracker;

The guard then means what it was written to mean — apply the final visibility unless a newer
toggle has started since this batch began. The stale-completion case it was added for still
works, because a subsequent toggle bumps _tracker past the captured value.

History

The guard arrived in a3a98e6d31 together with the switch from the captured newValue to the
_expanded field, which is what makes the intent unambiguous. The later c67940f5e6 ("Fix
expander layout cycle") removed a SizeChanged handler that rewrote the same margin, and is
unrelated to this.

Verification

Not built or run — a UWP/.NET Native build is not available in the environment this was
prepared in. The edited file was checked with Roslyn (CSharpSyntaxTree.ParseText) and parses
with no syntax errors; that confirms syntax only, not type checking. Worth a visual check that
collapsing still animates rather than snapping, since the collapse now ends in a real
visibility change.

🤖 Generated with Claude Code

The scoped batch's Completed handler compares _tracker against a value captured
with a post-increment, so the comparison was (n + 1) == n and never held. The
handler is what applies Visibility.Collapsed after the collapse animation, and
PopupRoot.Visibility is set to Visible unconditionally just above it, so
collapsed content stayed visible and kept participating in layout — hidden only
by PopupHost.Height = 0 and the negative margin.

Pre-increment gives the guard its intended meaning: apply the final visibility
unless a newer toggle has started since this batch began.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@FrayxRulez
FrayxRulez merged commit afb36b1 into develop Aug 10, 2026
@FrayxRulez
FrayxRulez deleted the settingsexpander-collapse-guard branch August 10, 2026 12:47
FrayxRulez added a commit that referenced this pull request Aug 10, 2026
The scoped batch's Completed handler compares _tracker against a value captured
with a post-increment, so the comparison was (n + 1) == n and never held. The
handler is what applies Visibility.Collapsed after the collapse animation, and
PopupRoot.Visibility is set to Visible unconditionally just above it, so
collapsed content stayed visible and kept participating in layout — hidden only
by PopupHost.Height = 0 and the negative margin.

Pre-increment gives the guard its intended meaning: apply the final visibility
unless a newer toggle has started since this batch began.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant