js 使用 DotNetObjectReference 调用 c# 函数
网上的方法
1,用 JSInvokable 标记需要被 js 调用的静态方法
/// <summary> /// 页面窗口改变事件回调 /// </summary> /// <param name="windowWidth"></param> /// <param name="windowHeight"></param> [JSInvokable] public static void ClientWindowResizeCallback(int windowWidth, int windowHeight) { PageResizeExcuter?.Invoke(windowWidth, windowHeight); }
2,js 通过 DotNet.invokeMethodAsync 调用这个方法
window.addEventListener("resize", (event) => { console.log(`窗口尺寸改变了!!!! w:${window.innerWidth} h:${window.innerHeight}`); // DotNet 是 blazor 内置的对象 // param 1 : 指定程序集 // param 2 : JSInvokable 标记的方法 // 后面跟方法的参数 DotNet.invokeMethodAsync("AJR.AGV.Experience", "ClientWindowResizeCallback", window.innerWidth, window.innerHeight); });
使用 DotNet.invokeMethodAsync 时,你不需要关心 JSInvokable 方法所在的类,只要需要提供该方法的程序集;
但是这种做法有个缺陷,因为是静态方法,当多个浏览器触发同一事件时,只有最后打开的浏览器能正确触发回调
改进版
不再使用静态方法,也不再使用内置的 DotNet 对象;
C# 仍然用 JSInvokable 标记需要被调用的方法
public class JSEventReceiver { public event Func<int, int, Task>? OnPageResize; /// <summary> /// 页面窗口改变事件回调 /// </summary> /// <param name="windowWidth"></param> /// <param name="windowHeight"></param> [JSInvokable] public void ClientWindowResizeCallback(int windowWidth, int windowHeight) { if (OnPageResize is null) return; var delegates = OnPageResize.GetInvocationList(); foreach (Func<int, int, Task> delegated in delegates) { try { delegated.Invoke(windowWidth, windowHeight); } catch (Exception ex) { OnPageResize -= delegated; } } } }
在页面上我们直接用 JSEventReceiver 创建一个 DotNetObjectReference 对象,并把这个对象传给前端 js,这样前端用到的 DoNet 对象就是由当前页面创建的,多个页面不会互相干扰;
这里的 JSEventReceiver 对象可以使用 Scope 注入,不想用注入就直接 new JSEventReceiver(); 效果是一样的
razor
protected override async Task OnAfterRenderAsync(bool firstRender) { await base.OnAfterRenderAsync(firstRender); if (firstRender) { #region 一个 js 调用 c# 的例子 _JsEventReceiver.OnPageResize -= HandlePageResize; _JsEventReceiver.OnPageResize += HandlePageResize; // 使用 DotNetObjectReference 向 JS 传递实例 var dotNetObjectRef = DotNetObjectReference.Create(_JsEventReceiver); // 注册 JavaScript 事件 await _IJsRuntime.InvokeVoidAsync("eventRegistResize", dotNetObjectRef); #endregion } }
如果使用传入的 DoNetObjectReference 对象,js 里就不再需要指定程序集了
js
function eventRegistResize(dotNetObjectRef) { window.addEventListener("resize", (event) => { console.log(`窗口尺寸改变了!!!! w:${window.innerWidth} h:${window.innerHeight}`); dotNetObjectRef.invokeMethodAsync("ClientWindowResizeCallback", window.innerWidth, window.innerHeight); }); }
另:
如果在 razor 组件里使用事件,别忘了在组件销毁时释放事件,必须实现 IDisposable 接口,Dispose 方法才生效
@page "/" @implements IDisposable @code { public void Dispose() { _JsEventReceiver.OnPageResize -= HandlePageResize; } }