2024年4月22日 星期一

在Blazor使用Fluent UI元件 - 1

作者:許薰尹  精誠資訊/恆逸教育訓練中心資深講師


Fluent UI Blazor 是一個由微軟開發的開源元件庫,可以讓程式開發人員在 Blazor 應用程式中使用 Fluent UI 來設計系統。Fluent UI 是一種設計套件,可幫助開發人員建立美觀、一致且易於使用的使用者介面。

 
Fluent UI Blazor 元件庫目前包含了超過 40 幾個元件,涵蓋了應用程式或網站中常見的使用者介面元素,例如按鈕、文字方塊、下拉式選單、表格、圖表等等。這些元件經過精心設計,可與 Fluent UI 設計系統的其餘部分無縫整合。

 
Fluent UI Blazor 元件庫的主要功能包括:
• 豐富的元件庫:包含超過 40 幾個常用元件,可滿足各種使用者介面需求。
• 易於使用:元件採用簡單易懂的 API,可快速上手,學習曲線短。
• 允許客製化:元件可根據需要進行客製化,以符合特定的設計的要求。
• 無障礙性:元件遵循無障礙性最佳做法,可供所有人使用。

 
Fluent UI Blazor 元件庫適用於以下場景:
• 建立新的 Blazor 應用程式。
• 將 Fluent UI 設計系統到用到現有的 Blazor 應用程式之中。
• 加速 Blazor 應用程式的使用者介面設計動作。

 
使用 Fluent UI Blazor 元件庫的優點包括:
• 可提高開發效率:使用現成的元件可節省開發時間和精力。
• 提升使用者體驗:Fluent UI 設計系統可幫助建立美觀、一致且易於使用的使用者介面。
• 降低維護成本:元件經過良好測試和維護,可降低應用程式的維護成本。

在這一篇文章中,我們將介紹一些適用於.NET 8,可在Blazor應用程式使用的Fluent UI元件基本功能。

安裝 Microsoft.FluentUI.AspNetCore.Templates套件
想要在Blazor應用程式之中開始使用 Fluent UI Blazor 元件進行設計的最簡單方法,就是利用官方提供的專案範本。專案範本可從以下網址下載:

https://www.nuget.org/packages/Microsoft.FluentUI.AspNetCore.Templates 

請參考下圖所示:


 

圖 1:專案範本。



在作業系統命令提示字元執行下列命令來安裝套件範本:
dotnet new install Microsoft.FluentUI.AspNetCore.Templates::4.6.0
執行結果請參考下圖所示:


 

圖 2:安裝套件範本。


範本安裝完成之後,就可以透過Visual Studio 2022來建立專案。

 
建立Blazor專案


首先使用Visual Studio 2022開發工具建立一個Blazor專案,建立步驟如下:啟動Visual Studio 2022開發環境,從「開始」視窗選取「Create a new project」選項。從Visual Studio 2022開發工具的「Create a new project」對話盒中,你可以看到許多「Fluent Blazor」字串開頭的範本,請參考下圖所示:





圖 3:「Blazor Web App 」項目。


選取「Create a new project」對話盒中的「Fluent Blazor Web App 」項目,然後按一下「Next」按鈕,下一步,在「Configure your new project」視窗中,設定Blazor專案名稱與專案存放路徑,然後按下「Next」按鈕,請參考下圖所示:


 


圖 4:設定Blazor專案名稱與專案存放路徑。


下一步,在「Additional information」視窗中,設定「Target Framework」為「NET 8.0 (Long Term Support)」;將「Authentication Type」設為「None」;「Interactive render mode」選擇「Auto(Server and WebAssembly)」;「Interactivity location」設定為「Per page/component」,然後勾選「Include sample pages」,然後按下「Create」按鈕,請參考下圖所示:


 


圖 5:設定「Target Framework」為「NET 8.0 (Long Term Support)」。


專案建立完成之後,從「Solution Explorer」視窗中可以看到目前包含兩個專案:其中「BlazorFluentUI」是伺服端專案,專案中包含的是Blazor Server元件;「BlazorFluentUI.Client」是用戶端專案,這專案將會以WebAssmbly的方式執行在用戶端瀏覽器,請參考下圖所示:


 


圖 6:專案結構。



從伺服端專案的「Packages」項目中,可以看到其中包含FluentUI相依套件,請參考下圖所示:

 


圖 7:伺服端專案FluentUI相依套件。


同樣在用戶端專案中,也可以看到其中包含FluentUI相依套件,請參考下圖所示:

 


圖 8:用戶端專案FluentUI相依套件。


在Visual Studio 2022開發工具,按CTRL+F5執行網站,目前網站首頁的執行結果參考如下圖所示,

 


圖 9:範本網站執行結果。


