レイアウト

レイアウトは、ルーティングされたページを共通の外枠で包みます。ヘッダー、ナビゲーション、フッターといったものです。BlazorCodeFirst のレイアウトは、コンポーネントと同じように書きます。 ChromeLayoutBase から派生し、設計時の UI の式を宣言し、レンダリングはソースジェネレーターに作らせます。

Chrome と Body#

ChromeLayoutBase は Blazor の LayoutComponentBase から派生しています。そのため、ルーティングされたページを受け取る Body パラメーターを既に備えています。レイアウト自身が描く外枠は、 Chrome という別のプロパティをオーバーライドして書きます。

using BlazorCodeFirst;
using static BlazorCodeFirst.Html;

public partial class MainLayout : ChromeLayoutBase
{
    protected override View Chrome =>
        Div.Class("shell")[
            Header[H1["My App"]],
            Main.Class("content")[Body],
            Footer["© 2026"]];
}

ここの Main[Body] は、Razor の <main>@Body</main> そのものです。ルーティングされたページを、要素の内容として置いています。下の出力は、ページが入る位置にプレースホルダーを置いたものです。

protected override View Chrome =>
    Div.Class("shell")[
        Header[H1["My App"]],
        Main.Class("content")[Body],
        Footer["© 2026"]];
<div class="shell">
    <header><h1>My App</h1></header>
    <main class="content">the routed page</main>
    <footer>© 2026</footer>
</div>

Body ではなく Chrome である理由#

Blazor は、レイアウトが包む内容を Body という名前のパラメーターで公開するよう求めます。名前は Body ちょうどでなければなりません。一方 C# は、1つの型に同じ名前のメンバーを2つ宣言できません。そこで Body は Razor での意味、つまり包まれる側のページのまま残し、レイアウト自身の設計時の式には Chrome という別の名前を付けました。

レイアウトを入れ子にする#

レイアウト自身を、別のレイアウトの中に置けます。ページに付けるのとまったく同じように、レイアウトの型に [Layout] を付けます。

using BlazorCodeFirst;
using Microsoft.AspNetCore.Components;
using static BlazorCodeFirst.Html;

[Layout(typeof(SiteLayout))]
public partial class DocsLayout : ChromeLayoutBase
{
    protected override View Chrome => Div.Class("docs")[Aside[TableOfContents()], Main[Body]];
}

入れ子を解決するのは Blazor で、BlazorCodeFirst ではありません。LayoutView がレイアウトの型から属性を読み、それを自分のレイアウトで包みます。BlazorCodeFirst のレイアウトは、通常の LayoutComponentBase の子孫です。どの段の Body も、その1つ下の段を受け取ります。SiteLayoutBody は描かれた DocsLayout で、その DocsLayoutBody がルーティングされたページです。

RenderFragment はそのまま内容になる#

Body は BlazorCodeFirst の型ではなく、ただの Blazor の RenderFragment? です。それでも上の Main[Body] は、専用の構文なしにコンパイルできます。ViewRenderFragment? からの暗黙の変換を持っているからです。そのためフラグメントは、要素の内容が来る場所ならどこにでも書けます。変換元はジェネリックでない RenderFragment だけで、RenderFragment<T> は変換されません。 FragmentRaw と同じように、RenderFragment はキーを付けられるフレームを開きません。よって ForEach の内容のルートにはできず(BCF3003)、装飾も付けられません(BCF3008)。

同じ仕組みで、BlazorCodeFirst のコンポーネントは Razor から渡された子を描けます。 [Parameter] public RenderFragment? ChildContent を持つコンポーネントは、それを Body とまったく同じように使います。

using BlazorCodeFirst;
using Microsoft.AspNetCore.Components;
using static BlazorCodeFirst.Html;

public partial class Card : BodyComponentBase
{
    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    protected override View Body => Div.Class("card")[ChildContent];
}

逆向き、つまり BlazorCodeFirst のコードから Razor や手書きのコンポーネントへ内容を渡す場合は、 Component<T>() を使います。子の内容を渡すを見てください。

読めるが、書き換えられない#

ChromeBody は、どちらもコンポーネントの状態を読めます。状態を UI へ映すことが、そもそもの役目だからです。ただし、どちらも状態を書き換えられません。ここでの BodyBodyComponentBase のもので、レイアウトがルーティングされたページを受け取るパラメーターではありません。どちらの中で状態を書き換えても BCF3001 を報告します。通常のコンポーネントの Body に当てはまるのと同じ診断です。

次に読むもの#

An unhandled error has occurred. Reload 🗙