This article explains how AXSG handles class-backed XAML, why it generates InitializeComponent, how that interacts with hand-written Avalonia code-behind, and why mixed-backend projects often keep a guarded AvaloniaXamlLoader.Load(this) fallback.
This article is about class-backed documents such as:
It is not about runtime-only includes or non-class-backed resource fragments.
For class-backed XAML, AXSG emits:
public void InitializeComponent(bool loadXaml = true)
The generated method is responsible for:
The important point is that the generated method is not a separate naming convention. It uses the same InitializeComponent name app authors already expect in Avalonia code-behind.
Most app constructors can remain:
public MainWindow()
{
InitializeComponent();
}
That call is fine because the generated method has an optional parameter. With no competing hand-written overload, InitializeComponent(); naturally binds to the generated method.
Older Avalonia code-behind often contains:
private void InitializeComponent()
{
global::Avalonia.Markup.Xaml.AvaloniaXamlLoader.Load(this);
}
That method is a different overload from AXSG's generated InitializeComponent(bool loadXaml = true), but it is a better overload match for the parameterless constructor call.
So this constructor:
public MainWindow()
{
InitializeComponent();
}
will call the hand-written parameterless method, not the generated AXSG method.
That means:
This is the key overload-resolution detail most integrations miss.
AXAML_SOURCEGEN_BACKEND existsAXSG's build integration defines the AXAML_SOURCEGEN_BACKEND conditional symbol when the project is using the AXSG backend. That lets a single code-behind file support both paths:
public MainWindow()
{
InitializeComponent();
}
#if !AXAML_SOURCEGEN_BACKEND
private void InitializeComponent()
{
global::Avalonia.Markup.Xaml.AvaloniaXamlLoader.Load(this);
}
#endif
Under AXSG:
Under a non-AXSG backend:
This pattern is why mixed-backend and multi-target repositories can migrate incrementally without splitting every code-behind file.
You can remove the hand-written AvaloniaXamlLoader.Load(this) method when all of the following are true:
This is common in sample apps or fully migrated application repos.
Keep the guarded fallback when:
This is the safer default for reusable libraries.
AvaloniaXamlLoaderAvalonia.Markup.Xaml.AvaloniaXamlLoader.Load(this) is not inherently wrong. It is simply the non-AXSG initialization path.
The problem is not the method itself. The problem is leaving a parameterless InitializeComponent() wrapper around it in the same class when AXSG is active, because that steals the constructor call from the generated AXSG method.
.UseAvaloniaSourceGeneratedXaml()These concerns operate at different layers:
InitializeComponent handles class-backed object graph initialization.UseAvaloniaSourceGeneratedXaml() wires AXSG runtime services into AppBuilderYou still want both in a normal AXSG app:
AppBuilderDo not treat the generated InitializeComponent method as a replacement for runtime bootstrap.
| Project shape | Hand-written fallback | Recommended state |
|---|---|---|
| Sourcegen-only app | private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } |
remove it |
| Mixed-backend app | same fallback | wrap it in #if !AXAML_SOURCEGEN_BACKEND |
| Shared library with multiple TFMs/backends | same fallback | keep the guarded version |
| AXSG-enabled app with no manual fallback yet | none | leave constructor as InitializeComponent(); |
Typical signs that the fallback is still intercepting AXSG:
obj, but the class behaves as if AXSG changes are ignoredAvaloniaXamlCompilerBackend=SourceGen..UseAvaloniaSourceGeneratedXaml().InitializeComponent().obj/... and confirm AXSG emitted InitializeComponent(bool loadXaml = true).#if !AXAML_SOURCEGEN_BACKEND.