讓我們探討一下專案範本的基本設定,在「Program.cs」檔案中可以看到這行程式碼「builder.Services.AddFluentUIComponents();」設定 Dependency Injection (DI) 容器,將 Fluent UI 的組件註冊到 DI 容器中。這樣在其他地方需要使用這些組件時,就可以透過 DI 容器來取得,而不需要手動創建實例。這種方式可以提高程式的模組化和可測試性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using BlazorFluentUI.Client.Pages;
using BlazorFluentUI.Components;
using Microsoft.FluentUI.AspNetCore.Components;
 
var builder = WebApplication.CreateBuilder( args );
 
// Add services to the container.
builder.Services.AddRazorComponents( )
    .AddInteractiveServerComponents( )
    .AddInteractiveWebAssemblyComponents( );
builder.Services.AddFluentUIComponents( );
 
var app = builder.Build( );
 
// Configure the HTTP request pipeline.
if ( app.Environment.IsDevelopment( ) ) {
  app.UseWebAssemblyDebugging( );
}
else {
  app.UseExceptionHandler( "/Error", createScopeForErrors: true );
  // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
  app.UseHsts( );
}
 
app.UseHttpsRedirection( );
 
app.UseStaticFiles( );
app.UseAntiforgery( );
 
app.MapRazorComponents<App>( )
    .AddInteractiveServerRenderMode( )
    .AddInteractiveWebAssemblyRenderMode( )
    .AddAdditionalAssemblies( typeof( BlazorFluentUI.Client._Imports ).Assembly );
 
app.Run( );


在「MainLayout.razor」檔案可看到預設使用 Fluent UI 的組件來定義一個網頁的基本版面配置,以確保所有網頁的外觀和風格一致,參考以下範例程式碼:

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@inherits LayoutComponentBase
 
<FluentLayout>
    <FluentHeader>
        BlazorFluentUI
    </FluentHeader>
    <FluentStack Class = "main" Orientation = "Orientation.Horizontal" Width = "100%">
        <NavMenu />
        <FluentBodyContent Class = "body-content">
            <div class = "content">
                @Body
            </div>
        </FluentBodyContent>
    </FluentStack>
    <FluentFooter>
        <div class = "link1">
            <a href = "https://www.fluentui-blazor.net" target = "_blank"> Documentation and demos </a>
        </div>
        <div class = "link2">
            <a href = "https://learn.microsoft.com/en-us/aspnet/core/blazor" target = "_blank"> About Blazor </a>
        </div>
    </FluentFooter>
</FluentLayout>
 
<div id = "blazor-error-ui">
    An unhandled error has occurred.
    <a href = "" class = "reload"> Reload </a>
    <a class = "dismiss"> x </a>
</div>


 
使用了 Razor 語法定義 Blazor 組件的版面。它使用了 Fluent UI 的組件來建立一個具有表頭、主體和表尾的網頁版面。其中的組件說明如下:
FluentLayout:  這是一個容器組件,用於包含整個頁面的版面。
FluentHeader:  這是表頭組件,顯示網站的名稱為「BlazorFluentUI」。
FluentStack:  這是一個堆疊組件,用於將子組件以堆疊的方式排列。在這裡,它包含了一個導覽選單 () 和主體內容 ()。
FluentBodyContent : 這是主體內容組件,裡面的 @Body 是一個 Razor定位點,用於插入其他 Razor 組件或 HTML 內容。
FluentFooter : 這是表尾組件,裡面包含了兩個連結,分別指向 Fluent UI 的文檔和關於 Blazor 的資訊。

用戶端有一個「Counter.razor」組件,參考以下程式碼:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@page "/counter"
@rendermode InteractiveAuto
 
<PageTitle> Counter </PageTitle>
 
<h1> Counter </h1>
 
<div role = "status" style = "padding-bottom: 1em;">
    Current count: <FluentBadge Appearance = "Appearance.Neutral"> @currentCount </FluentBadge>
</div>
 
<FluentButton Appearance = "Appearance.Accent" @onclick = "IncrementCount"> Click me </FluentButton>
 
@code {
    private int currentCount = 0;
 
    private void IncrementCount()
    {
        currentCount++;
    }
}

 
「Counter.razor」組件使用了 Fluent UI 的「FluentBadge」組件來顯示當前的計數值,以及Fluent UI 的 FluentButton 組件做為一個可以點擊來增加計數值的按鈕,「Appearance = "Appearance.Accent」 是用來設定這個按鈕的外觀風格。「Counter.razor」組件執行的結果,請參考下圖所示:


 

 

 


圖 10:使用Fluent UI組件設計組件畫面。


再來看看伺服端專案的「Weather.razor」組件程式碼:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@page "/weather"
@attribute [StreamRendering]
 
