ASP.NET Core Blazor のイベント処理

注意

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

警告

このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、「.NET および .NET Core サポート ポリシー」を参照してください。 現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

重要

この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。

現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

この記事では、イベント引数の型、イベントのコールバック、既定のブラウザー イベントの管理など、Blazor のイベント処理機能について説明します。

デリゲート イベント ハンドラー

次の @on{DOM EVENT}="{DELEGATE}"Razor 構文を使用して、Razor コンポーネント マークアップでデリゲート イベント ハンドラーを指定します。

  • {DOM EVENT} プレースホルダーは、DOM イベントです (たとえば、click)。
  • {DELEGATE} プレースホルダーは、C# デリゲート イベント ハンドラーです。

イベント処理の場合:

  • Blazor Web App デリゲート イベント ハンドラーは、対話型レンダリング モードを採用するコンポーネントでのみ呼び出されます。 この記事全体の例では、アプリがアプリのルート コンポーネント (通常は App コンポーネント) で対話型レンダリング モードをグローバルに採用することを前提としています。 詳細については、「ASP.NET Core Blazor レンダリング モード」を参照してください。
  • Task を返す非同期デリゲート イベント ハンドラーがサポートされています。
  • デリゲート イベント ハンドラーによって UI レンダリングが自動的にトリガーされるため、StateHasChanged を手動で呼び出す必要はありません。
  • 例外がログされます。
  • Task を返す非同期デリゲート イベント ハンドラーがサポートされています。
  • デリゲート イベント ハンドラーによって UI レンダリングが自動的にトリガーされるため、StateHasChanged を手動で呼び出す必要はありません。
  • 例外がログされます。

コード例を次に示します。

  • UI 内でボタンが選択されたときに UpdateHeading メソッドを呼び出します。
  • UI 内でチェックボックスが変更されたときに 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}";
}

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 は、以下の例では次のようになります。

  • ボタンが選択されると、非同期に呼び出されます。
  • 2 秒間待機してから、見出しを更新します。

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})";
    }
}

組み込みのイベント引数

イベント引数の型をサポートするイベントの場合、イベント メソッド定義でイベント パラメーターを指定する必要があるのは、イベントの型がメソッドで使用されている場合のみです。 次の例では、ReportPointerLocation メソッドで MouseEventArgs が使用されています。これにより、ユーザーが UI 内でボタンを選択すると、マウスの座標を報告するメッセージ テキストが設定されます。

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 を次の表に示します。

event クラス DOM に関する注意事項
クリップボードのトピック ClipboardEventArgs
ドラッグ DragEventArgs DataTransfer および DataTransferItem では、ドラッグされた項目データを保持します。

HTML ドラッグ アンド ドロップ API と共に JS 相互運用を使用し、Blazor アプリにドラッグ アンド ドロップを実装します。
エラー ErrorEventArgs
event EventArgs EventHandlers は、イベント名とイベント引数の型の間のマッピングを構成する属性を保持します。
フォーカス FocusEventArgs relatedTarget のサポートは含まれません。
入力 ChangeEventArgs
キーボード KeyboardEventArgs
マウス MouseEventArgs
マウス ポインター PointerEventArgs
マウス ホイール WheelEventArgs
進捗状況 ProgressEventArgs
Touch TouchEventArgs TouchPoint は、タッチを検知するデバイス上の単一接触点を表します。

