ASP.NET Core Blazor 事件处理

使用 @on{DOM EVENT}="{DELEGATE}" Razor 语法在 Razor 组件标记中指定委托事件处理程序:

对于事件处理:

  • 支持返回 Task 的异步委托事件处理程序。
  • 委托事件处理程序会自动触发 UI 呈现,因此无需手动调用 StateHasChanged
  • 记录异常。

下面的代码:

  • 在 UI 中选择该按钮时,调用 UpdateHeading 方法。
  • UI 中的该复选框发生更改时,调用 CheckChanged 方法。

Pages/EventHandlerExample1.razor:

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

<h1>@currentHeading</h1>

<p>
    <label>
        New title
        <input @bind="newHeading" />
    </label>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

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

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

    private void UpdateHeading()
    {
        currentHeading = $"{newHeading}!!!";
    }

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

在以下示例中,UpdateHeading

  • 在选择该按钮时以异步方式调用。
  • 在更新标题之前等待两秒。

Pages/EventHandlerExample2.razor:

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

<h1>@currentHeading</h1>

<p>
    <label>
        New title
        <input @bind="newHeading" />
    </label>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

@code {
    private string currentHeading = "Initial heading";
    private string newHeading;

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

        currentHeading = $"{newHeading}!!!";
    }
}

事件参数

内置事件参数

对于支持事件参数类型的事件,仅在方法使用事件类型时,才需要在事件方法定义中指定事件参数。 在下面的示例中,ReportPointerLocation 方法使用了 MouseEventArgs 来设置消息文本,以便在用户选择 UI 中的按钮时报告鼠标坐标。

Pages/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 oncut, oncopy, onpaste
拖动 DragEventArgs ondrag, ondragstart, ondragenter, ondragleave, ondragover, ondrop, ondragend

DataTransferDataTransferItem 保留拖动的项数据。

使用 JS 互操作HTML 拖放 API在 Blazor 应用中实现拖放。
错误 ErrorEventArgs onerror
事件 EventArgs 常规
onactivate, onbeforeactivate, onbeforedeactivate, ondeactivate, onfullscreenchange, onfullscreenerror, onloadeddata, onloadedmetadata, onpointerlockchange, onpointerlockerror, onreadystatechange, onscroll

剪贴板
onbeforecut, onbeforecopy, onbeforepaste

输入
oninvalid, onreset, onselect, onselectionchange, onselectstart, onsubmit

介质
oncanplay, oncanplaythrough, oncuechange, ondurationchange, onemptied, onended, onpause, onplay, onplaying, onratechange, onseeked, onseeking, onstalled, onstop, onsuspend, ontimeupdate, ontoggle, onvolumechange, onwaiting

EventHandlers 保留属性,以配置事件名称和事件参数类型之间的映射。
焦点 FocusEventArgs onfocus, onblur, onfocusin, onfocusout

不包含对 relatedTarget 的支持。
输入 ChangeEventArgs onchange, oninput
键盘 KeyboardEventArgs onkeydown, onkeypress, onkeyup
鼠标 MouseEventArgs onclick, oncontextmenu, ondblclick, onmousedown, onmouseup, onmouseover, onmousemove, onmouseout
鼠标指针 PointerEventArgs onpointerdown, onpointerup, onpointercancel, onpointermove, onpointerover, onpointerout, onpointerenter, onpointerleave, ongotpointercapture, onlostpointercapture
鼠标滚轮 WheelEventArgs onwheel, onmousewheel
进度 ProgressEventArgs onabort, onload, onloadend, onloadstart, onprogress, ontimeout
触控 TouchEventArgs ontouchstart, ontouchend, ontouchmove, ontouchenter, ontouchleave, ontouchcancel

TouchPoint 表示触控敏感型设备上的单个接触点。

有关更多信息,请参见以下资源:

自定义事件参数

