Поделиться через


Обработка событий Blazor в ASP.NET Core

Примечание.

Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 9 этой статьи.

Предупреждение

Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 9 этой статьи.

Внимание

Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

В текущем выпуске см . версию .NET 9 этой статьи.

В этой статье приведены сведения о функциях обработки событий Blazor, включая типы аргументов событий, обратные вызовы событий и управление событиями браузера по умолчанию.

Делегирование обработчиков событий

Укажите обработчики событий делегата в разметке компонента Razor с помощью синтаксиса @on{DOM EVENT}="{DELEGATE}"Razor:

  • Заполнитель {DOM EVENT} — это событие DOM (например, click).
  • Заполнитель {DELEGATE} является обработчиком событий делегата C#.

Для обработки событий:

  • Обработчики событий делегата в s вызываются только в Blazor Web Appкомпонентах, использующих интерактивный режим отрисовки. В примерах в этой статье предполагается, что приложение принимает интерактивный режим отрисовки глобально в корневом компоненте приложения, как правило, компонент App . Дополнительные сведения см. в режимах отрисовки ASP.NET CoreBlazor.
  • Поддерживаются асинхронные обработчики событий делегата, возвращающие Task.
  • Обработчики событий делегата автоматически инициируют отрисовку пользовательского интерфейса, поэтому не требуется вручную вызывать метод StateHasChanged.
  • Исключения заносятся в журнал.
  • Поддерживаются асинхронные обработчики событий делегата, возвращающие Task.
  • Обработчики событий делегата автоматически инициируют отрисовку пользовательского интерфейса, поэтому не требуется вручную вызывать метод StateHasChanged.
  • Исключения заносятся в журнал.

Следующий код:

  • Вызывает метод UpdateHeading, когда в пользовательском интерфейсе выбрана кнопка.
  • Вызывает метод CheckChanged, когда в пользовательском интерфейсе установлен или снят флажок.

EventHandler1.razor:

@page "/event-handler-1"

<PageTitle>Event Handler 1</PageTitle>

<h1>Event Handler Example 1</h1>

<h2>@headingValue</h2>

<p>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

<p>
    <label>
        <input type="checkbox" @onchange="CheckChanged" />
        @checkedMessage
    </label>
</p>

@code {
    private string headingValue = "Initial heading";
    private string checkedMessage = "Not changed yet";

    private void UpdateHeading() => headingValue = $"New heading ({DateTime.Now})";

    private void CheckChanged() => checkedMessage = $"Last change {DateTime.Now}";
}

EventHandler1.razor:

@page "/event-handler-1"

<PageTitle>Event Handler 1</PageTitle>

<h1>Event Handler Example 1</h1>

<h2>@headingValue</h2>

<p>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

<p>
    <label>
        <input type="checkbox" @onchange="CheckChanged" />
        @checkedMessage
    </label>
</p>

@code {
    private string headingValue = "Initial heading";
    private string checkedMessage = "Not changed yet";

    private void UpdateHeading() => headingValue = $"New heading ({DateTime.Now})";

    private void CheckChanged() => checkedMessage = $"Last change {DateTime.Now}";
}

EventHandlerExample1.razor:

@page "/event-handler-1"

<h1>@headingValue</h1>

<p>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

<p>
    <label>
        <input type="checkbox" @onchange="CheckChanged" />
        @checkedMessage
    </label>
</p>

@code {
    private string headingValue = "Initial heading";
    private string checkedMessage = "Not changed yet";

    private void UpdateHeading()
    {
        headingValue = $"New heading ({DateTime.Now})";
    }

    private void CheckChanged()
    {
        checkedMessage = $"Last changed at {DateTime.Now}";
    }
}

EventHandlerExample1.razor:

@page "/event-handler-1"

<h1>@headingValue</h1>

<p>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

<p>
    <label>
        <input type="checkbox" @onchange="CheckChanged" />
        @checkedMessage
    </label>
</p>

@code {
    private string headingValue = "Initial heading";
    private string checkedMessage = "Not changed yet";

    private void UpdateHeading()
    {
        headingValue = $"New heading ({DateTime.Now})";
    }

    private void CheckChanged()
    {
        checkedMessage = $"Last changed at {DateTime.Now}";
    }
}

EventHandlerExample1.razor:

@page "/event-handler-1"

<h1>@headingValue</h1>

<p>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

<p>
    <label>
        <input type="checkbox" @onchange="CheckChanged" />
        @checkedMessage
    </label>
</p>

@code {
    private string headingValue = "Initial heading";
    private string checkedMessage = "Not changed yet";

    private void UpdateHeading()
    {
        headingValue = $"New heading ({DateTime.Now})";
    }

    private void CheckChanged()
    {
        checkedMessage = $"Last changed at {DateTime.Now}";
    }
}

EventHandlerExample1.razor:

@page "/event-handler-1"

<h1>@headingValue</h1>

<p>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

<p>
    <label>
        <input type="checkbox" @onchange="CheckChanged" />
        @checkedMessage
    </label>
</p>

@code {
    private string headingValue = "Initial heading";
    private string checkedMessage = "Not changed yet";

    private void UpdateHeading()
    {
        headingValue = $"New heading ({DateTime.Now})";
    }

    private void CheckChanged()
    {
        checkedMessage = $"Last changed at {DateTime.Now}";
    }
}

В следующем примере UpdateHeading:

  • Вызывается асинхронно при выборе кнопки.
  • Ожидает две секунды перед обновлением заголовка.

EventHandler2.razor:

@page "/event-handler-2"

<PageTitle>Event Handler 2</PageTitle>

<h1>Event Handler Example 2</h1>

<h2>@headingValue</h2>

<p>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

@code {
    private string headingValue = "Initial heading";

    private async Task UpdateHeading()
    {
        await Task.Delay(2000);

        headingValue = $"New heading ({DateTime.Now})";
    }
}

EventHandler2.razor:

@page "/event-handler-2"

<PageTitle>Event Handler 2</PageTitle>