詳細については、次のリソースを参照してください。

  • ASP.NET Core 参照ソース内の EventArgs クラス (dotnet/aspnetcore main ブランチ)

    注意

    通常、.NET 参照ソースへのドキュメント リンクを使用すると、リポジトリの既定のブランチが読み込まれます。このブランチは、.NET の次回リリースに向けて行われている現在の開発を表します。 特定のリリースのタグを選択するには、[Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使います。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法」 (dotnet/AspNetCore.Docs #26205) を参照してください。

  • EventHandlers は、イベント名とイベント引数の型の間のマッピングを構成する属性を保持します。

カスタム イベント引数

Blazor ではカスタム イベント引数がサポートされています。このため、カスタム イベントを使用して任意のデータを .NET イベント ハンドラーに渡すことができます。

全般構成

カスタム イベント引数を使用したカスタム イベントは、一般に次の手順によって有効にされます。

JavaScript では、ソース イベントからカスタム イベント引数オブジェクトをビルドするための関数を定義します。

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

event パラメーターは、DOM イベント (MDN ドキュメント)です。

カスタム イベントを、前述の JavaScript 初期化子のハンドラーに登録します。 browserEventName に適切なブラウザ イベント名を指定してください。このセクションで示す例では、UI でボタンを選択するための click です。

wwwroot/{PACKAGE ID/ASSEMBLY NAME}.lib.module.js ({PACKAGE ID/ASSEMBLY NAME} プレースホルダーは、アプリのパッケージ ID またはアプリの名前です)。

Blazor Web App の場合:

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

Blazor Server または Blazor WebAssembly アプリの場合:

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

registerCustomEventType の呼び出しは、1 つのイベントにつき 1 回だけスクリプト内で実行されます。

registerCustomEventType の呼び出しには、Blazor 開始イベントによって提供される blazor パラメーター (b は小文字) を使用します。 Blazor オブジェクト (B は大文字) を使用しても登録は有効ですが、パラメーターを使用することをお勧めします。

前の例にあるカスタム イベント名 customevent は、予約された Blazor イベント名と一致しないようにする必要があります。 予約された名前は、Blazor フレームワーク参照ソース (registerBuiltInEventType 関数の呼び出しを参照) にあります。

Note

通常、.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 とマークを付けます。
  • このクラスにメンバーは必要ありません。
  • このクラスは、Razor コンパイラで検出できるように "EventHandlers" という名前にする "必要があります"。
  • クラスをアプリに固有の名前空間の下に配置します。
  • イベントが使用される Razor コンポーネント (.razor) に名前空間をインポートします。
using Microsoft.AspNetCore.Components;

namespace BlazorSample.CustomEvents;

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

1 つまたは複数の 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;
    }
}

@oncustomevent 属性が IntelliSense によって認識されない場合は、コンポーネントまたは _Imports.razor ファイルに、EventHandler クラスを含む名前空間の @using ステートメントが必ず含まれるようにしてください。

DOM 上でカスタム イベントが発生するたびに、JavaScript から渡されたデータを使用してイベント ハンドラーが呼び出されます。

カスタム イベントを発生させようとしている場合は、bubbles を有効にするために、その値を true に設定する必要があります。 それを行わない場合は、イベントが発生しても、処理用の Blazor ハンドラーが C# カスタム [EventHandler] 属性クラスに到達することはありません。 詳細については、「MDN Web Docs: イベントのバブリング」を参照してください。

カスタム クリップボードの貼り付けイベントの例

次の例では、貼り付けの時刻とユーザーが貼り付けたテキストを含む、カスタム クリップボードの貼り付けイベントを受け取ります。

イベントのカスタム名 (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 コードを追加します。 次の例では、テキストの貼り付けのみを処理しますが、任意の JavaScript API を使用して、ユーザーによる他の種類のデータ (画像など) の貼り付けを処理することもできます。

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 Server または Blazor WebAssembly アプリの場合:

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

前述の例では、ファイル名の {PACKAGE ID/ASSEMBLY NAME} プレースホルダーは、アプリのパッケージ ID またはアセンブリ名を表します。

Note

registerCustomEventType の呼び出しには、Blazor 開始イベントによって提供される blazor パラメーター (b は小文字) を使用します。 Blazor オブジェクト (B は大文字) を使用しても登録は有効ですが、パラメーターを使用することをお勧めします。

上記のコードでは、ネイティブの paste イベントが発生したときに、次のことをブラウザーに指示します。

  • custompaste イベントを発生させる。
  • 次に示したカスタムロジックを使用して、イベント引数データを指定する。
    • eventTimestamp の場合は、新しい日付を作成します。
    • pastedData の場合は、クリップボード データをテキストとして取得します。 詳細については、「MDN Web Docs: ClipboardEvent.clipboardData」を参照してください。

イベント名の規則は、.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";
}

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# メソッド パラメーターを使用して追加の値に集中すると便利な場合がよくあります。 次の例では、3 つのボタンを作成します。それぞれを押すと、UpdateHeading が呼び出され、次のデータが渡されます。

  • e に対してイベント引数 (MouseEventArgs)。
  • 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}";
}

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 のパフォーマンスに関するベスト プラクティス」をご覧ください。

