Skip to content

[leak-fix] Fix SwipeItemView.Command memory leak (Fixes #36343) - #36357

Merged
kubaflo merged 1 commit into
inflight/currentfrom
leak-fix/issue-36343-9e169274a0fd3455
Jul 8, 2026
Merged

kubaflo merged 1 commit into
inflight/currentfrom
leak-fix/issue-36343-9e169274a0fd3455

Conversation

@github-actions

@github-actions github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Important

🤖 AI-generated PR. This pull request was produced automatically by the Memory Leak Fixer agentic workflow on dotnet/maui. The regression test below was empirically proven to fail on main without the fix and pass with it during this run. Please review carefully before merging.

Fixes #36343
Refs: #36343
Target branch: main
Attempt: 1/3

The leak

SwipeItemView.OnCommandChanged() subscribed to the assigned command with a strong delegate:

Command.CanExecuteChanged += OnCommandCanExecuteChanged;

Because OnCommandCanExecuteChanged is an instance method, the delegate's Target is the SwipeItemView. Any command that raises CanExecuteChanged through a strong event (e.g. a custom ICommand, or CommunityToolkit.Mvvm's RelayCommand) therefore keeps the SwipeItemView — and its entire visual subtree — alive for as long as the command lives. The matching -= only ran in OnCommandChanging() (the property-changing path), so a view that is simply unloaded/discarded while a longer-lived command outlives it is never unsubscribed, and it leaks.

Microsoft.Maui.Controls.Command raises CanExecuteChanged through a WeakEventManager and does not leak — the regression test covers that non-leaking case too, to prove the fix preserves it.

The fix

Replace the raw subscription with the WeakCommandSubscription helper already used by Button, ImageButton, RefreshView, MenuItem, SearchBar, TextCell, etc. It links the command to the view through a DependentHandle, so the command references the view only weakly and the view can be collected once nothing else roots it:

WeakCommandSubscription _commandSubscription;

void OnCommandChanged()
{
    IsEnabled = Command?.CanExecute(CommandParameter) ?? true;

    if (Command == null)
        return;

    _commandSubscription = new WeakCommandSubscription(this, Command, OnCommandCanExecuteChanged);
}

void OnCommandChanging()
{
    _commandSubscription?.Dispose();
    _commandSubscription = null;
}
  • No public API change — the field is private and WeakCommandSubscription is internal.
  • All existing IsEnabled behavior is preserved; only the subscription lifetime changes.
  • Idiomatic: mirrors exactly how the sibling command-bearing controls already harden this same subscription.

Regression test

Added the SwipeItemView case to the existing CommandsSubscribedToCanExecuteCollect theory in src/Controls/tests/Core.UnitTests/CommandTests.cs, right alongside every other command-bearing control. It assigns a strong-event ICommand, drops the view in a nested scope, and asserts the view is collected (WaitForCollect returns false).

State SwipeItemView, useWeakEventHandler: False Full theory
Without fix (main) ❌ FAIL — Assert.False (view still alive) 15 passed / 1 failed
With fix ✅ PASS 16 passed / 0 failed

No regressions with the fix applied: CommandTests (45/45) and SwipeViewTests (24/24) pass. Validated by building MAUI from source on the net library TFM.

Scope

  • Managed, cross-platform product change only: src/Controls/src/Core/SwipeView/SwipeItemView.cs.
  • Test: src/Controls/tests/Core.UnitTests/CommandTests.cs.
  • No test was muted, skipped, weakened, or deleted; no [ActiveIssue]/Skip=; no public API change.

Generated by the Memory Leak Fixer agentic workflow (dotnet/maui). This description is AI-generated — please review before merging.

Generated by Memory Leak Fixer · 2.6K AIC · ⌖ 212.1 AIC · ⊞ 60.5K · ◷

Replace the strong ICommand.CanExecuteChanged subscription with a
WeakCommandSubscription (disposed on the command-changing path) so a
shared/long-lived command no longer roots the SwipeItemView. Adds a
Controls.Core.UnitTests regression test (SwipeItemView case in
CommandsSubscribedToCanExecuteCollect) that fails without this fix.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added agentic-workflows s/agent-review-in-progress AI review is currently running for this PR and removed s/agent-review-in-progress AI review is currently running for this PR labels Jul 3, 2026
@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 4, 2026
@MauiBot MauiBot added s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Jul 4, 2026
@kubaflo kubaflo added the perf/memory-leak 💦 Memory usage grows / objects live forever (sub: perf) label Jul 4, 2026
MauiBot

This comment was marked as outdated.

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review Summary

@github-actions[bot] — new AI review results are available based on this last commit: 27b91ee. To request a fresh review after new comments or commits, comment /review rerun.

Gate Passed Confidence Low Platform Android


🔍 Custom Prompt Analysis — maintainer-requested

● AI-generated — supplementary analysis from a maintainer-supplied custom prompt (GitHub Copilot CLI).

Requested: Is the weak-event/unsubscription approach in this SwipeItemView.Command leak fix correct and consistent with other leak fixes? Any missed unsubscription paths? Be concise.

SwipeItemView.Command leak fix

Verdict: looks correct. The change replaces the strong Command.CanExecuteChanged += OnCommandCanExecuteChanged reference with the existing WeakCommandSubscription helper, matching the pattern already used by CommandElement controls and SearchHandler.

Unsubscription paths

  • Command replacement/nulling: covered. OnCommandChanging() disposes _commandSubscription before the bindable property changes, so the old command is unsubscribed whether the new value is another command or null.
  • View collection while command is retained: covered by the weak subscription design. The command may retain the proxy, but the proxy uses DependentHandle so it does not keep the SwipeItemView alive; finalization/event delivery disposes and detaches the proxy.
  • Repeated Command changes: covered. The previous tracker is disposed and cleared before a new one is assigned, avoiding subscription accumulation.

Consistency with other leak fixes

  • Consistent with recent command leak fixes that centralize CanExecuteChanged handling through WeakCommandSubscription instead of direct event subscriptions.
  • SwipeItemView does not implement ICommandElement, so using a private tracker mirrors the manual SearchHandler pattern rather than the shared CommandElement.CleanupTracker path.

Notes

  • No missed unsubscription path found in the PR change.
  • Minor coverage caveat: the added test verifies SwipeItemView collection with weak and non-weak command implementations, but unlike ICommandElement/SearchHandler cases it cannot directly assert the private subscription/proxy collection. That is acceptable for the production leak being fixed.
🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix

Gate Result: ✅ PASSED

Platform: ANDROID · Base: main · Merge base: dab8abc4

Test Without Fix (expect FAIL) With Fix (expect PASS)
🧪 CommandTests CommandTests ✅ FAIL — 107s ✅ PASS — 79s
🔴 Without fix — 🧪 CommandTests: FAIL ✅ · 107s
  Determining projects to restore...
  Restored /home/vsts/work/1/s/src/TestUtils/src/TestUtils/TestUtils.csproj (in 4.14 sec).
  Restored /home/vsts/work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 118 ms).
  Restored /home/vsts/work/1/s/src/Essentials/src/Essentials.csproj (in 1.81 sec).
  Restored /home/vsts/work/1/s/src/Core/src/Core.csproj (in 2.26 sec).
  Restored /home/vsts/work/1/s/src/Core/maps/src/Maps.csproj (in 8.55 sec).
  Restored /home/vsts/work/1/s/src/Controls/src/Xaml/Controls.Xaml.csproj (in 41 ms).
  Restored /home/vsts/work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 29 ms).
  Restored /home/vsts/work/1/s/src/Controls/Maps/src/Controls.Maps.csproj (in 32 ms).
  Restored /home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj (in 2.52 sec).
  1 of 10 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.90-ci+azdo.14587796
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.90-ci+azdo.14587796
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.90-ci+azdo.14587796
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.90-ci+azdo.14587796
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0/Microsoft.Maui.Maps.dll
  ##vso[build.updatebuildnumber]10.0.90-ci+azdo.14587796
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.90-ci+azdo.14587796
  ##vso[build.updatebuildnumber]10.0.90-ci+azdo.14587796
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0/Microsoft.Maui.Controls.Maps.dll
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0/Microsoft.Maui.Controls.Xaml.dll
  TestUtils -> /home/vsts/work/1/s/artifacts/bin/TestUtils/Debug/netstandard2.0/Microsoft.Maui.TestUtils.dll
  Controls.Core.UnitTests -> /home/vsts/work/1/s/artifacts/bin/Controls.Core.UnitTests/Debug/net10.0/Microsoft.Maui.Controls.Core.UnitTests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.Core.UnitTests/Debug/net10.0/Microsoft.Maui.Controls.Core.UnitTests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.15]   Discovering: Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:01.21]   Discovered:  Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:01.24]   Starting:    Microsoft.Maui.Controls.Core.UnitTests
  Passed CanExecuteReturnsFalseIfParameterIsWrongValueType [5 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.SearchBar), useWeakEventHandler: False) [78 ms]
