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.
 
 
 
Artyom V. Gorchakov 5bbde81683
refactor: Introduce GeneratorOptions (#30)
5 years ago
.github/workflows housekeeping: Rename to Avalonia.NameGenerator (#16) 5 years ago
external feature: Support x:FieldModifier Directive (#20) 5 years ago
src refactor: Introduce GeneratorOptions (#30) 5 years ago
.gitignore feature: Add XamlXRawNameReferenceXamlParser (#4) 5 years ago
.gitmodules fix: Use https in .gitmodules 5 years ago
LICENSE housekeeping: Rename to Avalonia.NameGenerator (#16) 5 years ago
README.md housekeeping: Cleanup README.md 5 years ago
version.json feature: Add InitializeComponent Source Generator Behavior (#29) 5 years ago

README.md

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

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 classes referenced by the x:Class directive in XAML files. For example, for the following XAML markup:

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

A new C# partial class named SignUpView with a single public property named UserNameTextBox of type TextBox will be generated in the Sample.App namespace. We won't see the generated file, but we'll be able to access the generated property as shown below:

using Avalonia.Controls;

namespace Sample.App
{
    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 Avalonia.NameGenerator.Sandbox.Views
{
    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:

// 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);
        });
    }
}