Blazor 支持自定义事件参数。借助这些参数,你可以将任意数据通过自定义事件传递给 .NET 事件处理程序。

常规配置

通常,通过以下步骤启用具有自定义事件参数的自定义事件。

  1. 在 JavaScript 中,定义一个函数,用于通过源事件生成自定义事件参数对象:

    function eventArgsCreator(event) { 
      return {
        customProperty1: 'any value for property 1',
        customProperty2: event.srcElement.value
      };
    }
    
  2. wwwroot/index.html (Blazor WebAssembly) 或 Pages/_Layout.cshtml (Blazor Server) 中(紧随 Blazor <script> 之后)使用前面的处理程序注册自定义事件:

    <script>
      Blazor.registerCustomEventType('customevent', {
        createEventArgs: eventArgsCreator;
      });
    </script>
    

    备注

    每个事件仅在脚本中调用 registerCustomEventType 一次。

  3. 定义事件参数的类:

    public class CustomEventArgs : EventArgs
    {
        public string CustomProperty1 {get; set;}
        public string CustomProperty2 {get; set;}
    }
    
  4. 通过为自定义事件添加 EventHandlerAttribute 特性注释,连接自定义事件和事件参数。 此类不需要成员:

    [EventHandler("oncustomevent", typeof(CustomEventArgs), enableStopPropagation: true, enablePreventDefault: true)]
    static class EventHandlers
    {
    }
    
  5. 在一个或多个 HTML 元素上注册此事件处理程序。 在委托处理程序方法中访问从 JavaScript 传入的数据:

    <button @oncustomevent="HandleCustomEvent">Handle</button>
    
    @code
    {
        void HandleCustomEvent(CustomEventArgs eventArgs)
        {
            // eventArgs.CustomProperty1
            // eventArgs.CustomProperty2
        }
    }
    

每当在 DOM 上触发此自定义事件时,都会使用从 JavaScript 传入的数据调用此事件处理程序。

如果正在尝试触发自定义事件,则必须将 bubbles 的值设置为 true 以启用它。 否则,事件无法到达 Blazor 处理程序,进而无法受到处理并进入 C# 自定义 EventHandlerAttribute 方法。 有关详细信息,请参阅 MDN Web 文档:事件冒泡

自定义剪贴板粘贴事件示例

下面的示例接收包含粘贴时间和用户所粘贴文本的自定义剪贴板粘贴事件。

声明事件的自定义名称 (oncustompaste),并声明一个 .NET 类 (CustomPasteEventArgs) 以保存此事件的事件参数:

CustomEvents.cs:

[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 子类提供数据。 在 wwwroot/index.htmlPages/_Layout.cshtml 文件中,紧随 Blazor 脚本之后添加以下 <script> 标记和内容。 下面的示例仅处理粘贴文本,但你可以使用任意 JavaScript API 来处理用户粘贴的其他类型的数据(如图像)。

紧随 Blazor 脚本之后的 wwwroot/index.html (Blazor WebAssembly) 或 Pages/_Layout.cshtml (Blazor Server):

<script>
    Blazor.registerCustomEventType('custompaste', {
        browserEventName: 'paste',
        createEventArgs: event => {
            return {
                eventTimestamp: new Date(),
                pastedData: event.clipboardData.getData('text')
            };
        }
    });
</script>

前面的代码会在本机 paste 事件发生时告诉浏览器:

  • 引发 custompaste 事件。
  • 使用所述的自定义逻辑提供事件参数数据:

.NET 和 JavaScript 的事件名称约定有所不同:

  • 在 .NET 中,事件名称带有前缀“on”。
  • 在 JavaScript 中,事件名称不带前缀。

在 Razor 组件中,将自定义处理程序附加到元素中。

Pages/CustomPasteArguments.razor:

@page "/custom-paste-arguments"

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

Lambda 表达式

支持 Lambda 表达式作为委托事件处理程序。

Pages/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 并传递以下数据:

  • e 中的事件参数 (MouseEventArgs)。
  • buttonNumber 中的按钮编号。

Pages/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}";
    }
}

