2021年7月14日 星期三

Blazor Server 必會開發技能 - Hello 互動頁面與事件設計

Blazor Server 必會開發技能 - Hello 互動頁面與事件設計


Blazor Server 必會開發技能

Blazor Server 建立一個新的頁面

Blazor Server 元件生命週期事件 Component Life Cycle

Blazor Server C# 程式碼設計方法

Blazor Server 單向資料綁定 One Way Data Binding 與 重新轉譯 Binding

Blazor Server Hello 互動頁面與事件設計 Two Way Data Binding

Blazor Server 元件間的參數傳遞與回應事件 Component Parameter EventCallback

Blazor Server C# 與 JavaScript 互相呼叫 IJSRuntime

Blazor Server 表單和驗證 Form Validation 


在這篇文章將會來使用 Blazor 進行學習程式設計過程中,一定會學習的一個練習題,那就是 Hello World。

在這個練習中,將完全不需要用到 jQuery 或者 JavaScript 程式語言就可以做到動態網頁的效果,在完成設計的網頁上,將會有兩個文字輸入盒,分別代表要輸入 姓 與 名 這兩個欄位,另外再加入一個問安的按鈕,一旦輸入完成你的姓名並且按下問安按鈕之後,此時,在網頁的最下方將會出現向你問安的文字。

這裡說明的範例專案原始碼位於 BS05

建立 Blazor Server-Side 的專案

  • 打開 Visual Studio 2019

  • 點選右下方的 [建立新的專案] 按鈕

  • [建立新專案] 對話窗將會顯示在螢幕上

  • 從[建立新專案] 對話窗的中間區域,找到 [Blazor 應用程式] 這個專案樣板選項,並且選擇這個項目

  • 點選右下角的 [下一步] 按鈕

  • 現在 [設定新的專案] 對話窗將會出現

  • 請在這個對話窗內,輸入適當的 [專案名稱] 、 [位置] 、 [解決方案名稱]

    在這裡請輸入 [專案名稱] 為 BS05

  • 完成後,請點選 [建立] 按鈕

  • 當出現 [建立新的 Blazor 應用程式] 對話窗的時候

  • 請選擇最新版本的 .NET Core 與 [Blazor 伺服器應用程式]

  • 完成後,請點選 [建立] 按鈕

    稍微等會一段時間,Blazor 專案將會建立起來

設計問安的程式碼

  • 打開 [Pages] 資料夾內的 [Index.razor] 檔案
  • 請使用底下程式碼替換到這個檔案內容
@page "/"

<h1>Hello, 互動頁面與事件設計!</h1>

<div>
    <label></label>
</div>
<div>
    <input @bind="@LastName" />
    @LastName
</div>

<div>
    <label></label>
</div>
<div>
    <input @bind-value="@FirstName" @bind-value:event="oninput" />
    @FirstName
</div>

<div>
    <button class="btn btn-primary" @onclick="SayHello">Say Hello</button>
</div>

<div class="my-5 display-1 text-success">
    @FullName
</div>

@code{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string FullName { get; set; }

    void SayHello()
    {
        FullName = $"{LastName} {FirstName} 你好";
    }
}

當想要取得網頁上的使用者操作或者輸入的資料,需要使用 Blazor 的雙向資料綁定 Two Way Data Binding 機制,例如,在 <input> 這個 HTML 標籤而言,可以使用 @bind 這樣 指示詞來指定要做雙向綁定的 C# 物件,如同這樣使用 <input @bind="@LastName" />

若使用 <input value="@LastName" /> 這樣的使用方式,則是宣告這個 Input 採用單向資料綁定 One Way Data Binding ,這代表了當使用 C# 變數 (這裡指的是 LastName 這個屬性物件) 其物件值有變動的時候,會透過單向資料綁定機制將這個物件值更新到瀏覽器的 DOM 上,進而將會造成在網頁上可以看到這個變數物件值;但是,反向運作將不會正常運作,也就是說,若使用者在網頁上進行輸入任何資料,這些異動的資料是不會更新到 C# 屬性變數。

