Theme Generator
A theme generator uses options in a theme definition to guide it on how to create theme resources like brushes, thicknesses, etc. Distinct theme resources are generated for both Light
and Dark
theme variants, allowing for the theme generator to run a single time regardless of if an application toggles between light and dark modes at run-time.
Generation Workflow
The ThemeGenerator class is what houses the theme generator logic. Its public Generate method is passed a ThemeDefinition and interprets the theme definition's options to output a ResourceDictionary
. The ResourceDictionary
contains all the theme resources that were generated.
The Generate method has this general workflow:
- Multiple theme generator sessions are created.
- Each session invokes generator methods to create resources, which are then added to the session's
ResourceDictionary
. - Each session calls into the generator's OnSessionCompleted method where additional custom resources can optionally be added.
- The various session
ResourceDictionary
instances are combined into a singleResourceDictionary
hierarchy, which is what is returned by the Generate method.
Generator Sessions
A ThemeGeneratorSession instance is created for a "common" theme variant, and additional ThemeGeneratorSession instances are created for each theme variant (e.g. Light
and Dark
).
The ThemeGeneratorSession class provides access to important data that is used by the generator.
Property | Description |
---|---|
Definition | The ThemeDefinition that initiated the theme generation. |
ThemeVariant | A ThemeVariant indicating the target theme variant, which may be null for resources like thicknesses that are shared among all theme variants. |
IsDark | Whether the ThemeVariant is a dark theme variant. |
Palette | The ColorPalette that provides access to all colors. |
ResourceDictionary | The ResourceDictionary into which the theme generator session is appending resources. |
The following session properties return the resolved ColorRamp instances to use for various semantic colors, as specified in the theme definition.
Property | Description |
---|---|
NeutralColorRamp | Neutral (e.g., grayscale) colors. |
AccentColorRamp | Accent semantic colors. |
InformationColorRamp | Information semantic colors. |
SuccessColorRamp | Success semantic colors. |
WarningColorRamp | Warning semantic colors. |
DangerColorRamp | Danger semantic colors. |
Resource Types
Many types of resources can be generated. Each of the resource types has a ThemeGenerator method that is invoked to create the resource value for the specified ThemeResourceKind.
The following table shows all of the resource types that are generated, along with the related protected virtual method invoked on the ThemeGenerator class.
Resource Type | Related Method |
---|---|
Boolean |
GetBooleanResource |
BoxShadows |
GetBoxShadowResource |
Brush |
GetBrushResource |
Char |
GetCharResource |
Color |
GetColorResource |
CornerRadius |
GetCornerRadiusResource |
Double |
GetDoubleResource |
FontFamily |
GetFontFamilyResource |
FontWeight |
GetFontWeightResource |
Thickness |
GetThicknessResource |
Creating a Customized ThemeGenerator
The ThemeGenerator class has numerous methods that are protected virtual and can be overridden for extensibility purposes, such as for adjusting specific predefined resource values or appending custom resources. If this level of extensibility is necessary, create a class that inherits ThemeGenerator.
By default, an instance of ThemeGenerator is used to generate theme resources. By setting the ThemeDefinition.Generator property to an instance of a custom class that derives ThemeGenerator, the theme resource generation logic can be tailored for your needs.
The following example shows how a CustomThemeGenerator
class can be created and installed in a ThemeDefinition for use.
public class CustomThemeGenerator : ThemeGenerator {
// NOTE: Implement appropriate method overrides here to customize
// theme resource generation logic
}
<Application ...
xmlns:actipro="http://schemas.actiprosoftware.com/avaloniaui"
xmlns:generation="using:ActiproSoftware.UI.Avalonia.Themes.Generation">
<Application.Styles>
<actipro:ModernTheme>
<actipro:ModernTheme.Definition>
<generation:ThemeDefinition>
<generation:ThemeDefinition.Generator>
<!-- Custom generator logic installed -->
<local:CustomThemeGenerator />
</generation:ThemeDefinition.Generator>
</generation:ThemeDefinition>
</actipro:ModernTheme.Definition>
</actipro:ModernTheme>
</Application.Styles>
</Application>
Customizing Generated Resources
After following the instructions above to create a customized theme generator, override any of the methods listed in the "Resource Types" table above to customize generated resources.
For instance, the following logic could be implemented to alter the default checkerboard brushes:
protected override Brush? GetBrushResource(ThemeGeneratorSession session, ThemeResourceKind resourceKind) {
return resourceKind switch {
ThemeResourceKind.CheckerboardPrimaryBrush => new SolidColorBrush(Colors.Silver),
ThemeResourceKind.CheckerboardSecondaryBrush => new SolidColorBrush(Colors.Gray),
_ => base.GetBrushResource(session, resourceKind)
};
}
Appending Custom Resources
Once a ThemeGeneratorSession has had all of its resources generated, it calls the ThemeGenerator.OnSessionCompleted method, which is a protected internal method that is passed the ThemeGeneratorSession instance.
As an extensibility point, after following the instructions above to create a customized theme generator, override the ThemeGenerator.OnSessionCompleted method to append any custom resources you wish into the session. For instance, the following example shows how to add a new custom Brush
resource with Light
and Dark
variations.
protected override void OnSessionCompleted(ThemeGeneratorSession session) {
// If the session is for a specific theme variant (not the "common" session)...
if (session.ThemeVariant is not null) {
// Add a custom "MyBrush" resource brush
session.ResourceDictionary.Add("MyBrush",
new SolidColorBrush(session.IsDark ? Colors.LightBlue : Colors.DarkBlue));
}
}