5.0 KiB
Warning This tool hasn't been extensively tested, so use at your own risk.
C# SourceGenerator for Typed Avalonia x:Name References

This is a C# SourceGenerator built for generating strongly-typed references to controls with x:Name (or just Name) attributes declared in XAML (or, in .axaml). The idea is that you include your Avalonia XAML files into your project via <AdditionalFiles Include="**\*.xaml" /> and then decorate your view class with [GenerateTypedNameReferences] and the source generator will look for the xaml (or axaml) file with the same name as your C# class. The source generator then parses the XML markup, finds all XML tags with x:Name attributes and generates the C# code.
Getting Started
So in your project file you paste the following code:
<ItemGroup>
<!-- Note this AdditionalFiles directive. -->
<AdditionalFiles Include="**\*.xaml" />
</ItemGroup>
And then you reference the source generator by installing a NuGet package:
dotnet add package XamlNameReferenceGenerator
Or, if you are using submodules, you can reference the generator as such:
<ItemGroup>
<ProjectReference Include="..\Avalonia.NameGenerator\Avalonia.NameGenerator.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
</ItemGroup>
Finally, you declare your view class as partial and decorate it with [GenerateTypedNameReferences]:
using Avalonia.Controls;
[GenerateTypedNameReferences] // Note the 'partial' keyword.
public partial class SignUpView : Window
{
public SignUpView()
{
AvaloniaXamlLoader.Load(this);
UserNameTextBox.Text = "Joseph"; // Coolstuff!
}
}

What do the generated sources look like?
For the SignUpView view class from the sandbox project, we get the following generated output:
// <auto-generated />
using Avalonia.Controls;
namespace Your.View.Namespace
{
partial class SignUpView
{
internal global::Avalonia.NameGenerator.Sandbox.Controls.CustomTextBox UserNameTextBox => this.FindControl<global::Avalonia.NameGenerator.Sandbox.Controls.CustomTextBox>("UserNameTextBox");
internal global::Avalonia.Controls.TextBlock UserNameValidation => this.FindControl<global::Avalonia.Controls.TextBlock>("UserNameValidation");
internal global::Avalonia.Controls.TextBox PasswordTextBox => this.FindControl<global::Avalonia.Controls.TextBox>("PasswordTextBox");
internal global::Avalonia.Controls.TextBlock PasswordValidation => this.FindControl<global::Avalonia.Controls.TextBlock>("PasswordValidation");
internal global::Avalonia.Controls.TextBox ConfirmPasswordTextBox => this.FindControl<global::Avalonia.Controls.TextBox>("ConfirmPasswordTextBox");
internal global::Avalonia.Controls.TextBlock ConfirmPasswordValidation => this.FindControl<global::Avalonia.Controls.TextBlock>("ConfirmPasswordValidation");
internal global::Avalonia.Controls.Button SignUpButton => this.FindControl<global::Avalonia.Controls.Button>("SignUpButton");
internal global::Avalonia.Controls.TextBlock CompoundValidation => this.FindControl<global::Avalonia.Controls.TextBlock>("CompoundValidation");
}
}
Why do I need this?
The typed x:Name references might be useful if you decide to use e.g. ReactiveUI code-behind bindings:
[GenerateTypedNameReferences] // UserNameValidation and PasswordValidation are auto generated.
public partial class SignUpView : ReactiveWindow<SignUpViewModel>
{
public SignUpView()
{
AvaloniaXamlLoader.Load(this);
this.WhenActivated(disposables =>
{
this.BindValidation(ViewModel, x => x.UserName, x => x.UserNameValidation.Text)
.DisposeWith(disposables);
this.BindValidation(ViewModel, x => x.Password, x => x.PasswordValidation.Text)
.DisposeWith(disposables);
});
}
}