[xUnit.net 00:00:02.56]     CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.SwipeItemView), useWeakEventHandler: False) [FAIL]
[xUnit.net 00:00:02.58]       Assert.False() Failure
[xUnit.net 00:00:02.59]       Expected: False
[xUnit.net 00:00:02.62]       Actual:   True
[xUnit.net 00:00:02.62]       Stack Trace:
[xUnit.net 00:00:02.65]         /_/src/Controls/tests/Core.UnitTests/CommandTests.cs(354,0): at Microsoft.Maui.Controls.Core.UnitTests.CommandTests.CommandsSubscribedToCanExecuteCollect(Type controlType, Boolean useWeakEventHandler)
[xUnit.net 00:00:02.65]         --- End of stack trace from previous location ---
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.MenuItem), useWeakEventHandler: True) [95 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.ImageButton), useWeakEventHandler: False) [74 ms]
  Failed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.SwipeItemView), useWeakEventHandler: False) [1 s]
  Error Message:
   Assert.False() Failure
Expected: False
Actual:   True
  Stack Trace:
     at Microsoft.Maui.Controls.Core.UnitTests.CommandTests.CommandsSubscribedToCanExecuteCollect(Type controlType, Boolean useWeakEventHandler) in /_/src/Controls/tests/Core.UnitTests/CommandTests.cs:line 354
--- End of stack trace from previous location ---
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.Button), useWeakEventHandler: False) [66 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.ImageButton), useWeakEventHandler: True) [65 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.RefreshView), useWeakEventHandler: False) [64 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.TextCell), useWeakEventHandler: False) [89 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.SearchHandler), useWeakEventHandler: True) [65 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.SwipeItemView), useWeakEventHandler: True) [66 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.MenuItem), useWeakEventHandler: False) [61 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.RefreshView), useWeakEventHandler: True) [60 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.TextCell), useWeakEventHandler: True) [61 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.Button), useWeakEventHandler: True) [81 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.SearchBar), useWeakEventHandler: True) [119 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.SearchHandler), useWeakEventHandler: False) [75 ms]
  Passed ExecuteDoesNotRunIfParameterIsWrongReferenceType [< 1 ms]
  Passed ExecuteRunsIfReferenceTypeAndSetToNull [< 1 ms]
  Passed CanExecuteIgnoresParameterIfValueTypeAndSetToNull [< 1 ms]
  Passed CanExecute(expected: True) [4 ms]
  Passed CanExecute(expected: False) [< 1 ms]
  Passed GenericExecuteWithCanExecute [1 ms]
  Passed GenericThrowsWithNullExecute [< 1 ms]
  Passed Execute [< 1 ms]
  Passed GenericCanExecute(expected: True) [< 1 ms]
  Passed GenericCanExecute(expected: False) [< 1 ms]
  Passed ThrowsWithNullConstructor [< 1 ms]
  Passed ExecuteDoesNotRunIfParameterIsWrongValueType [< 1 ms]
  Passed GenericThrowsWithNullExecuteAndCanExecuteValid [< 1 ms]
  Passed ThrowsWithNullExecuteValidCanExecute [< 1 ms]
  Passed CanExecuteUsesParameterIfReferenceTypeAndSetToNull [< 1 ms]
  Passed ChangeCanExecute [< 1 ms]
  Passed GenericExecute [< 1 ms]
  Passed ThrowsWithNullCanExecute [< 1 ms]
  Passed ThrowsWithNullParameterizedConstructor [< 1 ms]
  Passed CanExecuteUsesParameterIfNullableAndSetToNull [< 1 ms]
[xUnit.net 00:00:03.47]   Finished:    Microsoft.Maui.Controls.Core.UnitTests
  Passed ExecuteDoesNotRunIfValueTypeAndSetToNull [< 1 ms]
  Passed Constructor [< 1 ms]
  Passed ThrowsWithNullParameterizedCanExecute [< 1 ms]
  Passed ExecuteRunsIfNullableAndSetToNull [< 1 ms]
  Passed ExecuteParameterized [3 ms]
  Passed GenericThrowsWithValidExecuteAndCanExecuteNull [< 1 ms]
  Passed ExecuteWithCanExecute [< 1 ms]
  Passed CanExecuteReturnsFalseIfParameterIsWrongReferenceType [< 1 ms]

Test Run Failed.
Total tests: 45
     Passed: 44
     Failed: 1
 Total time: 3.9548 Seconds

🟢 With fix — 🧪 CommandTests: PASS ✅ · 79s
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.90-ci+azdo.14587796
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.90-ci+azdo.14587796
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.90-ci+azdo.14587796
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  ##vso[build.updatebuildnumber]10.0.90-ci+azdo.14587796
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0/Microsoft.Maui.Maps.dll
  ##vso[build.updatebuildnumber]10.0.90-ci+azdo.14587796
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.90-ci+azdo.14587796
  ##vso[build.updatebuildnumber]10.0.90-ci+azdo.14587796
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0/Microsoft.Maui.Controls.Maps.dll
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0/Microsoft.Maui.Controls.Xaml.dll
  TestUtils -> /home/vsts/work/1/s/artifacts/bin/TestUtils/Debug/netstandard2.0/Microsoft.Maui.TestUtils.dll
  Controls.Core.UnitTests -> /home/vsts/work/1/s/artifacts/bin/Controls.Core.UnitTests/Debug/net10.0/Microsoft.Maui.Controls.Core.UnitTests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.Core.UnitTests/Debug/net10.0/Microsoft.Maui.Controls.Core.UnitTests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.14]   Discovering: Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:01.09]   Discovered:  Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:01.11]   Starting:    Microsoft.Maui.Controls.Core.UnitTests
  Passed CanExecuteReturnsFalseIfParameterIsWrongValueType [5 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.SearchBar), useWeakEventHandler: False) [75 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.MenuItem), useWeakEventHandler: True) [59 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.ImageButton), useWeakEventHandler: False) [58 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.SwipeItemView), useWeakEventHandler: False) [60 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.Button), useWeakEventHandler: False) [106 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.ImageButton), useWeakEventHandler: True) [58 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.RefreshView), useWeakEventHandler: False) [60 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.TextCell), useWeakEventHandler: False) [71 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.SearchHandler), useWeakEventHandler: True) [104 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.SwipeItemView), useWeakEventHandler: True) [89 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.MenuItem), useWeakEventHandler: False) [88 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.RefreshView), useWeakEventHandler: True) [91 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.TextCell), useWeakEventHandler: True) [67 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.Button), useWeakEventHandler: True) [64 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.SearchBar), useWeakEventHandler: True) [61 ms]
  Passed CommandsSubscribedToCanExecuteCollect(controlType: typeof(Microsoft.Maui.Controls.SearchHandler), useWeakEventHandler: False) [62 ms]
  Passed ExecuteDoesNotRunIfParameterIsWrongReferenceType [< 1 ms]
  Passed ExecuteRunsIfReferenceTypeAndSetToNull [< 1 ms]
  Passed CanExecuteIgnoresParameterIfValueTypeAndSetToNull [< 1 ms]
  Passed CanExecute(expected: True) [3 ms]
  Passed CanExecute(expected: False) [< 1 ms]
  Passed GenericExecuteWithCanExecute [1 ms]
  Passed GenericThrowsWithNullExecute [< 1 ms]
  Passed Execute [< 1 ms]
  Passed GenericCanExecute(expected: True) [< 1 ms]
  Passed GenericCanExecute(expected: False) [< 1 ms]
  Passed ThrowsWithNullConstructor [< 1 ms]
  Passed ExecuteDoesNotRunIfParameterIsWrongValueType [< 1 ms]
  Passed GenericThrowsWithNullExecuteAndCanExecuteValid [< 1 ms]
  Passed ThrowsWithNullExecuteValidCanExecute [< 1 ms]
  Passed CanExecuteUsesParameterIfReferenceTypeAndSetToNull [< 1 ms]
  Passed ChangeCanExecute [< 1 ms]
  Passed GenericExecute [< 1 ms]
  Passed ThrowsWithNullCanExecute [< 1 ms]
  Passed ThrowsWithNullParameterizedConstructor [< 1 ms]
  Passed CanExecuteUsesParameterIfNullableAndSetToNull [< 1 ms]
  Passed ExecuteDoesNotRunIfValueTypeAndSetToNull [< 1 ms]
  Passed Constructor [< 1 ms]
  Passed ThrowsWithNullParameterizedCanExecute [< 1 ms]
  Passed ExecuteRunsIfNullableAndSetToNull [< 1 ms]
