From 631870b17b2a20b16a1967f5824b823885310364 Mon Sep 17 00:00:00 2001 From: "Artyom V. Gorchakov" Date: Tue, 9 Feb 2021 13:43:29 +0300 Subject: [PATCH] feature: Document the AvaloniaNameGenerator Property, Don't Crash on Failure (#23) * Update README.md * Don't crash when something goes wrong * Add back the warning * Update version.json --- README.md | 42 ++++++++++++------- .../NameReferenceGenerator.cs | 23 +++++----- version.json | 4 +- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 469d51604e..03a4aa210c 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ -This is a [C# `SourceGenerator`](https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/) 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 subclasse of `Avalonia.INambe` and parses the XML markup, finds all XML tags with `x:Name` attributes and generates the C# code. +This is a [C# `SourceGenerator`](https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/) 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 -Add reference the source generator by installing a NuGet package: +In order to get started, just install the NuGet package: ``` dotnet add package XamlNameReferenceGenerator @@ -20,16 +20,28 @@ Or, if you are using [submodules](https://git-scm.com/docs/git-submodule), you c ```xml + + - - - ``` -Finally, you declare your view class as `partial` +### 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`, `ReactiveUserControl`). For example, for the following XAML markup: + +```xml + + + +``` + +A new C# public property named `UserNameTextBox` of type `TextBox` will be generated: ```cs using Avalonia.Controls; @@ -39,22 +51,22 @@ public partial class SignUpView : Window public SignUpView() { AvaloniaXamlLoader.Load(this); - UserNameTextBox.Text = "Joseph"; // Coolstuff! + UserNameTextBox.Text = "Joseph"; // Cool stuff! } } ``` -If you just want specific classes: +### Usage (Opt-in) -add at your csproj this lines: +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 `` tag: ```xml - + false - + ``` -Finally, decorate yours class with attribute `[GenerateTypedNameReferences]`: +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`. ```cs using Avalonia.Controls; @@ -65,7 +77,7 @@ public partial class SignUpView : Window public SignUpView() { AvaloniaXamlLoader.Load(this); - UserNameTextBox.Text = "Joseph"; // Coolstuff! + UserNameTextBox.Text = "Joseph"; // Cool stuff! } } ``` @@ -86,8 +98,8 @@ namespace Your.View.Namespace partial class SignUpView { internal global::Avalonia.NameGenerator.Sandbox.Controls.CustomTextBox UserNameTextBox => this.FindControl("UserNameTextBox"); - internal global::Avalonia.Controls.TextBlock UserNameValidation => this.FindControl("UserNameValidation"); - internal global::Avalonia.Controls.TextBox PasswordTextBox => this.FindControl("PasswordTextBox"); + public global::Avalonia.Controls.TextBlock UserNameValidation => this.FindControl("UserNameValidation"); + private global::Avalonia.Controls.TextBox PasswordTextBox => this.FindControl("PasswordTextBox"); internal global::Avalonia.Controls.TextBlock PasswordValidation => this.FindControl("PasswordValidation"); internal global::Avalonia.Controls.TextBox ConfirmPasswordTextBox => this.FindControl("ConfirmPasswordTextBox"); internal global::Avalonia.Controls.TextBlock ConfirmPasswordValidation => this.FindControl("ConfirmPasswordValidation"); diff --git a/src/Avalonia.NameGenerator/NameReferenceGenerator.cs b/src/Avalonia.NameGenerator/NameReferenceGenerator.cs index 687608a738..61955ee9f5 100644 --- a/src/Avalonia.NameGenerator/NameReferenceGenerator.cs +++ b/src/Avalonia.NameGenerator/NameReferenceGenerator.cs @@ -69,14 +69,14 @@ internal sealed class GenerateTypedNameReferencesAttribute : Attribute { } Diagnostic.Create( new DiagnosticDescriptor( "AXN0001", - "Unable to discover the relevant Avalonia XAML file.", + $"Unable to discover the relevant Avalonia XAML file for {typeSymbol.Name}.", "Unable to discover the relevant Avalonia XAML file " + $"neither at {xamlFileName} nor at {aXamlFileName}", "Usage", - DiagnosticSeverity.Error, + DiagnosticSeverity.Warning, true), Location.None)); - return; + continue; } try @@ -93,13 +93,12 @@ internal sealed class GenerateTypedNameReferencesAttribute : Attribute { } Diagnostic.Create( new DiagnosticDescriptor( "AXN0002", - "Unhandled exception occured while generating typed Name references.", + $"Unhandled exception occured while generating typed Name references for {typeSymbol.Name}.", $"Unhandled exception occured while generating typed Name references: {exception}", "Usage", - DiagnosticSeverity.Error, + DiagnosticSeverity.Warning, true), Location.None)); - return; } } } @@ -152,8 +151,13 @@ internal sealed class GenerateTypedNameReferencesAttribute : Attribute { } else { var missingPartialKeywordMessage = - $"The type {typeSymbol.Name} should be declared with the 'partial' keyword " + - "as it is annotated with the [GenerateTypedNameReferences] attribute."; + $"The type {typeSymbol?.Name} should be declared with the 'partial' keyword " + + "as it is either annotated with the [GenerateTypedNameReferences] attribute, " + + "or the property is set to 'true' in the C# project file (it is set " + + "to 'true' by default). In order to skip the processing of irrelevant files, put " + + "false into your .csproj file as " + + " descendant and decorate only relevant view classes with the " + + "[GenerateTypedNameReferences] attribute."; context.ReportDiagnostic( Diagnostic.Create( @@ -162,10 +166,9 @@ internal sealed class GenerateTypedNameReferencesAttribute : Attribute { } missingPartialKeywordMessage, missingPartialKeywordMessage, "Usage", - DiagnosticSeverity.Error, + DiagnosticSeverity.Warning, true), Location.None)); - return null; } } diff --git a/version.json b/version.json index b93e992194..7aa55b7351 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "0.1", + "version": "0.2.0-preview", "assemblyVersion": { "precision": "revision" }, @@ -8,4 +8,4 @@ "^refs/heads/main$", "^refs/heads/v\\d+\\.\\d+$" ] -} \ No newline at end of file +}