备注

不要直接在 Lambda 表达式中使用循环变量,如前面的 for 循环示例中的 i。 否则,所有 Lambda 表达式将使用相同的变量,这将导致在所有 Lambda 中使用相同的值。 始终在局部变量中捕获该变量的值,然后使用该值。 在上面的示例中:

  • 将循环变量 i 分配到 buttonNumber
  • buttonNumber 用于 lambda 表达式。

在一个循环中创建大量事件委托可能会导致呈现性能不佳。 有关详细信息,请参阅 ASP.NET Core Blazor 性能最佳做法

EventCallback

嵌套组件的一个常见方案:在子组件事件发生时执行父组件的方法。 子组件中发生的 onclick 事件是一个常见用例。 若要跨组件公开事件,请使用 EventCallback。 父组件可向子组件的 EventCallback 分配回调方法。

下面的 Child 组件演示如何设置按钮的 onclick 处理程序以从示例的 ParentComponent 接收 EventCallback 委托。 EventCallback 是用 MouseEventArgs 键入的,这适用于来自外围设备的 onclick 事件。

Shared/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; }
}

Parent 组件将子级的 EventCallback<TValue> (OnClickCallback) 设置为其 ShowMessage 方法。

Pages/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 Blazor 组件呈现

EventCallbackEventCallback<TValue> 允许异步委托。 EventCallback 是弱类型,允许将任何类型参数传入 InvokeAsync(Object)EventCallback<TValue> 是强类型,需要将 T 参数传入可分配到 TValueInvokeAsync(T) 中。

<ChildComponent 
    OnClickCallback="@(async () => { await Task.Yield(); messageText = "Blaze It!"; })" />

使用 InvokeAsync 调用 EventCallbackEventCallback<TValue> 并等待 Task

await OnClickCallback.InvokeAsync(arg);

使用 EventCallbackEventCallback<TValue> 处理事件和绑定组件参数。

优先使用强类型 EventCallback<TValue> 而非 EventCallbackEventCallback<TValue> 向用户提供更好的组件错误反馈。 与其他 UI 事件处理程序类似,指定事件参数是可选操作。 当没有值传递给回调时,使用 EventCallback

阻止默认操作

使用 @on{DOM EVENT}:preventDefault 指令特性防止事件的默认操作,其中 {DOM EVENT} 占位符是文档对象模型 (DOM) 事件

在输入设备上选择某个键并且元素焦点位于某个文本框上时,浏览器通常在该文本框中显示该键的字符。 在下面的示例中,通过指定 @onkeydown:preventDefault 指令属性来阻止默认行为。 当焦点位于 <input> 元素上时,计数器随着按 Shift++ 按键顺序而递增。 不会将 + 字符分配到 <input> 元素的值。 有关 keydown 的详细信息,请参阅 MDN Web Docs: Document: keydown 事件

Pages/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 是设置为 truefalsebool 字段:

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

...

@code {
    private bool shouldPreventDefault = true;
}

停止事件传播

使用 @on{DOM EVENT}:stopPropagation 指令特性来停止事件传播,其中 {DOM EVENT} 占位符是文档对象模型 (DOM) 事件

在下例中,选中复选框可阻止第二个子级 <div> 中的单击事件传播到父级 <div>。 由于传播的单击事件通常会触发 OnSelectParentDiv 方法,因此,如果未选中复选框,则选择第二个子级 <div> 会导致父级 div 消息出现。

Pages/EventHandlerExample7.razor:

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

<label>
    <input @bind="stopPropagation" type="checkbox" />
    Stop Propagation
</label>

<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 doesn't stop propagation when selected.
    </div>

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

<p>
    @message
</p>

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

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

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

聚焦元素

元素引用上调用 FocusAsync 来将代码中的元素作为焦点。 在下面的示例中,选择按钮可将 <input> 元素作为焦点。

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

