2024: Microsoft のチュートリアルの Blazor Todo リスト アプリを Radzen に変換

## Radzen の Blazor テンプレート
[FluentUI](https://qiita.com/Meister619/items/bd290564b9b4ce54b111) や [MudBlazor UI](https://qiita.com/Meister619/items/deb9e1bd805c92de97fb) コンポーネント フレームワークとは異なり、Radzen フレームワークは Visual Studio 用のテンプレート拡張機能を提供しません。手動でインストールする必要があります。

## Radzen プロジェクトの新規作成
1. Visual Studio 2022を開いて
2. プロジェクトの新規作成を押下
![project_create.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531993/ddb0a302-c56c-e48b-e5e2-17b37d34583d.png)
3. Fluent Blazorのウエブアプリテンプレートを選択
4. 「次」を押下
![blazor_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531993/d37c65f2-9ec3-8d10-5750-faedc411fece.png)
5. プロジェクト名を入力
6. 「次」を押下
![rbtl_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531993/b34759ba-9013-cdc7-8bc6-6ba5fcca0973.png)
7. 「作成」を押下
![fluent_create_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531993/7d64dc7d-4cf3-f569-ba6c-6b7dcd47a26c.png)

### デフォルトのアプリ
デフォルトのアプリはまだ [Radzen](https://blazor.radzen.com/) を使用していせん！

### Radzen フレームワークをインストール
1. ソリューションを右クリック
2. 右クリックすると表示されるコンテキストメニューで Manage NuGet Packages for Solution... を選択
![rbtl_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531993/a4839cfe-2a65-6211-bc3d-28825497e7de.png)
3. NuGet - Solution ペインの Browse タブを選択
4. 検索ボックスで radzen を入力
5. 左側にあるパッケージ一覧で Radzen.Blazor を選択
6. 左側に表示されるパッケージ詳細で「プロジェクト」のチェックボックスを選択
7. 現れた「インストール」ボタンを押下
![rbtl_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531993/4c847d9b-e794-a36b-14d1-ce54f1f4df84.png)
8. 表示された変更プレビューのダイアログの「適用」ボタンを押下
![rbtl_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531993/af4cd1d4-7d01-65ac-a59c-055d54c5be6c.png)
9. 表示された来仙背への同意の「同意する」ボタンを押下
![rbtl_8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531993/1e96843f-ca99-3ffe-b12a-2429b573b6a6.png)
10. Blazor アプリケーションの`Components\_Imports.razor`ファイルに次の２行を追加：
    ```csharp
    @using Radzen
    @using Radzen.Blazor
    ```
11. Blazor アプリケーションの`Components\App.razor`ファイルに次のどっちかのスニペットを追加してテーマ CSS ファイルを作成：
    ```csharp
     // マテリアルテーマの場合
    <link rel="stylesheet" href="_content/Radzen.Blazor/css/material-base.css">
    // 標準テーマの場合
    <link rel="stylesheet" href="_content/Radzen.Blazor/css/standard-base.css">
    ```
12. ブートストラップ CSS も用意されていることを確認
    ```csharp
     <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css">
    ```
13. Blazor アプリケーションの`Components\App.razor`ファイルに次のスニペットを追加して`Radzen.Blazor.js`をインクルード
     ```csharp
     <script src="_content/Radzen.Blazor/Radzen.Blazor.js?v=@(typeof(Radzen.Colors).Assembly.GetName().Version)"></script>
    ```
14. **Dialog**、**Notification**、**ContextMenu**、**Tooltip** コンポーネントを使用したい場合はBlazor アプリケーションの各ページの上のほうに次のスニペットを追加して：
    ```csharp
     <RadzenComponents/>
    ```
15. **Radzen** サービスを開始するためにBlazor アプリケーションの`Program.cs`ファイルに次のスニペットを追加して：
    ```csharp
     builder.Services.AddRadzenComponents();
    ```
## Todo リスト アプリを構築する
このアプリを、概要ページを備えた ToDo リスト アプリに変換しましょう。
### 不要機能の削除
まず、「Counter（カウンター）」と「Weather（天気）」のページを削除しましょう。
#### ファイル削除
1. `Components/Pages/Counter.razor`と`Components/Pages/Weather.razor`のファイルを選択
2. 右クリックすると表示されるコンテキストメニューで Delete を選択
![rbtl_9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531993/70bfd764-08b5-0433-189a-d04c1d4019f5.png)
#### リンク処理
`Components/Layout/NavMenu.razor`ファイルの中で：
1. 1番目の「Home（ホーム）」のリンクの名前を「Todo リスト」に変更
2. 1番目のリンクのアイコンをチェックされたチェックボックスに変更
    ```
    <RadzenStack Orientation="Orientation.Vertical" Gap="4px"  class="rz-p-4">
        <RadzenSidebarToggle Style="max-width: 3.6rem;"/>
        <RadzenPanelMenu DisplayStyle="MenuItemDisplayStyle.IconAndText" ShowArrow="false">
            <RadzenPanelMenuItem Text="Todo リスト" Path="/" Icon="check_box" />
            <RadzenPanelMenuItem Text="About" Path="/about" Icon="info_outline" />
        </RadzenPanelMenu>
    </RadzenStack>
    ```
### Todo リスト追加
1. `Components/Pages/Home.razor`を開いて
2. 相対 URL `/` とともに @page ディレクティブを追加。
3. そのまま静的にレンダリングされないように、ページの対話機能を有効にします。 対話型サーバー レンダリング モードにすると、コンポーネントでサーバーからの UI イベントを処理できます。
4. ページに HTML <title> 要素を追加することを可能にする PageTitle コンポーネントを使用してページ タイトルを追加します。
    ```
    @page "/"
    @rendermode InteractiveServer
    <RadzenComponents/>
    
    <PageTitle>Todo</PageTitle>
    
    <h3>Todo</h3>
    
    @code {
    
    }
    ```
5. プロジェクト（`RadzenBlazorTodoList`）フォルダーを右クリックすると表示されるコンテキストメニューで Add ＞ New Folder を選択
![rbtl_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531993/5510fbcb-10da-06d7-0fd4-d3576dd878ab.png)
6. モデル クラスの保持に使用するため、新しいフォルダーを「Models」に名付け
7. `Models`フォルダーを右クリックすると表示されるコンテキストメニューで Add ＞ New Item を選択
![rbtl_12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531993/e946c0ca-508d-f877-ad81-b09da6c15f81.png)
8.  Todo アイテムを表すクラスを保持するために、プロジェクト `Models`フォルダーに `TodoItem.cs` ファイルを追加
![rbtl_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531993/982a33b7-e6d7-9a17-da1d-ce6af8a13450.png)
9. 次の C# コードを挿入して `TodoItem` クラスを肉付け
    ```
    namespace RadzenBlazorTodoList.Models
    {
        public class TodoItem
        {
            public string? Title { get; set; }
            public DateTime? DueDate { get; set; }
            public bool IsDone { get; set; }
        }
    }
    ```
10. Todo コンポーネントに戻り
11. @namespace でモデルの `TodoItem` クラスを使用可能にする
12. Todo アイテム用のフィールドを @code ブロックに追加（Todo コンポーネントでは、このフィールドを使って ToDo リストの状態を維持）
13. リストを表す縦のフレックスボックス列（[`RadzenStack`](https://blazor.radzen.com/stack)）を追加
14. Todo リスト（[`RadzenDataList`](https://blazor.radzen.com/datalist)）の中、各 Todo アイテム（`Template`）を横のフレックスボックス（[`RadzenStack`](https://blazor.radzen.com/stack)）を含むカード（[`RadzenCard `](https://blazor.radzen.com/card)）としてレンダリング
    ```
    @page "/"
    @rendermode InteractiveServer
    @namespace RadzenBlazorTodoList.Models
    <RadzenComponents/>
    <PageTitle>Todo</PageTitle>
    
    <h3>Todo</h3>
    
    <RadzenStack Orientation="Orientation.Vertical" AlignItems="AlignItems.Center" >
        <RadzenDataList WrapItems="true" AllowPaging="true" 
                        Data="@todos" TItem="TodoItem" PageSize="5" PagerHorizontalAlign="HorizontalAlign.Left" ShowPagingSummary="true">
            <Template Context="todo">
                <RadzenCard Style="height:64px;">
                    <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center">
                        <RadzenCheckBox @bind-Value="@(todo.IsDone)" Name="CheckBox1" />
                        <RadzenLabel Text="Is Done?" Component="CheckBox1" />
                        <RadzenLabel Text="Title" Component="TextBox1" />
                        <RadzenTextBox Name="TextBox1" @bind-Value="@(todo.Title)" Placeholder="title" />
                        <RadzenLabel Text="Due" Component="DatePicker1" />
                        <RadzenDatePicker Name="DatePicker1" @bind-Value="@(todo.DueDate)" />
                    </RadzenStack>
                </RadzenCard>
            </Template>
        </RadzenDataList>
    </RadzenStack>
    
    @code {
        private List<Models.TodoItem> todos = new();
    }
    ```
15. Todo リスト（[`RadzenDataList`](https://blazor.radzen.com/datalist)）の縦のフレックスボックス列（[`RadzenStack`](https://blazor.radzen.com/stack)）の下に、新しい Todo 項目を追加する UI 要素を追加
16. 追加 UI のコントロールを含む横のスタック（[`RadzenStack`](https://blazor.radzen.com/stack)）を追加
17. 新しい Todo 項目のタイトル用にテキストボックス（[`RadzenTextBox`](https://blazor.radzen.com/textbox)）を追加
18. 新しい Todo 項目の期限用の日付ピッカー（[`RadzenDatePicker`](https://blazor.radzen.com/datepicker)）を追加
19. 追加アクションを実行するための「Add Todo」ボタン（[`RadzenButton`](https://blazor.radzen.com/button)）を追加
    ```
    @page "/"
    @rendermode InteractiveServer
    @namespace RadzenBlazorTodoList.Models
    <RadzenComponents/>
    
    <PageTitle>Todo</PageTitle>
    
    <h3>Todo</h3>
    
    <RadzenStack Orientation="Orientation.Vertical" AlignItems="AlignItems.Center" >
        <RadzenDataList WrapItems="true" AllowPaging="true" 
                        Data="@todos" TItem="TodoItem" PageSize="5" PagerHorizontalAlign="HorizontalAlign.Left" ShowPagingSummary="true">
            <Template Context="todo">
                <RadzenCard Style="height:64px;">
                    <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center">
                        <RadzenCheckBox @bind-Value="@(todo.IsDone)" Name="CheckBox1" />
                        <RadzenLabel Text="Is Done?" Component="CheckBox1" />
                        <RadzenLabel Text="Title" Component="TextBox1" />
                        <RadzenTextBox Name="TextBox1" @bind-Value="@(todo.Title)" Placeholder="title" />
                        <RadzenLabel Text="Due" Component="DatePicker1" />
                        <RadzenDatePicker Name="DatePicker1" @bind-Value="@(todo.DueDate)" />
                    </RadzenStack>
                </RadzenCard>
            </Template>
        </RadzenDataList>
        <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center">
            <RadzenLabel Text="New Todo" Component="TextBox2" />
            <RadzenTextBox Name="TextBox2" Placeholder="Something todo" />
            <RadzenLabel Text="Due" Component="DatePicker2" />
            <RadzenDatePicker Name="DatePicker2" TValue="DateOnly" />
             <RadzenButton Variant="Variant.Filled" Icon="add_circle_outline" Text="Add todo" ButtonStyle="ButtonStyle.Primary" />
       </RadzenStack>
    </RadzenStack>
    
    @code {
        private List<Models.TodoItem> todos = new();
    }
    ```
現時点では、ボタンにイベント・ハンドラーがアタッチされていないため、「Add Todo」ボタン（[`RadzenButton`](https://blazor.radzen.com/button)）を押しても何も起こりません。
20. Todo アイテムの追加アクションを処理するには、**@code** ブロックに AddTodo メソッドを追加

    @code {
        private List<Models.TodoItem> todos = new();
        private void AddTodo()
        {
            // // Todo: todo アイテムを追加
        }
    }
21 . 次に、**Click** 属性を使用して、AddTodo メソッドを「Add Todo」ボタンのクリック・イベント ハンドラーとして登録

    <RadzenButton Variant="Variant.Filled" Icon="add_circle_outline" Text="Add todo" ButtonStyle="ButtonStyle.Primary" C/>

22 . 新しい Todo アイテムのタイトルを取得するため、**@code** ブロックの先頭に `newTodoTitle` **String** フィールドを追加
23 . 新しい Todo アイテムの期限を取得するため、`newTodoDueDate` **DateTime** フィールドを **@code** ブロックの先頭に追加
24 . `AddTodo` メソッドを更新して、指定されたタイトルと期限を持つ `TodoItem` をリストに追加
25 . `newTodoTitle` を空の文字列に設定し、`newTodoDueDate` を **null** に設定して、テキストボックス（[`RadzenTextBox`](https://blazor.radzen.com/textbox)）と日付ピッカー（[`RadzenDatePicker`](https://blazor.radzen.com/datepicker)）の値をクリア

    @code {
        string? newTodoTitle;
        DateTime? newTodoDueDate;
        private List<Models.TodoItem> todos = new();
        private void AddTodo()
        {
            if (!string.IsNullOrWhiteSpace(newTodoTitle))
            {
                todos.Add(new TodoItem { Title = newTodoTitle, DueDate = newTodoDueDate });
                newTodoTitle = string.Empty;
                newTodoDueDate = null;
            }
        }
    }
26 . **@bind-Value** 属性を使用して、`newTodoTime` を テキストボックス（[`RadzenTextBox`](https://blazor.radzen.com/textbox)）要素にバインド
28 .   **@bind-Value** 属性を使用して、`newTodoDueDate` を日付ピッカー（[`RadzenDatePicker`](https://blazor.radzen.com/datepicker)）要素にバインド

        <RadzenLabel Text="New Todo" Component="TextBox2" />
        <RadzenTextBox Name="TextBox2" Placeholder="Something todo" @bind-Value=newTodoTitle />
        <RadzenLabel Text="Due" Component="DatePicker2" />
        <RadzenDatePicker Name="DatePicker2" @bind-Value=newTodoDueDate />

29 . Todo リストの前にカウンター・バッジ（[`RadzenBadge`](https://blazor.radzen.com/badge)）を追加して、完了していない Todo 項目の数を表示する (IsDone は false)。 Blazor がコンポーネントを再レンダリングするたびにその Razor 式が評価される

    <RadzenBadge BadgeStyle="BadgeStyle.Warning" Shade="Shade.Lighter" IsPill="@true"  class="rz-ml-2" >
        <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Normal">
            <RadzenIcon Icon="email" />
            <RadzenText TextStyle="TextStyle.Body1" >@(todos.Count(todo => !todo.IsDone).ToString())</RadzenText>
        </RadzenStack>
    </RadzenBadge>
#### 完成
    ```
    @page "/"
    @rendermode InteractiveServer
    @namespace RadzenBlazorTodoList.Models
    <RadzenComponents/>
    
    <PageTitle>Todo</PageTitle>
    
    <RadzenText TextStyle="TextStyle.H3">Todo</RadzenText>
    
    <RadzenStack Orientation="Orientation.Vertical" AlignItems="AlignItems.Center" >
        <RadzenBadge BadgeStyle="BadgeStyle.Warning" Shade="Shade.Lighter" IsPill="@true"  class="rz-ml-2" >
            <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Normal">
                <RadzenIcon Icon="email" />
                <RadzenText TextStyle="TextStyle.Body1" >@(todos.Count(todo => !todo.IsDone).ToString())</RadzenText>
            </RadzenStack>
        </RadzenBadge>
        <RadzenDataList WrapItems="true" AllowPaging="true" 
                        Data="@todos" TItem="TodoItem" PageSize="5" PagerHorizontalAlign="HorizontalAlign.Left" ShowPagingSummary="true">
            <Template Context="todo">
                <RadzenCard Style="height:64px;">
                    <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center">
                        <RadzenCheckBox @bind-Value="@(todo.IsDone)" Name="CheckBox1" />
                        <RadzenLabel Text="Is Done?" Component="CheckBox1" />
                        <RadzenLabel Text="Title" Component="TextBox1" />
                        <RadzenTextBox Name="TextBox1" @bind-Value="@(todo.Title)" Placeholder="title" />
                        <RadzenLabel Text="Due" Component="DatePicker1" />
                        <RadzenDatePicker Name="DatePicker1" @bind-Value="@(todo.DueDate)" />
                    </RadzenStack>
                </RadzenCard>
            </Template>
        </RadzenDataList>
        <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center">
            <RadzenLabel Text="New Todo" Component="TextBox2" />
            <RadzenTextBox Name="TextBox2" Placeholder="Something todo" @bind-Value=newTodoTitle />
            <RadzenLabel Text="Due" Component="DatePicker2" />
            <RadzenDatePicker Name="DatePicker2" @bind-Value=newTodoDueDate />
            <RadzenButton Variant="Variant.Filled" Icon="add_circle_outline" Text="Add todo" ButtonStyle="ButtonStyle.Primary" Click="AddTodo"/>
        </RadzenStack>
    </RadzenStack>
    
    @code {
        string? newTodoTitle;
        DateTime? newTodoDueDate;
        private List<Models.TodoItem> todos = new();
        private void AddTodo()
        {
            if (!string.IsNullOrWhiteSpace(newTodoTitle))
            {
                todos.Add(new TodoItem { Title = newTodoTitle, DueDate = newTodoDueDate });
                newTodoTitle = string.Empty;
                newTodoDueDate = null;
            }
        }
    }
    ```
![rbtl_14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531993/8fa04cdf-9005-4ff2-1c34-8110ab33f69d.png)

### About ページ追加
1. `Components/Pages`フォルダーを右クリックすると表示されるコンテキストメニューで Add ＞ New Component を選択
2. 「About.razor」を名付けて
3. 「追加」を押下
4. `Components/Pages/About.razor`を開いて
5. 次のコードを挿入して「About」のページを実装してください
    ```
    @page "/about"
    @rendermode InteractiveServer
    <RadzenComponents/>
    
    <PageTitle>About</PageTitle>
        
    <RadzenCard>
        <RadzenText TextStyle="TextStyle.H3">About</RadzenText>
    
        <RadzenBadge BadgeStyle="BadgeStyle.Success" Shade="Shade.Lighter" IsPill="@true" >
            <RadzenImage Path="https://avatars.githubusercontent.com/u/3477572?v=4" AlternateText="Meister 619" Style="width: 64px; height: 64px; border-radius: 50%;" />
        </RadzenBadge>
        <RadzenText TextStyle="TextStyle.H6">Meister 619</RadzenText>
    
        <RadzenText TextStyle="TextStyle.Body1"> .. blah blah blah ...</RadzenText>
    
    </RadzenCard>
    @code {
        
    }
    ```
#### 完成
![rbtl_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531993/bccb3a74-5dfa-b8e4-e29f-c353960d00eb.png)

## 参考
* MicrosoftのLearn/.NET/ASP.NET Core/[Blazor Todo リスト アプリ](https://learn.microsoft.com/ja-jp/aspnet/core/blazor/tutorials/build-a-blazor-app?view=aspnetcore-8.0)のチュートリアル
* [RadzenBlazor コンポネント ライブラリー](https://blazor.radzen.com/)
* [RadzenBlazor](https://github.com/radzenhq/radzen-blazor) の GitHub レポジトリー