[xUnit.net 00:00:02.39]   Finished:    Microsoft.Maui.Controls.Core.UnitTests
  Passed ExecuteParameterized [3 ms]
  Passed GenericThrowsWithValidExecuteAndCanExecuteNull [< 1 ms]
  Passed ExecuteWithCanExecute [< 1 ms]
  Passed CanExecuteReturnsFalseIfParameterIsWrongReferenceType [< 1 ms]

Test Run Successful.
Total tests: 45
     Passed: 45
 Total time: 2.8877 Seconds

📁 Fix files reverted (1 files)
  • src/Controls/src/Core/SwipeView/SwipeItemView.cs

📱 UI Tests — SwipeView

Detected UI test categories: SwipeView

✅ Deep UI tests — 75 passed, 0 failed across 1 category on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
SwipeView 75/75 ✓ —
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)

📋 Pre-Flight — Context & Validation

Issue: #36343 - [leak-scan] SwipeItemView.Command leaks the control via strong ICommand.CanExecuteChanged (no weak proxy, no unload teardown)
PR: #36357 - [leak-fix] Fix SwipeItemView.Command memory leak (Fixes #36343)
Platforms Affected: all platforms; managed cross-platform SwipeItemView command plumbing. Requested testing platform: android.
Files Changed: 1 implementation, 1 test

Key Findings

  • SwipeItemView.OnCommandChanged() on main subscribes to ICommand.CanExecuteChanged with an instance-method delegate. A long-lived strong-event command can retain the view and its visual subtree if the view is discarded without clearing Command.
  • PR #36357 replaces the raw event subscription with WeakCommandSubscription, matching established MAUI command-bearing controls and preserving existing IsEnabled recalculation semantics.
  • Regression coverage adds SwipeItemView to CommandsSubscribedToCanExecuteCollect for both weak-event MAUI Command and strong-event custom ICommand.
  • GitHub CLI authentication is unavailable in this environment, so authenticated PR surfaces and required-check exactness could not be queried with gh; public API/curl evidence was used where possible.
  • Prior MauiBot review requested changes because it selected an alternate candidate over the PR fix; no prior ❌ Error findings were found in public review, inline comment, or issue-comment surfaces.

Code Review Summary

Verdict: NEEDS_DISCUSSION
Confidence: low
Errors: 0 | Warnings: 0 | Suggestions: 0

Key code review findings:

  • ℹ No code-level errors or warnings found; the implementation is localized and consistent with existing MAUI WeakCommandSubscription usage.
  • ℹ Confidence is capped because gh pr checks --required cannot run without authentication.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #36357 Replace SwipeItemView raw CanExecuteChanged subscription with WeakCommandSubscription; add SwipeItemView to existing command GC theory. ✅ PASSED (Gate) src/Controls/src/Core/SwipeView/SwipeItemView.cs, src/Controls/tests/Core.UnitTests/CommandTests.cs Original PR; gate result supplied by caller.

🔬 Code Review — Deep Analysis

Code Review — PR #36357

Independent Assessment

What this changes: SwipeItemView replaces its strong Command.CanExecuteChanged subscription with WeakCommandSubscription, disposing it when the Command property changes. Tests add SwipeItemView to existing command GC coverage.

Inferred motivation: Prevent long-lived ICommand instances with strong events from retaining discarded SwipeItemView controls.

Reconciliation with PR Narrative

Author claims: Fixes SwipeItemView.Command memory leak via existing weak command subscription pattern.
Agreement/disagreement: Agrees. The implementation matches issue #36343's root cause and preserves existing IsEnabled behavior.

Prior Review Reconciliation

Prior ❌ Error Finding Source Status Evidence
None found Public GitHub API: reviews, inline comments, issue comments n/a MauiBot had CHANGES_REQUESTED, but its body stated "No prior ❌ Error findings found" and listed only suggestions/alternative preference. Inline comments count was 0.

Blast Radius Assessment

  • Runs for all instances: Yes, all SwipeItemView.Command assignments use the weak subscription.
  • Startup impact: No.
  • Static/shared state: No; new state is per-instance.
  • Platform impact: Managed cross-platform control code only.

CI Status

  • Required-check result: gh pr checks --required unavailable because gh is unauthenticated.
  • Public API evidence: head SHA 27b91ee has completed successful check-runs, including maui-pr per public review/status evidence.
  • Classification: Required-check exactness undetermined due auth limitation.
  • Action taken: Confidence capped low; no GitHub comments posted.

Findings

No ❌ Error, ⚠️ Warning, or 💡 Suggestion findings.

Failure-Mode Probing

  • Command set to null: existing subscription is disposed, IsEnabled becomes true.
  • Command replaced repeatedly: OnCommandChanging disposes the old subscription before the new one is created.
  • View discarded while command lives: WeakCommandSubscription avoids strongly retaining the view.
  • Command raises after view collection: proxy resolves missing target and disposes itself.
  • MAUI Command with weak events: behavior remains compatible; test covers both weak and strong command cases.

Verdict: NEEDS_DISCUSSION

Confidence: low, solely because authenticated required-check/prior-review verification was unavailable.
Summary: Code-only review found the fix sound, localized, and consistent with MAUI command subscription patterns. Public check-runs are green, but required-check status could not be verified exactly without gh auth, so the skill rules prevent LGTM.


🛠️ Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix Command-owned weak fan-out broker using ConditionalWeakTable<ICommand, Entry> and weak SwipeItemView references. ✅ PASS 2 files More complex than PR; not better despite passing tests.
2 try-fix Handler-scoped strong subscription; unsubscribe on handler disconnect and subscribe on handler connect. ❌ FAIL 2 files Leak test passes, but pre-handler CanExecuteChanged no longer updates IsEnabled; also initially hit PublicAPI analyzer with protected overrides.
3 try-fix Hybrid ICommandElement adoption for Command only; keep local CommandParameter callback to preserve commandless disabled-state behavior. ✅ PASS 2 files Best alternative: shared MAUI command infrastructure, preserves edge cases, avoids new primitives.
PR PR #36357 Per-view WeakCommandSubscription in SwipeItemView; add SwipeItemView to existing command GC theory. ✅ PASSED (Gate) 2 files Original PR; gate supplied by caller.

Cross-Pollination

Model Round New Ideas? Details
maui-expert-reviewer / gpt-5.5 1 Yes Command-owned weak fan-out broker; tested as try-fix-1.
maui-expert-reviewer / gpt-5.5 2 Yes Handler-scoped strong subscription; tested as try-fix-2 and rejected after semantic regression.
maui-expert-reviewer / gpt-5.5 3 Yes Hybrid ICommandElement adoption preserving local CommandParameter semantics; tested as try-fix-3 and selected.

Exhausted: No — stopped because candidate #3 passes all targeted tests and is demonstrably better than the PR's local-field fix for maintainability/consistency.
Selected Fix: Candidate #3 — Reuses shared MAUI CommandElement/ICommandElement infrastructure while preserving the SwipeItemView-specific edge case that full adoption broke.


