Repository navigation
XAML projects
How WPF, UWP and WinUI code-behind is made to compile without a real build.
WPF, UWP and WinUI code-behind is only half a class: the base type, the x:Name fields and
InitializeComponent come from a partial the markup compiler generates. That compiler runs only in
a real build — a design-time build reports no XAML items at all — so without help every code-behind
file in the solution reports errors that are not real. Measured on a 50-project UWP app mid-migration
to .NET 10: 2030 of them in one project.
So the markup is parsed and that partial is synthesised: base type, named fields with the
accessibility x:FieldModifier asked for, InitializeComponent, and the odds and ends real
generated code declares that hand-written code refers to. Element types are resolved out of the
Roslyn type universe, so third-party and in-house controls work like built-in ones; anything that
does not resolve is reported rather than guessed at. Nothing runnable is generated and nothing
reaches disk — the stubs are source-generated documents, readable with
rose_read_generated_document like any other.
Which flavour of XAML a project is written in is decided from what it references, since all three
use the same markup namespace. UWP, WinUI and WPF are all recognised, and the differences between
them are more than cosmetic: WPF's named fields default to internal where the Windows frameworks'
default to private, and WPF types a field for a named root element as the class it generates where
UWP types it as the element the markup writes. Both were read off real generated files rather than
reasoned about.
How much a project needs is a separate question from which dialect it is. WinUI's markup compiler takes part in design-time builds, and WPF's runs its first pass in one, so a WPF project needs a stub only for the files that pass cannot do: pass 1 resolves element types out of assemblies on disk, and a design-time build compiles nothing, so any file whose markup names a type from a project reference fails and gets no generated half. In a real WPF project that was 1 file of 26. Recognising the dialect still matters for the other 25: without it the project is reported as having no XAML framework, which is false and more misleading than silence.
Verified on net48 and net10 alike, which is worth doing rather than assuming — an SDK-style .NET
Framework project defaults to C# 7.3, where the #nullable disable a stub carries is itself three
errors. The language version is asked, not inferred from the framework.
Renaming knows about markup too, as far as anything can. rose_rename_symbol reports every place
XAML still names the old identifier — bindings, x:Name, event handlers, element names, x:Class —
and changes none of them. A binding path resolves at runtime against a DataContext only the running
application knows, so nothing can prove the mention refers to the symbol renamed; but a rename that
breaks forty bindings and says nothing is the worst outcome available, because the compiler will not
catch it either.
It closed 2030 errors to 19, all of them a genuinely unreferenced assembly. Against the files a real
UWP build had left in obj, all 450 synthesised classes agreed on the base type and on every field
name and type. workspace_status reports per project which dialect was chosen and on what evidence, how
many classes were stubbed, and any element type that could not be resolved. Pass --no-xaml-stubs
to a worker to see the workspace without them.
Getting started
Working with C#
Debugging and XAML
About