若採用上面提到的雙向綁定宣告方式,當使用者輸入資料的過程中,這些輸入的資料當時還不會更新到 C# 變數內,這要一直等到當使用者點選到網頁的其他地方,也就是要造成對於當時的控制項的焦距 Focus 離開之後,就會觸發雙向綁定運作機制。

可是,若想要做到當使用者在輸入資料過程中,可以隨時透過 C# 變數得知使用者當時輸入了甚麼資料,可以使用 <input @bind-value="@FirstName" @bind-value:event="oninput" /> 這樣的方式來做到。

當使用了 @bind-value 這樣的 指示詞 Directive 表示要進行 [value] 這個屬性的雙向資料綁定,這樣的用法將會可以指定要綁定的屬性名稱,而對於 @bind-value:event="oninput" 指示詞,則表示了當 value 這個屬性若有異動的時候,將會觸發 oninput 事件 這個 DOM 事件,如此,將會進行 C# 物件屬性值更新;由這裡可以看的出來,當使用 <input @bind="@LastName" /> 方式來宣告雙向資料綁定,則表示當使用者在輸入資料過程中,僅會在 onchange 事件 被出發的時候,才會更新到 C# 屬性內。

執行這個專案

  • 按下 [F5] 按鍵,開始執行這個 Blazor 專案

  • 一旦啟動完成,就會自動開以瀏覽器

  • 現在,請分別在 姓名 欄位輸入你的性與名,接著點選 [Say Hello] 按鈕

  • 請在輸入 姓 或者 名 兩個欄位的時候,使用者所輸入的內容將會出現在文字輸入盒 [input] 的右方,這裡使用兩種的雙向綁定宣告,請體驗這兩種設計方式的不同在哪裡。

  • 此時,透過雙向資料綁定機制的運作,在 C# 程式碼內將會抓取到剛剛在網頁輸入的欄位值,並且再度透過單向資料綁定機制,將你輸入的姓名組合起來並顯示在螢幕上,如下圖所示

 










2021年7月13日 星期二

Blazor Server 必會開發技能 - 元件間的參數傳遞與回應事件

Blazor Server 必會開發技能 - 元件間的參數傳遞與回應事件


Blazor Server 必會開發技能

Blazor Server 建立一個新的頁面

Blazor Server 元件生命週期事件 Component Life Cycle

Blazor Server C# 程式碼設計方法

Blazor Server 單向資料綁定 One Way Data Binding 與 重新轉譯 Binding

Blazor Server Hello 互動頁面與事件設計 Two Way Data Binding

Blazor Server 元件間的參數傳遞與回應事件 Component Parameter EventCallback

Blazor Server C# 與 JavaScript 互相呼叫 IJSRuntime

Blazor Server 表單和驗證 Form Validation 


對於在進行元件化設計的時候,將會有這些元件間互相傳遞資訊的需求,在這個練習 Blazor 專案內,將會設計一個子元件,這個子元件將會在 Index.razor 元件內會參考使用。

上層元件 (Index.razor) 提供一個按鈕,將會產生一個現在日期時間字串,透過綁定子元件參數的方式,把這個物件值傳遞到子元件內;而在子元件內會有一個文字輸入盒與一個按鈕,使用者在子元件內輸入任何文字到文字輸入盒內,接著按下按鈕,此時,會從子元件內把剛剛輸入的文字,傳遞到 Index.razor 元件內來顯示在網頁上。

這裡說明的範例專案原始碼位於 BS06