前の for ループの例の i など、ラムダ式内で直接ループ変数を使用することは避けてください。 そうしないと、すべてのラムダ式で同じ変数が使用され、すべてのラムダで同じ値が使用されることになります。 変数の値をローカル変数に取得してください。 前の例の場合:

  • ループ変数 ibuttonNumber に割り当てられます。
  • 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 ハンドラーがどのように、サンプルの ParentComponent から EventCallback デリゲートを受け取るように設定されているかを示しています。 EventCallbackMouseEventArgs によって型指定されます。これは、周辺機器の 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; }
}

Parent コンポーネントでは、子の 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})";
}

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 でボタンが選択されると:

  • Parent コンポーネントの ShowMessage メソッドが呼び出されます。 message が更新されて、Parent コンポーネントに表示されます。
  • コールバックのメソッド (ShowMessage) 内に、StateHasChanged の呼び出しは必要ありません。 StateHasChanged は、子イベントが子の中で実行されるイベント ハンドラーでコンポーネントのレンダリングをトリガーするのと同様に、Parent コンポーネントを再レンダリングするために自動的に呼び出されます。 詳しくは、「ASP.NET Core Razor コンポーネントのレンダリング」をご覧ください。

イベント処理とバインド コンポーネントのパラメーターには、EventCallbackEventCallback<TValue> を使用します。

厳密に型指定された EventCallback<TValue>EventCallback よりも優先します。 EventCallback<TValue> では、不適切な型が使用されている場合に強化されたエラー フィードバックが利用でき、コンポーネントのユーザーを正しい実装に向かいガイドします。 他の UI イベント ハンドラーと同様に、このイベント パラメーターの指定は省略可能です。 コールバックに渡される値がない場合は、EventCallback を使用します。

EventCallbackEventCallback<TValue> では非同期デリゲートを使用できます。 EventCallback は弱く型指定されており、InvokeAsync(Object) では任意の型の引数を渡すことができます。 EventCallback<TValue> は厳密に型指定されており、InvokeAsync(T) では TValue に代入可能な T 引数を渡す必要があります。

InvokeAsync を使用して EventCallback または EventCallback<TValue> を呼び出して、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 コンポーネントの 2 番目の出現は非同期コールバックを示し、新しい message2 値が割り当てられ、2 秒の遅延でレンダリングされます。

既定のアクションを止める

イベントの既定のアクションを防止するには、@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++;
        }
    }
}

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" と同じことになります。

式は、許可されている属性値でもあります。 次の例では、shouldPreventDefaulttrue または false のいずれかに設定される bool フィールドです。

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

...

@code {
    private bool shouldPreventDefault = true;
}

イベント伝達を停止する

Blazor スクリプト内でイベント伝達を停止するには、@on{DOM EVENT}:stopPropagation ディレクティブ属性を使用します。 {DOM EVENT}DOM イベント のプレースホルダーです。

stopPropagation ディレクティブ属性の効果は Blazor スコープに限定され、HTML DOM まで拡大されることはありません。 イベントは、それに基づいて Blazor が動作する前に HTML DOM ルートに反映される必要があります。 HTML DOM イベントの伝達を防止するメカニズムについては、次の方法を検討してください。

次の例では、チェックボックスをオンにすると、2 番目の子 <div> からのクリック イベントが親の <div> に伝達されなくなります。 クリック イベントが伝達されると、通常、OnSelectParentDiv メソッドが起動されるので、2 番目の子 <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}";
}

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();
    }
}

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();
    }
}