使用 @on{DOM EVENT}="{DELEGATE}" Razor 语法在 Razor 组件标记中指定委托事件处理程序:

对于事件处理:

  • 支持返回 Task 的异步委托事件处理程序。
  • 委托事件处理程序会自动触发 UI 呈现,因此无需手动调用 StateHasChanged
  • 记录异常。

下面的代码:

  • 在 UI 中选择该按钮时,调用 UpdateHeading 方法。
  • UI 中的该复选框发生更改时,调用 CheckChanged 方法。

Pages/EventHandlerExample1.razor:

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

<h1>@currentHeading</h1>

<p>
    <label>
        New title
        <input @bind="newHeading" />
    </label>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

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

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

    private void UpdateHeading()
    {
        currentHeading = $"{newHeading}!!!";
    }

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

在以下示例中,UpdateHeading

  • 在选择该按钮时以异步方式调用。
  • 在更新标题之前等待两秒。

Pages/EventHandlerExample2.razor:

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

<h1>@currentHeading</h1>

<p>
    <label>
        New title
        <input @bind="newHeading" />
    </label>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

@code {
    private string currentHeading = "Initial heading";
    private string newHeading;

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

        currentHeading = $"{newHeading}!!!";
    }
}

事件参数

对于支持事件参数类型的事件,仅在方法使用事件类型时,才需要在事件方法定义中指定事件参数。 在下面的示例中,ReportPointerLocation 方法使用了 MouseEventArgs 来设置消息文本,以便在用户选择 UI 中的按钮时报告鼠标坐标。

Pages/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 oncut, oncopy, onpaste
拖动 DragEventArgs ondrag, ondragstart, ondragenter, ondragleave, ondragover, ondrop, ondragend

DataTransferDataTransferItem 保留拖动的项数据。

使用 JS 互操作HTML 拖放 API在 Blazor 应用中实现拖放。
错误 ErrorEventArgs onerror
事件 EventArgs 常规
onactivate, onbeforeactivate, onbeforedeactivate, ondeactivate, onfullscreenchange, onfullscreenerror, onloadeddata, onloadedmetadata, onpointerlockchange, onpointerlockerror, onreadystatechange, onscroll

剪贴板
onbeforecut, onbeforecopy, onbeforepaste

输入
oninvalid, onreset, onselect, onselectionchange, onselectstart, onsubmit

介质
oncanplay, oncanplaythrough, oncuechange, ondurationchange, onemptied, onended, onpause, onplay, onplaying, onratechange, onseeked, onseeking, onstalled, onstop, onsuspend, ontimeupdate, ontoggle, onvolumechange, onwaiting

EventHandlers 保留属性,以配置事件名称和事件参数类型之间的映射。
焦点 FocusEventArgs onfocus, onblur, onfocusin, onfocusout

不包含对 relatedTarget 的支持。
输入 ChangeEventArgs onchange, oninput
键盘 KeyboardEventArgs onkeydown, onkeypress, onkeyup
鼠标 MouseEventArgs onclick, oncontextmenu, ondblclick, onmousedown, onmouseup, onmouseover, onmousemove, onmouseout
鼠标指针 PointerEventArgs onpointerdown, onpointerup, onpointercancel, onpointermove, onpointerover, onpointerout, onpointerenter, onpointerleave, ongotpointercapture, onlostpointercapture
鼠标滚轮 WheelEventArgs onwheel, onmousewheel
进度 ProgressEventArgs onabort, onload, onloadend, onloadstart, onprogress, ontimeout
触控 TouchEventArgs ontouchstart, ontouchend, ontouchmove, ontouchenter, ontouchleave, ontouchcancel

TouchPoint 表示触控敏感型设备上的单个接触点。

有关更多信息,请参见以下资源:

Lambda 表达式

支持 Lambda 表达式作为委托事件处理程序。