建立 Blazor Server-Side 的專案

  • 打開 Visual Studio 2019

  • 點選右下方的 [建立新的專案] 按鈕

  • [建立新專案] 對話窗將會顯示在螢幕上

  • 從[建立新專案] 對話窗的中間區域,找到 [Blazor 應用程式] 這個專案樣板選項,並且選擇這個項目

  • 點選右下角的 [下一步] 按鈕

  • 現在 [設定新的專案] 對話窗將會出現

  • 請在這個對話窗內,輸入適當的 [專案名稱] 、 [位置] 、 [解決方案名稱]

    在這裡請輸入 [專案名稱] 為 BS06

  • 完成後,請點選 [建立] 按鈕

  • 當出現 [建立新的 Blazor 應用程式] 對話窗的時候

  • 請選擇最新版本的 .NET Core 與 [Blazor 伺服器應用程式]

  • 完成後,請點選 [建立] 按鈕

    稍微等會一段時間,Blazor 專案將會建立起來

設計子元件的程式碼

  • 滑鼠右擊 Blazor 專案內的 [Pages] 資料夾
  • 選擇 [加入] > [Razor元件]
  • 當 [新增項目 - BS03] 對話窗出現之後,請在下方名稱欄位內,輸入 ChildView.razor
  • 最後點選 [新增] 按鈕
  • 請依據底下程式碼替換到這個檔案內容
<div class="card">
    <div class="card-header">
        子元件對話窗
    </div>
    <div class="card-body">
        <h5 class="card-title">警告,請輸入你的回應</h5>
        <input class="card-text" @bind="Para1" />
        <button class="btn btn-primary" @onclick="CloseDialog">確定</button>
    </div>
</div>
@code {
    [Parameter]
    public string Para1 { get; set; }
    [Parameter]
    public EventCallback<string> OnBtnCloseCallback { get; set; }
    void CloseDialog()
    {
        OnBtnCloseCallback.InvokeAsync($"你的選擇是 {Para1}");
    }
}

在這個元件內將使用 [Parameter] 來標示兩個屬性為這個元件的參數,這代表當使用這個元件的時候,可以透過這兩個參數來接收到來自於父元件所要傳遞的物件值。

這裡也設計一個按鈕與該按鈕綁定的觸發事件,使用這按下這個按鈕之後,會將使用者輸入的內容值透過 [EventCallback] 物件來執行其委派方法,而該委派方法將會綁定在父元件內的某個方法,這也代表可以讓子元件來執行父元件內的程式碼,另外,因為這裡使用了 [EventCallback] 泛型型別來宣告,這代表了當呼叫了這個 [EventCallback] 委派方法,將可以傳遞字串到父元件內。

修改參考子元件的 Index.razor 元件

  • 打開 [Pages] 資料夾內的 [Index.razor] 檔案
  • 請使用底下程式碼替換到這個檔案內容
@page "/"

<h1>Hello, 元件參數傳遞與回應事件!</h1>

@if (ShowMessagebox == false)
{
    <div>
        <button class="btn btn-primary"
                @onclick="OnShowMessagebox">
            Show Message
        </button>
        <p>@ResponseMessage</p>
    </div>
}
else
{
    <ChildView Para1="@PassArgument" OnBtnCloseCallback="BtnCloseCallback" />
}

@code{
    public bool ShowMessagebox { get; set; } = false;
    public string PassArgument { get; set; }
    public string ResponseMessage { get; set; }
    void OnShowMessagebox()
    {
        PassArgument = DateTime.Now.ToString();
        ShowMessagebox = true;
    }
    void BtnCloseCallback(string message)
    {
        ShowMessagebox = false;
        ResponseMessage = message;
    }
}

在這個元件內有設計一個變數 [ShowMessagebox] ,透過這個變數來設定 true 或者 false ,便可以控制這個元件要顯示那些內容,因此,當 [ShowMessagebox] 這個變數設定為 false,會顯示一個按鈕與子元件要回傳的文字,若為 true,則會把按鈕隱藏起來,顯示子元件。

當按下按鈕之後,將會設定 [PassArgument] 這個屬性值為當前的時間字串,此時,因為在宣告子元件的時候,使用 <ChildView Para1="@PassArgument" OnBtnCloseCallback="BtnCloseCallback" /> 這樣的宣告,所以,便可以透過資料綁定的方式,將這個 [PassArgument] 物件值傳遞到子元件內;而這裡也透過 [OnBtnCloseCallback] 這個子元件的參數名稱來綁定到 [BtnCloseCallback] 方法內,因此,在子元件內可以透過 [EventCallback] 物件來呼叫這個方法,並且可以傳遞引數到父元件內。

