This article explains how AXSG handles class-backed XAML, why it generates InitializeComponent, how that interacts with hand-written Avalonia code-behind, and how AXSG IL weaving changes the migration path for legacy AvaloniaXamlLoader.Load(...) wrappers.
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.
AXSG now ships a post-compile IL weaving pass that can rewrite supported legacy loader calls on the current instance:
AvaloniaXamlLoader.Load(this)AvaloniaXamlLoader.Load(serviceProvider, this)Those calls are rewritten to AXSG-generated __InitializeXamlSourceGenComponent(...) helper overloads on the same type.
That means a source file can still contain:
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
and, when AXSG IL weaving is enabled, the compiled app no longer uses Avalonia's runtime loader for that call site. It lands on AXSG's generated initialization body instead.
This is a migration bridge. It does not make every possible AvaloniaXamlLoader call compatible, and it does not replace the cleaner end-state of removing the wrapper entirely.
AXAML_SOURCEGEN_BACKEND still 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:
If the repo is AXSG-only and you just want to reduce migration friction, AXSG IL weaving is usually simpler than maintaining conditional fallback blocks everywhere.
AvaloniaXamlLoaderAvalonia.Markup.Xaml.AvaloniaXamlLoader.Load(this) is not inherently wrong. It is simply Avalonia's legacy loader entry point.
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.
AXSG IL weaving fixes that for supported same-instance call shapes by rewriting the wrapper body after compile. If weaving is disabled or the call shape is unsupported, the old overload-resolution problem still applies.
If you need the build-property and compatibility matrix behind that rule, use:
.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 or the IL weaving bridge as a replacement for runtime bootstrap.
| Project shape | Hand-written fallback | Recommended state |
|---|---|---|
| Sourcegen-only app | private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } |
remove it |
| Sourcegen-only app with IL weaving migration bridge | same fallback | allowed temporarily when XamlSourceGenIlWeavingEnabled=true; preferred end-state is still removal |
| 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().AvaloniaXamlLoader.Load(...), confirm XamlSourceGenIlWeavingEnabled=true and the call uses a supported direct same-instance shape.obj/... and confirm AXSG emitted InitializeComponent(bool loadXaml = true) and __InitializeXamlSourceGenComponent(...).#if !AXAML_SOURCEGEN_BACKEND.