# ImageNamesGenerator - Introduction
A generator that helps you avoid mistakes when dealing with ImageNames's.
# Intent
When writing XAF code we often need to deal with ImageNames
. Most of the time those come in different sizes if we use PNG
files or as SVG
files. Regardless of the format, in XAF, we often need to refer them as string constants. If we write a more complex module (like for example a custom editor module) we might need those images as Embedded Resources
, instances of System.Drawing.Image
or even raw bytes
. This may result often in a lot of trial and error (because of typos) or even runtime exceptions. This source generator tries to minimize this weakness.
# Usage
namespace Acme.Module
{
[Xenial.XenialImageNames]
internal partial class ImageNames { }
}
2
3
4
5
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<XenialImageNames Include="Images/*.png" />
<XenialImageNames Include="Images/*.svg" />
</ItemGroup>
</Project>
2
3
4
5
6
# Generated-Code
Given you have a project structure like:
Acme.Module
Acme.Module/Images
Acme.Module/Images/MyImage.png
2
3
This will output:
namespace Acme.Module
{
[CompilerGenerated]
partial class ImageNames
{
internal const string MyImage = "MyImage";
}
}
2
3
4
5
6
7
8
# Advantage
- Usage of strongly typed image names
- Centralized usage via common class name
ImageNames
inside the assembly - Usage in attributes
BusinessObjects
,[ActionAttribute]
andControllers
(Action.ImageName
) etc. - Intellisense for
ImageNames
- Preview of actual image when using the
CodeRush Rich Comments
feature - Use Images directly as Resources, Bytes and Stream without the need of
ImageLoader
(SVG support is experimental)
# Drawbacks/Issues
- No Grouping by Size, yet
- No Nested Folder support, yet
- No support for mixing SVG & PNG, yet
- No support for mapping multiple image sources to multiple classes, yet
- No support for different subdirectories (only
Images
will be treated), yet - Assembly size will increase by a couple of bytes even if some constants are not used, yet
# API-surface
A partial class marked with the Xenial.XenialImageNames
will follow the rules:
All MSBuild
Includes
that define theXenialImageNames
inside theImages
subdirectory will be treated as image<ItemGroup><XenialImageNames Include="Images/*.*" /></ItemGroup>
It will respect the visibility of the target class
# Options
# MSBuild
<GenerateXenialImageNamesAttribute>
- Control ifXenialImageNamesAttribute
will be emitted<XenialAttributesVisibility>
(global) - Control'sXenialImageNamesAttribute
visibility modifier (public
/internal
)<EmitCompilerGeneratedFiles>
(global) - Code will be flushed to disk (debug)<XenialDebugSourceGenerators>
(global) - Debugger will launch on code generation (debug)
# Code
Generator will follow the visibility of the Target Class:
/* SourceGenerator will follow the visibility of the class */
namespace Acme.Module
{
[Xenial.XenialImageNames]
// Declared as public so...
public partial class ImageNames { }
}
namespace Acme.Module
{
[CompilerGenerated]
partial class ImageNames
{
// ...the output will be public constants
public const string MyImage = "MyImage";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Property | Type | Behavior |
---|---|---|
SmartComments | Boolean | Will generate a comment that allows CodeRush to display an inline image |
ResourceAccessors | Boolean | Will emit additional subclasses to access Strongly typed resources |
Sizes | Boolean | (experimental) Will group by Size Suffixes (opens new window) |
# Tips and Tricks
Use the C# static using (opens new window) feature to remove the ImageNames
prefix:
using static Acme.Module.ImageNames;
//Know you have all constants in instant scope like:
string myImage = MyImage;
2
3
4
Mark the the target class public and name according to your module so external modules can use them:
using Xenial.Framework.Base;
namespace Acme.Accounting.Module
{
[Xenial.XenialImageNames]
/* this will make sure you can access the image names across assembly boundaries and avoid conflicting names later */
public partial class AccountingImageNames { }
}
2
3
4
5
6
7
Because those are partial, you can add custom code and mark them static
using Xenial.Framework.Base;
namespace Acme.Accounting.Module
{
[Xenial.XenialImageNames]
public static partial class AccountingImageNames
{
public const string CustomConstant = "Whatever constant you need";
}
}
2
3
4
5
6
7
8
9
When using the SmartComments and ResourceAccessors you can safe a lot of boiler plate code. Given you have one image in 4 sizes ImageNamesGenerator will produce the following code:
CAUTION
When using image names, make sure they are valid C# identifiers (don't start with numbers, special characters, etc.), otherwise the source generation will break
CAUTION
When updating from an older Xenial to a newer Xenial version, it's necessary to restart VisualStudio/VSCode after the upgrade, so Intellisense can reload the new SourceGenerator. So it may come to false positive warnings if they don't match.
# Diagnostics
ID | Severity | Message | Reason |
---|---|---|---|
XENGEN0010 | Error | Could not parse boolean MSBUILD variable <GenerateXenialImageNamesAttribute> | MsBuild variable needs to be in boolean parsable format: true /false /True /False |
XENGEN0010 | Error | Could not parse boolean MSBUILD variable <XenialImageNames> | MsBuild variable needs to be in boolean parsable format: true /false /True /False |
XENGEN0100 | Error | The class using the [XenialImageNamesAttribute] needs to be partial | We can not generate code for non partial classes |
XENGEN0101 | Error | The class using the [XenialImageNamesAttribute] needs to be in a namespace | We can not generate code in the global namespace |
# Demo-Source
You can find demo sources in the Xenial.Framework repository for in depth usage information.