2018年11月23日 星期五

Blazor 2 HTML Element 動態顯示或者隱藏的控制練習

Blazor 2 HTML Element 動態顯示或者隱藏的控制練習

更多關於 Blazor 教學影片,可以參考 Blazor 教學影片播放清單 或者 Blazor 快速體驗教學影片撥放清單。也歡迎訂閱本 .NET / Blazor / Xamarin.Forms 影片頻道 。

Blazor 開發框架下有個非常好用的功能,那就是 資料綁定 Data Binding 機制,這個機制可以做到當在一個 Blazor 元件 Component 內宣告一個欄位變數,例如該欄位名稱為 MyField ,則可以在 HTML 標記 Markup 中使用 @ 加上這個欄位名稱,便可以將這個欄位變數值與 HTML 標記整合再一起;而在這個練習中,想要設計一個元件,該元件上有個按鈕,當按下這個按鈕之後,該元件上的網頁部分內容將會隱藏起來,若再度點選一次這個按鈕,則剛剛隱藏的內容又會顯示出來。如同大家對於網頁設計的了解,在這裡將會透過 HTML DOM display 屬性來控制網頁上的 HTML Element 是否要顯示還是隱藏,不過,更為重要的是,這裡所說明的所有處理過程,都不會用到任何一行 JavaScript ,只需要透過 .NET C# 程式碼就可以做到,這就是 Blazor 好用的地方。本篇文章的範例原始碼,可以從 BlazorElementVisibility 取得。

建立測試專案

