js 使用 DotNetObjectReference 呼叫 c# 函式

cchong005發表於2024-05-31

網上的方法

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

相關文章