<PageTitle> Weather </PageTitle>
 
<h1>  Weather </h1>
 
<p> This component demonstrates showing data. </p>
 
@if ( forecasts == null )
{
    <p><em> Loading... </em></p>
}
else
{
    <!-- This page is rendered in SSR mode, so the FluentDataGrid component does not offer any interactivity (like sorting). -->
    <FluentDataGrid Id = "weathergrid" Items = "@forecasts" GridTemplateColumns = "1fr 1fr 1fr 2fr" TGridItem = "WeatherForecast">
        <PropertyColumn Title = "Date" Property = "@(c => c!.Date)" Align = "Align.Start"/>
        <PropertyColumn Title = "Temp. (C)" Property = "@(c => c!.TemperatureC)" Align = "Align.Center"/>
        <PropertyColumn Title = "Temp. (F)" Property = "@(c => c!.TemperatureF)" Align = "Align.Center"/>
        <PropertyColumn Title = "Summary" Property = "@(c => c!.Summary)" Align = "Align.End"/>
    </FluentDataGrid>
}
 
@code {
    private IQueryable<WeatherForecast>? forecasts;
 
    protected override async Task OnInitializedAsync()
    {
        // Simulate asynchronous loading to demonstrate streaming rendering
        await Task.Delay(500);
 
        var startDate = DateOnly.FromDateTime( DateTime.Now );
        var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
        forecasts = Enumerable.Range(1, 5).Select( index => new WeatherForecast
        {
            Date = startDate.AddDays( index ),
            TemperatureC = Random.Shared.Next( -20, 55 ),
            Summary = summaries[ Random.Shared.Next(summaries.Length) ]
        }).AsQueryable();
    }
 
    private class WeatherForecast
    {
        public DateOnly Date { get; set; }
        public int TemperatureC { get; set; }
        public string? Summary { get; set; }
        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    }
}



「Weather.razor」組件程式使用了 Fluent UI 的「FluentDataGrid」組件來顯示一個天氣預報的表格資料。「FluentDataGrid」組件的屬性設定如下:

 「Virtualize = "true"」: 啟用虛擬化,這可以提高大量數料集的渲染效能。
 「Items = "@forecasts":」:設定資料來源為「forecasts」。
 「GridTemplateColumns = "1fr 1fr 1fr 2fr"」: 設定表格的欄寬,這裡定義了四個直欄,每個直欄的寬度分別為 1fr、1fr、1fr 和 2fr。
 「TGridItem = "WeatherForecast」: 設定表格的資料類型為「WeatherForecast」。
 「PropertyColumn」組件用於定義表格的直欄。每個 PropertyColumn 組件透過資料繫結關聯到「WeatherForecast」型別的一個屬性,並設定了直欄的標題 (Title)和對齊方式 (Align)。

「Weather.razor」組件執行的結果,請參考下圖所示,可以看到畫面中的表格,顯示天氣預報的資訊,包括日期、攝氏溫度、華氏溫度和天氣總結。並且,每格直欄都可以進行排序,用戶可以點擊直欄的標題來變更資料的排序方式。

 

 



圖 11:「FluentDataGrid」組件。

 

使用Sortable設定排序


Blazor 提供了三種渲染模式:Server、WebAssembly 和 ServerPrerendered。「InteractiveServer」模式是Server渲染的一種模式,在「InteractiveServer」模式下,組件的執行和狀態管理都在伺服器端進行,並通過 SignalR 連線與客戶端進行即時的通訊。當用戶與組件互動(如點擊按鈕),事件會被發送到伺服器端進行處理,然後伺服器端將更新的 UI 差異發送回客戶端進行渲染。
Fluent UI提供的組件有些只能夠在伺服端專案渲染,不能在用戶端渲染,例如的排序功能就必需設定成伺服端渲染,要設定 Blazor 組件的渲染模式,可以使用「@rendermode」,修改程式如下,將「@rendermode」設為「InteractiveServer」;然後將「PropertyColumn」的「Sortable」設定為「true」:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@page "/weather"
@attribute [StreamRendering]
 
<PageTitle> Weather </PageTitle>
 
<h1>  Weather </h1>
 
<p> This component demonstrates showing data. </p>
 
@if ( forecasts == null )
{
    <p><em> Loading... </em></p>
}
else
{
    <!-- This page is rendered in SSR mode, so the FluentDataGrid component does not offer any interactivity (like sorting). -->
    <FluentDataGrid Id = "weathergrid" Items = "@forecasts" GridTemplateColumns = "1fr 1fr 1fr 2fr" TGridItem = "WeatherForecast">
        <PropertyColumn Title = "Date" Property = "@(c => c!.Date)" Align = "Align.Start"/>
        <PropertyColumn Title = "Temp. (C)" Property = "@(c => c!.TemperatureC)" Align = "Align.Center"/>
        <PropertyColumn Title = "Temp. (F)" Property = "@(c => c!.TemperatureF)" Align = "Align.Center"/>
        <PropertyColumn Title = "Summary" Property = "@(c => c!.Summary)" Align = "Align.End"/>
    </FluentDataGrid>
}
 
