Skip to content

TransformGroup.Children replacement leaks child transform PropertyChanged subscriptionsΒ #36033

Description

@AdamEssenmacher

Description

Description
TransformGroup subscribes to each child transform’s PropertyChanged event when the child is added to Children, but replacing the entire Children collection only unsubscribes from the old collection’s CollectionChanged event. It does not unsubscribe from the child transforms already in the old collection.

If a child transform is long-lived, such as a static/shared resource, singleton, or cached visual state, it can retain the abandoned TransformGroup. When that group was assigned to Path.RenderTransform, the retained group can also keep the old Path and its BindingContext alive.

Repro:
https://github.com/AdamEssenmacher/maui/tree/repro/transformgroup-children-leak/src/Controls/samples/TransformGroupLeakRepro

With the default repro settings on Mac Catalyst, the affected scenario retains:

Paths: 480/480
TransformGroups: 480/480
payload view models: 40/40
Payload retained by alive view models: 120.0 MB

The control and mitigation scenarios return to zero or near-zero retained objects. The mitigation removes the shared child transform from Children before replacing the collection, which points to the stale child PropertyChanged subscription as the retention root.

Steps to Reproduce

Repro project:
https://github.com/AdamEssenmacher/maui/tree/repro/transformgroup-children-leak/src/Controls/samples/TransformGroupLeakRepro

Steps

  1. Clone/checkout the repro branch:

    git clone https://github.com/AdamEssenmacher/maui.git
    cd maui
    git checkout repro/transformgroup-children-leak
  2. Build the MAUI build tasks:

    dotnet build Microsoft.Maui.BuildTasks.slnf
  3. Run the Mac Catalyst repro:

    dotnet run --project src/Controls/samples/TransformGroupLeakRepro/TransformGroupLeakRepro.csproj -f net10.0-maccatalyst
  4. Leave the default settings:
    Pages/run = 40, Paths/page = 12, Payload MB/page = 3, Dwell ms/page = 25.

  5. Click Run control.
    Expected: Paths, TransformGroups, and payload view models are 0/... or near zero after full GC.

  6. Click Run shared transform.
    Expected on an affected build:
    Paths: 480/480, TransformGroups: 480/480, payload view models: 40/40, retained payload about 120 MB.

  7. Click Run mitigation.
    Expected: counts return near the control run, usually 0/480 paths, 0/480 groups, and 0/40 payloads.

Why this demonstrates the leak

The leaky scenario adds a long-lived shared ScaleTransform to each TransformGroup, then replaces TransformGroup.Children. The old child transform keeps a stale PropertyChanged subscription to the abandoned TransformGroup.

The mitigation scenario removes the child transform from Children before replacing the collection. That returns retention to baseline, showing the stale child-transform event subscription is the retention root.

Link to public reproduction project repository

https://github.com/AdamEssenmacher/maui/tree/repro/transformgroup-children-leak/src/Controls/samples/TransformGroupLeakRepro

Version with bug

10.0.70

Is this a regression from previous behavior?

Not sure, did not test other versions

Last version that worked well

Unknown/Other

Affected platforms

iOS, Android, macOS, I was not able test on other platforms

Affected platform versions

No response

Did you find any workaround?

No response

Relevant log output

Activity

  1. HarishKumarSF4517 commented on Jun 22, 2026

    @HarishKumarSF4517
    Contributor

    This issue has been validated in Visual Studio Code 1.123.0 with .NET MAUI versions 9.0.0, 10.0.70 and 10.0.71. It was reproducible on Android, iOS and mac platform. I have attached the output below:
    iOS:
    Image

    Android:
    Image

  2. added
    s/verifiedVerified / Reproducible Issue ready for Engineering Triage
    s/triagedIssue has been reviewed
    on Jun 22, 2026
  3. added this to the .NET 10 SR11 milestone on Jun 23, 2026
  4. added a commit that references this issue on Jun 29, 2026
    7242c5a
  5. added 2 commits that reference this issue on Jul 3, 2026
    13d6a71
    c4226e8
  6. added 2 commits that reference this issue on Jul 7, 2026
    513f75c
    09a205e
  7. added 2 commits that reference this issue on Jul 10, 2026
    245239b
    9dadbda
  8. added a commit that references this issue on Jul 22, 2026
    e7f460e
  9. added 2 commits that reference this issue on Jul 28, 2026
    e68a8c9
    66b1871
  10. added a commit that references this issue on Aug 7, 2026
    f28a87e
  11. added a commit that references this issue on Aug 10, 2026
  12. locked and limited conversation to collaborators on Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-animationAnimation, Transitions, Transformspartner/syncfusionIssues / PR's with Syncfusion collaborationplatform/androidplatform/iosplatform/macosmacOS / Mac Catalysts/triagedIssue has been revieweds/verifiedVerified / Reproducible Issue ready for Engineering Triaget/bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions