前言
在Blazor中,我们使用@on{DOM EVENT}="{DELEGATE}"
这样的Razor语法在组件标记中指定委托事件处理程序:
<button @onclick="IncrementCount">Click me</button>
但是没有提供解除委托的方法。
比如,我们需要在某种条件下才执行事件处理:
有不有阻止事件触发的方案呢?
常规解决方案
我们可以在委托事件处理程序中加上判断:
<button @onclick="()=> { if (!checkedValue) { IncrementCount(); } }">Click me</button>
但是,如果有多个组件都需要加上这个判断,这样写感觉比较繁琐。
有不有更简化的方法?
深入分析
在前面的文章中,我们已经知道Blazor组件实际上会编译成C#语句。
在obj\Debug\net5.0\Razor\Pages目录下,可以看到@onclick="IncrementCount"
对应的代码如下:
__builder.AddAttribute(13, "onclick",Microsoft.AspNetCore.Components.EventCallback.Factory.Create<Microsoft.AspNetCore.Components.Web.MouseEventArgs>(this, IncrementCount));
原来是创建了一个EventCallback事件回调。
在github上查找到EventCallback.Factory.Create
的实现源码[1]:
private EventCallback CreateCore(object receiver, MulticastDelegate callback)
{return new EventCallback(callback?.Target as IHandleEvent ?? receiver as IHandleEvent, callback);
}
上面代码的意思,如果callback的来源是IHandleEvent
,则实际回调的是IHandleEvent
实现。
也就是说,只需要组件继承IHandleEvent
,那么就可以控制组件上所有的事件回调了。
实现
修改代码如下:
@implements IHandleEventTask IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, object arg)
{System.Console.WriteLine("HandleEven");return callback.InvokeAsync(arg);
}private void IncrementCount()
{System.Console.WriteLine("Clicked...");currentCount++;
}
运行后可以看到,确实先执行的HandleEventAsync
,再执行的@onclick
实现:
结论
通过本文,我们可以了解到,只要在组件上继承IHandleEvent
,就可以实现全局截获事件回调。
但是,你会发现有一个问题,Current count
没有发生变化!
这是为什么呢?请听下回分解!
参考资料
[1]
实现源码: https://github.com/dotnet/aspnetcore/blob/main/src/Components/Components/src/EventCallbackFactory.cs