@code {
    private IQueryable<WeatherForecast>? forecasts;
 
    protected override async Task OnInitializedAsync()
    {
        // Simulate asynchronous loading to demonstrate streaming rendering
        await Task.Delay(500);
 
        var startDate = DateOnly.FromDateTime( DateTime.Now );
        var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
        forecasts = Enumerable.Range(1, 5).Select( index => new WeatherForecast
        {
            Date = startDate.AddDays( index ),
            TemperatureC = Random.Shared.Next( -20, 55 ),
            Summary = summaries[ Random.Shared.Next(summaries.Length) ]
        }).AsQueryable();
    }
 
    private class WeatherForecast
    {
        public DateOnly Date { get; set; }
        public int TemperatureC { get; set; }
        public string? Summary { get; set; }
        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    }
}

 

「Weather.razor」組件執行的結果,請參考下圖所示,點選欄位表頭就可以進行排序:


 

 


圖 12:使用Sortable設定排序。


另一種設定「@rendermode」的方式是改用路由,如此個別元件就不需要進行設定,在「App.razor」加入以下程式碼:
App.razor

虛擬化(Virtualize)


FluentDataGrid 有一個「Virtualize」選項,用於啟用組件的虛擬化功能。虛擬化是一種效能優化的技術,用於處理大量的資料。當啟用虛擬化時,Blazor 只會渲染當前在視窗中可見的部分資料,而不是渲染整個資料集。當用戶捲動視窗時,Blazor 會自動地新增和移除元素,以確保只有可見的部分被渲染。這種方式可以大大提高渲染大型資料集的效能,因為它避免了不必要的渲染工作。這對於那些需要顯示大量資料的應用來說,是非常有用的。
我們先試試沒有啟用虛擬化時,程式運作的情況,首先修改Weather.razor程式碼,將Enumerable.Range方法第二個參數設定為「2000」模擬大量天氣資料,參考以下程式碼:

1
2
3
4
5
6
forecasts = Enumerable.Range( 1, 2000 ).Select( index => new WeatherForecast
{
    Date = startDate.AddDays( index) ,
    TemperatureC = Random.Shared.Next( -20, 55 ),
    Summary = summaries[ Random.Shared.Next(summaries.Length) ]
}).AsQueryable();


「Weather.razor」組件執行的結果,請參考下圖所示,所有資料一次呈現在網頁上:


 

 


圖 13:未啟用虛擬化。



修改「FluentDataGrid」組件程式碼,設定「Virtualize = "true"」,參考以下程式碼:

1
2
3
4
5
6
<FluentDataGrid Virtualize = "true" Id = "weathergrid" Items = "@forecasts" GridTemplateColumns = "1fr 1fr 1fr 2fr" TGridItem = "WeatherForecast">
      <PropertyColumn Title = "Date" Sortable = "true" Property = "@(c => c!.Date)" Align = "Align.Start"/>
  <PropertyColumn Title = "Temp. (C)" Sortable = "true" Property = "@(c => c!.TemperatureC)" Align = "Align.Center" />
  <PropertyColumn Title = "Temp. (F)" Sortable = "true"  Property = "@(c => c!.TemperatureF)" Align = "Align.Center" />
  <PropertyColumn Title = "Summary" Sortable = "true" Property = "@(c => c!.Summary)" Align = "Align.End" />
  </FluentDataGrid>



這次「Weather.razor」組件執行的結果,請參考下圖所示,第一次只載入部份資料,當捲動畫面到下方時,才動態呈現其它資料:


 

 


圖 14:啟用虛擬化。

 

總結


微軟的 Fluent UI Blazor 元件庫雖然是開源項目,但它並不是 ASP.NET Core 的正式組成部分,因此沒有官方支援,也不保證會與任何 .NET 更新一起發佈。儘管如此,它仍然由微軟員工 (和其他貢獻者) 建置和維護,並提供與大多數其他開源項目類似的支援,利用 GitHub 倉庫盡力協助使用者。
Fluent UI Blazor 元件庫是一個功能強大且易於使用的工具,可幫助開發人員在 Blazor 應用程式中使用 Fluent UI 設計系統。如果您正在尋找一種方法來提升 Blazor 應用程式的使用者介面設計,那麼 Fluent UI Blazor 元件庫是一個不錯的選擇。


學習推薦


0 意見:

張貼留言