<h1>Event Handler Example 2</h1>

<h2>@headingValue</h2>

<p>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

@code {
    private string headingValue = "Initial heading";

    private async Task UpdateHeading()
    {
        await Task.Delay(2000);

        headingValue = $"New heading ({DateTime.Now})";
    }
}

EventHandlerExample2.razor:

@page "/event-handler-2"

<h1>@headingValue</h1>

<p>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

@code {
    private string headingValue = "Initial heading";

    private async Task UpdateHeading()
    {
        await Task.Delay(2000);

        headingValue = $"New heading ({DateTime.Now})";
    }
}

EventHandlerExample2.razor:

@page "/event-handler-2"

<h1>@headingValue</h1>

<p>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

@code {
    private string headingValue = "Initial heading";

    private async Task UpdateHeading()
    {
        await Task.Delay(2000);

        headingValue = $"New heading ({DateTime.Now})";
    }
}

EventHandlerExample2.razor:

@page "/event-handler-2"

<h1>@headingValue</h1>

<p>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

@code {
    private string headingValue = "Initial heading";

    private async Task UpdateHeading()
    {
        await Task.Delay(2000);

        headingValue = $"New heading ({DateTime.Now})";
    }
}

EventHandlerExample2.razor:

@page "/event-handler-2"

<h1>@headingValue</h1>

<p>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

@code {
    private string headingValue = "Initial heading";

    private async Task UpdateHeading()
    {
        await Task.Delay(2000);

        headingValue = $"New heading ({DateTime.Now})";
    }
}

Встроенные аргументы событий

Для событий, которые поддерживают тип аргумента события, указание параметра события в определении метода события требуется только в том случае, если тип события используется в методе. В следующем примере MouseEventArgs используется в методе ReportPointerLocation для задания текста сообщения, которое информирует о координатах мыши, когда пользователь нажимает кнопку в пользовательском интерфейсе.

EventHandler3.razor:

@page "/event-handler-3"

<PageTitle>Event Handler 3</PageTitle>

<h1>Event Handler Example 3</h1>

@for (var i = 0; i < 4; i++)
{
    <p>
        <button @onclick="ReportPointerLocation">
            Where's my mouse pointer for this button?
        </button>
    </p>
}

<p>@mousePointerMessage</p>

@code {
    private string? mousePointerMessage;

    private void ReportPointerLocation(MouseEventArgs e) => 
        mousePointerMessage = $"Mouse coordinates: {e.ScreenX}:{e.ScreenY}";
}

EventHandler3.razor:

@page "/event-handler-3"

<PageTitle>Event Handler 3</PageTitle>

<h1>Event Handler Example 3</h1>

@for (var i = 0; i < 4; i++)
{
    <p>
        <button @onclick="ReportPointerLocation">
            Where's my mouse pointer for this button?
        </button>
    </p>
}

<p>@mousePointerMessage</p>

@code {
    private string? mousePointerMessage;

    private void ReportPointerLocation(MouseEventArgs e) => 
        mousePointerMessage = $"Mouse coordinates: {e.ScreenX}:{e.ScreenY}";
}

EventHandlerExample3.razor:

@page "/event-handler-example-3"

@for (var i = 0; i < 4; i++)
{
    <p>
        <button @onclick="ReportPointerLocation">
            Where's my mouse pointer for this button?
        </button>
    </p>
}

<p>@mousePointerMessage</p>

@code {
    private string? mousePointerMessage;

    private void ReportPointerLocation(MouseEventArgs e)
    {
        mousePointerMessage = $"Mouse coordinates: {e.ScreenX}:{e.ScreenY}";
    }
}

EventHandlerExample3.razor:

@page "/event-handler-example-3"

@for (var i = 0; i < 4; i++)
{
    <p>
        <button @onclick="ReportPointerLocation">
            Where's my mouse pointer for this button?
        </button>
    </p>
}

<p>@mousePointerMessage</p>

@code {
    private string? mousePointerMessage;

    private void ReportPointerLocation(MouseEventArgs e)
    {
        mousePointerMessage = $"Mouse coordinates: {e.ScreenX}:{e.ScreenY}";
    }
}

EventHandlerExample3.razor:

@page "/event-handler-example-3"

@for (var i = 0; i < 4; i++)
{
    <p>
        <button @onclick="ReportPointerLocation">
            Where's my mouse pointer for this button?
        </button>
    </p>
}

<p>@mousePointerMessage</p>

@code {
    private string mousePointerMessage;

    private void ReportPointerLocation(MouseEventArgs e)
    {
        mousePointerMessage = $"Mouse coordinates: {e.ScreenX}:{e.ScreenY}";
    }
}

EventHandlerExample3.razor:

@page "/event-handler-example-3"

@for (var i = 0; i < 4; i++)
{
    <p>
        <button @onclick="ReportPointerLocation">
            Where's my mouse pointer for this button?
        </button>
    </p>
}

<p>@mousePointerMessage</p>

@code {
    private string mousePointerMessage;

    private void ReportPointerLocation(MouseEventArgs e)
    {
        mousePointerMessage = $"Mouse coordinates: {e.ScreenX}:{e.ScreenY}";
    }
}

Поддерживаемые параметры EventArgs приведены в следующей таблице.

Мероприятие Класс Заметки DOM
Буфер обмена ClipboardEventArgs
Перетаскивание DragEventArgs DataTransfer и DataTransferItem содержат данные перетаскиваемого элемента.

Реализуйте перетаскивание в приложениях Blazor с помощью взаимодействия JSJS с API перетаскивания для HTML.
Ошибка ErrorEventArgs
Мероприятие EventArgs EventHandlers содержит атрибуты для настройки сопоставлений между именами событий и типами аргументов событий.
Фокус FocusEventArgs Не включает поддержку relatedTarget.
Входные данные ChangeEventArgs
Клавиатура KeyboardEventArgs
Мышь MouseEventArgs
Mouse pointer PointerEventArgs
Колесо мыши WheelEventArgs
Ход выполнения ProgressEventArgs
Сенсорный ввод TouchEventArgs TouchPoint представляет одну точку касания на устройстве с сенсорным вводом.

