Install
$ agentstack add skill-linuxdevel-avalonia-skills-avalonia-custom-controls ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo 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.
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
AvaloniaNuGet (notAvalonia.Desktop) - Include
Generic.axamlas 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 ofTemplatedControlfor lookless controls - Not calling
InitializeComponent()in UserControl constructor - Forgetting
partialkeyword on UserControl class - Using
StyledPropertyfor 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
- Source: linuxdevel/Avalonia-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.