In This Article

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:

  1. Multiple theme generator sessions are created.
  2. Each session invokes generator methods to create resources, which are then added to the session's ResourceDictionary.
  3. Each session calls into the generator's OnSessionCompleted method where additional custom resources can optionally be added.
  4. The various session ResourceDictionary instances are combined into a single ResourceDictionary 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));
	}
}