執行這個專案

  • 按下 [F5] 按鍵,開始執行這個 Blazor 專案

  • 一旦啟動完成,就會自動開以瀏覽器

  • 請點選 [Show Message] 按鈕

  • 現在子元件將會顯示出來,而且在父元件內會取得現在電腦時間,並且顯示在子元件內

  • 請修改文字輸入盒內的文字內容

  • 按下 [確定] 按鈕

  • 此時,會把剛剛輸入的文字回傳到父元件內,並且顯示在網頁上

 








2021年7月12日 星期一

Blazor Server 必會開發技能 - 單向資料綁定與重新轉譯

Blazor Server 必會開發技能 - 單向資料綁定與重新轉譯


Blazor Server 必會開發技能

Blazor Server 建立一個新的頁面

Blazor Server 元件生命週期事件 Component Life Cycle

Blazor Server C# 程式碼設計方法

Blazor Server 單向資料綁定 One Way Data Binding 與 重新轉譯 Binding

Blazor Server Hello 互動頁面與事件設計 Two Way Data Binding

Blazor Server 元件間的參數傳遞與回應事件 Component Parameter EventCallback

Blazor Server C# 與 JavaScript 互相呼叫 IJSRuntime

Blazor Server 表單和驗證 Form Validation 


在這篇文章將會來使用 Blazor 技術來設計一個小遊戲,那就是一個剪刀、石頭、布的猜謎遊戲,在這個練習專案內將學習 Blazor 的單向資料綁定用法,除了可以透過資料綁定 Data Binding 的方式將 C# 變數物件值顯示在瀏覽器的網頁上,並且也可以使用資料綁定功能來進行要顯示圖片來源、要套用的 CSS 類別名稱等方式的資料綁定。

透過資料綁定的方法,可以做到當 C# 執行個體值有變動的時候,透過了轉譯 Render 機制將這些變動的差異內容更新到瀏覽器上的 DOM 內,如此,網頁顯示的內容就會進行更新了,從這裡可以看到一個相當好用的技能,那就是想要做到動態網頁、更新或者讀取網頁上的內容,不再需要透過 jQuery 或者 JavaScript 程式語言就可以做到這樣的能力,因此,採用 Blazor 技術所開發的專案,幾乎不再需要使用到 JavaScript 程式語言了。

這裡說明的範例專案原始碼位於 BS04

建立 Blazor Server-Side 的專案

  • 打開 Visual Studio 2019

  • 點選右下方的 [建立新的專案] 按鈕

  • [建立新專案] 對話窗將會顯示在螢幕上

  • 從[建立新專案] 對話窗的中間區域,找到 [Blazor 應用程式] 這個專案樣板選項,並且選擇這個項目

  • 點選右下角的 [下一步] 按鈕

  • 現在 [設定新的專案] 對話窗將會出現

  • 請在這個對話窗內,輸入適當的 [專案名稱] 、 [位置] 、 [解決方案名稱]

    在這裡請輸入 [專案名稱] 為 BS04

  • 完成後,請點選 [建立] 按鈕

  • 當出現 [建立新的 Blazor 應用程式] 對話窗的時候

  • 請選擇最新版本的 .NET Core 與 [Blazor 伺服器應用程式]

  • 完成後,請點選 [建立] 按鈕

    稍微等會一段時間,Blazor 專案將會建立起來

加入靜態的剪刀、石頭、布的圖片檔案

  • 從 Images 這裡來下載該目錄下的四個圖片檔案到本機電腦上

  • 滑鼠右擊這個專案根目錄下的 [wwwroot] 節點

  • 點選 [加入] > [新增資料夾]

  • 重新命名這個資料夾為 [Images]

  • 使用檔案總管拖拉這剛剛下載的四個圖片檔案,到剛剛建立好的 [Images] 資料夾內

