Skip to content

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:

razor
<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:

csharp
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:

PropertyEvent args
OnClickMouseEventArgs
OnMouseEnter / OnMouseLeaveMouseEventArgs
OnMouseDown / OnMouseUp / OnMouseMoveMouseEventArgs
OnPointerDown / OnPointerUp / OnPointerMove / OnPointerCancelPointerEventArgs
OnLongPressPointerEventArgs
OnFocus / OnBlurFocusEventArgs
OnChangeChangeEventArgs
OnInputInputEventArgs
OnScrollScrollEventArgs
OnKeyDownKeyboardEventArgs

MouseEventArgs exposes the pointer coordinates (X, Y), among other fields.

Capture and bubbling ​

Events propagate through the element tree like in a browser:

  1. Capture phase — from the root down toward the target.
  2. Target phase — the target element itself.
  3. 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:

csharp
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.

Released under the MIT License.