xaml-csharp-development-skill-for-avalonia

Template Content and Func Template Patterns

Table of Contents

  1. Scope and APIs
  2. Generic Template Contracts (ITemplate)
  3. Function Templates (FuncTemplate)
  4. Name-Scope Registration Helpers
  5. Control Templates and IControlTemplate
  6. Recycling Data Templates (IRecyclingDataTemplate)
  7. Tree Templates (FuncTreeDataTemplate and TreeDataTemplate)
  8. Runtime Template Content Loading (TemplateContent)
  9. Inspecting Applied Template Children (TemplateExtensions)
  10. Best Practices
  11. Troubleshooting

Scope and APIs

Primary APIs:

Frequently referenced signatures:

Reference source files:

Generic Template Contracts (ITemplate)

Core contracts:

These generic interfaces are the shared base for control templates, data-template helpers, and function-based builders.

Use:

Function Templates (FuncTemplate)

FuncTemplate wrappers let you build templates from strongly typed delegates.

Types:

Constructors:

Example:

using Avalonia.Controls;
using Avalonia.Controls.Templates;

var noArg = new FuncTemplate<TextBlock>(() => new TextBlock { Text = "Hello" });
TextBlock built = noArg.Build();

var withParam = new FuncTemplate<string, TextBlock>((value, scope) =>
{
    var text = new TextBlock { Name = "PART_Label", Text = value };
    return text.RegisterInNameScope(scope);
});

TextBlock builtWithParam = withParam.Build("Template value");

Name-Scope Registration Helpers

FuncTemplateNameScopeExtensions provides helper registration for function-template output.

API:

Use this when function templates create named elements that must be discoverable from template name scope.

Control Templates and IControlTemplate

IControlTemplate is the typed contract for TemplatedControl template building.

API:

Practical guidance:

Recycling Data Templates (IRecyclingDataTemplate)

IRecyclingDataTemplate extends IDataTemplate with recycle-aware build:

Use when item controls are frequently re-bound and you can reliably reset reused state.

If recycling is enabled, ensure state cleanup is deterministic:

Tree Templates (FuncTreeDataTemplate and TreeDataTemplate)

Tree-template APIs:

Function-based hierarchical templates:

Example:

using Avalonia.Controls;
using Avalonia.Controls.Templates;

var treeTemplate = new FuncTreeDataTemplate<MyNode>(
    build: (node, scope) => new TextBlock { Name = "PART_Title", Text = node.Title }.RegisterInNameScope(scope),
    itemsSelector: node => node.Children);

XAML tree template path:

<TreeDataTemplate xmlns="https://github.com/avaloniaui"
                  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                  xmlns:vm="using:MyApp.ViewModels"
                  x:DataType="vm:NodeViewModel"
                  ItemsSource="{CompiledBinding Children}">
  <TextBlock Text="{CompiledBinding Title}" />
</TreeDataTemplate>

API-index compatibility note:

Runtime Template Content Loading (TemplateContent)

TemplateContent is the loader utility used by markup template wrappers.

APIs:

Use this when handling deferred template content in infrastructure code; app-level code normally consumes higher-level ControlTemplate, DataTemplate, and TreeDataTemplate.

Inspecting Applied Template Children (TemplateExtensions)

TemplateExtensions.GetTemplateChildren(this TemplatedControl control) traverses controls created from the control’s applied template.

Use cases:

Example:

using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;

protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
    base.OnApplyTemplate(e);

    foreach (var child in this.GetTemplateChildren())
    {
        // Inspect template-built controls tied to this templated parent.
    }
}

Best Practices

  1. Prefer XAML templates first.
    • Use FuncTemplate/function-template APIs for dynamic or infrastructure scenarios.
  2. Keep IRecyclingDataTemplate opt-in and explicit.
    • Recycle only when control reset behavior is proven safe.
  3. Keep name-scope behavior deterministic.
    • Register named function-template controls via RegisterInNameScope(...).
  4. Separate authoring and diagnostics concerns.
    • Use TemplateExtensions.GetTemplateChildren(...) for diagnostics and verification, not routine business logic.
  5. Keep tree-template child selection simple.
    • Prefer stable child enumerable selection functions and avoid side effects in selectors.

Troubleshooting

  1. Template-built parts cannot be found by name.
    • Ensure named controls are registered in the template INameScope (RegisterInNameScope(...) in function templates).
  2. Recycled items show stale data.
    • IRecyclingDataTemplate.Build(data, existing) must fully reset view state before reuse.
  3. Tree nodes render but children do not expand.
    • Verify child selector (FuncTreeDataTemplate selector or TreeDataTemplate.ItemsSource) returns expected enumerable.
  4. Template traversal returns unexpected controls.
    • GetTemplateChildren(...) is scoped to templated-parent ownership, not all visual descendants.
  5. Template content fails to load.
    • Validate that deferred content shape matches what TemplateContent.Load(...) expects.