📝 Recommended PR Title & Description

Assessment: ✏️ Recommend updating — the current description accurately explains the raw PR fix, but the winning candidate is try-fix-3, which uses ICommandElement/CommandElement rather than a private WeakCommandSubscription field.

Recommended title

SwipeItemView: Fix Command memory leak via CommandElement weak subscription

Recommended description

Fixes #36343
Refs: dotnet/maui#36343
Target branch: `main`

## The leak

`SwipeItemView.OnCommandChanged()` subscribed to the assigned command with a strong delegate:

```csharp
Command.CanExecuteChanged += OnCommandCanExecuteChanged;

Because OnCommandCanExecuteChanged is an instance method, the delegate's Target is the SwipeItemView. Any command that raises CanExecuteChanged through a strong event, such as a custom ICommand or CommunityToolkit.Mvvm's RelayCommand, can keep the SwipeItemView and its visual subtree alive for as long as the command lives. The matching unsubscribe only ran in the property-changing path, so a view that is discarded while a longer-lived command outlives it is never unsubscribed and leaks.

Microsoft.Maui.Controls.Command raises CanExecuteChanged through a WeakEventManager and does not leak; the regression test covers that non-leaking case too.

The fix

Make SwipeItemView implement the internal ICommandElement pattern and route CommandProperty changes through CommandElement.OnCommandChanging / CommandElement.OnCommandChanged. This reuses MAUI's existing WeakCommandSubscription infrastructure through the shared CommandElement path already used by command-bearing controls.

The CommandParameterProperty callback intentionally stays local instead of using CommandElement.OnCommandParameterChanged, so changing CommandParameter while Command is null preserves the existing disabled state rather than re-enabling the item.

  • No public API change: ICommandElement and WeakCommandSubscription are internal implementation details.
  • Managed, cross-platform product change only: src/Controls/src/Core/SwipeView/SwipeItemView.cs.
  • Existing IsEnabled behavior is preserved, including handlerless CanExecuteChanged updates.

Regression tests

Added SwipeItemView to the existing CommandsSubscribedToCanExecuteCollect theory in src/Controls/tests/Core.UnitTests/CommandTests.cs, alongside the other command-bearing controls. The test assigns both weak-event and strong-event commands, drops the view in a nested scope, and asserts the view is collected. Because the winning implementation uses ICommandElement, the existing test also verifies the CleanupTracker and proxy collectibility for SwipeItemView.

Additional semantic coverage verifies:

  • SwipeItemViewCanExecuteChangedUpdatesWithoutHandler: CanExecuteChanged still updates IsEnabled before handler attachment.
  • SwipeItemViewCommandParameterChangeWithoutCommandPreservesIsEnabled: changing CommandParameter without a command does not re-enable a disabled SwipeItemView.
State Result
Without fix (main) Fails for SwipeItemView, useWeakEventHandler: False because the view remains alive
With winning fix Passes the leak regression and focused CommandTests / SwipeViewTests coverage

Scope

  • Product: src/Controls/src/Core/SwipeView/SwipeItemView.cs
  • Tests: src/Controls/tests/Core.UnitTests/CommandTests.cs
  • Platform tested: Android
  • No test was muted, skipped, weakened, or deleted; no [ActiveIssue]/Skip=; no public API change.


</details>

---

<details>
<summary><strong>🏁 Report — Final Recommendation</strong></summary>
<br/>

## Comparative Report — PR #36357

### Candidates compared

| Rank | Candidate | Regression result | Assessment |
|---:|---|---|---|
| 1 | `try-fix-3` | ✅ PASS | Best overall. Reuses MAUI's shared `ICommandElement` / `CommandElement` weak command-subscription infrastructure, preserves handlerless `CanExecuteChanged` updates, and preserves `SwipeItemView`'s commandless `CommandParameter` behavior. Additional semantic probes passed. |
| 2 | `pr` | ✅ PASSED (Gate) | Correct, localized fix. Uses `WeakCommandSubscription` directly in `SwipeItemView` and the expert reviewer found no required changes. Ranked below `try-fix-3` because it keeps a bespoke command-subscription path instead of the shared command infrastructure. |
| 2 | `pr-plus-reviewer` | ✅ PASSED (same as `pr`) | The expert reviewer produced no actionable feedback, so this is identical to `pr`. |
| 4 | `try-fix-1` | ✅ PASS | Functionally passes tests, but introduces a new command-owned broker with `ConditionalWeakTable`, weak-reference fan-out, locking, pruning, and reentrancy surface. It is more complex than both the PR and shared `CommandElement` approach without a compensating benefit. |
| 5 | `try-fix-2` | ❌ FAIL | Must rank below passing candidates. It avoids the leak by subscribing only while a handler is connected, but regresses managed semantics: `CanExecuteChanged` no longer updates `SwipeItemView.IsEnabled` before handler attachment. |

### Winner

**Winner:** `try-fix-3`

`try-fix-3` wins because it fixes the same leak using the existing shared command infrastructure already used by MAUI command-bearing controls, without introducing a new broker and without changing the edge-case behaviors discovered during try-fix. The raw PR is acceptable and expert-reviewed as LGTM, but `try-fix-3` is the stronger maintainability choice.

### Ranking rule compliance

`try-fix-2` failed targeted regression/semantic testing and is ranked below all passing candidates. The remaining passing candidates were ranked by maintainability, consistency with existing MAUI command infrastructure, and semantic coverage.

### Recommended implementation

Use the `try-fix-3` implementation:

- Add `using Microsoft.Maui.Controls.Internals;`
- Make `SwipeItemView` implement `ICommandElement`.
- Route `CommandProperty` through `CommandElement.OnCommandChanging` / `OnCommandChanged`.
- Implement `ICommandElement.CanExecuteChanged` with `CommandElement.GetCanExecute(this, CommandProperty)`.
- Keep a local guarded `OnCommandParameterChanged()` so `CommandParameter` changes do not alter `IsEnabled` when `Command` is null.
- Keep the added leak regression and add the two semantic tests from `try-fix-3`.


</details>

</details>
<!-- SESSION:27b91ee END -->

---

<details>
<summary><strong>🧭 Next Steps</strong> — alternative fix proposed (<code>try-fix-3</code>)</summary>
<br/>

**Automated review — alternative fix proposed**

The expert-reviewer evaluation compared the PR fix against automatically generated candidates and selected <code>try-fix-3</code> as the strongest fix.

**Why:** try-fix-3 won because it fixes the same SwipeItemView.Command strong-event leak using MAUI shared CommandElement/ICommandElement weak-subscription infrastructure while preserving handlerless CanExecuteChanged updates and commandless CommandParameter semantics. The raw PR and pr-plus-reviewer are valid and expert-reviewed as LGTM, but try-fix-3 is the stronger maintainability choice; try-fix-2 failed regression/semantic testing.

Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.

<details><summary>Candidate diff (<code>try-fix-3</code>)</summary>

````diff
diff --git a/src/Controls/src/Core/SwipeView/SwipeItemView.cs b/src/Controls/src/Core/SwipeView/SwipeItemView.cs
index 346ada5523..a2d8717014 100644
--- a/src/Controls/src/Core/SwipeView/SwipeItemView.cs
+++ b/src/Controls/src/Core/SwipeView/SwipeItemView.cs
@@ -2,6 +2,7 @@
 using System;
 using System.ComponentModel;
 using System.Windows.Input;
+using Microsoft.Maui.Controls.Internals;
 
 namespace Microsoft.Maui.Controls
 {
@@ -9,12 +10,12 @@ namespace Microsoft.Maui.Controls
 	/// Represents a swipe item that displays custom content in a <see cref="SwipeView"/>.
 	/// </summary>
 	[ContentProperty(nameof(Content))]
-	public partial class SwipeItemView : ContentView, Controls.ISwipeItem, Maui.ISwipeItemView
+	public partial class SwipeItemView : ContentView, Controls.ISwipeItem, Maui.ISwipeItemView, ICommandElement
 	{
 		/// <summary>Bindable property for <see cref="Command"/>.</summary>
 		public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(SwipeItemView), null,
-			propertyChanging: (bo, o, n) => ((SwipeItemView)bo).OnCommandChanging(),
-			propertyChanged: (bo, o, n) => ((SwipeItemView)bo).OnCommandChanged());
+			propertyChanging: CommandElement.OnCommandChanging,
+			propertyChanged: CommandElement.OnCommandChanged);
 
 		/// <summary>Bindable property for <see cref="CommandParameter"/>.</summary>
 		public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(SwipeItemView), null,
@@ -52,35 +53,17 @@ namespace Microsoft.Maui.Controls
 			Invoked?.Invoke(this, EventArgs.Empty);
 		}
 
-		void OnCommandChanged()
+		void ICommandElement.CanExecuteChanged(object sender, EventArgs e)
 		{
-			IsEnabled = Command?.CanExecute(CommandParameter) ?? true;
-
-			if (Command == null)
-				return;
-
-			Command.CanExecuteChanged += OnCommandCanExecuteChanged;
-		}
-
-		void OnCommandChanging()
-		{
-			if (Command == null)
-				return;
-
-			Command.CanExecuteChanged -= OnCommandCanExecuteChanged;
+			IsEnabled = CommandElement.GetCanExecute(this, CommandProperty);
 		}
 
 		void OnCommandParameterChanged()
 		{
-			if (Command == null)
-				return;
-
-			IsEnabled = Command.CanExecute(CommandParameter);
+			if (Command != null)
+				((ICommandElement)this).CanExecuteChanged(this, EventArgs.Empty);
 		}
 
-		void OnCommandCanExecuteChanged(object sender, EventArgs eventArgs)
-		{
-			IsEnabled = Command.CanExecute(CommandParameter);
-		}
+		WeakCommandSubscription ICommandElement.CleanupTracker { get; set; }
 	}
 }
\ No newline at end of file
diff --git a/src/Controls/tests/Core.UnitTests/CommandTests.cs b/src/Controls/tests/Core.UnitTests/CommandTests.cs
index 3f4237785c..0bb4d28c52 100644
--- a/src/Controls/tests/Core.UnitTests/CommandTests.cs
+++ b/src/Controls/tests/Core.UnitTests/CommandTests.cs
@@ -271,6 +271,8 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
 		[InlineData(typeof(SearchBar), false)]
 		[InlineData(typeof(SearchHandler), true)]
 		[InlineData(typeof(SearchHandler), false)]
+		[InlineData(typeof(SwipeItemView), true)]
+		[InlineData(typeof(SwipeItemView), false)]
 		public async Task CommandsSubscribedToCanExecuteCollect(Type controlType, bool useWeakEventHandler)
 		{
 			// Create a view model with a Command
@@ -310,6 +312,9 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
 						sh.Command = command;
 						sh.ClearPlaceholderCommand = command;
 						break;
+					case SwipeItemView sv:
+						sv.Command = command;
+						break;
 				}
 
 				// Create a weak reference to the button
@@ -352,13 +357,56 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
 
 		class CommandWithoutWeakEventHandler : ICommand
 		{
+			readonly Func<bool> _canExecute;
+
+			public CommandWithoutWeakEventHandler()
+			{
+				_canExecute = () => true;
+			}
+
+			public CommandWithoutWeakEventHandler(Func<bool> canExecute)
+			{
+				_canExecute = canExecute;
+			}
+
 			public event EventHandler CanExecuteChanged;
 
-			public bool CanExecute(object parameter) => true;
+			public bool CanExecute(object parameter) => _canExecute();
 
 			public void Execute(object parameter) { }
 
 			public void ChangeCanExecute() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
 		}
+
+		[Fact]
+		public void SwipeItemViewCanExecuteChangedUpdatesWithoutHandler()
+		{
+			bool canExecute = false;
+			var command = new CommandWithoutWeakEventHandler(() => canExecute);
+			var swipeItemView = new SwipeItemView
+			{
+				Command = command
+			};
+
+			Assert.False(swipeItemView.IsEnabled);
+
+			canExecute = true;
+			command.ChangeCanExecute();
+
+			Assert.True(swipeItemView.IsEnabled);
+		}
+
+		[Fact]
+		public void SwipeItemViewCommandParameterChangeWithoutCommandPreservesIsEnabled()
+		{
+			var swipeItemView = new SwipeItemView
+			{
+				IsEnabled = false
+			};
+
+			swipeItemView.CommandParameter = new object();
+
+			Assert.False(swipeItemView.IsEnabled);
+		}
 	}
 }
\ No newline at end of file

@kubaflo
kubaflo changed the base branch from main to inflight/current July 8, 2026 10:56
@kubaflo
kubaflo marked this pull request as ready for review July 8, 2026 10:57
@kubaflo
kubaflo merged commit 8917822 into inflight/current Jul 8, 2026
20 of 29 checks passed
@kubaflo
kubaflo deleted the leak-fix/issue-36343-9e169274a0fd3455 branch July 8, 2026 10:58
@github-actions github-actions Bot added this to the .NET 10 SR9 milestone Jul 8, 2026
kubaflo added a commit that referenced this pull request Jul 10, 2026
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

> [!IMPORTANT]
> 🤖 **AI-generated PR.** This pull request was produced automatically by
the **Memory Leak Fixer** agentic workflow on dotnet/maui. The
regression test below was empirically proven to **fail on `main` without
the fix** and **pass with it** during this run. Please review carefully
before merging.

Fixes #36343
Refs: #36343
Target branch: `main`
Attempt: 1/3

## The leak

`SwipeItemView.OnCommandChanged()` subscribed to the assigned command
with a **strong** delegate:

```csharp
Command.CanExecuteChanged += OnCommandCanExecuteChanged;
```

Because `OnCommandCanExecuteChanged` is an instance method, the
delegate's `Target` is the `SwipeItemView`. Any command that raises
`CanExecuteChanged` through a **strong** event (e.g. a custom
`ICommand`, or `CommunityToolkit.Mvvm`'s `RelayCommand`) therefore keeps
the `SwipeItemView` — and its entire visual subtree — alive for as long
as the command lives. The matching `-=` only ran in
`OnCommandChanging()` (the property-*changing* path), so a view that is
simply unloaded/discarded while a longer-lived command outlives it is
never unsubscribed, and it leaks.

> `Microsoft.Maui.Controls.Command` raises `CanExecuteChanged` through a
`WeakEventManager` and does **not** leak — the regression test covers
that non-leaking case too, to prove the fix preserves it.

## The fix

Replace the raw subscription with the `WeakCommandSubscription` helper
already used by `Button`, `ImageButton`, `RefreshView`, `MenuItem`,
`SearchBar`, `TextCell`, etc. It links the command to the view through a
`DependentHandle`, so the command references the view only **weakly**
and the view can be collected once nothing else roots it:

```csharp
WeakCommandSubscription _commandSubscription;

