顯示具有 MVP2020 標籤的文章。 顯示所有文章
顯示具有 MVP2020 標籤的文章。 顯示所有文章

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 上] 按鈕

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

 




2021年3月18日 星期四

.NET Core 使用地址來查詢 GPS 經度緯度

.NET Core 使用地址來查詢 GPS 經度緯度

因為專案需求,需要使用地址來查詢出所在地點的經度與緯度數值,做為日後定位處理依據,經過上網搜尋,選用了 Generic C# Geocoding API 這裡的套件來幫忙設計這樣的需求。

這個套件支援了不同的地圖提供者的存取 API,在這裡將會使用 Google Map 這個提供者作為練習範例

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

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

加入所需要使用到的 NuGet 套件

  • 滑鼠右擊 [csAddressToGPS] 專案內的 [相依性] 節點
  • 從彈出功能表中,選擇 [管理 NuGet 套件]
  • 當 [NuGet: csAddressToGPS] 視窗出現後,切換到 [瀏覽] 標籤頁次
  • 搜尋 [Geocoding.Core] 並且安裝並且套件
  • 搜尋 [Geocoding.Google] 並且安裝並且套件

設計程式碼

請把 Main 方法改成底下的程式碼

記得要將 this-is-my-optional-google-api-key 這個文字,改成你申請到的 Google Map API Key

static async Task Main(string[] args)
{
    IGeocoder geocoder = new GoogleGeocoder() { ApiKey = "this-is-my-optional-google-api-key" };
    IEnumerable<Address> addresses = await geocoder.GeocodeAsync("高雄市鼓山區明倫路59號");
    Console.WriteLine("Formatted: " + addresses.First().FormattedAddress); 
    Console.WriteLine("Coordinates: " + addresses.First().Coordinates.Latitude + ", " + addresses.First().Coordinates.Longitude); 
}

執行結果

請按下 F5 開始執行這個專案,將會看到底下的執行結果

Formatted: No. 59, Minglun Road, Gushan District, Kaohsiung City, Taiwan 804 

Coordinates: 22.6676008, 120.2975978 




2021年3月15日 星期一

.NET 6 preview2 初體驗 - 執行 Maui 專案

.NET 6 preview2 初體驗 - 執行 Maui 專案

在 03月11日,微軟發表了 Announcing .NET 6 Preview 2 ,由於本身就已經從事於 Xamarin.Forms 多年教學與開發經驗,並且坐進也在使用 Blazor 進行各種專案開發,理所當然地對於 .NET 6 有著極高的興趣,所以,就在今天建立起一個 Windows 10 作業系統環境,一開始將 Hyper-V 相關服務開啟。

安裝 Visual Studio 2019 Preview

接著就到 Visual Studio Preview 網站下在 Visual Studio 2019 Preview IDE 到系統上。

安裝 .NET 6 SDK

最後,從 Download .NET 6.0 來下載 SDK 6.0.100-preview.2.21155.3 並且安裝到這台電腦上,下載下來的檔案名稱為 dotnet-sdk-6.0.100-preview.2.21155.3-win-x64.exe。

請執行這個安裝檔案,就會出現底下畫面

Microsoft.NET SDK 6.0.100-preview.2.21155.3

請點選 [安裝] 按鈕

安裝完成之後,請點選 [關閉] 按鈕

Microsoft.NET SDK 6.0.100-preview.2.21155.3

設定 Visual Studio 2019 可以使用 .NET 6 Preview

打開 Visual Studio 2019

點選 [工具] > [選項]

.NET 6 的 Maui 範例程式碼的安裝設定

根據 net6-mobile-samples 網頁說明,還需要額外安裝

Android:

Windows: Microsoft.NET.Workload.Android.11.0.200.148.msi

iOS:

Windows: Microsoft.NET.Workload.iOS.14.4.100-ci.main.1192.msi

執行 .NET 6 的 原生 Android 範例程式碼

這裡是現在 .NET6 Preview 2 的 Maui 範例 方案內容

執行 .NET6 Maui 範例程式碼

請下載這個專案 Repository net6-mobile-samples ,現階段似乎無法透過 Visual Studio 2019 來執行這些範例程式碼,因此,根據文件上的說明,打開命令提示字元視窗,切換到該專案的目錄下。

輸入這個命令 dotnet build HelloAndroid

編譯 .NET6 Maui for Android 範例程式碼

接著輸入 dotnet build HelloAndroid -t:Run

執行 .NET6 原生 Android 範例程式碼

執行 .NET 6 的 Maui for Android 範例程式碼

請下載這個專案 Repository net6-mobile-samples ,現階段似乎無法透過 Visual Studio 2019 來執行這些範例程式碼,因此,根據文件上的說明,打開命令提示字元視窗,切換到該專案的目錄下。