Дополнительные сведения см. на следующих ресурсах:

Аргументы пользовательских событий

Blazor поддерживает аргументы пользовательских событий, которые позволяют передавать произвольные данные в обработчики событий .NET с пользовательскими событиями.

Общая конфигурация

Для включения пользовательских событий с аргументами пользовательских событий обычно нужно выполнить следующие действия.

В JavaScript определите функцию для создания объекта аргумента пользовательского события из исходного события:

function eventArgsCreator(event) { 
  return {
    customProperty1: 'any value for property 1',
    customProperty2: event.srcElement.id
  };
}

Параметр event — это событие DOM (документация по MDN).

Зарегистрируйте настраиваемое событие с помощью предыдущего обработчика в инициализаторе JavaScript. Укажите соответствующее имя browserEventNameсобытия браузера, в котором пример, показанный в этом разделе, предназначен click для выбора кнопки в пользовательском интерфейсе.

wwwroot/{PACKAGE ID/ASSEMBLY NAME}.lib.module.js (заполнитель {PACKAGE ID/ASSEMBLY NAME} — это идентификатор пакета или имя сборки приложения):

Blazor Web AppДля :

export function afterWebStarted(blazor) {
  blazor.registerCustomEventType('customevent', {
    browserEventName: 'click',
    createEventArgs: eventArgsCreator
  });
}

Для приложения или Blazor WebAssembly приложенияBlazor Server:

export function afterStarted(blazor) {
  blazor.registerCustomEventType('customevent', {
    browserEventName: 'click',
    createEventArgs: eventArgsCreator
  });
}

Вызов registerCustomEventType выполняется в скрипте только один раз для каждого события.

Для вызова registerCustomEventTypeиспользуйте blazor параметр (нижний регистр b), предоставленный событием Blazor начала. Хотя регистрация допустима при использовании Blazor объекта (в верхнем регистре B), предпочтительный подход — использовать параметр.

Имя настраиваемого события в customevent предыдущем примере не должно совпадать с зарезервированным Blazor именем события. Зарезервированные имена можно найти в справочном источнике Blazor платформы (см. вызовы registerBuiltInEventType функции).

Примечание.

По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Определите класс для аргументов события:

namespace BlazorSample.CustomEvents;

public class CustomEventArgs : EventArgs
{
    public string? CustomProperty1 {get; set;}
    public string? CustomProperty2 {get; set;}
}

Подключите настраиваемое событие с аргументами события, добавив заметку [EventHandler] атрибута для настраиваемого события:

  • Чтобы компилятор найти [EventHandler] класс, он должен быть помещен в файл класса C# (.cs), что делает его обычным классом верхнего уровня.
  • Пометьте класс public.
  • Классу не требуются элементы.
  • Класс должен называться "EventHandlers" для обнаружения компилятором Razor .
  • Поместите класс под пространством имен, определенным для приложения.
  • Импортируйте пространство имен в Razor компонент (.razor), где используется событие.
using Microsoft.AspNetCore.Components;

namespace BlazorSample.CustomEvents;

[EventHandler("oncustomevent", typeof(CustomEventArgs),
    enableStopPropagation: true, enablePreventDefault: true)]
public static class EventHandlers
{
}

регистрировали обработчик событий в одном или нескольких элементах HTML; обращались к данным, переданным из JavaScript, в методе обработчика делегата:

@using BlazorSample.CustomEvents

<button id="buttonId" @oncustomevent="HandleCustomEvent">Handle</button>

@if (!string.IsNullOrEmpty(propVal1) && !string.IsNullOrEmpty(propVal2))
{
    <ul>
        <li>propVal1: @propVal1</li>
        <li>propVal2: @propVal2</li>
    </ul>
}

@code
{
    private string? propVal1;
    private string? propVal2;

    private void HandleCustomEvent(CustomEventArgs eventArgs)
    {
        propVal1 = eventArgs.CustomProperty1;
        propVal2 = eventArgs.CustomProperty2;
    }
}

Если IntelliSense не распознает атрибут @oncustomevent, убедитесь, что компонент или файл _Imports.razor содержит инструкцию @using для пространства имен, содержащего класс EventHandler.

При каждом запуске пользовательского события в модели DOM обработчик событий вызывается с данными, передаваемыми из JavaScript.

Если вы пытаетесь запустить пользовательское событие, необходимо включить bubbles, задав для него значение true. В противном случае событие не достигает обработчика Blazor для обработки в пользовательском [EventHandler] классе атрибутов C#. Дополнительные сведения см. в веб-документации MDN о восходящей маршрутизации событий.

Пример пользовательского события вставки в буфер обмена

В следующем примере показано получение пользовательского события вставки в буфер обмена, включающее время вставки и вставленный текст.

Объявите пользовательское имя (oncustompaste) для события и класса .NET (CustomPasteEventArgs) для хранения аргументов события для этого события:

CustomEvents.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample.CustomEvents;

[EventHandler("oncustompaste", typeof(CustomPasteEventArgs), 
    enableStopPropagation: true, enablePreventDefault: true)]
public static class EventHandlers
{
}

public class CustomPasteEventArgs : EventArgs
{
    public DateTime EventTimestamp { get; set; }
    public string? PastedData { get; set; }
}

Добавьте код JavaScript для предоставления данных для EventArgs подкласса с предыдущим обработчиком в инициализаторе JavaScript. В следующем примере выполняется обработка только текста, но для взаимодействия с пользователями, вставляющими другие типы данных, например изображения, можно использовать произвольные API JavaScript.

wwwroot/{PACKAGE ID/ASSEMBLY NAME}.lib.module.js:

Blazor Web AppДля :

export function afterWebStarted(blazor) {
  blazor.registerCustomEventType('custompaste', {
    browserEventName: 'paste',
    createEventArgs: event => {
      return {
        eventTimestamp: new Date(),
        pastedData: event.clipboardData.getData('text')
      };
    }
  });
}