void OnCommandChanged()
{
    IsEnabled = Command?.CanExecute(CommandParameter) ?? true;

    if (Command == null)
        return;

    _commandSubscription = new WeakCommandSubscription(this, Command, OnCommandCanExecuteChanged);
}

void OnCommandChanging()
{
    _commandSubscription?.Dispose();
    _commandSubscription = null;
}
```

- No public API change — the field is private and
`WeakCommandSubscription` is internal.
- All existing `IsEnabled` behavior is preserved; only the subscription
lifetime changes.
- Idiomatic: mirrors exactly how the sibling command-bearing controls
already harden this same subscription.

## Regression test

Added the `SwipeItemView` case to the existing
`CommandsSubscribedToCanExecuteCollect` theory in
`src/Controls/tests/Core.UnitTests/CommandTests.cs`, right alongside
every other command-bearing control. It assigns a strong-event
`ICommand`, drops the view in a nested scope, and asserts the view is
collected (`WaitForCollect` returns `false`).

| State | `SwipeItemView, useWeakEventHandler: False` | Full theory |
|-------|---------------------------------------------|-------------|
| Without fix (`main`) | ❌ FAIL — `Assert.False` (view still alive) | 15
passed / **1 failed** |
| With fix | ✅ PASS | **16 passed** / 0 failed |

No regressions with the fix applied: `CommandTests` (45/45) and
`SwipeViewTests` (24/24) pass. Validated by building MAUI from source on
the net library TFM.

## Scope

- Managed, cross-platform product change only:
`src/Controls/src/Core/SwipeView/SwipeItemView.cs`.
- Test: `src/Controls/tests/Core.UnitTests/CommandTests.cs`.
- No test was muted, skipped, weakened, or deleted; no
`[ActiveIssue]`/`Skip=`; no public API change.

---
_Generated by the Memory Leak Fixer agentic workflow (dotnet/maui). This
description is AI-generated — please review before merging._




> Generated by [Memory Leak
Fixer](https://github.com/dotnet/maui/actions/runs/28685456694) · 2.6K
AIC · ⌖ 212.1 AIC · ⊞ 60.5K ·
[◷](https://github.com/search?q=repo%3Adotnet%2Fmaui+%22gh-aw-workflow-id%3A+leak-fixer%22&type=pullrequests)

<!-- gh-aw-agentic-workflow: Memory Leak Fixer, engine: copilot,
version: 1.0.60, model: claude-opus-4.8, id: 28685456694, workflow_id:
leak-fixer, run: https://github.com/dotnet/maui/actions/runs/28685456694
-->

<!-- gh-aw-workflow-id: leak-fixer -->
<!-- gh-aw-workflow-call-id: dotnet/maui/leak-fixer -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
kubaflo added a commit that referenced this pull request Jul 15, 2026
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

> [!IMPORTANT]
> 🤖 **AI-generated PR.** This pull request was produced automatically by
the **Memory Leak Fixer** agentic workflow on dotnet/maui. The
regression test below was empirically proven to **fail on `main` without
the fix** and **pass with it** during this run. Please review carefully
before merging.

Fixes #36343
Refs: #36343
Target branch: `main`
Attempt: 1/3

## The leak

`SwipeItemView.OnCommandChanged()` subscribed to the assigned command
with a **strong** delegate:

```csharp
Command.CanExecuteChanged += OnCommandCanExecuteChanged;
```

Because `OnCommandCanExecuteChanged` is an instance method, the
delegate's `Target` is the `SwipeItemView`. Any command that raises
`CanExecuteChanged` through a **strong** event (e.g. a custom
`ICommand`, or `CommunityToolkit.Mvvm`'s `RelayCommand`) therefore keeps
the `SwipeItemView` — and its entire visual subtree — alive for as long
as the command lives. The matching `-=` only ran in
`OnCommandChanging()` (the property-*changing* path), so a view that is
simply unloaded/discarded while a longer-lived command outlives it is
never unsubscribed, and it leaks.

> `Microsoft.Maui.Controls.Command` raises `CanExecuteChanged` through a
`WeakEventManager` and does **not** leak — the regression test covers
that non-leaking case too, to prove the fix preserves it.

## The fix

Replace the raw subscription with the `WeakCommandSubscription` helper
already used by `Button`, `ImageButton`, `RefreshView`, `MenuItem`,
`SearchBar`, `TextCell`, etc. It links the command to the view through a
`DependentHandle`, so the command references the view only **weakly**
and the view can be collected once nothing else roots it:

```csharp
WeakCommandSubscription _commandSubscription;