建立 Razor 元件

  • 滑鼠右擊 Blazor 專案內的 [Pages] 資料夾
  • 選擇 [加入] > [Razor元件]
  • 當 [新增項目 - BS04] 對話窗出現之後,請在下方名稱欄位內,輸入 GameView.razor
  • 最後點選 [新增] 按鈕
  • 請依據底下程式碼替換到這個檔案內容
@using System.Threading
@using System.Threading.Tasks

<div>
    <div>請選擇 剪刀、石頭、布</div>
    <div>
        @for (int i = 1; i <= 3; i++)
        {
            int idx = i;
            <img src="@allItems[idx].Image" class="@allItems[idx].Background" style="width:7em;height:7em;"
                 @onclick="async ()=>{await Choose(allItems[idx]); }">
        }
    </div>
    <div class="my-5">
        <span>
            <img src="@allItems[You].Image" style="width:7em;height:7em;"></span>
        <span class="mx-4"> vs </span>
        <span>
            電腦
            <img src="@allItems[Computer].Image" style="width:7em;height:7em;">
        </span>
    </div>
</div>
@code {
    string GameMessage = "";
    List<GameItem> allItems = new List<GameItem>()
{
        new GameItem { Name = "Empty" },
        new GameItem { Name = "Scissors" },
        new GameItem { Name = "Rock" },
        new GameItem { Name = "Paper" },
    };
    int Computer = 1;
    int You = 0;

    async Task Choose(GameItem item)
    {
        for (int i = 1; i < allItems.Count; i++)
        {
            if (allItems[i].Name == item.Name)
            {
                allItems[i].Selected = true;
                You = i;
            }
            else
                allItems[i].Selected = false;
        }

        Random random = new Random();
        var count = random.Next(20, 35);

        for (int i = 0; i < count; i++)
        {
            Computer = (i % 3) + 1;
            await Task.Delay(100);
            StateHasChanged();
        }
    }

    public class GameItem
    {
        public string Name { get; set; }

        public string Image
        {
            get
            {
                return $"/Images/{Name}.png";
            }
        }
        public bool Selected { get; set; }
        public string Background
        {
            get
            {
                if (Selected == true)
                {
                    return "bg-secondary";
                }
                else
                {
                    return "";
                }
            }
        }
    }
}

在這個元件內使用了 @for (int i = 1; i <= 3; i++) 指示詞來建立一個三次的迴圈,分別要在網頁上顯示三個圖片 HTML 標籤,不過,這三個圖片標籤的圖片來源、使用的CSS,將會透過資料綁定的方式來套用的這些圖片屬性值。

另外,這裡也使用 @onclick 這個指示詞 Directive 來進行資料綁定到 C# 內的委派事件方法,這代表了當使用者在網頁上點選了這三個圖片的任何一個,將會觸發這裡所綁定的 Lambda 委派方法;當這個委派方法執行之後,將會採用發同步的方式來執行 await Choose(allItems[idx]); 敘述。

在 [Choose] 這個非同步方法內,將會建立一個 [Random] 類別物件,並且建立 20 ~ 35 之間的隨機亂數,接著進入迴圈內,每執行一次迴圈,將會暫時休息 0.1 秒,接著呼叫 StateHasChanged() 方法,這樣就會造成有動畫的效果,

Random random = new Random();
var count = random.Next(20, 35);
for (int i = 0; i < count; i++)
{
    Computer = (i % 3) + 1;
    await Task.Delay(100);
    StateHasChanged();
}

使用這個元件

  • 打開 [Pages] 資料夾內的 [Index.razor] 檔案
  • 請使用底下程式碼替換到這個檔案內容
@page "/"

<h1>
    Hello, 單向資料綁定與重新轉譯!
</h1>

<GameView/>

