AspNetCore.Docs/aspnetcore/blazor/event-handling.md

248 lines
11 KiB
Markdown
Raw Normal View History

2020-02-14 05:38:27 +08:00
---
title: ASP.NET Core Blazor event handling
author: guardrex
description: Learn about Blazor's event handling scenarios, including event argument types, event callbacks, and managing default browser events.
monikerRange: '>= aspnetcore-3.1'
ms.author: riande
ms.custom: mvc
ms.date: 02/12/2020
no-loc: [Blazor, SignalR]
uid: blazor/event-handling
---
# ASP.NET Core Blazor event handling
By [Luke Latham](https://github.com/guardrex) and [Daniel Roth](https://github.com/danroth27)
Razor components provide event handling features. For an HTML element attribute named `on{EVENT}` (for example, `onclick` and `onsubmit`) with a delegate-typed value, Razor components treats the attribute's value as an event handler. The attribute's name is always formatted [`@on{EVENT}`](xref:mvc/views/razor#onevent).
The following code calls the `UpdateHeading` method when the button is selected in the UI:
```razor
<button class="btn btn-primary" @onclick="UpdateHeading">
Update heading
</button>
@code {
private void UpdateHeading(MouseEventArgs e)
{
...
}
}
```
The following code calls the `CheckChanged` method when the check box is changed in the UI:
```razor
<input type="checkbox" class="form-check-input" @onchange="CheckChanged" />
@code {
private void CheckChanged()
{
...
}
}
```
Event handlers can also be asynchronous and return a <xref:System.Threading.Tasks.Task>. There's no need to manually call [StateHasChanged](xref:blazor/lifecycle#state-changes). Exceptions are logged when they occur.
In the following example, `UpdateHeading` is called asynchronously when the button is selected:
```razor
<button class="btn btn-primary" @onclick="UpdateHeading">
Update heading
</button>
@code {
private async Task UpdateHeading(MouseEventArgs e)
{
...
}
}
```
## Event argument types
2020-02-14 05:58:47 +08:00
For some events, event argument types are permitted. Specifying an event type in the method call is only necessary if the event type is used in the method.
2020-02-14 05:38:27 +08:00
Supported `EventArgs` are shown in the following table.
| Event | Class | DOM events and notes |
| ---------------- | -------------------- | -------------------- |
| Clipboard | `ClipboardEventArgs` | `oncut`, `oncopy`, `onpaste` |
| Drag | `DragEventArgs` | `ondrag`, `ondragstart`, `ondragenter`, `ondragleave`, `ondragover`, `ondrop`, `ondragend`<br><br>`DataTransfer` and `DataTransferItem` hold dragged item data. |
| Error | `ErrorEventArgs` | `onerror` |
| Event | `EventArgs` | *General*<br>`onactivate`, `onbeforeactivate`, `onbeforedeactivate`, `ondeactivate`, `onended`, `onfullscreenchange`, `onfullscreenerror`, `onloadeddata`, `onloadedmetadata`, `onpointerlockchange`, `onpointerlockerror`, `onreadystatechange`, `onscroll`<br><br>*Clipboard*<br>`onbeforecut`, `onbeforecopy`, `onbeforepaste`<br><br>*Input*<br>`oninvalid`, `onreset`, `onselect`, `onselectionchange`, `onselectstart`, `onsubmit`<br><br>*Media*<br>`oncanplay`, `oncanplaythrough`, `oncuechange`, `ondurationchange`, `onemptied`, `onpause`, `onplay`, `onplaying`, `onratechange`, `onseeked`, `onseeking`, `onstalled`, `onstop`, `onsuspend`, `ontimeupdate`, `onvolumechange`, `onwaiting` |
| Focus | `FocusEventArgs` | `onfocus`, `onblur`, `onfocusin`, `onfocusout`<br><br>Doesn't include support for `relatedTarget`. |
| Input | `ChangeEventArgs` | `onchange`, `oninput` |
| Keyboard | `KeyboardEventArgs` | `onkeydown`, `onkeypress`, `onkeyup` |
| Mouse | `MouseEventArgs` | `onclick`, `oncontextmenu`, `ondblclick`, `onmousedown`, `onmouseup`, `onmouseover`, `onmousemove`, `onmouseout` |
| Mouse pointer | `PointerEventArgs` | `onpointerdown`, `onpointerup`, `onpointercancel`, `onpointermove`, `onpointerover`, `onpointerout`, `onpointerenter`, `onpointerleave`, `ongotpointercapture`, `onlostpointercapture` |
| Mouse wheel | `WheelEventArgs` | `onwheel`, `onmousewheel` |
| Progress | `ProgressEventArgs` | `onabort`, `onload`, `onloadend`, `onloadstart`, `onprogress`, `ontimeout` |
| Touch | `TouchEventArgs` | `ontouchstart`, `ontouchend`, `ontouchmove`, `ontouchenter`, `ontouchleave`, `ontouchcancel`<br><br>`TouchPoint` represents a single contact point on a touch-sensitive device. |
For more information, see the following resources:
* [EventArgs classes in the ASP.NET Core reference source (dotnet/aspnetcore release/3.1 branch)](https://github.com/dotnet/aspnetcore/tree/release/3.1/src/Components/Web/src/Web).
* [MDN web docs: GlobalEventHandlers](https://developer.mozilla.org/docs/Web/API/GlobalEventHandlers) &ndash; Includes information on which HTML elements support each DOM event.
## Lambda expressions
[Lambda expressions](/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions) can also be used:
```razor
<button @onclick="@(e => Console.WriteLine("Hello, world!"))">Say hello</button>
```
It's often convenient to close over additional values, such as when iterating over a set of elements. The following example creates three buttons, each of which calls `UpdateHeading` passing an event argument (`MouseEventArgs`) and its button number (`buttonNumber`) when selected in the UI:
```razor
<h2>@_message</h2>
@for (var i = 1; i < 4; i++)
{
var buttonNumber = i;
<button class="btn btn-primary"
@onclick="@(e => UpdateHeading(e, buttonNumber))">
Button #@i
</button>
}
@code {
private string _message = "Select a button to learn its position.";
private void UpdateHeading(MouseEventArgs e, int buttonNumber)
{
_message = $"You selected Button #{buttonNumber} at " +
$"mouse position: {e.ClientX} X {e.ClientY}.";
}
}
```
> [!NOTE]
> Do **not** use the loop variable (`i`) in a `for` loop directly in a lambda expression. Otherwise the same variable is used by all lambda expressions causing `i`'s value to be the same in all lambdas. Always capture its value in a local variable (`buttonNumber` in the preceding example) and then use it.
## EventCallback
A common scenario with nested components is the desire to run a parent component's method when a child component event occurs&mdash;for example, when an `onclick` event occurs in the child. To expose events across components, use an `EventCallback`. A parent component can assign a callback method to a child component's `EventCallback`.
The `ChildComponent` in the sample app (*Components/ChildComponent.razor*) demonstrates how a button's `onclick` handler is set up to receive an `EventCallback` delegate from the sample's `ParentComponent`. The `EventCallback` is typed with `MouseEventArgs`, which is appropriate for an `onclick` event from a peripheral device:
[!code-razor[](common/samples/3.x/BlazorWebAssemblySample/Components/ChildComponent.razor?highlight=5-7,17-18)]
The `ParentComponent` sets the child's `EventCallback<T>` (`OnClickCallback`) to its `ShowMessage` method.
*Pages/ParentComponent.razor*:
```razor
@page "/ParentComponent"
<h1>Parent-child example</h1>
<ChildComponent Title="Panel Title from Parent"
OnClickCallback="@ShowMessage">
Content of the child component is supplied
by the parent component.
</ChildComponent>
<p><b>@_messageText</b></p>
@code {
private string _messageText;
private void ShowMessage(MouseEventArgs e)
{
_messageText = $"Blaze a new trail with Blazor! ({e.ScreenX}, {e.ScreenY})";
}
}
```
When the button is selected in the `ChildComponent`:
* The `ParentComponent`'s `ShowMessage` method is called. `_messageText` is updated and displayed in the `ParentComponent`.
* A call to [StateHasChanged](xref:blazor/lifecycle#state-changes) isn't required in the callback's method (`ShowMessage`). `StateHasChanged` is called automatically to rerender the `ParentComponent`, just as child events trigger component rerendering in event handlers that execute within the child.
`EventCallback` and `EventCallback<T>` permit asynchronous delegates. `EventCallback<T>` is strongly typed and requires a specific argument type. `EventCallback` is weakly typed and allows any argument type.
```razor
<ChildComponent
OnClickCallback="@(async () => { await Task.Yield(); _messageText = "Blaze It!"; })" />
```
Invoke an `EventCallback` or `EventCallback<T>` with `InvokeAsync` and await the <xref:System.Threading.Tasks.Task>:
```csharp
await callback.InvokeAsync(arg);
```
Use `EventCallback` and `EventCallback<T>` for event handling and binding component parameters.
Prefer the strongly typed `EventCallback<T>` over `EventCallback`. `EventCallback<T>` provides better error feedback to users of the component. Similar to other UI event handlers, specifying the event parameter is optional. Use `EventCallback` when there's no value passed to the callback.
## Prevent default actions
Use the [`@on{EVENT}:preventDefault`](xref:mvc/views/razor#oneventpreventdefault) directive attribute to prevent the default action for an event.
When a key is selected on an input device and the element focus is on a text box, a browser normally displays the key's character in the text box. In the following example, the default behavior is prevented by specifying the `@onkeypress:preventDefault` directive attribute. The counter increments, and the **+** key isn't captured into the `<input>` element's value:
```razor
<input value="@_count" @onkeypress="KeyHandler" @onkeypress:preventDefault />
@code {
private int _count = 0;
private void KeyHandler(KeyboardEventArgs e)
{
if (e.Key == "+")
{
_count++;
}
}
}
```
Specifying the `@on{EVENT}:preventDefault` attribute without a value is equivalent to `@on{EVENT}:preventDefault="true"`.
The value of the attribute can also be an expression. In the following example, `_shouldPreventDefault` is a `bool` field set to either `true` or `false`:
```razor
<input @onkeypress:preventDefault="_shouldPreventDefault" />
```
An event handler isn't required to prevent the default action. The event handler and prevent default action scenarios can be used independently.
## Stop event propagation
Use the [`@on{EVENT}:stopPropagation`](xref:mvc/views/razor#oneventstoppropagation) directive attribute to stop event propagation.
In the following example, selecting the check box prevents click events from the second child `<div>` from propagating to the parent `<div>`:
```razor
<label>
<input @bind="_stopPropagation" type="checkbox" />
Stop Propagation
</label>
<div @onclick="OnSelectParentDiv">
<h3>Parent div</h3>
<div @onclick="OnSelectChildDiv">
Child div that doesn't stop propagation when selected.
</div>
<div @onclick="OnSelectChildDiv" @onclick:stopPropagation="_stopPropagation">
Child div that stops propagation when selected.
</div>
</div>
@code {
private bool _stopPropagation = false;
private void OnSelectParentDiv() =>
Console.WriteLine($"The parent div was selected. {DateTime.Now}");
private void OnSelectChildDiv() =>
Console.WriteLine($"A child div was selected. {DateTime.Now}");
}
```