Repository navigation
Description
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.
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
-
Clone/checkout the repro branch:
git clone https://github.com/AdamEssenmacher/maui.git cd maui git checkout repro/transformgroup-children-leak -
Build the MAUI build tasks:
dotnet build Microsoft.Maui.BuildTasks.slnf
-
Run the Mac Catalyst repro:
dotnet run --project src/Controls/samples/TransformGroupLeakRepro/TransformGroupLeakRepro.csproj -f net10.0-maccatalyst
-
Leave the default settings:
Pages/run = 40,Paths/page = 12,Payload MB/page = 3,Dwell ms/page = 25. -
Click
Run control.
Expected:Paths,TransformGroups, andpayload view modelsare0/...or near zero after full GC. -
Click
Run shared transform.
Expected on an affected build:
Paths: 480/480,TransformGroups: 480/480,payload view models: 40/40, retained payload about120 MB. -
Click
Run mitigation.
Expected: counts return near the control run, usually0/480paths,0/480groups, and0/40payloads.
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
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
Activity
HarishKumarSF4517 commentedon Jun 22, 2026
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:
Android:

Shapes: Fix TransformGroup child subscription leaks (#36150)
Shapes: Fix TransformGroup child subscription leaks (#36150)
Shapes: Fix TransformGroup child subscription leaks (#36150)
Shapes: Fix TransformGroup child subscription leaks (#36150)
Shapes: Fix TransformGroup child subscription leaks (#36150)
Shapes: Fix TransformGroup child subscription leaks (#36150)
Shapes: Fix TransformGroup child subscription leaks (#36150)
Shapes: Fix TransformGroup child subscription leaks (#36150)
Shapes: Fix TransformGroup child subscription leaks (#36150)
Shapes: Fix TransformGroup child subscription leaks (#36150)
Shapes: Fix TransformGroup child subscription leaks (#36150)
July 13th, Candidate (#36411)