# Avalonia Custom Controls

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

- **Type:** Skill
- **Install:** `agentstack add skill-linuxdevel-avalonia-skills-avalonia-custom-controls`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [linuxdevel](https://agentstack.voostack.com/s/linuxdevel)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [linuxdevel](https://github.com/linuxdevel)
- **Source:** https://github.com/linuxdevel/Avalonia-skills/tree/main/skills/avalonia/avalonia-custom-controls

## Install

```sh
agentstack add skill-linuxdevel-avalonia-skills-avalonia-custom-controls
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## 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

```xml

    
        
    

```

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

## TemplatedControl

```csharp
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`:

```xml

    
        
            
                
            
        
    

```

## 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 |

```csharp
// 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

```csharp
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)

```csharp
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

```csharp
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

```csharp
// 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:
```xml

    
        
            
        
    

    
        
            
        
    

```
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.

- **Author:** [linuxdevel](https://github.com/linuxdevel)
- **Source:** [linuxdevel/Avalonia-skills](https://github.com/linuxdevel/Avalonia-skills)
- **License:** MIT

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

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-linuxdevel-avalonia-skills-avalonia-custom-controls
- Seller: https://agentstack.voostack.com/s/linuxdevel
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
