.NET6 Blazor Server SyncFusion Theme & Slidebar Menu 実装

Visual Studio 2022 v17.1.1

# Theme 実装

## NuGet でコンポーネントをインストール
- Visual Studio で「Blazor Server アプリ」認証なし 作成
- NuGet でインストール
  Ver 20.1.1.47
  Syncfusion.Blazor.Button
  Syncfusion.Blazor.DropDowns
  Syncfusion.Blazor.Grid
  Syncfusion.Blazor.Navigasions
  Syncfusion.Blazor.Theme
  ※ Syncfusion.Blazor だけではうまくいかなかった、個別で入れないといけないようだ。

## Pages/_Layout.cshtml 変更
```Pages/_Layout.cshtml
・・・
    @* SyncFusin Theme *@
    <link id="theme" href="_content/Syncfusion.Blazor.Themes/bootstrap4.css" rel="stylesheet" />
    <script src="_content/Syncfusion.Blazor.Core/scripts/syncfusion-blazor.min.js" type="text/javascript"></script>
    <script>
    function setTheme(theme) {
        document.getElementsByTagName('body')[0].style.display = 'none';
        let synclink = document.getElementById('theme');
        synclink.href = '_content/Syncfusion.Blazor.Themes/' + theme + '.css';
        setTimeout(function () { document.getElementsByTagName('body')[0].style.display = 'block'; }, 200);
    }
    </script>
</head>
・・・
``` 

## Programs.cs にサービス追加
```Programs.cs
・・・
// SyncFusion using 追加
using Syncfusion.Blazor;

・・・

// SyncFusion Service を追加
builder.Services.AddSyncfusionBlazor(options => { options.IgnoreScriptIsolation = true; });

var app = builder.Build();
・・・
```
# テーマスイッチャー・コンポーネント作成
```Shared/ThemeSwitcher.razor@.cs
@inject NavigationManager UrlHelper;
@inject IJSRuntime JSRuntime;
@using Syncfusion.Blazor.DropDowns;
@using Syncfusion.Blazor.Buttons;
@using Microsoft.AspNetCore.WebUtilities;

<div class="theme-switcher">
    @*Theme switcher *@
    <SfDropDownList TItem="ThemeDetails" TValue="string" @bind-Value="themeName" DataSource="@Themes">
        <DropDownListFieldSettings Text="Text" Value="ID"></DropDownListFieldSettings>
        <DropDownListEvents TItem="ThemeDetails" TValue="string" ValueChange="OnThemeChange"></DropDownListEvents>
    </SfDropDownList>
</div>

@code {
    private string themeName;

    public class ThemeDetails
    {
        public string ID { get; set; }
        public string Text { get; set; }
    }

    private List<ThemeDetails> Themes = new List<ThemeDetails>() {
        new ThemeDetails(){ ID = "material", Text = "Material" },
        new ThemeDetails(){ ID = "bootstrap", Text = "Bootstrap" },
        new ThemeDetails(){ ID = "fabric", Text = "Fabric" },
        new ThemeDetails(){ ID = "bootstrap4", Text = "Bootstrap 4" },
        new ThemeDetails(){ ID = "bootstrap5", Text = "Bootstrap 5" },
        new ThemeDetails(){ ID = "tailwind", Text = "TailWind"},
        new ThemeDetails(){ ID = "tailwind-dark", Text = "TailWind Dark" },
        new ThemeDetails(){ ID = "material-dark", Text = "Material Dark" },
        new ThemeDetails(){ ID = "bootstrap-dark", Text = "Bootstrap Dark" },
        new ThemeDetails(){ ID = "fabric-dark", Text = "Fabric Dark" },
        new ThemeDetails(){ ID = "highcontrast", Text = "High Contrast" }
    };
    

    public void OnThemeChange(Syncfusion.Blazor.DropDowns.ChangeEventArgs<string, ThemeDetails> args)
    {
        JSRuntime.InvokeAsync<object>("setTheme", args.ItemData.ID);                
    }
    
    private string GetThemeName()
    {
        var uri = UrlHelper.ToAbsoluteUri(UrlHelper.Uri);
        QueryHelpers.ParseQuery(uri.Query).TryGetValue("theme", out var theme);
        return theme.Count > 0 ? theme.First() : "bootstrap4";
    }

    protected override void OnInitialized()
    {
        var theme = GetThemeName();
        themeName = theme.Contains("bootstrap4") ? "bootstrap4" : theme;
    }

}
```







## MainLayout 全置換え
https://blazor.syncfusion.com/demos/sidebar/sidebar-with-menu?theme=bootstrap5
SIdeBar Menu のソースを `Shared/MainLayout.razor` にコピーする。
そして修正する。

```Shared/MainLayout.razor.html
@inherits LayoutComponentBase
@inject NavigationManager Navigation

@using SidebarMenu.Shared
@using Syncfusion.Blazor.Navigations

<div id="wrapper">
    <div>
        @*Initialize the Toolbar component*@
        <SfToolbar>
            <ToolbarEvents Clicked="@Toggle"></ToolbarEvents>
            <ToolbarItems>
                <ToolbarItem PrefixIcon="e-menu" TooltipText="Menu"></ToolbarItem>
                <ToolbarItem>
                    <Template>
                        <div class="e-folder">
                            <div class="e-folder-name">Navigation Pane</div>
                        </div>
                    </Template>
                </ToolbarItem>
                <ToolbarItem  Align="ItemAlign.Right">
                    <Template>
                        <ThemeSwitcher />
                    </Template>
                </ToolbarItem>
            </ToolbarItems>
        </SfToolbar>
    </div>
    @*main content declaration*@
    <div class="main-content" id="maintext">
        <div class="content">
            @Body
        </div>
    </div>
    @*Initialize the Sidebar component*@
    <SfSidebar HtmlAttributes="@HtmlAttribute" Target=".main-content" Width="220px" DockSize="50px" EnableDock="true" @bind-IsOpen="SidebarToggle" Type="@SidebarType.Auto">
        <ChildContent>
            <div class="main-menu">
                <div>
                    <SfMenu CssClass="dock-menu" Items="@MainMenuItems" Orientation="@VerOrientation" @ref="MenuObj">
                        <MenuEvents ItemSelected="Selected" TValue="MenuItem"></MenuEvents> 
                    </SfMenu>
                </div>
            </div>
        </ChildContent>
    </SfSidebar>
</div>

@code{
・・・
    public List<MenuItem> MainMenuItems = new List<MenuItem> {
        new MenuItem {
            Text = "Overview", IconCss = "icon-user icon",
                Items = new List<MenuItem> {
                    new MenuItem{ Text = "Home", Url="/" },
                    new MenuItem{ Text = "Counter", Url="/counter" },
                    new MenuItem{ Text = "Weather forecast", Url="/fetchdata" }
                }
            },
・・・
    // Select イベント
    SfMenu<MenuItem> MenuObj;
    private void Selected(MenuEventArgs<MenuItem> args) 
    { 
        var item = args.Item;
        var menuLevel = MenuObj.GetItemIndex(item); // メニュー深さの位置 1=トップレベルのメニュー
        var text = item.Text;
        var id = item.Id;
        var url = item.Url;

        if (!String.IsNullOrWhiteSpace(url))
        {
            Navigation.NavigateTo(url);            
        }
    } 
}

<style>
・・・
</style>
```
※ CSS分離したかったが、メニューが崩れてうまくいかなかった orz