Primary APIs from the Avalonia codebase:
TemplatedControlTemplatePartAttributeTemplateAppliedEventArgsControlThemeStyledElement.ThemeTemplateBindingIControlTemplateTemplateExtensions.GetTemplateChildren(...)ThemeVariantScopeImportant members:
TemplatedControl.TemplatePropertyTemplatedControl.ApplyTemplate()TemplatedControl.OnApplyTemplate(TemplateAppliedEventArgs e)TemplatedControl.TemplateApplied eventTemplatePartAttribute(Name, Type) and IsRequiredControlTheme.TargetTypeControlTheme.BasedOnStyledElement.ThemeTemplateBinding.DescriptionReference source files:
src/Avalonia.Controls/Primitives/TemplatedControl.cssrc/Avalonia.Base/Controls/Metadata/TemplatePartAttribute.cssrc/Avalonia.Base/Styling/ControlTheme.cssrc/Avalonia.Base/Data/TemplateBinding.cssrc/Avalonia.Controls/Templates/IControlTemplate.cssrc/Avalonia.Controls/Templates/TemplateExtensions.csTemplatedControl.[TemplatePart].OnApplyTemplate and pull parts from e.NameScope.ControlTheme keyed by {x:Type YourControl}.TemplateBinding inside the control template for runtime-fast property flow.using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
[TemplatePart("PART_ContentHost", typeof(ContentPresenter), IsRequired = true)]
public class FastCard : TemplatedControl
{
public static readonly StyledProperty<string?> TitleProperty =
AvaloniaProperty.Register<FastCard, string?>(nameof(Title));
private ContentPresenter? _contentHost;
public string? Title
{
get => GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_contentHost = e.NameScope.Find<ContentPresenter>("PART_ContentHost");
}
}
ControlTheme<ControlTheme x:Key="{x:Type local:FastCard}" TargetType="local:FastCard">
<Setter Property="Padding" Value="12" />
<Setter Property="Template">
<ControlTemplate>
<Border Padding="{TemplateBinding Padding}">
<StackPanel Spacing="8">
<TextBlock Text="{TemplateBinding Title}" FontWeight="SemiBold" />
<ContentPresenter x:Name="PART_ContentHost"
Content="{TemplateBinding Content}" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter>
</ControlTheme>
Use TemplateBinding for templated parent properties:
Padding="{TemplateBinding Padding}"Foreground="{TemplateBinding Foreground}"BorderBrush="{TemplateBinding BorderBrush}"For diagnostics and tooling output, TemplateBinding.Description returns a concise "TemplateBinding: <Property>" string.
Use nested styles in themes for pseudo-classes:
<Style Selector="^:pointerover /template/ Border">
<Setter Property="Background" Value="{DynamicResource CardHoverBrush}" />
</Style>
Build theme inheritance with BasedOn:
<ControlTheme x:Key="DangerFastCard"
TargetType="local:FastCard"
BasedOn="{StaticResource {x:Type local:FastCard}}">
<Setter Property="BorderBrush" Value="Tomato" />
</ControlTheme>
Apply per-instance theme explicitly:
<local:FastCard Theme="{StaticResource DangerFastCard}" />
For advanced template helper APIs used by framework-style control authoring, see:
Key bridge APIs:
IControlTemplateITemplate<TemplatedControl, TemplateResult<Control>?>TemplateExtensions.GetTemplateChildren(...)TemplateContent.Load(...)Use this layer for diagnostics and advanced template composition, while keeping normal control-theme authoring in XAML.
OnApplyTemplate as idempotent. Templates can be reapplied.ControlTheme over giant global selectors for reusable controls.PART_.OnApplyTemplate not firing as expected:
Template exists through a ControlTheme or local setter.null:
[TemplatePart] and x:Name.TargetType so template did not apply.ControlTheme, nested selectors must use ^ nesting anchors./template/ when targeting template visuals.ControlTheme.CornerRadius, Padding, etc.).Default mode:
XAML-first complete example:
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyApp.Controls"
x:Class="MyApp.App">
<Application.Resources>
<ControlTheme x:Key="{x:Type local:FastCard}" TargetType="local:FastCard">
<Setter Property="Template">
<ControlTemplate>
<Border Padding="{TemplateBinding Padding}" Background="#20242B">
<StackPanel Spacing="6">
<TextBlock Text="{TemplateBinding Title}" FontWeight="Bold" />
<ContentPresenter x:Name="PART_ContentHost" Content="{TemplateBinding Content}" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter>
</ControlTheme>
</Application.Resources>
</Application>
Code-only alternative (on request):
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.Media;
using Avalonia.Styling;
var theme = new ControlTheme(typeof(FastCard));
theme.Setters.Add(new Setter(TemplatedControl.TemplateProperty,
new FuncControlTemplate<FastCard>((owner, scope) =>
{
var stack = new StackPanel { Spacing = 6 };
stack.Children.Add(new TextBlock { Text = owner.Title, FontWeight = FontWeight.Bold });
stack.Children.Add(new ContentPresenter { Name = "PART_ContentHost", Content = owner.Content });
return new Border { Padding = owner.Padding, Background = Brushes.DimGray, Child = stack };
})));
Application.Current!.Resources[typeof(FastCard)] = theme;