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 734f3441e8
fix: Don't Throw when Attached Properties are Unknown (#12)
5 years ago
.github/workflows housekeeping: Add Basic GitHub Actions Configuration (#11) 5 years ago
external feature: Add XamlXRawNameReferenceXamlParser (#4) 5 years ago
src fix: Don't Throw when Attached Properties are Unknown (#12) 5 years ago
.gitignore feature: Add XamlXRawNameReferenceXamlParser (#4) 5 years ago
.gitmodules feature: Add XamlXRawNameReferenceXamlParser (#4) 5 years ago
LICENSE housekeeping: Add Basic GitHub Actions Configuration (#11) 5 years ago
README.md fix: Don't Throw when Attached Properties are Unknown (#12) 5 years ago
version.json housekeeping: Add Basic GitHub Actions Configuration (#11) 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 sandbox app is targeting net5 which is still in preview, so this source generator is an early proof-of-concept. 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 write the following code:

<ItemGroup>
    <Compile Update="**\*.xaml.cs">
        <DependentUpon>%(Filename)</DependentUpon>
    </Compile>
    <AvaloniaResource Include="**\*.xaml">
        <SubType>Designer</SubType>
    </AvaloniaResource>

    <!-- 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, reference the generator as such:

<ItemGroup>
    <ProjectReference Include="../XamlNameReferenceGenerator/XamlNameReferenceGenerator.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 XamlNameReferenceGenerator.Sandbox
{
    partial class SignUpView
    {
        internal XamlNameReferenceGenerator.Sandbox.Controls.CustomTextBox UserNameTextBox => this.FindControl<XamlNameReferenceGenerator.Sandbox.Controls.CustomTextBox>("UserNameTextBox");
        internal Avalonia.Controls.TextBlock UserNameValidation => this.FindControl<Avalonia.Controls.TextBlock>("UserNameValidation");
        internal Avalonia.Controls.TextBox PasswordTextBox => this.FindControl<Avalonia.Controls.TextBox>("PasswordTextBox");
        internal Avalonia.Controls.TextBlock PasswordValidation => this.FindControl<Avalonia.Controls.TextBlock>("PasswordValidation");
        internal Avalonia.Controls.TextBox ConfirmPasswordTextBox => this.FindControl<Avalonia.Controls.TextBox>("ConfirmPasswordTextBox");
        internal Avalonia.Controls.TextBlock ConfirmPasswordValidation => this.FindControl<Avalonia.Controls.TextBlock>("ConfirmPasswordValidation");
        internal Avalonia.Controls.Button SignUpButton => this.FindControl<Avalonia.Controls.Button>("SignUpButton");
        internal Avalonia.Controls.TextBlock CompoundValidation => this.FindControl<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);
        });
    }
}