Pages/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 并传递以下数据:

  • e 中的事件参数 (MouseEventArgs)。
  • buttonNumber 中的按钮编号。

Pages/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}";
    }
}

备注

不要直接在 Lambda 表达式中使用循环变量,如前面的 for 循环示例中的 i。 否则,所有 Lambda 表达式将使用相同的变量,这将导致在所有 Lambda 中使用相同的值。 始终在局部变量中捕获该变量的值,然后使用该值。 在上面的示例中:

  • 将循环变量 i 分配到 buttonNumber
  • buttonNumber 用于 lambda 表达式。

在一个循环中创建大量事件委托可能会导致呈现性能不佳。 有关详细信息,请参阅 ASP.NET Core Blazor 性能最佳做法

EventCallback

嵌套组件的一个常见方案:在子组件事件发生时执行父组件的方法。 子组件中发生的 onclick 事件是一个常见用例。 若要跨组件公开事件,请使用 EventCallback。 父组件可向子组件的 EventCallback 分配回调方法。

下面的 Child 组件演示如何设置按钮的 onclick 处理程序以从示例的 ParentComponent 接收 EventCallback 委托。 EventCallback 是用 MouseEventArgs 键入的,这适用于来自外围设备的 onclick 事件。

Shared/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; }
}

Parent 组件将子级的 EventCallback<TValue> (OnClickCallback) 设置为其 ShowMessage 方法。

Pages/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 Blazor 组件呈现

EventCallbackEventCallback<TValue> 允许异步委托。 EventCallback 是弱类型,允许将任何类型参数传入 InvokeAsync(Object)EventCallback<TValue> 是强类型,需要将 T 参数传入可分配到 TValueInvokeAsync(T) 中。

<ChildComponent 
    OnClickCallback="@(async () => { await Task.Yield(); messageText = "Blaze It!"; })" />

使用 InvokeAsync 调用 EventCallbackEventCallback<TValue> 并等待 Task

await OnClickCallback.InvokeAsync(arg);

使用 EventCallbackEventCallback<TValue> 处理事件和绑定组件参数。

优先使用强类型 EventCallback<TValue> 而非 EventCallbackEventCallback<TValue> 向用户提供更好的组件错误反馈。 与其他 UI 事件处理程序类似,指定事件参数是可选操作。 当没有值传递给回调时,使用 EventCallback

阻止默认操作

使用 @on{DOM EVENT}:preventDefault 指令特性防止事件的默认操作,其中 {DOM EVENT} 占位符是文档对象模型 (DOM) 事件

在输入设备上选择某个键并且元素焦点位于某个文本框上时,浏览器通常在该文本框中显示该键的字符。 在下面的示例中,通过指定 @onkeydown:preventDefault 指令属性来阻止默认行为。 当焦点位于 <input> 元素上时,计数器随着按 Shift++ 按键顺序而递增。 不会将 + 字符分配到 <input> 元素的值。 有关 keydown 的详细信息,请参阅 MDN Web Docs: Document: keydown 事件

Pages/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 是设置为 truefalsebool 字段:

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

...

@code {
    private bool shouldPreventDefault = true;
}

停止事件传播

使用 @on{DOM EVENT}:stopPropagation 指令特性来停止事件传播,其中 {DOM EVENT} 占位符是文档对象模型 (DOM) 事件

在下例中,选中复选框可阻止第二个子级 <div> 中的单击事件传播到父级 <div>。 由于传播的单击事件通常会触发 OnSelectParentDiv 方法,因此,如果未选中复选框,则选择第二个子级 <div> 会导致父级 div 消息出现。

Pages/EventHandlerExample7.razor:

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

<label>
    <input @bind="stopPropagation" type="checkbox" />
    Stop Propagation
</label>

<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 doesn't stop propagation when selected.
    </div>

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

<p>
    @message
</p>

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

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

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

聚焦元素

