xaml-csharp-development-skill-for-avalonia

RelativeSource, StaticResource, and Name Resolution Markup

Table of Contents

  1. Scope and APIs
  2. RelativeSource Core Model
  3. RelativeSourceExtension in XAML
  4. Wiring RelativeSource in Binding and ReflectionBindingExtension
  5. FindAncestor Patterns (Visual vs Logical Tree)
  6. StaticResourceExtension Semantics
  7. ResolveByNameExtension Semantics
  8. Binding.TypeResolver and Type Name Resolution
  9. Best Practices
  10. Troubleshooting

Scope and APIs

Primary APIs:

Key members:

Reference source files:

RelativeSource Core Model

RelativeSource describes where binding source values come from relative to the target.

Important options:

Ancestor search controls:

Notes:

RelativeSourceExtension in XAML

Use RelativeSourceExtension for inline relative-source markup. This is the canonical style used by Avalonia themes and samples.

Core members:

Inline example:

<TextBlock xmlns="https://github.com/avaloniaui"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           Text="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag}" />

Ancestor example (canonical inline syntax):

<TextBlock xmlns="https://github.com/avaloniaui"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:controls="using:MyApp.Controls">
  <TextBlock.Text>
    <Binding Path="Header"
             RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=controls:CardHost, AncestorLevel=1, Tree=Visual}" />
  </TextBlock.Text>
</TextBlock>

Wiring RelativeSource in Binding and ReflectionBindingExtension

Two common surfaces:

  1. Binding.RelativeSource (Avalonia.Markup.Data.Binding)
  2. ReflectionBindingExtension.RelativeSource

ReflectionBindingExtension usage is explicit in XAML:

<TextBlock xmlns="https://github.com/avaloniaui"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           Text="{ReflectionBinding RelativeSource={RelativeSource Self}, Path=Tag}" />

Equivalent C# pattern with Binding.RelativeSource:

using Avalonia.Data;
using Avalonia.Markup.Data;

textBlock.Bind(TextBlock.TextProperty, new Binding("Tag")
{
    RelativeSource = new RelativeSource(RelativeSourceMode.Self)
});

Culture behavior on reflection path:

FindAncestor Patterns (Visual vs Logical Tree)

Use visual tree for template/composition ancestry:

<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Border}, AncestorLevel=1, Tree=Visual}, Path=Name}" />

Use logical tree for content/ownership ancestry:

<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}, AncestorLevel=1, Tree=Logical}, Path=Title}" />

Rule of thumb:

StaticResourceExtension Semantics

StaticResourceExtension resolves a resource key during markup value provisioning.

Key APIs:

Example:

<StackPanel xmlns="https://github.com/avaloniaui"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <StackPanel.Resources>
    <SolidColorBrush x:Key="AccentBrush" Color="#0A84FF" />
  </StackPanel.Resources>

  <TextBlock Foreground="{StaticResource AccentBrush}" Text="Static resource lookup" />
</StackPanel>

Behavior details:

Use StaticResource when value is expected to be stable after load. Use DynamicResource for runtime-changing keys.

ResolveByNameExtension Semantics

ResolveByNameExtension performs name-scope lookup by string name.

Key APIs:

Example:

<Grid xmlns="https://github.com/avaloniaui"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Border x:Name="Host" />
  <TextBlock Tag="{ResolveByName Host}" />
</Grid>

Behavior details:

Binding.TypeResolver and Type Name Resolution

Binding.TypeResolver is a function hook for resolving type names used by reflection binding path parsing.

API:

ReflectionBindingExtension sets type-resolution behavior from service provider context when it creates the binding instance.

Use cases:

Most apps should use default resolution and avoid custom TypeResolver unless runtime composition requires it.

Best Practices

  1. Prefer explicit relative-source intent.
    • Use Mode=Self, Mode=TemplatedParent, or Mode=FindAncestor deliberately.
  2. Set Tree intentionally for ancestor lookups.
    • Wrong tree choice is a common reason for unresolved bindings.
  3. Keep AncestorLevel minimal.
    • AncestorLevel=1 is the default and usually correct.
  4. Use StaticResource for stable keys.
    • Switch to DynamicResource only when runtime key changes are required.
  5. Keep name-scope lookups local.
    • Prefer direct element references in templates/views before adding generic name-resolution indirection.
  6. Treat reflection binding knobs as opt-in.
    • Binding.TypeResolver and reflection markup paths are advanced configuration points.

Troubleshooting

  1. Ancestor binding never resolves.
    • Verify AncestorType and Tree (Visual vs Logical) match actual tree shape.
  2. Ancestor lookup returns wrong node.
    • Check AncestorLevel; it is 1-based and defaults to first match.
  3. StaticResource throws not found.
    • Confirm ResourceKey exists in local/app/theme dictionaries at lookup time.
  4. Name lookup returns null or stays unset.
    • Ensure the target is inside a valid INameScope and the named element is registered there.
  5. Reflection binding path type segments fail.
    • Validate or remove custom Binding.TypeResolver; fallback to default when possible.