| name | avalonia-input-interaction |
| description | Use when handling pointer events, keyboard input, gestures, focus, drag-and-drop, or hot keys in Avalonia. Covers PointerPressed/Released/Moved, KeyDown/KeyUp, TextInput, HotKeyManager, KeyGesture, focus management, TapGestureRecognizer, PinchGestureRecognizer, DragDrop.DoDragDrop, DataObject, and Cursor types. |
Avalonia Input & Interaction
Overview
All input routes as routed events (bubbling by default, tunneling with "Preview" prefix). Pointer events are unified across mouse, touch, and stylus.
Pointer Events
| Event | Fires when |
|---|
PointerPressed | Button/finger down |
PointerReleased | Button/finger up |
PointerMoved | Pointer moves over control |
PointerEntered | Pointer enters bounds |
PointerExited | Pointer leaves bounds |
PointerCaptureLost | Capture released |
Tapped | Quick tap/click |
DoubleTapped | Double tap/click |
RightTapped | Right-click / long press |
myControl.PointerPressed += (s, e) =>
{
var point = e.GetPosition(myControl);
var props = e.GetCurrentPoint(myControl).Properties;
if (props.IsLeftButtonPressed) { }
if (props.IsRightButtonPressed) { }
if (props.IsMiddleButtonPressed) { }
};
Capture pointer (receive events outside bounds):
myControl.PointerPressed += (s, e) =>
{
e.Pointer.Capture(myControl);
};
myControl.PointerReleased += (s, e) =>
{
e.Pointer.Capture(null);
};
Check pointer type:
myControl.PointerPressed += (s, e) =>
{
if (e.Pointer.Type == PointerType.Touch) { }
if (e.Pointer.Type == PointerType.Pen) { }
};
Keyboard Events
| Event | Fires when |
|---|
KeyDown | Key pressed (repeats while held) |
KeyUp | Key released |
TextInput | Text character produced |
myControl.KeyDown += (s, e) =>
{
if (e.Key == Key.Enter && e.KeyModifiers == KeyModifiers.Control)
{
e.Handled = true;
SubmitForm();
}
};
myTextBox.TextInput += (s, e) =>
{
if (!char.IsDigit(e.Text![0]))
e.Handled = true;
};
KeyModifiers flags: None, Alt, Control, Shift, Meta (combinable with |)
if (e.Key == Key.Z && e.KeyModifiers == (KeyModifiers.Control | KeyModifiers.Shift))
Hot Keys
XAML registration:
<Button HotKey="Ctrl+S" Command="{Binding SaveCommand}" Content="Save"/>
<MenuItem HotKey="Ctrl+Z" Header="Undo" Command="{Binding UndoCommand}"/>
Code registration:
HotKeyManager.RegisterHotKey(myControl, new KeyGesture(Key.S, KeyModifiers.Control));
HotKeyManager.SetHotKey(myButton, new KeyGesture(Key.F5));
Unregister:
HotKeyManager.SetHotKey(myControl, null);
Focus
myControl.Focus();
var fm = TopLevel.GetTopLevel(this)?.FocusManager;
fm?.Focus(myControl, NavigationMethod.Tab);
var focused = fm?.GetFocusedElement();
XAML focus control:
<TextBox IsTabStop="False"/>
<TextBox TabIndex="2"/>
<TextBox AutoFocus="True"/>
Pseudoclasses for styling:
<Style Selector="TextBox:focus">
<Setter Property="BorderBrush" Value="Blue"/>
</Style>
<Style Selector="Button:focus-visible">
<Setter Property="BorderThickness" Value="2"/>
</Style>
Focus scope (contains Tab navigation):
<Panel FocusManager.IsFocusScope="True">
</Panel>
Gesture Recognizers
Add to any InputElement.GestureRecognizers:
<Border Background="LightBlue" Width="200" Height="200">
<Border.GestureRecognizers>
<TapGestureRecognizer Tapped="OnTapped"/>
<PinchGestureRecognizer PinchUpdated="OnPinchUpdated"/>
</Border.GestureRecognizers>
</Border>
private void OnPinchUpdated(object? sender, PinchEventArgs e)
{
_scale *= e.Scale;
myBorder.RenderTransform = new ScaleTransform(_scale, _scale);
}
| Recognizer | Events |
|---|
TapGestureRecognizer | Tapped |
DoubleTapGestureRecognizer | DoubleTapped |
PinchGestureRecognizer | PinchUpdated, PinchEnded |
PullGestureRecognizer | PullDelta, PullEnded |
ScrollGestureRecognizer | ScrollGesture, ScrollGestureEnded, ScrollGestureInertiaStarting |
Drag and Drop
Source (initiates drag):
private async void OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
var data = new DataObject();
data.Set(DataFormats.Text, "dragged text");
data.Set("custom/format", myObject);
var result = await DragDrop.DoDragDrop(e, data,
DragDropEffects.Copy | DragDropEffects.Move);
}
Target (receives drop):
<Border DragDrop.AllowDrop="True"
Drop="OnDrop"
DragOver="OnDragOver"
DragEnter="OnDragEnter"
DragLeave="OnDragLeave"/>
private void OnDragOver(object? sender, DragEventArgs e)
{
e.DragEffects = e.Data.Contains(DataFormats.Text)
? DragDropEffects.Copy
: DragDropEffects.None;
e.Handled = true;
}
private void OnDrop(object? sender, DragEventArgs e)
{
if (e.Data.Contains(DataFormats.Text))
{
var text = e.Data.GetText();
}
if (e.Data.Contains(DataFormats.Files))
{
var files = e.Data.GetFiles();
}
}
Built-in DataFormats: Text, Files, FileNames
Cursor
<Border Cursor="Hand"/>
<TextBox Cursor="IBeam"/>
<Border Cursor="Wait"/>
Available cursors: Default, Arrow, Cross, Hand, Help, IBeam, No, SizeAll, SizeNESW, SizeNS, SizeNWSE, SizeWE, TopSide, BottomSide, LeftSide, RightSide, Wait, None
Custom cursor from bitmap:
var cursor = new Cursor(myBitmap, new PixelPoint(hotspotX, hotspotY));
myControl.Cursor = cursor;
Scroll Events
myControl.PointerWheelChanged += (s, e) =>
{
var delta = e.Delta;
ScrollBy(-delta.Y * 30);
};
Common Mistakes
- Not setting
e.Handled = true on KeyDown — event bubbles to parent; TextBox will still consume keys
DragDrop.DoDragDrop must be called on pointer event — calling after await loses the pointer event context
Focus() before control is attached to visual tree — silently does nothing; call in OnAttachedToVisualTree or later
- Gesture recognizers vs. events —
Tapped/DoubleTapped fire on InputElement directly; TapGestureRecognizer needed only for custom gesture behavior
DragDrop.AllowDrop is an attached property — must be set on the drop target, not a parent
PointerPressed fires before Tapped — don't handle both if one is sufficient