全文翻訳ではなく、一部翻訳が不正確と思われる場合は原文のままにしています。
Blazor TabControl コンポーネントを作成します。目標の知識ポイントは2つです:
- RenderFragment にデータを渡してコンテキストを与える。
- CascadingParameter を使用して親 TabControl コンポーネントを子 TabPage コンポーネントに渡す。
以下が最終的な動作イメージです:

実践開始:
まず Blazor プロジェクトを作成します(Blazor Client または Server どちらでも構いません。ここでは Blazor Server を例とします)。
最初のステップとして、2つのコンポーネントを作成します:TabControl と TabPage。TabPage コンポーネントは親 TabControl のプロパティ参照(プロパティ名 Parent、CascadingParameter 属性を付加)を持ちます。
TabControl コンポーネント:
ファイルパス:./Shared/TabControl.razor
<div>これはTabControlです</div>
<CascadingValue Value="this"> @ChildContent </CascadingValue>
@code { // TabPageを<TabPage>タグ形式で使用したい場合、以下のコードが必要です
[Parameter] public RenderFragment? ChildContent { get; set; } }
TabPage コンポーネント:
ファイルパス:./Shared/TabPage.razor
<div>これはTabPageです</div>
@ChildContent @code { [CascadingParameter] private TabControl? Parent { get;
set; } [Parameter] public RenderFragment? ChildContent { get; set; } protected
override void OnInitialized() { if (Parent == null) throw new
ArgumentNullException(nameof(Parent), "TabPageはTabControlの参照を含む必要があります");
base.OnInitialized(); } }
TabControl と TabPage の関連付け
TabPage の OnInitialized メソッドに以下の1行を追加して、TabPage を TabControl に関連付けます:
Parent.AddPage(this);
AddPage メソッドは以下のコードにあります。TabControl で AddPage メソッドを呼び出して参照を保存した後、TabControl に ActivePage プロパティを追加します。同じく以下のコードを参照してください:
public TabPage? ActivePage { get; set; }
readonly List<TabPage> _pages = new();
internal void AddPage(TabPage tabPage)
{
_pages.Add(tabPage);
if (_pages.Count == 1)
ActivePage = tabPage;
StateHasChanged();
}
AddPage コンポーネントに表示用の Text プロパティを追加します。
[Parameter]
public string? Text { get; set; }
TabControl に以下のタグを追加します(ChildContent のレンダリングより前)。これらのタグは一度にすべてレンダリングされ、特定の TabPage をクリックすると TabControl の選択項目が変更されます。
<div class="btn-group" role="group">
@foreach (TabPage tabPage in Pages)
{
<button type="button"
class="btn @GetButtonClass(tabPage)"
@onclick=@( ()=>ActivatePage(tabPage) )>
@tabPage.Text
</button>
}
</div>
上記のタグは標準の Bootstrap ボタングループを作成します。各 TabPage に対して以下の特徴を持つボタンが作成されます:
- CSS クラスは "btn" に設定され、
GetButtonClassメソッドによって CSS クラス名が追加されます。現在のTabPageがActivePageの場合は CSS クラスbtn-primaryが追加され、それ以外の場合はbtn-secondaryが追加されます。 - ボタンがクリックされると、クリックされた
TabPageがアクティブになります。
注意:@onclick は引数なしのメソッドに関連付ける必要があるため、ラムダ式はインラインの @( ) を使ってクリックされた TabPage を ActivatePage に設定します。
- ボタンのテキストは
TabPageのTextプロパティで設定されます。
以下のコードを TabControl のコード領域に追加します。
string GetButtonClass(TabPage page)
{
return page == ActivePage ? "btn-primary" : "btn-secondary";
}
void ActivatePage(TabPage page)
{
ActivePage = page;
}
TabControl の使用
TabControlTest コンポーネントを追加します:
ファイル名:./Pages/TabControlTest.razor
@page "/tabcontroltest"
<TabControl>
<TabPage Text="Tab 1">
<h1>最初のタブ</h1>
</TabPage>
<TabPage Text="Tab 2">
<h1>2番目のタブ</h1>
</TabPage>
<TabPage Text="Tab 3">
<h1>3番目のタブ</h1>
</TabPage>
</TabControl>
@code { }
./Shared/NavMenu に TabControlTest のルートを追加します
一部省略
<div class="nav-item px-3">
<NavLink class="nav-link" href="tabcontroltest">
<span class="oi oi-plus" aria-hidden="true"></span> TabControl Test
</NavLink>
</div>
一部省略
これで終わりでしょうか?現在の動作を確認します:

おかしいですね。3つの TabPage の内容がすべて表示されています。この問題を解決するには、TabPage で ChildContent をレンダリングする際に、現在の TabPage が TabControl で選択されたページかどうかを判断し、選択されている場合のみレンダリングするようにします:
@if (Parent.ActivePage == this)
{
@ChildContent
}
OK、コードは以上です。動作結果は記事冒頭の画像を参照してください。
文中のコードはこちら: GitHub