Primary APIs:
InteractiveRoutedEvent and RoutedEvent<TEventArgs>RoutingStrategiesRoutedEventArgsEventRouteRoutedEventRegistryInputElementPointerEventArgs, PointerPressedEventArgs, PointerReleasedEventArgsKeyEventArgsGesturesInteractiveExtensionsImportant members:
Interactive.AddHandler(...), RemoveHandler(...), RaiseEvent(...)RoutedEvent.Register<TOwner, TEventArgs>(...)RoutedEvent.AddClassHandler(...)RoutedEvent.EventArgsType, RoutedEvent.HasRaisedSubscriptionsRoutedEvent.Raised, RoutedEvent.RouteFinishedRoutedEventArgs.Handled, Route, Source, RoutedEventCancelRoutedEventArgs, CancelRoutedEventArgs.CancelInputElement.KeyDownEvent, KeyUpEvent, TextInputEventInputElement.PointerPressedEvent, PointerMovedEvent, PointerReleasedEvent, PointerWheelChangedEventPointerEventArgs.GetPosition(...), GetCurrentPoint(...), PreventGestureRecognition()Gestures.TappedEvent, DoubleTappedEvent, HoldingEvent, PinchEvent, ScrollGestureEventEventRoute.HasHandlersRoutedEventRegistry.GetAllRegistered()InteractiveExtensions.GetObservable(...), AddDisposableHandler(...), GetInteractiveParent()Reference source files:
src/Avalonia.Base/Interactivity/Interactive.cssrc/Avalonia.Base/Interactivity/RoutedEvent.cssrc/Avalonia.Base/Interactivity/RoutedEventArgs.cssrc/Avalonia.Base/Interactivity/EventRoute.cssrc/Avalonia.Base/Interactivity/RoutedEventRegistry.cssrc/Avalonia.Base/Interactivity/InteractiveExtensions.cssrc/Avalonia.Base/Input/InputElement.cssrc/Avalonia.Base/Input/PointerEventArgs.cssrc/Avalonia.Base/Input/KeyEventArgs.cssrc/Avalonia.Base/Input/Gestures.csRuntime flow in app code:
Direct, Tunnel, Bubble).Handled controls downstream processing.Routing strategy quick guide:
Direct: source only.Tunnel: root to source.Bubble: source to root.Use Tunnel for interception, Bubble for normal control-level handling.
Use routed-event metadata APIs when auditing event topology in larger control trees:
RoutedEvent.EventArgsType lets you validate event-args contracts before wiring generic handlers.RoutedEvent.HasRaisedSubscriptions tells you whether RoutedEvent.Raised has observers.RoutedEventRegistry.Instance.GetAllRegistered() gives a global routed-event inventory for diagnostics.InteractiveExtensions.GetInteractiveParent() is a fast parent hop for event-path inspection on Interactive.Example diagnostics pass:
using Avalonia.Interactivity;
foreach (var routedEvent in RoutedEventRegistry.Instance.GetAllRegistered())
{
_logger.Debug(
"Event={Name}, Owner={Owner}, Args={Args}, HasRaisedSubscribers={HasSubscribers}",
routedEvent.Name,
routedEvent.OwnerType.Name,
routedEvent.EventArgsType.Name,
routedEvent.HasRaisedSubscriptions);
}
You can instrument one routed event to inspect route activity:
using Avalonia.Interactivity;
IDisposable raisedSub = InputElement.PointerPressedEvent.Raised
.Subscribe(tuple =>
{
(object sender, RoutedEventArgs args) = tuple;
_ = sender;
_ = args.Route;
});
IDisposable finishedSub = InputElement.PointerPressedEvent.RouteFinished
.Subscribe(args => _ = args.Handled);
CancelRoutedEventArgs is the canonical cancelable routed-event args type. Constructor forms:
new CancelRoutedEventArgs()new CancelRoutedEventArgs(routedEvent)new CancelRoutedEventArgs(routedEvent, source)Cancelable custom event pattern:
using Avalonia.Interactivity;
public class DocumentHost : Avalonia.Controls.Control
{
public static readonly RoutedEvent<CancelRoutedEventArgs> BeforeCloseEvent =
RoutedEvent.Register<DocumentHost, CancelRoutedEventArgs>(
nameof(BeforeClose),
RoutingStrategies.Bubble);
public event EventHandler<CancelRoutedEventArgs>? BeforeClose
{
add => AddHandler(BeforeCloseEvent, value);
remove => RemoveHandler(BeforeCloseEvent, value);
}
public bool RequestClose()
{
var args = new CancelRoutedEventArgs(BeforeCloseEvent, this);
RaiseEvent(args);
return !args.Cancel;
}
}
Low-level route creation with EventRoute is uncommon in app code, but when used:
EventRoute.HasHandlers before raising work-heavy payloads,using Avalonia.Interactivity;
public class CommitPanel : Avalonia.Controls.Control
{
public static readonly RoutedEvent<RoutedEventArgs> CommitRequestedEvent =
RoutedEvent.Register<CommitPanel, RoutedEventArgs>(
nameof(CommitRequested),
RoutingStrategies.Bubble);
public event EventHandler<RoutedEventArgs>? CommitRequested
{
add => AddHandler(CommitRequestedEvent, value);
remove => RemoveHandler(CommitRequestedEvent, value);
}
protected void RaiseCommitRequested()
{
RaiseEvent(new RoutedEventArgs(CommitRequestedEvent));
}
}
root.AddHandler(
Avalonia.Input.InputElement.PointerPressedEvent,
(sender, e) =>
{
if (ShouldBlockPointer(e))
e.Handled = true;
},
RoutingStrategies.Tunnel,
handledEventsToo: false);
using Avalonia.Interactivity;
IDisposable sub = myControl
.GetObservable(Avalonia.Input.InputElement.KeyDownEvent)
.Subscribe(e => HandleKey(e));
void OnPointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e)
{
var point = e.GetCurrentPoint((Avalonia.Visual)e.Source!);
if (point.Properties.IsLeftButtonPressed)
{
// Use point.Position and modifiers for deterministic handling.
}
}
static readonly.Tunnel only for pre-filtering and guard logic.handledEventsToo: true unless truly required.AddHandler<TEventArgs> overloads for maintainability.Gestures events instead of reimplementing recognizers.IsHitTestVisible = false) or disabled.Handled = true.handledEventsToo where needed.PreventGestureRecognition).Tunnel and Bubble are subscribed without guards.Default mode:
XAML-first references:
KeyBinding collections in XAMLXAML-first usage example:
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyApp.Views.EditorView"
PointerPressed="OnPointerPressed"
KeyDown="OnKeyDown">
<UserControl.KeyBindings>
<KeyBinding Gesture="Ctrl+Enter" Command="{Binding CommitCommand}" />
</UserControl.KeyBindings>
</UserControl>
Code-only alternative (on request):
AddHandler(InputElement.PointerPressedEvent, OnPointerPressed, RoutingStrategies.Bubble);
AddHandler(InputElement.KeyDownEvent, OnKeyDown, RoutingStrategies.Bubble);