若已經將 Blazor 開發工具安裝與設定完成之後,現在可以開始建立第一個 Blazor 開發專案。
  • 啟動 Visual Studio 2017
  • 點選功能表的 [檔案] > [新增] > [專案] 功能表選項
  • 在 [新增專案] 對話窗中左邊區域,選擇 [已安裝] > [Visual C#] > [Web] > [ASP.NET Core Web 應用程式]
  • 在對話窗的下方的 [名稱] 欄位中,輸入這個練習專案的名稱 BlazorElementVisibility
  • 在對話窗的下方的 [位置] 欄位中,選擇這個專案要儲存的檔案路徑
  • 最後點選對話窗的右下方 [確定] 按鈕
  • 在 [新增 ASP.NET Core Web 應用程式] 對話窗左上方區域,在第一個下拉選單,選擇 [.NET Core] 在第二個下拉選單,請選擇最新的 ASP.NET Core 版本,在現在這個時間點,可以選擇 [ASP.NET Core 2.1]
  • 在對話窗中間區域,請點選 [Blazor] 這個項目
  • 最後點選對話窗的右下方 [確定] 按鈕

練習步驟

接下來將要建立一個元件 Component,在這個元件中將會使用 div 標籤 (Tag) ,在裡面會有8個按鈕顯示在畫面上。在最後面會有一個 切換顯示狀態 按鈕,當點選這個按鈕之後,剛剛 div 標籤內的8個按鈕就會消失不見,若再度點選 切換顯示狀態 按鈕,則這8個按鈕就會再度出現。
  • 建立一個名為 EleVisible 的 Razor 檢視檔案
    • 在 [Pages] 資料夾使用滑鼠右擊,選擇 [加入] > [新增項目]
    • 在 [新增項目] 對話窗中,點選 [已安裝] > [ASP.NET Core] > [Razor 頁面] 選項,接著,在下方名稱欄位,輸入 EleVisible.cshtml ,最後點選右下方的 [新增] 按鈕
  • 將 EleVisible.cshtml 檔案使用底下 Razor 頁面的 HTML 宣告與 C# 成程式碼來替換
 Pages > EleVisible.cshtml
@page "/ElementVisibility"

<h1>HTML Element 動態顯示或者隱藏的控制練習</h1>

<p>練習步驟</p>
<p>建立一個名為 EleVisible 的 Razor 檢視檔案</p>
<p>使用 ShowAction 字串變數來控制 style display 的值</p>
<p>綁定按鈕事件,動態的切換 ShowAction 字串內容,造成 HTML 項目可以動態的顯示或者隱藏</p>
<p>修正 NavMenu 元件 Component,使其可以顯示這個 Blazor 元件在網頁上</p>

<hr />

<div style="display: @ShowAction">
    <button type="button" class="btn btn-primary">Primary</button>
    <button type="button" class="btn btn-secondary">Secondary</button>
    <button type="button" class="btn btn-success">Success</button>
    <button type="button" class="btn btn-danger">Danger</button>
    <button type="button" class="btn btn-warning">Warning</button>
    <button type="button" class="btn btn-info">Info</button>
    <button type="button" class="btn btn-light">Light</button>
    <button type="button" class="btn btn-dark">Dark</button>
</div>

<hr />

<button class="btn btn-primary" onclick="@ChangeStatus">切換顯示狀態</button>

<hr />

<div>ShowAction = @ShowAction</div>

@functions {
bool IsShow = true;
string ShowAction = "";
void ChangeStatus()
{
    IsShow = !IsShow;
    ChangeShowMode();
}

void ChangeShowMode()
{
    if (IsShow == true)
    {
        ShowAction = "";
    }
    else
    {
        ShowAction = "none";
    }
}
}
  • 在 [Shared] 資料夾內,找到 [NavMenu.cshtml] Razor 檔案,接著將底下 HTML 標記宣告內容,加入到 <ul class="nav flex-column"> 內。
 Pages > EleVisible.cshtml
@page "/ElementVisibility"

<h1>HTML Element 動態顯示或者隱藏的控制練習</h1>

<p>練習步驟</p>
<p>建立一個名為 EleVisible 的 Razor 檢視檔案</p>
<p>使用 ShowAction 字串變數來控制 style display 的值</p>
<p>綁定按鈕事件,動態的切換 ShowAction 字串內容,造成 HTML 項目可以動態的顯示或者隱藏</p>
<p>修正 NavMenu 元件 Component,使其可以顯示這個 Blazor 元件在網頁上</p>

<hr />

<div style="display: @ShowAction">
    <button type="button" class="btn btn-primary">Primary</button>
    <button type="button" class="btn btn-secondary">Secondary</button>
    <button type="button" class="btn btn-success">Success</button>
    <button type="button" class="btn btn-danger">Danger</button>
    <button type="button" class="btn btn-warning">Warning</button>
    <button type="button" class="btn btn-info">Info</button>
    <button type="button" class="btn btn-light">Light</button>
    <button type="button" class="btn btn-dark">Dark</button>
</div>

<hr />

<button class="btn btn-primary" onclick="@IncrementCount">切換顯示狀態</button>

<hr />

<div>ShowAction = @ShowAction</div>

@functions {
bool IsShow = true;
string ShowAction = "";
void IncrementCount()
{
    IsShow = !IsShow;
    ChangeShowMode();
}

void ChangeShowMode()
{
    if (IsShow == true)
    {
        ShowAction = "";
    }
    else
    {
        ShowAction = "none";
    }
}
}
 Shared > NavMenu.cshtml
<li class="nav-item px-3">
    <NavLink class="nav-link" href="ElementVisibility">
        <span class="oi oi-list-rich" aria-hidden="true"></span> Element Visibility
    </NavLink>
</li>

EleVisible.cshtml 設計說明

在這個 EleVisible.cshtml Razor 檔案內,將會看到 <div style="display: @ShowAction"> 這樣的宣告,而對於 DOM 的 Style display 屬性值將會由 C# 中的 ShowAction 欄位值來提供;在 Blazor 中,當把一個變數宣告到 HTML 語言中來使用的時候,將會預設採用單向資料綁定機制來運作,也就是說,當 ShowAction 這個欄位值有變更的話,此時,在 DOM 的 Style display 屬性值也會跟著進行更新。
當使用者點選了 切換顯示狀態 按鈕,將會觸發 ChangeStatus 按鈕事件,這是因為在 HTML 中,使用了 <button class="btn btn-primary" onclick="@ChangeStatus"> 宣告。而在 ChangeStatus 按鈕事件內,將會判對當時的 ShowAction 是否為 空字串,若為空字串,此時, div 標籤內的內容將會顯示出來,並且將該 ShowAction 欄位值為 "none",這樣就會造成 div 標籤內的這些按鈕將會隱藏在網頁上;反之,若 ShowAction 的值為"none",當按按鈕事件又被觸發的話,ShowAction 的值就會設定為空字串,這樣 div 標籤內的這些按鈕又會顯示出來了。
因為 WebAssembly 內的 C# 程式碼無法直接存取網頁內的 DOM,所以,將會透 Blazor 提供的 JavaScript Rendering 來幫助進行存取網頁 DOM 內容,因此,當 ShowAction 屬性值有變動的時候,將會透過 Blazor 建立的 Render Tree 呈現樹,透過 Render Tree Rendering 來將 C# 內最新的單向綁定的屬性值,透過 Blazor 提供的 JavaScript 更新到網頁上的 DOM,也就會更新 DOM 中的標籤內的 CSS 屬性值。

執行結果

現在,請執行這個專案,將會在網頁上看如下圖的畫面,此時可以看到有8個按鈕顯示在網頁上
當按下 切換顯示狀態 按鈕,將會看到這8個按鈕就會隱藏起來了




2018年11月20日 星期二

Blazor 1 開發環境的安裝與設定

Blazor 1 開發環境的安裝與設定

更多關於 Blazor 教學影片,可以參考 Blazor 教學影片播放清單 或者 Blazor 快速體驗教學影片撥放清單。也歡迎訂閱本 .NET / Blazor / Xamarin.Forms 影片頻道 。

Blazor 提供可以使用 .NET C# 程式語言來進行開發出網頁 SAP 類型的應用程式,他與微軟前一代 Silverlight 工具不同處在於使用 Blazor 開發出來的專案,不需要透過任網頁插件 Plugins 安裝到瀏覽器上,就可以直接在網頁上執行;這都需要憑藉著 WASM Web Assembly 這個標準,也就是說,現今大部分的瀏覽器都有支援 WASM,因此,透過 Blazor 開發出來的網頁專案,可以直接使用 .NET 開發環境所建置、編譯出來 組件 Assembly 檔案,直接在網頁上執行,而不是將這些 .NET C# 程式語言轉譯成為 JavaScript 語言來執行。在這篇文章中,將會說明如何安裝與設定一個 Blazor 的開發環境,並且使用 Visual Studio 2017 建立起第一個 Blazor,並且實際執行在瀏覽器上,接著,將會說明由 Blazor 專案樣本所建立起來的第一個專案的架構與這個專案內的程式碼是如何設計的。本篇文章的範例原始碼,可以從 BlazorFirstTime 取得。

安裝與設定

在這篇文章所使用的 Visual Studio 2017 為 15.9.1 版本,因此,將會建立起 Blazor 0.7.0 的專案;首先,請執行 Visual Studio Installer 這個程式,並且點選 [修改] 按鈕。
請在 [工作負載] 勾選 [.NET Core 跨平台開發] 項目,這樣會進行安裝 NET Core 2.1 SDK
請在 [工作負載] 勾選 [ASP.NET 與網頁程式開發] 項目
最後,點選右下角的 [修改] 按鈕,開啟進行 Blazor 開發環境所需要的基本套件。
一旦相關 Blazor 開發環境所需要的基本套件安裝完成之後,還需要安裝一個 Blazor 的 Visual Studio 市集 Marketplace 擴充功能套件,請使用瀏覽器打開 ASP.NET Core Blazor Language Services 網頁,下載安裝這個 Viusal Studio 擴充功能。
若想要使用 Visual Studio Code 來進行開發 Blazor 專案,需要安裝 dotnet 命令列下使用的專案樣板,因此,打開 [命令提示字元視窗] ,輸入底下命令
dotnet new -i Microsoft.AspNetCore.Blazor.Templates

建立測試專案

若已經將 Blazor 開發工具安裝與設定完成之後,現在可以開始建立第一個 Blazor 開發專案。
  • 啟動 Visual Studio 2017
  • 點選功能表的 [檔案] > [新增] > [專案] 功能表選項
  • 在 [新增專案] 對話窗中左邊區域,選擇 [已安裝] > [Visual C#] > [Web] > [ASP.NET Core Web 應用程式]
  • 在對話窗的下方的 [名稱] 欄位中,輸入這個練習專案的名稱 BlazorFirstTime
  • 在對話窗的下方的 [位置] 欄位中,選擇這個專案要儲存的檔案路徑
  • 最後點選對話窗的右下方 [確定] 按鈕
  • 在 [新增 ASP.NET Core Web 應用程式] 對話窗左上方區域,在第一個下拉選單,選擇 [.NET Core] 在第二個下拉選單,請選擇最新的 ASP.NET Core 版本,在現在這個時間點,可以選擇 [ASP.NET Core 2.1]
  • 在對話窗中間區域,請點選 [Blazor] 這個項目
  • 最後點選對話窗的右下方 [確定] 按鈕

執行 Blazor 專案

請按下 [Ctrl + F5] 按鈕,開始執行這個 Blazor 專案,此時,瀏覽器將會開啟,顯示這個 Blazor 專案所設計的頁面,如下圖所示。在這個專案樣板所建立的 Blazor 專案,將會展示三個透過 Blazor 元件 Component 所設計的功能,分別是:Home, Counter, Fetch Data。

Blazor 專案架構

現在,請在 Visual Studio 的方案總管視窗上,來檢視 Blazor 專案樣板所建立的專案,是提供了那些檔案內容,以便可以做到上面的網頁效果;從下面方案總管螢幕截圖上,可以看到有兩個資料夾,Pages 資料夾,這裡是要存放 Blazor 元件 Component 的地方,這些元件檔案都是 Razor 類型的檔案,當 Blazor 專案建置成功之後,每個元件檔案 (.cshtml) 將會產生出 C# 類別;第二個資料夾則是 Shared 資料夾,這裡將會存放著共用檔案之用,例如,整個頁面的版面配置 Layout 用的 MainLayout.cshtml 與最左方的功能表選項 NavMenu.cshtml 。
對於該網頁會用到的靜態檔案,如 CSS, JavaScript, 圖片等等,將都會存放在 wwwroot 這個節點之內。而在專案根目錄與 Page 目錄下,都會看到有 _ViewImports.cshtml 這個檔案,其為 Razor 共用指示詞,這對於許多檢視都會用到共用的指示詞,可以定義在這個 _ViewImports.cshtml 檔案中,例如:@addTagHelper@removeTagHelper@tagHelperPrefix@using@model@inherits@inject;最後兩個檔案,那就是 Programs.cs 與 Startup.cs 檔案,這兩個檔案與 ASP.NET Core 專案中的同樣的檔案,將會提供同樣的服務與功能。
對於這個專案的設定檔案,也是相當的精簡,使用滑鼠右鍵點選該專案的節點,選擇 [編輯 BlazorFirstTime.csproj],會出現下面的 XML 內容;在這個 .csproj 檔案內,加入了兩個參考套件: Microsoft.AspNetCore.Blazor.Browser 與 Microsoft.AspNetCore.Blazor.Build ,這兩個套件則是要在開發 Blazor 專案時候會用的。
 BlazorFirstTime.csproj 檔案
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <RunCommand>dotnet</RunCommand>
    <RunArguments>blazor serve</RunArguments>
    <LangVersion>7.3</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Blazor.Browser" Version="0.7.0" />
    <PackageReference Include="Microsoft.AspNetCore.Blazor.Build" Version="0.7.0" PrivateAssets="all" />

    <DotNetCliToolReference Include="Microsoft.AspNetCore.Blazor.Cli" Version="0.7.0" />
  </ItemGroup>

</Project>

Blazor 應用程式啟動流程

現在來看看 Blazor 專案的程式進入點 Program Entry Point ,也就是 Program.cs 檔案,如同 .NET Core 的專案相同,Blazor 專案的程式進入點也是 Main 這個靜態方法,而且在這裡比起 ASP.NET Core 類型專案,顯得更加的精簡,只要在使用 UseBlazorStartup 這個泛型方法,就可以開始啟動與執行 Blazor 專案了。
 Program.cs
public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }
    public static IWebAssemblyHostBuilder CreateHostBuilder(string[] args) =>
        BlazorWebAssemblyHost.CreateDefaultBuilder()
            .UseBlazorStartup<Startup>();
}
同樣的,Blazor 一樣使用 OWIN 框架來設定這個專案的運作與起的功能,不過,可以看到,在底下的 Startup.cs 這個檔案中,程式碼也是相當的精簡,若要設定 .NET Core IoC 容器 Container 的型別註冊,也就是自己開發的抽象介面與具體實作類別對應,可以在 ConfigureServices 這個方法內來執行相關的陳述式。
 Startup.cs
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
    }
    public void Configure(IBlazorApplicationBuilder app)
    {
        app.AddComponent<App>("app");
    }
}
對於根目錄下的 App.cshtml 檔案,在這裡所進行宣告的這些東西是暫時的,之後將會把這裡的宣告移動到 Program.cs ,因此,可以忽略掉這個檔案內容。
當 Blazor 專案啟動之後,可以透過瀏覽器來執行 Blazor 專案的應用程式,預設的開啟網頁將會位於 [wwwroot] 資料夾內的 index.html 檔案,底下為這個檔案的內容。在這裡除了對於網頁畫面的設計使用到 bootstrap 功能,最重要的是這裡會執行 blazor.webassembly.js 這個 JavaScript,這個檔案將會於建置 Blazor 專案的時候會產生出來,所以,一旦 Blazor 專案建置完成之後,可以在方案總管視窗中,點選工具列上的 [顯示所有檔案] 按鈕,就可以在 [bin] > [Debug] > [netstandard2.0] > [dist] > [_framework] 目錄下看到這個檔案。
這個 blazor.webassembly.js JavaScript 將會下載 blazor.boot.json 這個檔案,這裡將會描述 Blazor 專案的 .NET 組件的程式進入點,以及會參考到那些其他 .NET 組件: {"main":"BlazorFirstTime.dll","entryPoint":"BlazorFirstTime.Program::Main","assemblyReferences":["Microsoft.AspNetCore.Blazor.Browser.dll","Microsoft.AspNetCore.Blazor.dll","Microsoft.AspNetCore.Blazor.TagHelperWorkaround.dll","Microsoft.Extensions.DependencyInjection.Abstractions.dll","Microsoft.Extensions.DependencyInjection.dll","Microsoft.JSInterop.dll","Mono.WebAssembly.Interop.dll","mscorlib.dll","System.Core.dll","System.dll","System.Net.Http.dll","BlazorFirstTime.pdb"],"cssReferences":[],"jsReferences":[],"linkerEnabled":true} ,透過這個檔案的描述,便會開始逐一將這些 .NET 組件 Assembly 檔案下載到瀏覽器本地端。
想要看到開啟一個 Blazor 網頁的時候,會下載來些內容與檔案,可以在瀏覽器下按下 [F12] 按鈕,切換到 [網路] 標籤頁次,將會看如下面截圖的更加詳盡的資訊。blazor.webassembly.js 也會下載 mono.wasm 檔案,這是一個 Mono 框架,使用 Wasm 設計出來的 .NET CLR 執行環境,有了這個檔案,就可以開始執行 blazor.boot.json 所提到的各個 .NET 組件 Assembly 檔案了。
因此,Blazor 並不是為了要能夠讓 C# 程式語言可以在網頁上執行,而把 C# 程式語言轉換成為 JavaScript 程式語言,而是把 Blazor 專案內的所有 .NET 組件檔案,透過使用 Wasm 開發出來的 .NET CLR 環境,直接在瀏覽器上執行 .NET 的程式碼;這與微軟之前的 Silverlight 開發框架有所不同,因為想要在瀏覽器上執行 Silverlight 的程式,必須要能夠在瀏覽器上安裝一個 Silverlight 插件,才能夠執行 Silverlight 的程式,而 Blazor 開發出來的程式,則不需要安裝任何插件到瀏覽器上,就可以直接執行 .NET C# 程式碼。
一旦 Blazor 應用程式啟動完成之後,將會透過 DOM 搜尋網頁上的 App 標籤 Tag,將第一個要顯示的 Blazor 元件所產生的相關網頁內容,替換到 App 標籤的 Loading... 文字,所以,當 Blazor 顯示完成之後,將會看到如下圖的 HTML 元素出現在網頁上。
 index.html 檔案內容
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width">
    <title>BlazorFirstTime</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/site.css" rel="stylesheet" />
