AgentStack
SKILL verified MIT Self-run

Avalonia Custom Controls

skill-linuxdevel-avalonia-skills-avalonia-custom-controls · by linuxdevel

Use when creating custom UserControls, TemplatedControls, basic drawing controls, custom panels, control libraries, attached properties, or custom flyouts in Avalonia.

No reviews yet
0 installs
10 views
0.0% view→install

Install

$ agentstack add skill-linuxdevel-avalonia-skills-avalonia-custom-controls

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Avalonia Custom Controls? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Avalonia Custom Controls

Overview

Three control types: UserControl (XAML composition, app-specific), TemplatedControl (lookless, restyled per theme), Control (custom drawing with Render override). Choose by reusability needs.

Choosing a Control Type

| Type | Inherit from | Use when | |---|---|---| | UserControl | UserControl | App-specific view/page, composed of existing controls | | TemplatedControl | TemplatedControl | Reusable, restyled control for libraries/themes | | Basic control | Control | Custom drawing (charts, gauges, games) | | Panel | Panel | Custom layout algorithm |

UserControl


    
        
    
// MyCard.axaml.cs
public partial class MyCard : UserControl
{
    public MyCard() => InitializeComponent();
}

TemplatedControl

public class RatingControl : TemplatedControl
{
    public static readonly StyledProperty ValueProperty =
        AvaloniaProperty.Register(nameof(Value), defaultValue: 0);

    public int Value
    {
        get => GetValue(ValueProperty);
        set => SetValue(ValueProperty, value);
    }
}

Default template in Themes/Generic.axaml:


    
        
            
                
            
        
    

StyledProperty vs DirectProperty

| | StyledProperty | DirectProperty | |---|---|---| | Styling support | Yes | No | | Binding | All priorities | LocalValue only | | Value inheritance | Yes | No | | Per-instance storage | Prioritized list | Field | | Use when | Needs styling, most cases | High-perf, won't be styled |

// StyledProperty
public static readonly StyledProperty FillProperty =
    AvaloniaProperty.Register(nameof(Fill));

// DirectProperty
public static readonly DirectProperty CountProperty =
    AvaloniaProperty.RegisterDirect(
        nameof(Count), o => o.Count, (o, v) => o.Count = v);
private int _count;
public int Count
{
    get => _count;
    set => SetAndRaise(CountProperty, ref _count, value);
}

AttachedProperty

public static readonly AttachedProperty IsHighlightedProperty =
    AvaloniaProperty.RegisterAttached("IsHighlighted");

public static bool GetIsHighlighted(Control element) => element.GetValue(IsHighlightedProperty);
public static void SetIsHighlighted(Control element, bool value) => element.SetValue(IsHighlightedProperty, value);

Usage: ``

Custom Drawing (Render Override)

public class CircleControl : Control
{
    public override void Render(DrawingContext context)
    {
        var brush = new SolidColorBrush(Colors.CornflowerBlue);
        var pen = new Pen(Brushes.Navy, 2);
        context.DrawEllipse(brush, pen,
            new Point(Bounds.Width / 2, Bounds.Height / 2),
            Bounds.Width / 2 - 2, Bounds.Height / 2 - 2);
    }

    protected override Size MeasureOverride(Size availableSize)
        => new Size(100, 100); // desired size
}

Invalidate drawing: InvalidateVisual()

Custom Panel

public class CircularPanel : Panel
{
    protected override Size MeasureOverride(Size availableSize)
    {
        foreach (var child in Children)
            child.Measure(availableSize);
        return availableSize;
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        double angle = 0, step = 2 * Math.PI / Children.Count;
        double cx = finalSize.Width / 2, cy = finalSize.Height / 2;
        double r = Math.Min(cx, cy) * 0.8;
        foreach (var child in Children)
        {
            var x = cx + r * Math.Cos(angle) - child.DesiredSize.Width / 2;
            var y = cy + r * Math.Sin(angle) - child.DesiredSize.Height / 2;
            child.Arrange(new Rect(x, y, child.DesiredSize.Width, child.DesiredSize.Height));
            angle += step;
        }
        return finalSize;
    }
}

StyleKeyOverride

// Make MyButton style as base Button (picks up Button's ControlTheme)
protected override Type StyleKeyOverride => typeof(Button);

Control Library Project

  • Separate class library project
  • Reference Avalonia NuGet (not Avalonia.Desktop)
  • Include Generic.axaml as embedded resource: avares://MyLib/Themes/Generic.axaml
  • Register in consuming app: ``

Community Tools for Control Development

| Tool / Library | NuGet / Repo | Purpose | |---|---|---| | HotAvalonia | HotAvalonia | Hot reload for AXAML during control development | | Avant Garde | GitHub: kuiperzone/AvantGarde | Standalone XAML previewer for control libraries | | ShowMeTheXaml.Avalonia | ShowMeTheXaml.Avalonia | Show rendered XAML at runtime (useful for demos) | | Avalonia.Xaml.Behaviors | Avalonia.Xaml.Behaviors | Attach behaviors to controls without subclassing | | Sortable.Avalonia | sortable-avalonia | Drag-drop sort behavior for ItemsControls |

Behaviors (Avalonia.Xaml.Behaviors)

Add interactivity without code-behind or subclassing:


    
        
            
        
    

    
        
            
        
    

This is the Avalonia replacement for WPF DataTrigger and EventTrigger.

Common Mistakes

  • Inheriting from Control (WPF habit) instead of TemplatedControl for lookless controls
  • Not calling InitializeComponent() in UserControl constructor
  • Forgetting partial keyword on UserControl class
  • Using StyledProperty for high-frequency properties that don't need styling (perf issue)
  • Not providing x:Key="{x:Type local:MyControl}" in Generic.axaml ControlTheme

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.