Store Pitfalls #4: .NET Trimming Silently Broke WinUI 3 Data Binding
2026-05-31
Tags: Windows · Microsoft Store · Store Pitfalls
This was an issue I ran into very early on. While writing this pitfall series and digging through old emails, I came across a rejection notice from long ago — I hope this record can serve as a reference for developers just getting started with WinUI 3 / .NET publishing.
What Happened
One of my WinUI 3 apps was submitted for Microsoft Store certification. The certification report came back:
Status: Attention needed
10.1.2.10 Functionality
Unusable Feature: "Download to local cache", "Open File" - The product fails to download copied items to local cache and "Open File" button in the product is not usable
Wait — I had tested this locally. "Download to local cache" and "Open File" both worked perfectly. Why were they failing on Microsoft's test machines?
Investigation
The most suspicious difference: I was using Debug builds locally, but the Store submission was a Release build. Checking the .csproj file, I found a critical difference between Debug and Release configurations:
<!-- Debug -->
<PublishTrimmed>False</PublishTrimmed>
<!-- Release -->
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>The Release build had .NET trimming enabled.
What Is .NET Trimming
.NET trimming is an optimization feature provided by the .NET SDK. When you publish an app, the trimmer performs static analysis to find assemblies, types, and methods that are never directly referenced in code, and removes them to reduce the app size.
The problem: the trimmer only sees static references. If your code invokes a type at runtime through reflection, but there is no static reference to it at compile time, the trimmer considers that type "unused" and removes it.
Why WinUI 3 Is Especially Vulnerable
WinUI 3 and its underlying WinRT interop layer rely heavily on reflection and dynamic features. Traditional {Binding}, value converters, and WinRT interop metadata reflection all produce runtime calls that the trimmer's static analysis cannot track. When the trimmer removes data models, commands, converters, and other types that bindings depend on, treating them as "unused," the bindings can't find their targets at runtime — buttons stop working, text stops updating. No errors, no crashes, things just silently stop working.
The Fix
The most straightforward solution: disable trimming in .csproj:
- <PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
+ <PublishTrimmed>False</PublishTrimmed>Repackage, resubmit, certification passed.
If you genuinely need trimming to reduce package size, you can make it compatible by:
- Annotating required types with
[DynamicDependency]or[DynamicallyAccessedMembers]attributes to tell the trimmer to preserve them - Configuring
<TrimmerRootAssembly>in.csprojto preserve entire assemblies
But for most WinUI 3 apps, simply disabling trimming is the safest choice.
Key Takeaways
- .NET trimming removes code it considers "unused," but cannot detect reflection-based calls
- WinUI 3 data binding relies on reflection, making it highly susceptible to being trimmed incorrectly
- If your WinUI 3 app fails Store certification with "unusable feature" but works locally, trimming should be the first thing you investigate
- Debug builds don't trim by default, but Release builds may have trimming enabled — always test Release/Publish builds before submitting to the Store
- We'll be writing a follow-up article with some Store submission testing tips — stay tuned
This article is part of the Store Pitfalls series.