Для приложения или Blazor WebAssembly приложенияBlazor Server:

export function afterStarted(blazor) {
  blazor.registerCustomEventType('custompaste', {
    browserEventName: 'paste',
    createEventArgs: event => {
      return {
        eventTimestamp: new Date(),
        pastedData: event.clipboardData.getData('text')
      };
    }
  });
}

В предыдущем примере {PACKAGE ID/ASSEMBLY NAME} заполнитель файла представляет идентификатор пакета или имя сборки приложения.

Примечание.

Для вызова registerCustomEventTypeиспользуйте blazor параметр (нижний регистр b), предоставленный событием Blazor начала. Хотя регистрация допустима при использовании Blazor объекта (в верхнем регистре B), предпочтительный подход — использовать параметр.

Приведенный выше код указывает браузеру, что при возникновении собственного события paste, нужно сделать следующее:

  • создать событие custompaste;
  • Укажите аргументы события с помощью пользовательской логики:

Соглашения об именах событий отличаются в .NET и JavaScript:

  • В .NET имена событий начинаются с префикса "on".
  • В JavaScript у имен событий нет префикса.

В компоненте Razor присоедините пользовательский обработчик к элементу.

CustomPasteArguments.razor:

@page "/custom-paste-arguments"
@using BlazorSample.CustomEvents

<label>
    Try pasting into the following text box:
    <input @oncustompaste="HandleCustomPaste" />
</label>

<p>
    @message
</p>

@code {
    private string? message;

    private void HandleCustomPaste(CustomPasteEventArgs eventArgs)
    {
        message = $"At {eventArgs.EventTimestamp.ToShortTimeString()}, " +
            $"you pasted: {eventArgs.PastedData}";
    }
}

Лямбда-выражения

Лямбда-выражения поддерживаются в качестве обработчика событий делегата.

EventHandler4.razor:

@page "/event-handler-4"

<PageTitle>Event Handler 4</PageTitle>

<h1>Event Handler Example 4</h1>

<h2>@heading</h2>

<p>
    <button @onclick="@(e => heading = "New heading!!!")">
        Update heading
    </button>
</p>

@code {
    private string heading = "Initial heading";
}

EventHandler4.razor:

@page "/event-handler-4"

<PageTitle>Event Handler 4</PageTitle>

<h1>Event Handler Example 4</h1>

<h2>@heading</h2>

<p>
    <button @onclick="@(e => heading = "New heading!!!")">
        Update heading
    </button>
</p>

@code {
    private string heading = "Initial heading";
}

EventHandlerExample4.razor:

@page "/event-handler-example-4"

<h1>@heading</h1>

<p>
    <button @onclick="@(e => heading = "New heading!!!")">
        Update heading
    </button>
</p>

@code {
    private string heading = "Initial heading";
}

EventHandlerExample4.razor:

@page "/event-handler-example-4"

<h1>@heading</h1>

<p>
    <button @onclick="@(e => heading = "New heading!!!")">
        Update heading
    </button>
</p>

@code {
    private string heading = "Initial heading";
}

EventHandlerExample4.razor:

@page "/event-handler-example-4"

<h1>@heading</h1>

<p>
    <button @onclick="@(e => heading = "New heading!!!")">
        Update heading
    </button>
</p>

@code {
    private string heading = "Initial heading";
}

EventHandlerExample4.razor:

@page "/event-handler-example-4"

<h1>@heading</h1>

<p>
    <button @onclick="@(e => heading = "New heading!!!")">
        Update heading
    </button>
</p>

@code {
    private string heading = "Initial heading";
}

Часто бывает удобно замыкать дополнительные значения с помощью параметров метода C#, например при переборе набора элементов. В следующем примере создаются три кнопки, каждая из которых вызывает UpdateHeading и передает следующие данные:

  • аргумент события (MouseEventArgs) в e;
  • номер кнопки в buttonNumber.

EventHandler5.razor:

@page "/event-handler-5"

<PageTitle>Event Handler 5</PageTitle>

<h1>Event Handler Example 5</h1>

<h2>@heading</h2>

@for (var i = 1; i < 4; i++)
{
    var buttonNumber = i;

    <p>
        <button @onclick="@(e => UpdateHeading(e, buttonNumber))">
            Button #@i
        </button>
    </p>
}

@code {
    private string heading = "Select a button to learn its position";

    private void UpdateHeading(MouseEventArgs e, int buttonNumber) => 
        heading = $"Selected #{buttonNumber} at {e.ClientX}:{e.ClientY}";
}

EventHandler5.razor:

@page "/event-handler-5"

<PageTitle>Event Handler 5</PageTitle>

<h1>Event Handler Example 5</h1>

<h2>@heading</h2>

@for (var i = 1; i < 4; i++)
{
    var buttonNumber = i;

    <p>
        <button @onclick="@(e => UpdateHeading(e, buttonNumber))">
            Button #@i
        </button>
    </p>
}

@code {
    private string heading = "Select a button to learn its position";

    private void UpdateHeading(MouseEventArgs e, int buttonNumber) => 
        heading = $"Selected #{buttonNumber} at {e.ClientX}:{e.ClientY}";
}

EventHandlerExample5.razor:

@page "/event-handler-example-5"

<h1>@heading</h1>

@for (var i = 1; i < 4; i++)
{
    var buttonNumber = i;

    <p>
        <button @onclick="@(e => UpdateHeading(e, buttonNumber))">
            Button #@i
        </button>
    </p>
}

@code {
    private string heading = "Select a button to learn its position";

    private void UpdateHeading(MouseEventArgs e, int buttonNumber)
    {
        heading = $"Selected #{buttonNumber} at {e.ClientX}:{e.ClientY}";
    }
}

EventHandlerExample5.razor:

@page "/event-handler-example-5"

<h1>@heading</h1>

