Browse Source

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
pull/10407/head 0.2.0-preview
Artyom V. Gorchakov 5 years ago
committed by GitHub
parent
commit
631870b17b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 42
      README.md
  2. 23
      src/Avalonia.NameGenerator/NameReferenceGenerator.cs
  3. 4
      version.json

42
README.md

@ -6,11 +6,11 @@
<img src="https://hsto.org/webt/6a/j6/v5/6aj6v5vemc3g6zqcks0wm_irg1s.gif" /> <img src="https://hsto.org/webt/6a/j6/v5/6aj6v5vemc3g6zqcks0wm_irg1s.gif" />
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 ### 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 dotnet add package XamlNameReferenceGenerator
@ -20,16 +20,28 @@ Or, if you are using [submodules](https://git-scm.com/docs/git-submodule), you c
```xml ```xml
<ItemGroup> <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" <ProjectReference Include="..\Avalonia.NameGenerator\Avalonia.NameGenerator.csproj"
OutputItemType="Analyzer" OutputItemType="Analyzer"
ReferenceOutputAssembly="false" /> ReferenceOutputAssembly="false" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<AdditionalFiles Include="**\*.xaml"/>
</ItemGroup>
``` ```
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<T>`, `ReactiveUserControl<T>`). For example, for the following XAML markup:
```xml
<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:
```cs ```cs
using Avalonia.Controls; using Avalonia.Controls;
@ -39,22 +51,22 @@ public partial class SignUpView : Window
public SignUpView() public SignUpView()
{ {
AvaloniaXamlLoader.Load(this); 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 `<Project />` tag:
```xml ```xml
<PropertyGroup> <PropertyGroup>
<AvaloniaNameGenerator>false</AvaloniaNameGenerator> <AvaloniaNameGenerator>false</AvaloniaNameGenerator>
</PropertyGroup> </PropertyGroup>
``` ```
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 ```cs
using Avalonia.Controls; using Avalonia.Controls;
@ -65,7 +77,7 @@ public partial class SignUpView : Window
public SignUpView() public SignUpView()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
UserNameTextBox.Text = "Joseph"; // Coolstuff! UserNameTextBox.Text = "Joseph"; // Cool stuff!
} }
} }
``` ```
@ -86,8 +98,8 @@ namespace Your.View.Namespace
partial class SignUpView partial class SignUpView
{ {
internal global::Avalonia.NameGenerator.Sandbox.Controls.CustomTextBox UserNameTextBox => this.FindControl<global::Avalonia.NameGenerator.Sandbox.Controls.CustomTextBox>("UserNameTextBox"); 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"); public 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"); 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.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.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.TextBlock ConfirmPasswordValidation => this.FindControl<global::Avalonia.Controls.TextBlock>("ConfirmPasswordValidation");

23
src/Avalonia.NameGenerator/NameReferenceGenerator.cs

@ -69,14 +69,14 @@ internal sealed class GenerateTypedNameReferencesAttribute : Attribute { }
Diagnostic.Create( Diagnostic.Create(
new DiagnosticDescriptor( new DiagnosticDescriptor(
"AXN0001", "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 " + "Unable to discover the relevant Avalonia XAML file " +
$"neither at {xamlFileName} nor at {aXamlFileName}", $"neither at {xamlFileName} nor at {aXamlFileName}",
"Usage", "Usage",
DiagnosticSeverity.Error, DiagnosticSeverity.Warning,
true), true),
Location.None)); Location.None));
return; continue;
} }
try try
@ -93,13 +93,12 @@ internal sealed class GenerateTypedNameReferencesAttribute : Attribute { }
Diagnostic.Create( Diagnostic.Create(
new DiagnosticDescriptor( new DiagnosticDescriptor(
"AXN0002", "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}", $"Unhandled exception occured while generating typed Name references: {exception}",
"Usage", "Usage",
DiagnosticSeverity.Error, DiagnosticSeverity.Warning,
true), true),
Location.None)); Location.None));
return;
} }
} }
} }
@ -152,8 +151,13 @@ internal sealed class GenerateTypedNameReferencesAttribute : Attribute { }
else else
{ {
var missingPartialKeywordMessage = var missingPartialKeywordMessage =
$"The type {typeSymbol.Name} should be declared with the 'partial' keyword " + $"The type {typeSymbol?.Name} should be declared with the 'partial' keyword " +
"as it is annotated with the [GenerateTypedNameReferences] attribute."; "as it is either annotated with the [GenerateTypedNameReferences] attribute, " +
"or the <AvaloniaNameGenerator> 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 " +
"<AvaloniaNameGenerator>false</AvaloniaNameGenerator> into your .csproj file as " +
"<PropertyGroup> descendant and decorate only relevant view classes with the " +
"[GenerateTypedNameReferences] attribute.";
context.ReportDiagnostic( context.ReportDiagnostic(
Diagnostic.Create( Diagnostic.Create(
@ -162,10 +166,9 @@ internal sealed class GenerateTypedNameReferencesAttribute : Attribute { }
missingPartialKeywordMessage, missingPartialKeywordMessage,
missingPartialKeywordMessage, missingPartialKeywordMessage,
"Usage", "Usage",
DiagnosticSeverity.Error, DiagnosticSeverity.Warning,
true), true),
Location.None)); Location.None));
return null;
} }
} }

4
version.json

@ -1,6 +1,6 @@
{ {
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
"version": "0.1", "version": "0.2.0-preview",
"assemblyVersion": { "assemblyVersion": {
"precision": "revision" "precision": "revision"
}, },
@ -8,4 +8,4 @@
"^refs/heads/main$", "^refs/heads/main$",
"^refs/heads/v\\d+\\.\\d+$" "^refs/heads/v\\d+\\.\\d+$"
] ]
} }

Loading…
Cancel
Save