A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

6.3 KiB

NuGet Stats downloads Build License Size

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 source generator will look for the xaml (or axaml) file with the same name as your partial C# class that is a subclass of Avalonia.INamed and parses the XAML markup, finds all XAML tags with x:Name attributes and generates the C# code.

Getting Started

In order to get started, just install the NuGet package:

dotnet add package XamlNameReferenceGenerator

Or, if you are using submodules, you can reference the generator as such:

<ItemGroup>
    <!-- Remember to include XAML files via <AdditionalFiles>,
         otherwise C# source generators won't see the XAML files.
         If you are using a NuGet package, this is done automatically. -->
    <AdditionalFiles Include="**\*.xaml"/>
    <ProjectReference Include="..\Avalonia.NameGenerator\Avalonia.NameGenerator.csproj"
                      OutputItemType="Analyzer"
                      ReferenceOutputAssembly="false" />
</ItemGroup>

Usage (Default)

After installing the NuGet package, declare your view class as partial. Typed C# references to Avalonia controls declared in XAML files will be generated for all classes that inherit from the Avalonia.INamed interface (including those classes that inherit from Window, UserControl, ReactiveWindow<T>, ReactiveUserControl<T>). For example, for the following XAML markup:

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <TextBox x:Name="UserNameTextBox" x:FieldModifier="public" />
</Window>

A new C# public property named UserNameTextBox of type TextBox will be generated:

using Avalonia.Controls;

public partial class SignUpView : Window
{
    public SignUpView()
    {
        AvaloniaXamlLoader.Load(this);
        UserNameTextBox.Text = "Joseph"; // Cool stuff!
    }
}

Usage (Opt-in)

If you don't want to generate typed x:Name references for every window or user control in your assembly, you can always turn off this default behavior by setting the AvaloniaNameGenerator MsBuild property to false in your C# project file (.csproj). Just add the following property group to your <Project /> tag:

<PropertyGroup>
    <AvaloniaNameGenerator>false</AvaloniaNameGenerator>
</PropertyGroup>

From now on, the source generator will process only those files that are decorated with the [GenerateTypedNameReferences] attribute. Other window or user control classes will be left unchanged, and you won't have to mark them as partial.

using Avalonia.Controls;

[GenerateTypedNameReferences]
public partial class SignUpView : Window
{
    public SignUpView()
    {
        AvaloniaXamlLoader.Load(this);
        UserNameTextBox.Text = "Joseph"; // Cool stuff!
    }
}

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");
        public global::Avalonia.Controls.TextBlock UserNameValidation => this.FindControl<global::Avalonia.Controls.TextBlock>("UserNameValidation");
        private 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);
        });
    }
}