Events
Miko dispatches input with a DOM-style event model, including a capture phase and a bubbling phase. You can handle events either with Razor @on... directives or directly on DOM elements.
In Razor components
In .razor markup, attach handlers with the familiar @onclick (and similar) directives:
<button @onclick="Increment">Clicked @_count times</button>
@code {
private int _count;
private void Increment() => _count++;
}After an event handler runs, the component re-renders automatically — you do not need to call StateHasChanged() yourself for synchronous handlers.
Re-renders requested while an event is handled wait until the handler returns or reaches its first await. A child that reports two bound values in one handler, such as IsOpenChanged followed by OnDidDismiss, therefore re-renders its parent once, with both values in place.
The available directives (@onclick, @oninput, @onchange, @onpointermove, @onscroll, …) are listed in Elements. Event arguments are optional: a handler may take none, or the matching *EventArgs type.
Directives apply to elements. A component exposes its events as EventCallback parameters instead — <IonButton OnClick="Save">. An event directive written on a component binds to the component parameter of the same name, ignoring case (@onclick on IonButton sets OnClick); when the component has no such parameter, the handler listens on the element the component renders, so <IonChip @onclick="Select"> fires when the chip is tapped.
High-frequency events
Every @on… directive re-renders the component after each event. For events that fire every frame — @onscroll, @onpointermove, @onmousemove during a drag — that rebuilds the component's subtree each frame. Attach those handlers to the element in code instead (element.OnScroll = …, element.OnPointerMove = …, see below) and call StateHasChanged() only when the visible state actually changes.
On DOM elements
When building the DOM directly (see Using the Engine Directly), elements expose convenience handler properties and a generic AddEventListener.
Handlers use the MikoEventHandler<T> delegate, which takes a single event-args argument:
using Miko.Core.DomElements;
using Miko.Events;
var button = new ButtonElement { TextContent = "Click me" };
// Convenience property
button.OnClick = args =>
{
Console.WriteLine($"Clicked at ({args.X}, {args.Y})");
};
// Generic listener (capture/bubbling participating)
button.AddEventListener<MouseEventArgs>("click", args =>
{
// custom handling
});Convenience handler properties on Element include:
| Property | Event args |
|---|---|
OnClick | MouseEventArgs |
OnMouseEnter / OnMouseLeave | MouseEventArgs |
OnMouseDown / OnMouseUp / OnMouseMove | MouseEventArgs |
OnPointerDown / OnPointerUp / OnPointerMove / OnPointerCancel | PointerEventArgs |
OnLongPress | PointerEventArgs |
OnFocus / OnBlur | FocusEventArgs |
OnChange | ChangeEventArgs |
OnInput | InputEventArgs |
OnScroll | ScrollEventArgs |
OnKeyDown | KeyboardEventArgs |
MouseEventArgs exposes the pointer coordinates (X, Y), among other fields.
Capture and bubbling
Events propagate through the element tree like in a browser:
- Capture phase — from the root down toward the target.
- Target phase — the target element itself.
- Bubbling phase — from the target back up toward the root.
A listener registered on an ancestor therefore sees events that originate on its descendants as they bubble up.
Global key handling
For app-wide shortcuts, register a global key handler on the app builder. Return true to mark the key as handled:
builder.AddGlobalKeyHandler(key =>
{
if (key == MikoKey.Escape)
{
// handle escape
return true;
}
return false;
});Async handlers
Event handlers may be async. See Async & Lifecycle for how Miko awaits them and re-renders when the task completes.