@for (var i = 1; i < 4; i++)
{
    var buttonNumber = i;

    <p>
        <button @onclick="@(e => UpdateHeading(e, buttonNumber))">
            Button #@i
        </button>
    </p>
}

@code {
    private string heading = "Select a button to learn its position";

    private void UpdateHeading(MouseEventArgs e, int buttonNumber)
    {
        heading = $"Selected #{buttonNumber} at {e.ClientX}:{e.ClientY}";
    }
}

EventHandlerExample5.razor:

@page "/event-handler-example-5"

<h1>@heading</h1>

@for (var i = 1; i < 4; i++)
{
    var buttonNumber = i;

    <p>
        <button @onclick="@(e => UpdateHeading(e, buttonNumber))">
            Button #@i
        </button>
    </p>
}

@code {
    private string heading = "Select a button to learn its position";

    private void UpdateHeading(MouseEventArgs e, int buttonNumber)
    {
        heading = $"Selected #{buttonNumber} at {e.ClientX}:{e.ClientY}";
    }
}

EventHandlerExample5.razor:

@page "/event-handler-example-5"

<h1>@heading</h1>

@for (var i = 1; i < 4; i++)
{
    var buttonNumber = i;

    <p>
        <button @onclick="@(e => UpdateHeading(e, buttonNumber))">
            Button #@i
        </button>
    </p>
}

@code {
    private string heading = "Select a button to learn its position";

    private void UpdateHeading(MouseEventArgs e, int buttonNumber)
    {
        heading = $"Selected #{buttonNumber} at {e.ClientX}:{e.ClientY}";
    }
}

Создание большого количества делегатов событий в цикле может привести к ухудшению производительности отрисовки. Дополнительные рекомендации см. в статье Рекомендации по повышению производительности ASP.NET Core Blazor.

Избегайте использования переменной цикла непосредственно в лямбда-выражении, например i в предыдущем for примере цикла. В противном случае одна и та же переменная будет использоваться во всех лямбда-выражениях, в результате чего значение будет одинаковым во всех лямбда-выражениях. Захват значения переменной в локальной переменной. В предыдущем примере:

  • Переменной цикла i присваивается значение buttonNumber.
  • buttonNumber используется в лямбда-выражении.

Кроме того, используйте цикл foreach с диапазоном Enumerable.Range, который не затронула предыдущая проблема:

@foreach (var buttonNumber in Enumerable.Range(1, 3))
{
    <p>
        <button @onclick="@(e => UpdateHeading(e, buttonNumber))">
            Button #@buttonNumber
        </button>
    </p>
}

EventCallback

Распространенный сценарий с вложенными компонентами выполняет метод в родительском компоненте при возникновении события дочернего компонента. например, когда в дочернем элементе возникает событие onclick. Чтобы обеспечить доступ к событиям в компонентах, используйте EventCallback. Родительский компонент может назначить метод обратного вызова EventCallback дочернего компонента.

Следующий компонент Child демонстрирует, как обработчик onclick кнопки настроен на получение делегата EventCallback из ParentComponent образца. EventCallback вводится с MouseEventArgs, что подходит для события onclick от периферийного устройства.

Child.razor:

<p>
    <button @onclick="OnClickCallback">
        Trigger a Parent component method
    </button>
</p>