執行這個專案

  • 按下 [F5] 按鍵,開始執行這個 Blazor 專案

  • 一旦啟動完成,就會自動開以瀏覽器

  • 此時,請猜測電腦會出哪個手勢,點選上方的三個手勢的其中一個,看看你是否會贏電腦

  • 經過 2~3.5 秒後,電腦將會選出一個手勢

  • 按下 [Shift] + [F5] 按鈕,終止這個專案執行

  • 在 [GameView.razor] 檔案內,搜尋找到 StateHasChanged() 方法,將這個方法註解起來

  • 按下 [F5] 按鍵,開始執行這個 Blazor 專案

  • 一旦啟動完成,就會自動開以瀏覽器

  • 點選上方的三個手勢的其中一個,現在就會看不到動畫效果

 













2021年7月10日 星期六

.NET 6 Preview 5 MAUI Blazor App 體驗使用經驗 2

.NET 6 Preview 5 MAUI Blazor App 體驗使用經驗 2

在上一篇文章中,.NET 6 Preview 5 MAUI App 體驗使用經驗 1,實際體驗建立一個 Maui App 的專案,也看到了執行結果;在這篇文章中,將會來體驗看看建立一個 [Maui Blazor App] 專案,這個專案顧名思義就是採用 Blazor 的開發方式來建立跨平台的應用程式。

作者本身對於這樣的技術感到相當的興奮,因為一旦這個功能趨近於成熟並且正式推出之後,對於一個身為 Blazor WASM 或者 Blazor Server 的開發者而言,可以很容易的自動切換本身成為一個跨平台的專案開發人員。

  • 首先,先要建立一個 [.NET MAUI Blazor App] 專案

  • 現在來在 Android 平台下來執行這個 [.NET MAUI Blazor App] 專案

  • 底下將會是啟動後的首頁畫面

  • 點選右上方的漢堡按鈕,從彈出清單內可以切換到 [Counter] 這個頁面

  • 當專案建立完成後, [方案總管] 的結構如下

  • 其中在 MauiApp2 的專案內,將會看到 [MainPage.xaml] 這個檔案

  • 先來查看 [MainPage.xaml] 這個檔案

  • 在這個主要頁面內,只有使用了一個 BlazorWebView 控制項,並沒有其他的 XAML 項目在使用,在這裡將會使用這個 Web View 元件來顯示 Blazor 元件

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
			 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
			 xmlns:b="clr-namespace:Microsoft.AspNetCore.Components.WebView.Maui;assembly=Microsoft.AspNetCore.Components.WebView.Maui"
			 xmlns:local="clr-namespace:MauiApp2"
			 x:Class="MauiApp2.MainPage"
			 BackgroundColor="{DynamicResource PageBackgroundColor}">

	<b:BlazorWebView HostPage="wwwroot/index.html">
		<b:BlazorWebView.RootComponents>
			<b:RootComponent Selector="app" ComponentType="{x:Type local:Main}" />
		</b:BlazorWebView.RootComponents>
	</b:BlazorWebView>
</ContentPage>
  • 打開 [Main.razor] 檔案,將會看到底下程式碼
<Router AppAssembly="@GetType().Assembly">
	<Found Context="routeData">
		<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
	</Found>
	<NotFound>
		<LayoutView Layout="@typeof(MainLayout)">
			<p>Sorry, there's nothing at this address.</p>
		</LayoutView>
	</NotFound>
</Router>
  • 對於有在使用 Blazor 來開發專案的開發者而言將並不陌生,這裡是 Blazor 專案內的起始進入點

  • 由這裡可以看出,對於 [MAUI Blazor App] 專案將會採用 Razor 元件來進行設計的

  • 接著展開 [Pages] 資料夾

  • 在這個資料夾內,將會看到有三個 Razor 元件頁面,如同採用 Web 方式開發的專案一樣的內容

  • 打開 [Pages] > [Counter.razor] ,其程式碼應該不會太意外,如同底下

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
	private int currentCount = 0;

	private void IncrementCount()
	{
		currentCount++;
	} 

}