元素引用上调用 FocusAsync 来将代码中的元素作为焦点。 在下面的示例中,选择按钮可将 <input> 元素作为焦点。

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

使用 @on{DOM EVENT}="{DELEGATE}" Razor 语法在 Razor 组件标记中指定委托事件处理程序:

对于事件处理:

  • 支持返回 Task 的异步委托事件处理程序。
  • 委托事件处理程序会自动触发 UI 呈现,因此无需手动调用 StateHasChanged
  • 记录异常。

下面的代码:

  • 在 UI 中选择该按钮时,调用 UpdateHeading 方法。
  • UI 中的该复选框发生更改时,调用 CheckChanged 方法。

Pages/EventHandlerExample1.razor:

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

<h1>@currentHeading</h1>

<p>
    <label>
        New title
        <input @bind="newHeading" />
    </label>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

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

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

    private void UpdateHeading()
    {
        currentHeading = $"{newHeading}!!!";
    }

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

在以下示例中,UpdateHeading

  • 在选择该按钮时以异步方式调用。
  • 在更新标题之前等待两秒。

Pages/EventHandlerExample2.razor:

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

<h1>@currentHeading</h1>

<p>
    <label>
        New title
        <input @bind="newHeading" />
    </label>
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

@code {
    private string currentHeading = "Initial heading";
    private string newHeading;

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

        currentHeading = $"{newHeading}!!!";
    }
}

事件参数

对于支持事件参数类型的事件,仅在方法使用事件类型时,才需要在事件方法定义中指定事件参数。 在下面的示例中,ReportPointerLocation 方法使用了 MouseEventArgs 来设置消息文本,以便在用户选择 UI 中的按钮时报告鼠标坐标。

Pages/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 oncut, oncopy, onpaste
拖动 DragEventArgs ondrag, ondragstart, ondragenter, ondragleave, ondragover, ondrop, ondragend

DataTransferDataTransferItem 保留拖动的项数据。

使用 JS 互操作HTML 拖放 API在 Blazor 应用中实现拖放。
错误 ErrorEventArgs onerror
事件 EventArgs 常规
onactivate, onbeforeactivate, onbeforedeactivate, ondeactivate, onfullscreenchange, onfullscreenerror, onloadeddata, onloadedmetadata, onpointerlockchange, onpointerlockerror, onreadystatechange, onscroll

剪贴板
onbeforecut, onbeforecopy, onbeforepaste

输入
oninvalid, onreset, onselect, onselectionchange, onselectstart, onsubmit

介质
oncanplay, oncanplaythrough, oncuechange, ondurationchange, onemptied, onended, onpause, onplay, onplaying, onratechange, onseeked, onseeking, onstalled, onstop, onsuspend, ontimeupdate, onvolumechange, onwaiting

EventHandlers 保留属性,以配置事件名称和事件参数类型之间的映射。
焦点 FocusEventArgs onfocus, onblur, onfocusin, onfocusout

不包含对 relatedTarget 的支持。
输入 ChangeEventArgs onchange, oninput
键盘 KeyboardEventArgs onkeydown, onkeypress, onkeyup
鼠标 MouseEventArgs onclick, oncontextmenu, ondblclick, onmousedown, onmouseup, onmouseover, onmousemove, onmouseout
鼠标指针 PointerEventArgs onpointerdown, onpointerup, onpointercancel, onpointermove, onpointerover, onpointerout, onpointerenter, onpointerleave, ongotpointercapture, onlostpointercapture
鼠标滚轮 WheelEventArgs onwheel, onmousewheel
进度 ProgressEventArgs onabort, onload, onloadend, onloadstart, onprogress, ontimeout
触控 TouchEventArgs ontouchstart, ontouchend, ontouchmove, ontouchenter, ontouchleave, ontouchcancel

TouchPoint 表示触控敏感型设备上的单个接触点。

有关更多信息,请参见以下资源:

Lambda 表达式