void OnCommandChanged()
{
    IsEnabled = Command?.CanExecute(CommandParameter) ?? true;

    if (Command == null)
        return;

    _commandSubscription = new WeakCommandSubscription(this, Command, OnCommandCanExecuteChanged);
}

void OnCommandChanging()
{
    _commandSubscription?.Dispose();
    _commandSubscription = null;
}
```

- No public API change — the field is private and
`WeakCommandSubscription` is internal.
- All existing `IsEnabled` behavior is preserved; only the subscription
lifetime changes.
- Idiomatic: mirrors exactly how the sibling command-bearing controls
already harden this same subscription.

## Regression test

Added the `SwipeItemView` case to the existing
`CommandsSubscribedToCanExecuteCollect` theory in
`src/Controls/tests/Core.UnitTests/CommandTests.cs`, right alongside
every other command-bearing control. It assigns a strong-event
`ICommand`, drops the view in a nested scope, and asserts the view is
collected (`WaitForCollect` returns `false`).

| State | `SwipeItemView, useWeakEventHandler: False` | Full theory |
|-------|---------------------------------------------|-------------|
| Without fix (`main`) | ❌ FAIL — `Assert.False` (view still alive) | 15
passed / **1 failed** |
| With fix | ✅ PASS | **16 passed** / 0 failed |

No regressions with the fix applied: `CommandTests` (45/45) and
`SwipeViewTests` (24/24) pass. Validated by building MAUI from source on
the net library TFM.

## Scope

- Managed, cross-platform product change only:
`src/Controls/src/Core/SwipeView/SwipeItemView.cs`.
- Test: `src/Controls/tests/Core.UnitTests/CommandTests.cs`.
- No test was muted, skipped, weakened, or deleted; no
`[ActiveIssue]`/`Skip=`; no public API change.

---
_Generated by the Memory Leak Fixer agentic workflow (dotnet/maui). This
description is AI-generated — please review before merging._




> Generated by [Memory Leak
Fixer](https://github.com/dotnet/maui/actions/runs/28685456694) · 2.6K
AIC · ⌖ 212.1 AIC · ⊞ 60.5K ·
[◷](https://github.com/search?q=repo%3Adotnet%2Fmaui+%22gh-aw-workflow-id%3A+leak-fixer%22&type=pullrequests)

<!-- gh-aw-agentic-workflow: Memory Leak Fixer, engine: copilot,
version: 1.0.60, model: claude-opus-4.8, id: 28685456694, workflow_id:
leak-fixer, run: https://github.com/dotnet/maui/actions/runs/28685456694
-->

<!-- gh-aw-workflow-id: leak-fixer -->
<!-- gh-aw-workflow-call-id: dotnet/maui/leak-fixer -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
kubaflo added a commit that referenced this pull request Jul 22, 2026
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

> [!IMPORTANT]
> 🤖 **AI-generated PR.** This pull request was produced automatically by
the **Memory Leak Fixer** agentic workflow on dotnet/maui. The
regression test below was empirically proven to **fail on `main` without
the fix** and **pass with it** during this run. Please review carefully
before merging.

Fixes #36343
Refs: #36343
Target branch: `main`
Attempt: 1/3

## The leak

`SwipeItemView.OnCommandChanged()` subscribed to the assigned command
with a **strong** delegate:

```csharp
Command.CanExecuteChanged += OnCommandCanExecuteChanged;
```

Because `OnCommandCanExecuteChanged` is an instance method, the
delegate's `Target` is the `SwipeItemView`. Any command that raises
`CanExecuteChanged` through a **strong** event (e.g. a custom
`ICommand`, or `CommunityToolkit.Mvvm`'s `RelayCommand`) therefore keeps
the `SwipeItemView` — and its entire visual subtree — alive for as long
as the command lives. The matching `-=` only ran in
`OnCommandChanging()` (the property-*changing* path), so a view that is
simply unloaded/discarded while a longer-lived command outlives it is
never unsubscribed, and it leaks.

> `Microsoft.Maui.Controls.Command` raises `CanExecuteChanged` through a
`WeakEventManager` and does **not** leak — the regression test covers
that non-leaking case too, to prove the fix preserves it.

## The fix

Replace the raw subscription with the `WeakCommandSubscription` helper
already used by `Button`, `ImageButton`, `RefreshView`, `MenuItem`,
`SearchBar`, `TextCell`, etc. It links the command to the view through a
`DependentHandle`, so the command references the view only **weakly**
and the view can be collected once nothing else roots it:

```csharp
WeakCommandSubscription _commandSubscription;

void OnCommandChanged()
{
    IsEnabled = Command?.CanExecute(CommandParameter) ?? true;

    if (Command == null)
        return;

    _commandSubscription = new WeakCommandSubscription(this, Command, OnCommandCanExecuteChanged);
}

