2021年3月19日 星期五

使用 Blazor 專案在 JavaScript 內呼叫 C# 執行個體方法

使用 Blazor 專案在 JavaScript 內呼叫 C# 執行個體方法

當在進行 Blazor 專案程式設計的時候,原則上許多的功能是不需要自行設計相關 JavaScript 程式碼來進行呼叫,因為 Blazor 開發框架中已經具備了許多機制,例如 資料綁定路由 等相關機制,輕鬆地就能做到原先要使用 JavaScript 程式語言能夠做到的事情。

不過,Blazor 存在的目的,並不是要完全取代掉不去使用 JavaScript 程式語言功能,有些時候,需要能夠執行某一 JavaScript 程式碼之後,緊接著要能夠在 JavaScript 內來呼叫 .NET C# 的程式碼,這樣的需求在 Blazor 可以完成的實現出來,而在這裡將會有兩種情境可以使用: 靜態 .NET 方法呼叫 與 實例方法呼叫

在這篇文章中,將會設計一個按鈕,該按鈕點選下去之後,將會執行所綁定 JavaScript,這部分的功能將會是最基本的 HTML 網頁設計功能,而在這個按鈕所綁定的 JavaScript 方法內 (這裡將會呼叫 showPrompt 方法),該 JavaScript 方法將會呼叫 prompt 函式,讓使用者輸入自己的姓名文字,接著將會透過在 .NET C# 所產生的 DotNetObjectReference 物件,在 JavaScript 使用這樣的敘述 DotNetObject.invokeMethodAsync('SayHello', result); 執行該物件所擁有的 SayHello 方法,當然,該方法是使用 C# 所設計的,並且會在 .NET 環境下執行。

透過這樣的機制,便可以將 JavaScript 所取得的字串,傳遞到 .NET C# 變數內,接著透過 Blazor 所提供的資料綁定機制,將這段字串顯示在網頁上。

不過,在這裡首先需要建立起 DotNetObjectReference 物件,在這裡需要指定所用到的執行個體型別,也就是 BindDotNetInstance ,接下來使用 objRef = DotNetObjectReference.Create(helloHelper); 敘述來建立起 DotNetObjectReference物件。

不過,要把這個 objRef 物件傳遞到 JavaScript 函式內,將這個物件設定為 JavaScript 的全域變數,這裡又有兩種方式選擇,可以使用一個按鈕或者使用 Blazor 生命週期的事件 ComponentBase.OnAfterRenderAsync來做到,此時要呼叫 await jsRuntime.InvokeVoidAsync("SetDotNetObjectJS", objRef); 即可。

另外,為了要讓這個 Blazor 元件取得傳遞於 JavaScript 環境中的字串,需要綁定一個委派方法到 helloHelper.HelloHandler 內。

現在來看看如何做出這樣的範例成程式碼。

這篇文章的原始碼位於 bzCallCsharpInstanceMethodFromJavaScript

建立測試用主控台應用程式專案

  • 開啟 Visual Studio 2019
  • 選擇右下方的 [建立新的專案] 按鈕
  • 在 [建立新專案] 對話窗中
  • 從右上方的專案類型下拉按鈕中,找到並選擇 [Web]
  • 從可用專案範本清單內,找到並選擇 [Blazor Server 應用程式]
  • 點選左下方 [下一步] 按鈕
  • 在 [設定新的專案] 對話窗中
  • 在 [專案名稱] 欄位中輸入 bzCallCsharpInstanceMethodFromJavaScript
  • 點選左下方 [下一步] 按鈕
  • 在 [其他資訊] 對話窗中
  • 在 [目標 Framework] 下拉選單中,選擇 [.NET 5.0 (目前)]
  • 點選左下方 [建立] 按鈕

加入客製化 JavaScript

  • 滑鼠右擊 [bzCallCsharpInstanceMethodFromJavaScript] 專案下的 [wwwroot] 節點
  • 從彈出功能表選擇 [加入] > [新增資料夾]
  • 在新增的資料夾,設定該資料夾名稱為 js
  • 接著,滑鼠右擊 [js] 資料夾節點
  • 從彈出功能表選擇 [加入] > [新增項目]
  • 當出現 [新增項目 - bzCallCsharpInstanceMethodFromJavaScript] 對話窗
  • 在右方選擇 [已安裝] > [Visual C#] > [ASP.NET Core] > [Web] > [指令碼]
  • 在中間部份選擇 [JavaScript 檔] 這個項目
  • 在下方名稱欄位內輸入 [JavaScript.js]
  • 最後點選右下方的 [新增] 按鈕

請將底下的 JavaScript 程式碼輸入到這個檔案內

var DotNetObject = {}
function showPrompt(text) {
    var result = prompt(text, 'Type your name here');
    //呼叫該 .NET 執行個體(由類別 BindDotNetInstance 所產生)的 SayHello 方法
    DotNetObject.invokeMethodAsync('SayHello', result);
}
function SetDotNetObjectJS(dotnetHelper) {
    //將該 .NET 執行個體設定成為 JavaScript 的全域變數
    DotNetObject = dotnetHelper;
}

修正 _Host.cshtml 檔案

  • 請在 [Pages] 資料夾內找到並且打開 [_Host.cshtml] 檔案
  • 請找到 <script src="_framework/blazor.server.js"></script> 敘述
  • 在其上方加入這個敘述 <script src="/js/JavaScript.js"></script>

修正 index.razor

  • 請在 [Pages] 資料夾內找到並且打開 [index.razor] 檔案
  • 把底下的程式碼替換掉原先的程式碼
@page "/"
@inject IJSRuntime jsRuntime
@implements IDisposable

<h1>Hello, Blazor 專案在 JavaScript 內呼叫 C# 執行個體方法!</h1>

<div>
    <button class="btn btn-primary" @onclick="SetDotNetObject">設定物件</button>
</div>
<div>
    <button type="button" class="btn btn-primary" onclick="showPrompt('請輸入你的名字')">
        綁定按鈕事件到 JavaScript 上
    </button>
</div>
<div>
    <div class="text-success">@Name</div>
</div>

@code {
    BindDotNetInstance helloHelper = new BindDotNetInstance();
    DotNetObjectReference<BindDotNetInstance> objRef;
    public string Name = "";
    async Task SetDotNetObject()
    {
        // 進行綁定該執行個體的委派方法,以便進行 callback 呼叫
        helloHelper.HelloHandler = x =>
        {
        //將 JavaScript 程式碼取得的文字內容,設定到該 Blazor 元件內的 C# 變數中
        Name = x;
        //通知 Blazor 重新產生最新狀態的 Render Tree
        StateHasChanged();
        };
        objRef = DotNetObjectReference.Create(helloHelper);
        await jsRuntime.InvokeAsync<string>(
            "SetDotNetObjectJS", objRef);
    }
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender == true)
        {
            helloHelper.HelloHandler = x =>
            {
                Name = x;
                StateHasChanged();
            };
            objRef = DotNetObjectReference.Create(helloHelper);
            await jsRuntime.InvokeVoidAsync(
                "SetDotNetObjectJS", objRef);
        }
    }
    public void Dispose()
    {
        objRef?.Dispose();
    }
    class BindDotNetInstance
    {
        public Action<string> HelloHandler;
        [JSInvokable]
        public void SayHello(string message)
        {
            var result = $"你好, {message}!";
            HelloHandler?.Invoke(result);
        }
    }
}

執行並且測試

按下 F5 開始執行這個專案

現在將會看到底下的畫面

請點選 [綁定按鈕事件到 JavaScript 上] 按鈕

最後點選確定按鈕,就會看到底下執行結果

 




沒有留言:

張貼留言