avalonia-events
Skill50 starsChanged 9 days agoMITView on GitHub
Use when working with Avalonia routed events — defining custom RoutedEvent, handling bubbling or tunneling events, attaching handlers with AddHandler, RoutingStrategies, the OnApplyTemplate/PART_ pattern, or class-level static handlers via AddClassHandler.
In this repository23 other files it gives its agents.
- avalonia-app-developmentSkill
- avalonia-controlsSkill
- avalonia-custom-controlsSkill
- avalonia-data-bindingSkill
- avalonia-data-templatesSkill
- avalonia-deploymentSkill
- avalonia-graphics-animationSkill
- avalonia-input-interactionSkill
- avalonia-interop-renderingSkill
- avalonia-layoutSkill
- avalonia-mobileSkill
- avalonia-mvvmSkill
- avalonia-performanceSkill
- avalonia-pro-maxSkill
- avalonia-property-systemSkill
- avalonia-servicesSkill
- avalonia-stylingSkill
- avalonia-testingSkill
- avalonia-threadingSkill
- avalonia-versionsSkill
- avalonia-wpf-migrationSkill
- avalonia-xamlSkill
- avaloniaSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 13
All input events (PointerPressed, KeyDown, etc.) are routed.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Avalonia Routed Events
Overview
Avalonia uses routed events that travel the logical tree. Three strategies:
- Bubble — source → root (default for most input events)
- Tunnel — root → source (
Previewprefix by convention) - Direct — only raised on source element
All input events (PointerPressed, KeyDown, etc.) are routed. Version labels: see avalonia-versions.
Handling Events
XAML:
<Button Click="Button_Click"/>
<StackPanel PointerPressed="Panel_PointerPressed"/>Code-behind (direct +=):
myButton.Click += Button_Click;
private void Button_Click(object? sender, RoutedEventArgs e) { }AddHandler — explicit routing strategy, required for tunneling:
// Catch bubbled clicks from any child Button
myPanel.AddHandler(Button.ClickEvent, OnAnyButtonClick, RoutingStrategies.Bubble);
// Intercept before children see it (tunneling)
myPanel.AddHandler(InputElement.PointerPressedEvent, OnPreviewPointerPressed,
RoutingStrategies.Tunnel);
private void OnAnyButtonClick(object? sender, RoutedEventArgs e)
{
Console.WriteLine($"Clicked: {e.Source}");
}Remove handler:
myPanel.RemoveHandler(Button.ClickEvent, OnAnyButtonClick);Stopping Propagation
private void OnKeyDown(object? sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
e.Handled = true; // stops further bubbling/tunneling
}
}e.Handled = true prevents subsequent handlers in the route from firing.
Defining Custom Routed Events
public class MyControl : Control
{
// Event registration
public static readonly RoutedEvent<RoutedEventArgs> ActivatedEvent =
RoutedEvent.Register<MyControl, RoutedEventArgs>(
name: "Activated",
routingStrategy: RoutingStrategies.Bubble);
// CLR event wrapper
public event EventHandler<RoutedEventArgs> Activated
{
add => AddHandler(ActivatedEvent, value);
remove => RemoveHandler(ActivatedEvent, value);
}
// Raise it
protected void RaiseActivated()
=> RaiseEvent(new RoutedEventArgs(ActivatedEvent, this));
}Custom event args:
public class ValueChangedEventArgs : RoutedEventArgs
{
public double OldValue { get; }
public double NewValue { get; }
public ValueChangedEventArgs(RoutedEvent routedEvent, object source,
double oldValue, double newValue)
: base(routedEvent, source)
{
OldValue = oldValue;
NewValue = newValue;
}
}
public static readonly RoutedEvent<ValueChangedEventArgs> ValueChangedEvent =
RoutedEvent.Register<MyControl, ValueChangedEventArgs>(
"ValueChanged", RoutingStrategies.Bubble);Tunneling Events (Preview)
Convention: tunneling events prefixed with Preview.
public static readonly RoutedEvent<PointerPressedEventArgs> PreviewPointerPressedEvent =
RoutedEvent.Register<InputElement, PointerPressedEventArgs>(
"PreviewPointerPressed",
RoutingStrategies.Tunnel);Use pattern:
// Parent intercepts before child
myParent.AddHandler(InputElement.PointerPressedEvent, Handler, RoutingStrategies.Tunnel);RoutingStrategies Enum
| Value | Meaning |
Direct | Only raised on source element |
Bubble | Source → root |
Tunnel | Root → source |
All | Both tunnel and bubble (fire twice) |
OnApplyTemplate Pattern
Called when ControlTemplate is applied. Use to wire up named template parts:
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
// Get required part (throws if missing)
_button = e.NameScope.Get<Button>("PART_Button");
_button.Click += OnPartButtonClick;
// Get optional part (returns null if missing)
_indicator = e.NameScope.Find<Border>("PART_Indicator");
}
// Clean up old subscriptions if template changes
private Button? _button;
private Border? _indicator;Template XAML:
<ControlTemplate TargetType="local:MyControl">
<StackPanel>
<Border Name="PART_Indicator" Background="Gray"/>
<Button Name="PART_Button" Content="Click"/>
</StackPanel>
</ControlTemplate>Convention: template part names prefixed with PART_.
Declare expected parts (documentation + tooling):
[TemplatePart("PART_Button", typeof(Button))]
[TemplatePart("PART_Indicator", typeof(Border))]
public class MyControl : TemplatedControl { }Class Handlers (Static, All Instances)
static MyControl()
{
// Fires for every instance when a child Button is clicked
Button.ClickEvent.AddClassHandler<MyControl>((instance, e) =>
instance.OnChildButtonClicked(e));
// Intercept tunneling on all instances
InputElement.PointerPressedEvent.AddClassHandler<MyControl>(
(instance, e) => instance.OnPreviewPointerPressed(e),
RoutingStrategies.Tunnel);
}
private void OnChildButtonClicked(RoutedEventArgs e)
{
Console.WriteLine($"Button clicked in {this}");
}Focus and Gesture Events
// (12.0+) FocusChangedEventArgs: OldFocusedElement, NewFocusedElement, NavigationMethod, KeyModifiers
private void OnGotFocus(object? sender, FocusChangedEventArgs e) { }
// (11.x) GotFocusEventArgs(12.0+) Cancellable: GettingFocus / LosingFocus raise FocusChangingEventArgs (TryCancel(), TrySetNewFocusedElement(el)).
Gesture events are routed events declared on InputElement: Tapped, RightTapped, DoubleTapped, Holding, Pinch, PinchEnded, PullGesture, ScrollGesture, SwipeGesture, etc.
<Border Pinch="OnPinch"/> <!-- (12.0+) -->
<Border Gestures.Pinch="OnPinch"/> <!-- (11.x) -->myPanel.AddHandler(InputElement.TappedEvent, OnTapped); // works in all versionsInteraction Events (Non-Routed) vs Routed
Control.AttachedToVisualTree / DetachedFromVisualTree — not routed, fires on control directly:
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
// Safe to access TopLevel, start subscriptions
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
// Clean up subscriptions
}Common Mistakes
+=vsAddHandlerrouting strategies —myPanel.PointerPressed += Handleronly catchesBubbleby default; useAddHandlerwithRoutingStrategies.Tunnelfor preview intercept- Tunnel events won't fire with default
Bubblestrategy — must explicitly passRoutingStrategies.TunneltoAddHandler new RoutedEventArgs(MyEvent)missing source — always passthisas source:new RoutedEventArgs(MyEvent, this)- Forgetting
e.Handled = truein a handler that should consume the event — other handlers in the route still fire OnApplyTemplatewithout cleaning up previous subscriptions — template can be re-applied; detach old handlers before attaching new ones- (12.0+)
GotFocusEventArgs/Gestures.prefix — replaced byFocusChangedEventArgs/ events directly onInputElement e.NameScope.Get<T>throws if part missing — useFind<T>for optional parts to avoidKeyNotFoundException
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