</head>
<body>
    <app>Loading...</app>

    <script src="_framework/blazor.webassembly.js"></script>
</body>
</html>
Index.cshtml 是個 Razor 檔案,透過 Razor 語法來嵌入到 HTML 標記宣告語言內,就可以在這裡撰寫 C# 程式語言了。
 Index.cshtml
@page "/"

<h1>Hello, world!</h1>

Welcome to your new app.

<SurveyPrompt Title="How is Blazor working for you?" />
在這個由專案樣板產生的範例專案中,提供了三個由 Blazor 元件所設計出來的畫面,第一個元件 Index.cshtml 則是說明如何使用元件
第二個元件則是 Counter.cshtml 將會展示如何撰寫出一個按鈕元素,並且使用 C# 程式語言來設計該按鈕的點選事件,在這以往網頁應用程式開發環境下,一定需要透過 JavaScript 程式語言的呼叫,才能夠完成這樣的需求,如今,透過 Blazor 的技術,可以使用 C# 程式語言就可以直接做到了。
@page "/counter"

<h1>Counter</h1>

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

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

@functions {
    int currentCount = 0;

    void IncrementCount()
    {
        currentCount++;
    }
}
最後一個 Blazor 元件為 FetchData.cshtml ,在這裡將會說明如何使用 Blazor 的生命週期事件,當這個元件已經準備好了,將會觸發 OnInitAsync 非同步事件,因此,可以在這個事件內使用相依性注入設計模式,取得 HttpClient 這個類別實作物件,進行網路資源存取,並且配合 Razor 語法將這抓取到遠端 JSON 資料,把這些集合物件顯示在瀏覽器上。
@page "/fetchdata"
@inject HttpClient Http

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from the server.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>
}

@functions {
    WeatherForecast[] forecasts;

    protected override async Task OnInitAsync()
    {
        forecasts = await Http.GetJsonAsync<WeatherForecast[]>("sample-data/weather.json");
    }

    class WeatherForecast
    {
        public DateTime Date { get; set; }
        public int TemperatureC { get; set; }
        public int TemperatureF { get; set; }
        public string Summary { get; set; }
    }
}