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.
The colors of theme-generated brushes are based on a customizable color palette. This allows all the theme's neutral colors to be tinted towards a certain color tone, accent and other semantic colors to be configurable, and more.
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:
- A color palette is generated using the factory specified in the ColorPaletteFactory property.
- 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.
Color Palettes
The ColorPaletteFactory property contains a factory object that can create a ColorPalette. This color palette provides all of the color ramps and colors to the theme generator, used when generating brush resources.
The DefaultColorPaletteFactory class is a default implementation of IColorPaletteFactory that creates a palette of color ramps using a designated midtone Color
for each ramp. The default midtone colors have been carefully selected to create a beautiful, balanced color palette.
Some developers may wish to adjust the default color palette. The midtone color for each color ramp can be altered via DefaultColorPaletteFactory properties, such as NeutralMidtoneColor, BlueMidtoneColor, etc.
Tinting a Theme
Since a majority of a themes brushes are based on the neutral color ramp, changing the DefaultColorPaletteFactory.NeutralMidtoneColor will tint a theme. The default neutral midtone color is a basic gray with a slight blue tone.
These subtle neutral midtone colors are recommended starting points:
#6c7281
- Gray (default)#64738a
- Slate#71717b
- Zinc#79716b
- Stone
By using a more saturated color, the tinting effect will be more dramatic. Tinting is generally more noticeable in dark theme variants compared to light theme variants. Here are several saturated midtone color options:
#5f86b1
- Blue#527d52
- Green#716378
- Purple
Tip
Use the Color Palette utility in the samples app to visualize the generated color palette, and even perform some adjustments to neutral color tints and the theme accent color.
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));
}
}