支持 Lambda 表达式作为委托事件处理程序。

Pages/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 并传递以下数据:

  • e 中的事件参数 (MouseEventArgs)。
  • buttonNumber 中的按钮编号。

Pages/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}";
    }
}

备注

不要直接在 Lambda 表达式中使用循环变量,如前面的 for 循环示例中的 i。 否则,所有 Lambda 表达式将使用相同的变量,这将导致在所有 Lambda 中使用相同的值。 始终在局部变量中捕获该变量的值,然后使用该值。 在上面的示例中:

  • 将循环变量 i 分配到 buttonNumber
  • buttonNumber 用于 lambda 表达式。

在一个循环中创建大量事件委托可能会导致呈现性能不佳。 有关详细信息,请参阅 ASP.NET Core Blazor 性能最佳做法

EventCallback

嵌套组件的一个常见方案:在子组件事件发生时执行父组件的方法。 子组件中发生的 onclick 事件是一个常见用例。 若要跨组件公开事件,请使用 EventCallback。 父组件可向子组件的 EventCallback 分配回调方法。

下面的 Child 组件演示如何设置按钮的 onclick 处理程序以从示例的 ParentComponent 接收 EventCallback 委托。 EventCallback 是用 MouseEventArgs 键入的,这适用于来自外围设备的 onclick 事件。

Shared/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; }
}

Parent 组件将子级的 EventCallback<TValue> (OnClickCallback) 设置为其 ShowMessage 方法。

Pages/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 Blazor 组件呈现

EventCallbackEventCallback<TValue> 允许异步委托。 EventCallback 是弱类型,允许将任何类型参数传入 InvokeAsync(Object)EventCallback<TValue> 是强类型,需要将 T 参数传入可分配到 TValueInvokeAsync(T) 中。

<ChildComponent 
    OnClickCallback="@(async () => { await Task.Yield(); messageText = "Blaze It!"; })" />

使用 InvokeAsync 调用 EventCallbackEventCallback<TValue> 并等待 Task

await OnClickCallback.InvokeAsync(arg);

使用 EventCallbackEventCallback<TValue> 处理事件和绑定组件参数。

优先使用强类型 EventCallback<TValue> 而非 EventCallbackEventCallback<TValue> 向用户提供更好的组件错误反馈。 与其他 UI 事件处理程序类似,指定事件参数是可选操作。 当没有值传递给回调时,使用 EventCallback

阻止默认操作

使用 @on{DOM EVENT}:preventDefault 指令特性防止事件的默认操作,其中 {DOM EVENT} 占位符是文档对象模型 (DOM) 事件

在输入设备上选择某个键并且元素焦点位于某个文本框上时,浏览器通常在该文本框中显示该键的字符。 在下面的示例中,通过指定 @onkeydown:preventDefault 指令属性来阻止默认行为。 当焦点位于 <input> 元素上时,计数器随着按 Shift++ 按键顺序而递增。 不会将 + 字符分配到 <input> 元素的值。 有关 keydown 的详细信息,请参阅 MDN Web Docs: Document: keydown 事件

Pages/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 是设置为 truefalsebool 字段:

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

...

@code {
    private bool shouldPreventDefault = true;
}

停止事件传播

使用 @on{DOM EVENT}:stopPropagation 指令特性来停止事件传播,其中 {DOM EVENT} 占位符是文档对象模型 (DOM) 事件

在下例中,选中复选框可阻止第二个子级 <div> 中的单击事件传播到父级 <div>。 由于传播的单击事件通常会触发 OnSelectParentDiv 方法,因此,如果未选中复选框,则选择第二个子级 <div> 会导致父级 div 消息出现。

Pages/EventHandlerExample7.razor:

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

<label>
    <input @bind="stopPropagation" type="checkbox" />
    Stop Propagation
</label>

<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 doesn't stop propagation when selected.
    </div>

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

<p>
    @message
</p>

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

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

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