@code {
    [Parameter]
    public string? Title { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<p>
    <button @onclick="OnClickCallback">
        Trigger a Parent component method
    </button>
</p>

@code {
    [Parameter]
    public string? Title { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<p>
    <button @onclick="OnClickCallback">
        Trigger a Parent component method
    </button>
</p>

@code {
    [Parameter]
    public string? Title { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<p>
    <button @onclick="OnClickCallback">
        Trigger a Parent component method
    </button>
</p>

@code {
    [Parameter]
    public string? Title { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<p>
    <button @onclick="OnClickCallback">
        Trigger a Parent component method
    </button>
</p>

@code {
    [Parameter]
    public string Title { get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<p>
    <button @onclick="OnClickCallback">
        Trigger a Parent component method
    </button>
</p>

@code {
    [Parameter]
    public string Title { get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}

Родительский компонент задает EventCallback<TValue> дочерний (OnClickCallback) метод.ShowMessage

ParentChild.razor:

@page "/parent-child"

<PageTitle>Parent Child</PageTitle>

<h1>Parent Child Example</h1>

<Child Title="Panel Title from Parent" OnClickCallback="ShowMessage">
    Content of the child component is supplied by the parent component.
</Child>

<p>@message</p>

@code {
    private string? message;

    private void ShowMessage(MouseEventArgs e) => 
        message = $"Blaze a new trail with Blazor! ({e.ScreenX}:{e.ScreenY})";
}

ParentChild.razor:

@page "/parent-child"

<PageTitle>Parent Child</PageTitle>

<h1>Parent Child Example</h1>

<Child Title="Panel Title from Parent" OnClickCallback="ShowMessage">
    Content of the child component is supplied by the parent component.
</Child>

<p>@message</p>

@code {
    private string? message;

    private void ShowMessage(MouseEventArgs e) => 
        message = $"Blaze a new trail with Blazor! ({e.ScreenX}:{e.ScreenY})";
}

Parent.razor:

@page "/parent"

<h1>Parent-child example</h1>

<Child Title="Panel Title from Parent" OnClickCallback="ShowMessage">
    Content of the child component is supplied by the parent component.
</Child>

<p>@message</p>

@code {
    private string? message;

    private void ShowMessage(MouseEventArgs e)
    {
        message = $"Blaze a new trail with Blazor! ({e.ScreenX}:{e.ScreenY})";
    }
}

Parent.razor:

@page "/parent"

<h1>Parent-child example</h1>

<Child Title="Panel Title from Parent" OnClickCallback="ShowMessage">
    Content of the child component is supplied by the parent component.
</Child>

<p>@message</p>

@code {
    private string? message;

    private void ShowMessage(MouseEventArgs e)
    {
        message = $"Blaze a new trail with Blazor! ({e.ScreenX}:{e.ScreenY})";
    }
}

Parent.razor:

@page "/parent"

<h1>Parent-child example</h1>

<Child Title="Panel Title from Parent" OnClickCallback="ShowMessage">
    Content of the child component is supplied by the parent component.
</Child>

<p>@message</p>

@code {
    private string message;

    private void ShowMessage(MouseEventArgs e)
    {
        message = $"Blaze a new trail with Blazor! ({e.ScreenX}:{e.ScreenY})";
    }
}

Parent.razor:

@page "/parent"

<h1>Parent-child example</h1>

<Child Title="Panel Title from Parent" OnClickCallback="ShowMessage">
    Content of the child component is supplied by the parent component.
</Child>

<p>@message</p>

@code {
    private string message;

    private void ShowMessage(MouseEventArgs e)
    {
        message = $"Blaze a new trail with Blazor! ({e.ScreenX}:{e.ScreenY})";
    }
}

При выборе кнопки в ChildComponent:

  • Вызывается метод ShowMessage компонента Parent. message обновляется и отображается в компоненте Parent.
  • Вызов StateHasChanged не требуется в методе обратного вызова (ShowMessage). StateHasChanged вызывается автоматически для повторной отрисовки компонента Parent, так же как и дочерние события запускают повторную отрисовку компонента в обработчиках событий, которые выполняются в дочернем элементе. Дополнительные сведения см. в статье Отрисовка компонентов Razor ASP.NET Core.

Используйте EventCallback и EventCallback<TValue> для обработки событий и параметров компонента привязки.

Предпочтительнее использовать строго типизированный EventCallback<TValue> вместо EventCallback. EventCallback<TValue> предоставляет расширенные отзывы об ошибках, если используется недопустимый тип, направляющий пользователей компонента к правильной реализации. Как и в случае с другими обработчиками событий пользовательского интерфейса, указание параметра события является необязательным. Используйте EventCallback, если в обратный вызов не было передано значение.

EventCallback и EventCallback<TValue> разрешают выполнять асинхронные делегаты. Структура EventCallback слабо типизирована и позволяет передавать любой аргумент типа в InvokeAsync(Object). Структура EventCallback<TValue> сильно типизирована и требует передачи аргумента T в InvokeAsync(T), который можно назначить TValue.

Вызов EventCallback или EventCallback<TValue> с InvokeAsync и ожидание Task:

await OnClickCallback.InvokeAsync({ARGUMENT});

В предыдущем примере {ARGUMENT} заполнитель является необязательным аргументом.

В следующем примере родительского дочернего элемента демонстрируется методика.

Child2.razor:

<h3>Child2 Component</h3>

<button @onclick="TriggerEvent">Click Me</button>

@code {
    [Parameter]
    public EventCallback<string> OnClickCallback { get; set; }

    private async Task TriggerEvent()
    {
        await OnClickCallback.InvokeAsync("Blaze It!");
    }
}

ParentChild2.razor:

@page "/parent-child-2"

<PageTitle>Parent Child 2</PageTitle>

<h1>Parent Child 2 Example</h1>

<div>
    <Child2 OnClickCallback="(value) => { message1 = value; }" />
    @message1
</div>

<div>
    <Child2 OnClickCallback=
        "async (value) => { await Task.Delay(2000); message2 = value; }" /> 
    @message2
</div>

@code {
    private string message1 = string.Empty;
    private string message2 = string.Empty;
}

Второе вхождение Child2 компонента демонстрирует асинхронный обратный вызов, а новое message2 значение назначается и отрисовывается с задержкой в два секунды.

Запрет действий по умолчанию

@on{DOM EVENT}:preventDefault Используйте атрибут директивы, чтобы предотвратить действие по умолчанию для события, где {DOM EVENT} заполнитель является событием DOM.

Если на устройстве ввода выбран ключ, а фокус находится на текстовом поле, то в браузере в текстовом поле обычно отображается символ ключа. В следующем примере поведение по умолчанию запрещено путем указания атрибута директивы @onkeydown:preventDefault. Когда фокус находится на элементе <input>, счетчик увеличивается при использовании сочетания клавиш SHIFT++. Символ + не назначается значению элемента <input>. Дополнительные сведения о keydown см. в разделе о событии MDN Web Docs: Document: keydown.

EventHandler6.razor:

@page "/event-handler-6"

<PageTitle>Event Handler 6</PageTitle>

<h1>Event Handler Example 6</h1>

<p>For this example, give the <code><input></code> focus.</p>

<p>
    <label>
        Count of '+' key presses: 
        <input value="@count" @onkeydown="KeyHandler" @onkeydown:preventDefault />
    </label>
</p>

@code {
    private int count = 0;

    private void KeyHandler(KeyboardEventArgs e)
    {
        if (e.Key == "+")
        {
            count++;
        }
    }
}

EventHandler6.razor:

@page "/event-handler-6"

<PageTitle>Event Handler 6</PageTitle>

<h1>Event Handler Example 6</h1>

<p>For this example, give the <code><input></code> focus.</p>

<p>
    <label>
        Count of '+' key presses: 
        <input value="@count" @onkeydown="KeyHandler" @onkeydown:preventDefault />
    </label>
</p>

@code {
    private int count = 0;

    private void KeyHandler(KeyboardEventArgs e)
    {
        if (e.Key == "+")
        {
            count++;
        }
    }
}

EventHandlerExample6.razor:

@page "/event-handler-example-6"

<p>
    <input value="@count" @onkeydown="KeyHandler" @onkeydown:preventDefault />
</p>

@code {
    private int count = 0;

    private void KeyHandler(KeyboardEventArgs e)
    {
        if (e.Key == "+")
        {
            count++;
        }
    }
}

EventHandlerExample6.razor:

@page "/event-handler-example-6"

<p>
    <input value="@count" @onkeydown="KeyHandler" @onkeydown:preventDefault />
</p>

@code {
    private int count = 0;

    private void KeyHandler(KeyboardEventArgs e)
    {
        if (e.Key == "+")
        {
            count++;
        }
    }
}

EventHandlerExample6.razor:

@page "/event-handler-example-6"

<p>
    <input value="@count" @onkeydown="KeyHandler" @onkeydown:preventDefault />
</p>

@code {
    private int count = 0;

    private void KeyHandler(KeyboardEventArgs e)
    {
        if (e.Key == "+")
        {
            count++;
        }
    }
}

EventHandlerExample6.razor:

@page "/event-handler-example-6"

<p>
    <input value="@count" @onkeydown="KeyHandler" @onkeydown:preventDefault />
</p>

@code {
    private int count = 0;

    private void KeyHandler(KeyboardEventArgs e)
    {
        if (e.Key == "+")
        {
            count++;
        }
    }
}

Указание атрибута @on{DOM EVENT}:preventDefault без значения эквивалентно @on{DOM EVENT}:preventDefault="true".

Выражение также является допустимым значением атрибута. В следующем примере shouldPreventDefault является полем bool, для которого задано значение true или false:

<input @onkeydown:preventDefault="shouldPreventDefault" />

...

@code {
    private bool shouldPreventDefault = true;
}

Остановка распространения событий

Используйте атрибут директивы @on{DOM EVENT}:stopPropagation, чтобы остановить распространение событий в области Blazor. {DOM EVENT} — это заполнитель для события DOM.

Результат атрибута директивы stopPropagation ограничен областью Blazor и не распространяется на модель DOM HTML. События должны распространиться в корневой элемент DOM HTML, прежде чем Blazor сможет предпринимать действия с ними. Для механизма предотвращения распространения событий модели DOM HTML используйте следующий подход:

В приведенном ниже примере установка флажка предотвращает распространение событий щелчка мышью из второго дочернего элемента <div> в родительский элемент <div>. Так как распространенные события щелчка мышью обычно вызывают метод OnSelectParentDiv, выбор второго дочернего элемента <div> приводит к отображению родительского сообщения <div> в случае, если флажок не установлен.

EventHandler7.razor:

@page "/event-handler-7"

<PageTitle>Event Handler 7</PageTitle>

<h1>Event Handler Example 7</h1>

<div>
    <b>stopPropagation</b>: @stopPropagation
</div>

<div>
    <button @onclick="StopPropagation">
        Stop Propagation (stopPropagation = true)
    </button>
    <button @onclick="EnablePropagation">
        Enable Propagation (stopPropagation = false)
    </button>
</div>

<div class="m-1 p-1 border border-primary" @onclick="OnSelectParentDiv">
    <h3>Parent div</h3>

    <div class="m-1 p-1 border" @onclick="OnSelectChildDiv">
        Child div that never stops propagation to the parent div when 
        selected.
    </div>

    <div class="m-1 p-1 border" @onclick="OnSelectChildDiv" 
            @onclick:stopPropagation="stopPropagation">
        Child div that stops propagation when selected if 
        <b>stopPropagation</b> is <b>true</b>.
    </div>
</div>

<p>
    @message
</p>

@code {
    private bool stopPropagation = false;
    private string? message;

    private void StopPropagation() => stopPropagation = true;

    private void EnablePropagation() => stopPropagation = false;

    private void OnSelectParentDiv() => 
        message = $"The parent div was selected. {DateTime.Now}";

    private void OnSelectChildDiv() => 
        message = $"The child div was selected. {DateTime.Now}";
}

EventHandler7.razor:

@page "/event-handler-7"

<PageTitle>Event Handler 7</PageTitle>

<h1>Event Handler Example 7</h1>

<div>
    <b>stopPropagation</b>: @stopPropagation
</div>

<div>
    <button @onclick="StopPropagation">
        Stop Propagation (stopPropagation = true)
    </button>
    <button @onclick="EnablePropagation">
        Enable Propagation (stopPropagation = false)
    </button>
</div>

<div class="m-1 p-1 border border-primary" @onclick="OnSelectParentDiv">
    <h3>Parent div</h3>

    <div class="m-1 p-1 border" @onclick="OnSelectChildDiv">
        Child div that never stops propagation to the parent div when 
        selected.
    </div>

    <div class="m-1 p-1 border" @onclick="OnSelectChildDiv" 
            @onclick:stopPropagation="stopPropagation">
        Child div that stops propagation when selected if 
        <b>stopPropagation</b> is <b>true</b>.
    </div>
</div>

<p>
    @message
</p>

@code {
    private bool stopPropagation = false;
    private string? message;

    private void StopPropagation() => stopPropagation = true;

    private void EnablePropagation() => stopPropagation = false;

    private void OnSelectParentDiv() => 
        message = $"The parent div was selected. {DateTime.Now}";

    private void OnSelectChildDiv() => 
        message = $"The child div was selected. {DateTime.Now}";
}

EventHandlerExample7.razor:

@page "/event-handler-example-7"

<div>
    <b>stopPropagation</b>: @stopPropagation
</div>

<div>
    <button @onclick="StopPropagation">
        Stop Propagation (stopPropagation = true)
    </button>
    <button @onclick="EnablePropagation">
        Enable Propagation (stopPropagation = false)
    </button>
</div>

<div class="m-1 p-1 border border-primary" @onclick="OnSelectParentDiv">
    <h3>Parent div</h3>

    <div class="m-1 p-1 border" @onclick="OnSelectChildDiv">
        Child div that never stops propagation to the parent div when 
        selected.
    </div>

    <div class="m-1 p-1 border" @onclick="OnSelectChildDiv" 
            @onclick:stopPropagation="stopPropagation">
        Child div that stops propagation when selected if 
        <b>stopPropagation</b> is <b>true</b>.
    </div>
</div>

<p>
    @message
</p>

@code {
    private bool stopPropagation = false;
    private string? message;

    private void StopPropagation() => stopPropagation = true;

    private void EnablePropagation() => stopPropagation = false;

    private void OnSelectParentDiv() =>
        message = $"The parent div was selected. {DateTime.Now}";

    private void OnSelectChildDiv() =>
        message = $"The child div was selected. {DateTime.Now}";
}

EventHandlerExample7.razor:

@page "/event-handler-example-7"

<div>
    <b>stopPropagation</b>: @stopPropagation
</div>

<div>
    <button @onclick="StopPropagation">
        Stop Propagation (stopPropagation = true)
    </button>
    <button @onclick="EnablePropagation">
        Enable Propagation (stopPropagation = false)
    </button>
</div>

<div class="m-1 p-1 border border-primary" @onclick="OnSelectParentDiv">
    <h3>Parent div</h3>

    <div class="m-1 p-1 border" @onclick="OnSelectChildDiv">
        Child div that never stops propagation to the parent div when 
        selected.
    </div>

    <div class="m-1 p-1 border" @onclick="OnSelectChildDiv" 
            @onclick:stopPropagation="stopPropagation">
        Child div that stops propagation when selected if 
        <b>stopPropagation</b> is <b>true</b>.
    </div>
</div>

<p>
    @message
</p>

@code {
    private bool stopPropagation = false;
    private string? message;

    private void StopPropagation() => stopPropagation = true;

    private void EnablePropagation() => stopPropagation = false;

    private void OnSelectParentDiv() =>
        message = $"The parent div was selected. {DateTime.Now}";

    private void OnSelectChildDiv() =>
        message = $"The child div was selected. {DateTime.Now}";
}

EventHandlerExample7.razor:

@page "/event-handler-example-7"

<div>
    <b>stopPropagation</b>: @stopPropagation
</div>

<div>
    <button @onclick="StopPropagation">
        Stop Propagation (stopPropagation = true)
    </button>
    <button @onclick="EnablePropagation">
        Enable Propagation (stopPropagation = false)
    </button>
</div>

<div class="m-1 p-1 border border-primary" @onclick="OnSelectParentDiv">
    <h3>Parent div</h3>

    <div class="m-1 p-1 border" @onclick="OnSelectChildDiv">
        Child div that never stops propagation to the parent div when 
        selected.
    </div>

    <div class="m-1 p-1 border" @onclick="OnSelectChildDiv" 
            @onclick:stopPropagation="stopPropagation">
        Child div that stops propagation when selected if 
        <b>stopPropagation</b> is <b>true</b>.
    </div>
</div>

<p>
    @message
</p>

@code {
    private bool stopPropagation = false;
    private string message;

    private void StopPropagation() => stopPropagation = true;

    private void EnablePropagation() => stopPropagation = false;

    private void OnSelectParentDiv() =>
        message = $"The parent div was selected. {DateTime.Now}";

    private void OnSelectChildDiv() =>
        message = $"The child div was selected. {DateTime.Now}";
}

EventHandlerExample7.razor:

@page "/event-handler-example-7"

<div>
    <b>stopPropagation</b>: @stopPropagation
</div>

<div>
    <button @onclick="StopPropagation">
        Stop Propagation (stopPropagation = true)
    </button>
    <button @onclick="EnablePropagation">
        Enable Propagation (stopPropagation = false)
    </button>
</div>

<div class="m-1 p-1 border border-primary" @onclick="OnSelectParentDiv">
    <h3>Parent div</h3>

    <div class="m-1 p-1 border" @onclick="OnSelectChildDiv">
        Child div that never stops propagation to the parent div when 
        selected.
    </div>

    <div class="m-1 p-1 border" @onclick="OnSelectChildDiv" 
            @onclick:stopPropagation="stopPropagation">
        Child div that stops propagation when selected if 
        <b>stopPropagation</b> is <b>true</b>.
    </div>
</div>

<p>
    @message
</p>

@code {
    private bool stopPropagation = false;
    private string message;

    private void StopPropagation() => stopPropagation = true;

    private void EnablePropagation() => stopPropagation = false;

    private void OnSelectParentDiv() =>
        message = $"The parent div was selected. {DateTime.Now}";

    private void OnSelectChildDiv() =>
        message = $"The child div was selected. {DateTime.Now}";
}

Установка фокуса на элемент

Вызовите FocusAsync для ссылки на элемент, чтобы установить фокус на элемент в коде. В следующем примере нажмите кнопку, чтобы установить фокус на элемент <input>.

EventHandler8.razor:

@page "/event-handler-8"

<PageTitle>Event Handler 8</PageTitle>

<h1>Event Handler Example 8</h1>

<p>Select the button to give the <code><input></code> focus.</p>

<p>
    <label>
        Input: 
        <input @ref="exampleInput" />
    </label>
    
</p>

<button @onclick="ChangeFocus">
    Focus the Input Element
</button>

@code {
    private ElementReference exampleInput;

    private async Task ChangeFocus()
    {
        await exampleInput.FocusAsync();
    }
}

EventHandler8.razor:

@page "/event-handler-8"

<PageTitle>Event Handler 8</PageTitle>

<h1>Event Handler Example 8</h1>

<p>Select the button to give the <code><input></code> focus.</p>

<p>
    <label>
        Input: 
        <input @ref="exampleInput" />
    </label>
    
</p>

<button @onclick="ChangeFocus">
    Focus the Input Element
</button>

@code {
    private ElementReference exampleInput;

    private async Task ChangeFocus()
    {
        await exampleInput.FocusAsync();
    }
}

EventHandlerExample8.razor:

@page "/event-handler-example-8"

<p>
    <input @ref="exampleInput" />
</p>

<button @onclick="ChangeFocus">
    Focus the Input Element
</button>

@code {
    private ElementReference exampleInput;

    private async Task ChangeFocus()
    {
        await exampleInput.FocusAsync();
    }
}

EventHandlerExample8.razor:

@page "/event-handler-example-8"

<p>
    <input @ref="exampleInput" />
</p>

<button @onclick="ChangeFocus">
    Focus the Input Element
</button>

@code {
    private ElementReference exampleInput;

    private async Task ChangeFocus()
    {
        await exampleInput.FocusAsync();
    }
}