void OnCommandChanging()
{
    _commandSubscription?.Dispose();
    _commandSubscription = null;
}
```

- No public API change — the field is private and
`WeakCommandSubscription` is internal.
- All existing `IsEnabled` behavior is preserved; only the subscription
lifetime changes.
- Idiomatic: mirrors exactly how the sibling command-bearing controls
already harden this same subscription.

## Regression test

Added the `SwipeItemView` case to the existing
`CommandsSubscribedToCanExecuteCollect` theory in
`src/Controls/tests/Core.UnitTests/CommandTests.cs`, right alongside
every other command-bearing control. It assigns a strong-event
`ICommand`, drops the view in a nested scope, and asserts the view is
collected (`WaitForCollect` returns `false`).

| State | `SwipeItemView, useWeakEventHandler: False` | Full theory |
|-------|---------------------------------------------|-------------|
| Without fix (`main`) | ❌ FAIL — `Assert.False` (view still alive) | 15
passed / **1 failed** |
| With fix | ✅ PASS | **16 passed** / 0 failed |

No regressions with the fix applied: `CommandTests` (45/45) and
`SwipeViewTests` (24/24) pass. Validated by building MAUI from source on
the net library TFM.

## Scope

- Managed, cross-platform product change only:
`src/Controls/src/Core/SwipeView/SwipeItemView.cs`.
- Test: `src/Controls/tests/Core.UnitTests/CommandTests.cs`.
- No test was muted, skipped, weakened, or deleted; no
`[ActiveIssue]`/`Skip=`; no public API change.

---
_Generated by the Memory Leak Fixer agentic workflow (dotnet/maui). This
description is AI-generated — please review before merging._




> Generated by [Memory Leak
Fixer](https://github.com/dotnet/maui/actions/runs/28685456694) · 2.6K
AIC · ⌖ 212.1 AIC · ⊞ 60.5K ·
[◷](https://github.com/search?q=repo%3Adotnet%2Fmaui+%22gh-aw-workflow-id%3A+leak-fixer%22&type=pullrequests)

<!-- gh-aw-agentic-workflow: Memory Leak Fixer, engine: copilot,
version: 1.0.60, model: claude-opus-4.8, id: 28685456694, workflow_id:
leak-fixer, run: https://github.com/dotnet/maui/actions/runs/28685456694
-->

<!-- gh-aw-workflow-id: leak-fixer -->
<!-- gh-aw-workflow-call-id: dotnet/maui/leak-fixer -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
kubaflo added a commit that referenced this pull request Jul 28, 2026
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

> [!IMPORTANT]
> 🤖 **AI-generated PR.** This pull request was produced automatically by
the **Memory Leak Fixer** agentic workflow on dotnet/maui. The
regression test below was empirically proven to **fail on `main` without
the fix** and **pass with it** during this run. Please review carefully
before merging.

Fixes #36343
Refs: #36343
Target branch: `main`
Attempt: 1/3

## The leak

`SwipeItemView.OnCommandChanged()` subscribed to the assigned command
with a **strong** delegate:

```csharp
Command.CanExecuteChanged += OnCommandCanExecuteChanged;
```

Because `OnCommandCanExecuteChanged` is an instance method, the
delegate's `Target` is the `SwipeItemView`. Any command that raises
`CanExecuteChanged` through a **strong** event (e.g. a custom
`ICommand`, or `CommunityToolkit.Mvvm`'s `RelayCommand`) therefore keeps
the `SwipeItemView` — and its entire visual subtree — alive for as long
as the command lives. The matching `-=` only ran in
`OnCommandChanging()` (the property-*changing* path), so a view that is
simply unloaded/discarded while a longer-lived command outlives it is
never unsubscribed, and it leaks.

> `Microsoft.Maui.Controls.Command` raises `CanExecuteChanged` through a
`WeakEventManager` and does **not** leak — the regression test covers
that non-leaking case too, to prove the fix preserves it.

## The fix

Replace the raw subscription with the `WeakCommandSubscription` helper
already used by `Button`, `ImageButton`, `RefreshView`, `MenuItem`,
`SearchBar`, `TextCell`, etc. It links the command to the view through a
`DependentHandle`, so the command references the view only **weakly**
and the view can be collected once nothing else roots it:

```csharp
WeakCommandSubscription _commandSubscription;

void OnCommandChanged()
{
    IsEnabled = Command?.CanExecute(CommandParameter) ?? true;

    if (Command == null)
        return;

    _commandSubscription = new WeakCommandSubscription(this, Command, OnCommandCanExecuteChanged);
}

void OnCommandChanging()
{
    _commandSubscription?.Dispose();
    _commandSubscription = null;
}
```

- No public API change — the field is private and
`WeakCommandSubscription` is internal.
- All existing `IsEnabled` behavior is preserved; only the subscription
lifetime changes.
- Idiomatic: mirrors exactly how the sibling command-bearing controls
already harden this same subscription.

## Regression test

Added the `SwipeItemView` case to the existing
`CommandsSubscribedToCanExecuteCollect` theory in
`src/Controls/tests/Core.UnitTests/CommandTests.cs`, right alongside
every other command-bearing control. It assigns a strong-event
`ICommand`, drops the view in a nested scope, and asserts the view is
collected (`WaitForCollect` returns `false`).

| State | `SwipeItemView, useWeakEventHandler: False` | Full theory |
|-------|---------------------------------------------|-------------|
| Without fix (`main`) | ❌ FAIL — `Assert.False` (view still alive) | 15
passed / **1 failed** |
| With fix | ✅ PASS | **16 passed** / 0 failed |

No regressions with the fix applied: `CommandTests` (45/45) and
`SwipeViewTests` (24/24) pass. Validated by building MAUI from source on
the net library TFM.

## Scope

- Managed, cross-platform product change only:
`src/Controls/src/Core/SwipeView/SwipeItemView.cs`.
- Test: `src/Controls/tests/Core.UnitTests/CommandTests.cs`.
- No test was muted, skipped, weakened, or deleted; no
`[ActiveIssue]`/`Skip=`; no public API change.

---
_Generated by the Memory Leak Fixer agentic workflow (dotnet/maui). This
description is AI-generated — please review before merging._




> Generated by [Memory Leak
Fixer](https://github.com/dotnet/maui/actions/runs/28685456694) · 2.6K
AIC · ⌖ 212.1 AIC · ⊞ 60.5K ·
[◷](https://github.com/search?q=repo%3Adotnet%2Fmaui+%22gh-aw-workflow-id%3A+leak-fixer%22&type=pullrequests)

<!-- gh-aw-agentic-workflow: Memory Leak Fixer, engine: copilot,
version: 1.0.60, model: claude-opus-4.8, id: 28685456694, workflow_id:
leak-fixer, run: https://github.com/dotnet/maui/actions/runs/28685456694
-->

<!-- gh-aw-workflow-id: leak-fixer -->
<!-- gh-aw-workflow-call-id: dotnet/maui/leak-fixer -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
kubaflo added a commit that referenced this pull request Jul 29, 2026
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

> [!IMPORTANT]
> 🤖 **AI-generated PR.** This pull request was produced automatically by
the **Memory Leak Fixer** agentic workflow on dotnet/maui. The
regression test below was empirically proven to **fail on `main` without
the fix** and **pass with it** during this run. Please review carefully
before merging.

Fixes #36343
Refs: #36343
Target branch: `main`
Attempt: 1/3

## The leak

`SwipeItemView.OnCommandChanged()` subscribed to the assigned command
with a **strong** delegate:

```csharp
Command.CanExecuteChanged += OnCommandCanExecuteChanged;
```

Because `OnCommandCanExecuteChanged` is an instance method, the
delegate's `Target` is the `SwipeItemView`. Any command that raises
`CanExecuteChanged` through a **strong** event (e.g. a custom
`ICommand`, or `CommunityToolkit.Mvvm`'s `RelayCommand`) therefore keeps
the `SwipeItemView` — and its entire visual subtree — alive for as long
as the command lives. The matching `-=` only ran in
`OnCommandChanging()` (the property-*changing* path), so a view that is
simply unloaded/discarded while a longer-lived command outlives it is
never unsubscribed, and it leaks.

> `Microsoft.Maui.Controls.Command` raises `CanExecuteChanged` through a
`WeakEventManager` and does **not** leak — the regression test covers
that non-leaking case too, to prove the fix preserves it.

## The fix

Replace the raw subscription with the `WeakCommandSubscription` helper
already used by `Button`, `ImageButton`, `RefreshView`, `MenuItem`,
`SearchBar`, `TextCell`, etc. It links the command to the view through a
`DependentHandle`, so the command references the view only **weakly**
and the view can be collected once nothing else roots it:

```csharp
WeakCommandSubscription _commandSubscription;

