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" />
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
<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>
<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
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 `<Project />` tag:
```xml
<PropertyGroup>
<PropertyGroup>
<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
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<global::Avalonia.NameGenerator.Sandbox.Controls.CustomTextBox>("UserNameTextBox");
internal 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");
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");

23
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 <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(
Diagnostic.Create(
@ -162,10 +166,9 @@ internal sealed class GenerateTypedNameReferencesAttribute : Attribute { }
missingPartialKeywordMessage,
missingPartialKeywordMessage,
"Usage",
DiagnosticSeverity.Error,
DiagnosticSeverity.Warning,
true),
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",
"version": "0.1",
"version": "0.2.0-preview",
"assemblyVersion": {
"precision": "revision"
},
@ -8,4 +8,4 @@
"^refs/heads/main$",
"^refs/heads/v\\d+\\.\\d+$"
]
}
}

Loading…
Cancel
Save