輸入這個命令 dotnet build HelloMaui --no-restore -t:Run -f net6.0-android

執行 .NET6 Maui 範例程式碼 




2021年2月16日 星期二

使用 APM 非同步設計模式加上 callback 的各個觸發時間

使用 APM 非同步設計模式加上 callback 的各個觸發時間

當在進行 APM 非同步程式設計的時候,首先會先呼叫 BeginXXX 方法,啟動非同步作業,接著,當使用 callback 方式的話,當非同步作業完成之後,就會呼叫這個 callback 委派方法;不過,此時,若想要設計出在主執行緒中,使用封鎖 Block 等待的方法,等候這個方同步作業與 callback 完成,若不使用其他執行緒同步功能,是否可以做到呢?

也就是說,在主執行緒下,呼叫 IAsyncResult.AsyncWaitHandle.WaitOne() 方法的時候,究竟會在執行 callback 的當時解除封鎖 block,還是於 callback 方法之後呢? 為了要了解此一問題,設計了底下的程式碼來一探究竟。

首先,這裡使用 WebRequest.Create 建立起一個 HttpWebRequest 物件,而該 URL 會花費 5 秒鐘的時間,才會執行完畢,在主執行緒內,接著呼叫 myHttpWebRequest1.BeginGetResponse(callBack, myHttpWebRequest1) 方法,啟動非同步作業,這裡有宣告一個 callBack 委派方法,會於非同步作業完成之後,呼叫這個方法。最後,使用了 asyncResult.AsyncWaitHandle.WaitOne() 方法來等待非同步作業的完成。

不論在主執行緒內與委派 callback 方法內,都會使用 Console.WriteLine 方法輸出一些檢查點訊息,用來幫助確認這個問題。

static void Main(string[] args)
{
    try
    {
        string url = $"https://hyperfullstack.azurewebsites.net/api/HandOnLab/Add/1/2/5";
 
        // 針對非同步請求,產生委派方法,用於處理非同步工作執行完成後的結果
        AsyncCallback callBack = new AsyncCallback(ResponseCallback);
 
        // 使用 WebRequest.Create 工廠方法建立一個 HttpWebrequest 物件
        HttpWebRequest myHttpWebRequest1 = (HttpWebRequest)WebRequest.Create(url);
 
        // 呼叫 BeginXXX 啟動非同步工作
        Console.WriteLine("Main 1");
        IAsyncResult asyncResult =
          (IAsyncResult)myHttpWebRequest1.BeginGetResponse(callBack, myHttpWebRequest1);
 
        Console.WriteLine("Main 2");
        asyncResult.AsyncWaitHandle.WaitOne();
        Console.WriteLine("Main 3");
 
        for (int i = 0; i < 3; i++)
        {
            Console.WriteLine($"   處理其他事情");
            Thread.Sleep(1000);
        }
 
        // 主執行緒的工作已經完成
        Console.WriteLine("Press any key for continuing...");
        Console.ReadKey();
    }
    catch (WebException e)
    {
        Console.WriteLine("\nException raised!");
        Console.WriteLine("\nMessage:{0}", e.Message);
        Console.WriteLine("\nStatus:{0}", e.Status);
        Console.WriteLine("Press any key to continue..........");
    }
    catch (Exception e)
    {
        Console.WriteLine("\nException raised!");
        Console.WriteLine("Source :{0} ", e.Source);
        Console.WriteLine("Message :{0} ", e.Message);
        Console.WriteLine("Press any key to continue..........");
        Console.Read();
    }
}

private static void ResponseCallback(IAsyncResult ar)
{
    HttpWebRequest request = ar.AsyncState as HttpWebRequest;
    Console.WriteLine("Callback 1");
    Thread.Sleep(3000);
    Console.WriteLine("Callback 2");
    HttpWebResponse webResponse = request.EndGetResponse(ar) as HttpWebResponse;//取得資料
    Console.WriteLine("Callback 3");
 
    Stream ReceiveStream = webResponse.GetResponseStream();
    StreamReader reader = new StreamReader(ReceiveStream);
    string result = reader.ReadToEnd();
 
    Console.WriteLine("Callback 4");
}

這裡是檢查結果,從這裡可以看到,一旦非同步作業完成之後,就會使用訊號通知方式,解除封鎖 Block 等待,也就是說,住執行緒可以繼續往下執行,而此時,callback 委派方法會在另外一個執行緒下,繼續來執行。

Main 1
Main 2
Main 3
   處理其他事情
Callback 1
   處理其他事情
   處理其他事情
Callback 2
Callback 3
Callback 4 

Press any key for continuing...