void OnCommandChanged()
{
    IsEnabled = Command?.CanExecute(CommandParameter) ?? true;

    if (Command == null)
        return;

    _commandSubscription = new WeakCommandSubscription(this, Command, OnCommandCanExecuteChanged);
}

void OnCommandChanging()
{
    _commandSubscription?.Dispose();
    _commandSubscription = null;
}
```

- No public API change — the field is private and
`WeakCommandSubscription` is internal.
- All existing `IsEnabled` behavior is preserved; only the subscription
lifetime changes.
- Idiomatic: mirrors exactly how the sibling command-bearing controls
already harden this same subscription.

## Regression test

Added the `SwipeItemView` case to the existing
`CommandsSubscribedToCanExecuteCollect` theory in
`src/Controls/tests/Core.UnitTests/CommandTests.cs`, right alongside
every other command-bearing control. It assigns a strong-event
`ICommand`, drops the view in a nested scope, and asserts the view is
collected (`WaitForCollect` returns `false`).

| State | `SwipeItemView, useWeakEventHandler: False` | Full theory |
|-------|---------------------------------------------|-------------|
| Without fix (`main`) | ❌ FAIL — `Assert.False` (view still alive) | 15
passed / **1 failed** |
| With fix | ✅ PASS | **16 passed** / 0 failed |

No regressions with the fix applied: `CommandTests` (45/45) and
`SwipeViewTests` (24/24) pass. Validated by building MAUI from source on
the net library TFM.

## Scope

- Managed, cross-platform product change only:
`src/Controls/src/Core/SwipeView/SwipeItemView.cs`.
- Test: `src/Controls/tests/Core.UnitTests/CommandTests.cs`.
- No test was muted, skipped, weakened, or deleted; no
`[ActiveIssue]`/`Skip=`; no public API change.

---
_Generated by the Memory Leak Fixer agentic workflow (dotnet/maui). This
description is AI-generated — please review before merging._




> Generated by [Memory Leak
Fixer](https://github.com/dotnet/maui/actions/runs/28685456694) · 2.6K
AIC · ⌖ 212.1 AIC · ⊞ 60.5K ·
[◷](https://github.com/search?q=repo%3Adotnet%2Fmaui+%22gh-aw-workflow-id%3A+leak-fixer%22&type=pullrequests)

<!-- gh-aw-agentic-workflow: Memory Leak Fixer, engine: copilot,
version: 1.0.60, model: claude-opus-4.8, id: 28685456694, workflow_id:
leak-fixer, run: https://github.com/dotnet/maui/actions/runs/28685456694
-->

<!-- gh-aw-workflow-id: leak-fixer -->
<!-- gh-aw-workflow-call-id: dotnet/maui/leak-fixer -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
kubaflo added a commit that referenced this pull request Aug 7, 2026
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

> [!IMPORTANT]
> 🤖 **AI-generated PR.** This pull request was produced automatically by
the **Memory Leak Fixer** agentic workflow on dotnet/maui. The
regression test below was empirically proven to **fail on `main` without
the fix** and **pass with it** during this run. Please review carefully
before merging.

Fixes #36343
Refs: #36343
Target branch: `main`
Attempt: 1/3

## The leak

`SwipeItemView.OnCommandChanged()` subscribed to the assigned command
with a **strong** delegate:

```csharp
Command.CanExecuteChanged += OnCommandCanExecuteChanged;
```

Because `OnCommandCanExecuteChanged` is an instance method, the
delegate's `Target` is the `SwipeItemView`. Any command that raises
`CanExecuteChanged` through a **strong** event (e.g. a custom
`ICommand`, or `CommunityToolkit.Mvvm`'s `RelayCommand`) therefore keeps
the `SwipeItemView` — and its entire visual subtree — alive for as long
as the command lives. The matching `-=` only ran in
`OnCommandChanging()` (the property-*changing* path), so a view that is
simply unloaded/discarded while a longer-lived command outlives it is
never unsubscribed, and it leaks.

> `Microsoft.Maui.Controls.Command` raises `CanExecuteChanged` through a
`WeakEventManager` and does **not** leak — the regression test covers
that non-leaking case too, to prove the fix preserves it.

## The fix

Replace the raw subscription with the `WeakCommandSubscription` helper
already used by `Button`, `ImageButton`, `RefreshView`, `MenuItem`,
`SearchBar`, `TextCell`, etc. It links the command to the view through a
`DependentHandle`, so the command references the view only **weakly**
and the view can be collected once nothing else roots it:

```csharp
WeakCommandSubscription _commandSubscription;

void OnCommandChanged()
{
    IsEnabled = Command?.CanExecute(CommandParameter) ?? true;

    if (Command == null)
        return;

    _commandSubscription = new WeakCommandSubscription(this, Command, OnCommandCanExecuteChanged);
}

void OnCommandChanging()
{
    _commandSubscription?.Dispose();
    _commandSubscription = null;
}
```

- No public API change — the field is private and
`WeakCommandSubscription` is internal.
- All existing `IsEnabled` behavior is preserved; only the subscription
lifetime changes.
- Idiomatic: mirrors exactly how the sibling command-bearing controls
already harden this same subscription.

## Regression test

Added the `SwipeItemView` case to the existing
`CommandsSubscribedToCanExecuteCollect` theory in
`src/Controls/tests/Core.UnitTests/CommandTests.cs`, right alongside
every other command-bearing control. It assigns a strong-event
`ICommand`, drops the view in a nested scope, and asserts the view is
collected (`WaitForCollect` returns `false`).

| State | `SwipeItemView, useWeakEventHandler: False` | Full theory |
|-------|---------------------------------------------|-------------|
| Without fix (`main`) | ❌ FAIL — `Assert.False` (view still alive) | 15
passed / **1 failed** |
| With fix | ✅ PASS | **16 passed** / 0 failed |

No regressions with the fix applied: `CommandTests` (45/45) and
`SwipeViewTests` (24/24) pass. Validated by building MAUI from source on
the net library TFM.

## Scope

- Managed, cross-platform product change only:
`src/Controls/src/Core/SwipeView/SwipeItemView.cs`.
- Test: `src/Controls/tests/Core.UnitTests/CommandTests.cs`.
- No test was muted, skipped, weakened, or deleted; no
`[ActiveIssue]`/`Skip=`; no public API change.

---
_Generated by the Memory Leak Fixer agentic workflow (dotnet/maui). This
description is AI-generated — please review before merging._




> Generated by [Memory Leak
Fixer](https://github.com/dotnet/maui/actions/runs/28685456694) · 2.6K
AIC · ⌖ 212.1 AIC · ⊞ 60.5K ·
[◷](https://github.com/search?q=repo%3Adotnet%2Fmaui+%22gh-aw-workflow-id%3A+leak-fixer%22&type=pullrequests)

<!-- gh-aw-agentic-workflow: Memory Leak Fixer, engine: copilot,
version: 1.0.60, model: claude-opus-4.8, id: 28685456694, workflow_id:
leak-fixer, run: https://github.com/dotnet/maui/actions/runs/28685456694
-->

<!-- gh-aw-workflow-id: leak-fixer -->
<!-- gh-aw-workflow-call-id: dotnet/maui/leak-fixer -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 8, 2026
@github-actions github-actions Bot modified the milestones: .NET 10 SR9, .NET 10 SR11 Sep 22, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

agentic-workflows perf/memory-leak 💦 Memory usage grows / objects live forever (sub: perf) s/agent-fix-win AI found a better alternative fix than the PR s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[leak-scan] SwipeItemView.Command leaks the control via strong ICommand.CanExecuteChanged (no weak proxy, no unload teardown)

2 participants