ASP.NET Core MVC のビュー

作成者: Steve SmithDave Brock

このドキュメントでは、ASP.NET Core MVC アプリケーションで使用されるビューについて説明します。 Razor Pages について詳しくは、「ASP.NET Core での Razor Pages の概要」を参照してください。

Model-View-Controller (MVC) パターンでは、ビューがアプリのデータ表示とユーザー操作を処理します。 ビューは、Razor マークアップが埋め込まれた HTML テンプレートです。 Razor マークアップは、クライアントに送信する Web ページを生成するために、HTML マークアップと対話するコードです。

ASP.NET Core MVC のビューは、Razor マークアップで C# プログラミング言語が使用されている .cshtml ファイルです。 通常、ビュー ファイルは、各アプリのコントローラーの名前が付いたフォルダーにグループ化されます。 これらのフォルダーは、アプリのルートにある Views フォルダーに格納されます。

Views folder in Solution Explorer of Visual Studio is open with the Home folder open to show About.cshtml, Contact.cshtml, and Index.cshtml files

Home コントローラーは、Views フォルダー内の Home フォルダーによって表されます。 Home フォルダーには、AboutContactIndex (ホームページ) の Web ページのビューが含まれています。 ユーザーがこれら 3 つの Web ページのうちの 1 つを要求すると、Home コントローラー内のコントローラー アクションによって、3 つのビューのどれをビルドに使用するかの決定が下され、ユーザーに Web ページが返されます。

レイアウトを使用して、一貫性のある Web ページ セクションを提供し、コードの繰り返しを削減します。 多くの場合、レイアウトには、ヘッダー、ナビゲーションとメニュー要素、フッターが含まれています。 ヘッダーとフッターには通常、多くのメタデータ要素とスクリプトおよびスタイル アセットへのリンクの定型マークアップが含まれます。 レイアウトは、ビューでこの定型マークアップを回避するのに役立ちます。

部分ビューは、再利用可能なビューの部分を管理することで、コードの重複を削減します。 たとえば、ブログ Web サイトで複数のビューに表示される作成者の略歴に、部分ビューが役立ちます。 作成者の略歴は、通常のビュー コンテンツで、Web ページのコンテンツを生成するためにコードを実行する必要はありません。 作成者の略歴コンテンツは、モデル バインディングだけでビューで使用することができるため、この種類のコンテンツには部分ビューを使用するのが最適です。

ビュー コンポーネントは、コードの繰り返しを削減できる点は部分ビューと似ていますが、Web ページをレンダリングするために、コードをサーバーで実行する必要があるビュー コンテンツに適しています。 ビュー コンポーネントは、レンダリングされたコンテンツで、Web サイトのショッピング カートのように、データベースとの対話を必要とする場合に便利です。 ビュー コンポーネントは、Web ページの出力を生成するためのモデル バインディングに限定されるものではありません。

ビューを使用するメリット

ビューは、ユーザー インターフェイス マークアップをアプリの他の部分から分離して、MVC アプリ内で懸念事項の分離を確立するのに役立ちます。 SoC 設計に従うことで、アプリをモジュール化することができます。これにより次のような複数のメリットがもたらされます。

  • より効率的に整理されるため、アプリの維持が容易になります。 ビューは通常、アプリの機能によってグループ化されます。 これにより、機能を使用する際に、関連するビューが見つけやすくなります。
  • アプリの部分は弱く結合されています。 ビジネス ロジックとデータ アクセス コンポーネントと切り離して、アプリのビューをビルドおよび更新できます。 アプリの他の部分を更新しなくても、アプリのビューを変更できます。
  • ビューは個別の単位であるため、アプリのユーザー インターフェイス部分をより簡単にテストできます。
  • より効率的に整理されるため、ユーザー インターフェイスのセクションを誤って繰り返す可能性が低くなります。

ビューの作成

コントローラーに固有のビューは、Views/[ControllerName] フォルダー内に作成されます。 コントローラー間で共有されるビューは、Views/Shared フォルダー内に配置されます。 ビューを作成するには、新しいファイルを追加し、それに関連付けられたコントローラー アクションと同じ名前にファイル拡張子.cshtml を付けたものをファイル名とします。 Home コントローラー About アクションに対応するビューを作成するには、Views/Home フォルダー内に About.cshtml ファイルを作成します。

@{
    ViewData["Title"] = "About";
}
<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>

<p>Use this area to provide additional information.</p>

Razor マークアップは、@ 記号で始まります。 中かっこ ({ ... }) で始まる Razor コード ブロック内に、C# コードを配置して、C# ステートメントを実行します。 例として、上に示されている ViewData["Title"] への "About" の割り当てを参照してください。 @ 記号を使用して値を参照するだけで、HTML 内に値を表示することができます。 上記の <h2> 要素と <h3> 要素のコンテンツを参照してください。

上記のビュー コンテンツは、ユーザーにレンダリングされる Web ページ全体の一部のみを示しています。 残りのページのレイアウトとビューのその他の共通する側面は、他のビュー ファイルで指定されます。 詳細については、「Layout」 (レイアウト) のトピックを参照してください。

コントローラーがビューを指定する方法

ビューは通常、ActionResult の型である ViewResult として、アクションから返されます。 アクション メソッドで ViewResult を作成して直接返すことはできますが、一般的には行われていません。 ほとんどのコントローラーは、Controller から継承されるため、View ヘルパー メソッドを使って、ViewResult を返します。

HomeController.cs:

public IActionResult About()
{
    ViewData["Message"] = "Your application description page.";

    return View();
}

このアクションが返されると、最後のセクションに表示されている About.cshtml ビューが、次の Web ページとしてレンダリングされます。

About page rendered in the Edge browser

View ヘルパー メソッドには複数のオーバーロードがあります。 必要に応じて以下を指定できます。

  • 返す明示的なビュー:

    return View("Orders");
    
  • ビューに渡すモデル:

    return View(Orders);
    
  • ビューとモデルの両方:

    return View("Orders", Orders);
    

ビューの検出

アクションがビューを返すときに、ビューの検出と呼ばれるプロセスが行われます。 このプロセスでは、ビュー名に基づいて、使用するビュー ファイルを決定します。

View メソッド (return View();) の既定の動作は、呼び出し元のアクション メソッドと同じ名前を持つビューを返すことです。 たとえば、コントローラーの AboutActionResult メソッド名は、About.cshtml という名前のビュー ファイルの検索に使用されます。 最初に、ランタイムによって、ビューの Views/[ControllerName] フォルダーが検索されます。 そこで一致するビューが見つからない場合は、ビューの Shared フォルダーが検索されます。

return View(); を使用して ViewResult を暗黙的に返すか、return View("<ViewName>"); を使用してビュー名を View メソッドに明示的に渡すかは関係ありません。 どちらの場合も、ビューの検出は、次の順序で一致するビュー ファイルを検索します。

  1. Views/\[ControllerName]/\[ViewName].cshtml
  2. Views/Shared/\[ViewName].cshtml

ビュー名の代わりに、ビュー ファイル パスを指定できます。 アプリのルートから始まる (必要に応じて、"/" または "~/" で始まる) 絶対パスを使用する場合は、.cshtml 拡張子を指定する必要があります。

return View("Views/Home/About.cshtml");

.cshtml 拡張子を指定せずに、相対パスを使用して、異なるディレクトリ内にあるビューを指定することもできます。 HomeController の内部で、相対パスを使用して、Manage ビューの Index ビューを返すことができます。

return View("../Manage/Index");

同様に、プレフィックス "./" を使用して、現在のコントローラーに固有のディレクトリを指定することができます。

return View("./About");

部分ビュービュー コンポーネントは、まったく同じではありませんが、同様の検出メカニズムを使用します。

カスタムの IViewLocationExpander を使用して、ビューをアプリ内に配置する方法の既定の規則をカスタマイズすることができます。

ビューの検出は、ファイル名によるビュー ファイルの検出に依存しています。 基になるファイル システムが大文字と小文字を区別する場合は、ビューの名前も大文字と小文字を区別する可能性があります。 オペレーティング システム間の互換性のため、コントローラー、アクション名、関連するビュー フォルダー、ファイル名の間で、大文字と小文字の区別を一致させます。 大文字と小文字を区別するファイル システムを使用していて、ビュー ファイルが見つからないというエラーが発生する場合は、要求されたビュー ファイルと実際のビュー ファイルの名前の大文字と小文字が一致していることを確認します。

保守性を高め、わかりやすくするため、ビューのファイル構造を整理するためのベスト プラクティスに従って、コントローラー、アクション、およびビューのリレーションシップを反映します。

ビューにデータを渡す

ビューにデータを渡すには、いくつかの方法があります。

  • 厳密に型指定されたデータ: viewmodel
  • 弱く型指定されたデータ
    • ViewData (ViewDataAttribute)
    • ViewBag

厳密に型指定されたデータ (viewmodel)

最も確実な方法は、ビューでモデルの型を指定することです。 このモデルは、一般的に viewmodel と呼ばれます。 viewmodel 型のインスタンスをアクションからビューに渡します。

viewmodel を使用してデータをビューに渡すことで、ビューで厳密な型チェックを利用できるようになります。 厳密な型指定 (または厳密に型指定された) は、すべての変数および定数に明示的に定義された型 (stringintDateTime など) があることを意味します。 ビューで使用される型の妥当性は、コンパイル時にチェックされます。

Visual StudioVisual Studio Code では、IntelliSense と呼ばれる機能を使用して、厳密に型指定されたクラス メンバーを一覧表示します。 viewmodel のプロパティを表示する場合は、viewmodel の変数名に続けてピリオド (.) を入力します。 これにより、エラーの少ないコードをより早く記述できます。

@model ディレクティブを使用してモデルを指定します。 @Model を使用してモデルを使用します。

@model WebApplication1.ViewModels.Address

<h2>Contact</h2>
<address>
    @Model.Street<br>
    @Model.City, @Model.State @Model.PostalCode<br>
    <abbr title="Phone">P:</abbr> 425.555.0100
</address>

モデルをビューに提供するため、コントローラーはモデルをパラメーターとして渡します。

public IActionResult Contact()
{
    ViewData["Message"] = "Your contact page.";

    var viewModel = new Address()
    {
        Name = "Microsoft",
        Street = "One Microsoft Way",
        City = "Redmond",
        State = "WA",
        PostalCode = "98052-6399"
    };

    return View(viewModel);
}

ビューに提供できるモデルの型に制限はありません。 Plain Old CLR Object (POCO) viewmodel を、動作 (メソッド) をほとんど定義せずに使用することをお勧めします。 通常、viewmodel クラスは、Models フォルダー、またはアプリのルートにある ViewModels フォルダーのいずれかに格納されます。 上記の例で使用されている Address viewmodel は、Address.cs という名前のファイルに格納されている POCO viewmodel です。

namespace WebApplication1.ViewModels
{
    public class Address
    {
        public string Name { get; set; }
        public string Street { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string PostalCode { get; set; }
    }
}

viewmodel 型とビジネス モデル型の両方に同じクラスを使用することを妨げるものはありません。 しかし、別のモデルを使用することで、ビジネス ロジックとアプリのデータ アクセス部分からは独立して、ビューを変えることができます。 モデルと viewmodel を分離することで、モデルがモデル バインディング検証を使用する際に、ユーザーによってアプリに送信されるデータに対してセキュリティ上の利点ももたらされます。

弱く型指定されたデータ (ViewData[ViewData] 属性、および ViewBag)

ViewBag は、Razor PagesPageModel クラスでは既定で使用できません。

厳密に型指定されたビューに加え、ビューはデータの弱く型指定された (緩く型指定されたともいう) コレクションにもアクセスできます。 厳密な型とは異なり、弱い型 (または緩い型) は、使用するデータの型を明示的に宣言しないことを意味します。 弱く型指定されたデータのコレクションを使用して、少量のデータをコントローラーとビュー間でやり取りすることができます。

データをやり取りする相手
コントローラーとビュー ドロップダウン リストにデータを読み込む。
ビューとレイアウト ビュー ビュー ファイルから、レイアウト ビューの <title> 要素の内容を設定する。
部分ビューとビュー ユーザーが要求した Web ページに基づいてデータを表示するウィジェット。

このコレクションは、コントローラーおよびビューで ViewData または ViewBag のいずれかのプロパティを通じて参照できます。 ViewData プロパティは、弱く型指定されたオブジェクトのディクショナリです。 ViewBag プロパティは、基になる ViewData コレクションに動的プロパティを提供する ViewData をラップするラッパーです。 注: キー参照は、ViewDataViewBag のどちらも、大文字と小文字の区別はありません。

ViewData および ViewBag は実行時に動的に解決されます。 これらはコンパイル時の型チェックを提供していないため、どちらも viewmodel を使用する場合よりも一般的にエラーが発生しやすくなります。 そのため、開発者の中には、ViewData および ViewBag の使用を最小限に抑えるか、まったく使用しない人もいます。

ViewData

ViewData は、string キーを介してアクセスされる ViewDataDictionary オブジェクトです。 文字列データは、格納してキャストなしで直接使用できますが、特定の型を抽出するときには、他の ViewData オブジェクトの値をこれらの型にキャストする必要があります。 ViewData を使用して、データをコントローラーからビューとビュー内部 (部分ビューおよびレイアウトを含む) に渡すことができます。

次の例では、1 つのアクションで ViewData を使用して、あいさつ文とアドレスに値を設定します。

public IActionResult SomeAction()
{
    ViewData["Greeting"] = "Hello";
    ViewData["Address"]  = new Address()
    {
        Name = "Steve",
        Street = "123 Main St",
        City = "Hudson",
        State = "OH",
        PostalCode = "44236"
    };

    return View();
}

ビューでデータを使用します。

@{
    // Since Address isn't a string, it requires a cast.
    var address = ViewData["Address"] as Address;
}

@ViewData["Greeting"] World!

<address>
    @address.Name<br>
    @address.Street<br>
    @address.City, @address.State @address.PostalCode
</address>

[ViewData] 属性

ViewDataDictionary を使用するもう 1 つの方法は、ViewDataAttribute です。 コントローラーや Razor Page のモデルのプロパティが [ViewData] 属性でマークされている場合、その値をディクショナリに格納して、そこから読み込むことができます。

次の例の Home コントローラーには、[ViewData] でマークされた Title プロパティが含まれています。 About メソッドは、About ビューのタイトルを設定します。

public class HomeController : Controller
{
    [ViewData]
    public string Title { get; set; }

    public IActionResult About()
    {
        Title = "About Us";
        ViewData["Message"] = "Your application description page.";

        return View();
    }
}

レイアウトでは、タイトルは ViewData ディクショナリから読み込まれます。

<!DOCTYPE html>
<html lang="en">
<head>
    <title>@ViewData["Title"] - WebApplication</title>
    ...

ViewBag

ViewBag は、Razor PagesPageModel クラスでは既定で使用できません。

ViewBag は、ViewData に格納されているオブジェクトへの動的アクセスを提供する Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.DynamicViewData オブジェクトです。 ViewBag はキャストを必要としないため、より簡単に使用できます。 次の例は、上記の ViewData を使用した時と同じ結果になるように、ViewBag を使用する方法を示しています。

public IActionResult SomeAction()
{
    ViewBag.Greeting = "Hello";
    ViewBag.Address  = new Address()
    {
        Name = "Steve",
        Street = "123 Main St",
        City = "Hudson",
        State = "OH",
        PostalCode = "44236"
    };

    return View();
}
@ViewBag.Greeting World!

<address>
    @ViewBag.Address.Name<br>
    @ViewBag.Address.Street<br>
    @ViewBag.Address.City, @ViewBag.Address.State @ViewBag.Address.PostalCode
</address>

ViewDataViewBag の同時使用

ViewBag は、Razor PagesPageModel クラスでは既定で使用できません。

ViewDataViewBag は基になる同じ ViewData コレクションを参照しているため、値を読み書きするときに、ViewDataViewBag の両方を使用して、それらを組み合わせることができます。

About.cshtml ビューの最上部で、ViewBag を使用してタイトルを設定し、ViewData を使用して説明を設定します。

@{
    Layout = "/Views/Shared/_Layout.cshtml";
    ViewBag.Title = "About Contoso";
    ViewData["Description"] = "Let us tell you about Contoso's philosophy and mission.";
}

プロパティを読み取りますが、ViewDataViewBag の使用を反転します。 _Layout.cshtml ファイルで、ViewData を使用してタイトルを取得し、ViewBag を使用して説明を取得します。

<!DOCTYPE html>
<html lang="en">
<head>
    <title>@ViewData["Title"]</title>
    <meta name="description" content="@ViewBag.Description">
    ...

文字列には ViewData のキャストを必要としないことに注意してください。 キャストせずに @ViewData["Title"] を使用できます。

ViewDataViewBag の両方を同時に使用することは、プロパティの読み取りと書き込みを組み合わせることと同様に可能です。 次のマークアップがレンダリングされます。

<!DOCTYPE html>
<html lang="en">
<head>
    <title>About Contoso</title>
    <meta name="description" content="Let us tell you about Contoso's philosophy and mission.">
    ...

ViewDataViewBag の相違点の概要

ViewBag は、Razor PagesPageModel クラスでは既定で使用できません。

  • ViewData
    • ViewDataDictionary から派生しているため、ContainsKeyAddRemoveClear などの役に立つディクショナリ プロパティがあります。
    • ディクショナリ内のキーは文字列なので、空白が許可されます。 例: ViewData["Some Key With Whitespace"]
    • ViewData を使用するには、ビューで string 以外のすべての型をキャストする必要があります。
  • ViewBag
    • Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.DynamicViewData から派生しているため、ドット表記 (@ViewBag.SomeKey = <value or object>) を使用して、動的プロパティを作成することができます。キャストは必要ありません。 ViewBag の構文は、コントローラーとビューへの追加を高速化します。
    • null 値のチェックを簡素化します。 例: @ViewBag.Person?.Name

ViewData または ViewBag を使用する場合

コントローラーとビュー間で少量のデータを渡すには、ViewDataViewBag はどちらも等しく有効な方法です。 どちらを使用するかは、任意で選択できます。 ViewData オブジェクトと ViewBag オブジェクトを組み合わせることはできますが、1 つの方法を一貫して使用した方が、コードが読みやすくなり、維持も容易になります。 どちらの方法も実行時に動的に解決されるため、実行時エラーが発生しやすくなります。 開発チームの中には、これらを避けているところもあります。

動的ビュー

@model を使用してモデル型は宣言しないが、渡されるモデル インスタンス (return View(Address); など) があるビューは、インスタンスのプロパティを動的に参照できます。

<address>
    @Model.Street<br>
    @Model.City, @Model.State @Model.PostalCode<br>
    <abbr title="Phone">P:</abbr> 425.555.0100
</address>

この機能は柔軟性を提供しますが、コンパイルの保護や IntelliSense は提供していません。 プロパティが存在しない場合は、実行時に Web ページの生成が失敗します。

ビューのその他の機能

タグ ヘルパーは、既存の HTML タグへのサーバー側の動作の追加を容易にします。 タグ ヘルパーを使用すると、ビュー内でカスタム コードやヘルパーを記述する必要がなくなります。 タグ ヘルパーは、HTML 要素に属性として適用され、タグ ヘルパーを処理できないエディターでは無視されます。 これにより、さまざまなツールでビューのマークアップの編集およびレンダリングが可能になります。

カスタムの HTML マークアップの生成は、さまざまな組み込み HTML ヘルパーを使って行えます。 より複雑なユーザー インターフェイス ロジックは、ビュー コンポーネントで処理できます。 ビュー コンポーネントは、コントローラーとビューが提供しているのと同じ SoC を提供します。 これにより、共通のユーザー インターフェイス要素で使用されるデータを扱うアクションおよびビューの必要がなくなります。

ASP.NET Core の他の多くの側面と同様に、ビューは、サービスをビューに挿入することを許可する、依存性の注入をサポートしています。

CSS の分離

CSS スタイルを個々のページ、ビュー、コンポーネントに分離して、次のものを減らすか回避します。

  • 維持が困難なグローバル スタイルへの依存関係。
  • 入れ子になったコンテンツでのスタイルの競合。

ページまたはビューの "スコープ付き CSS ファイル" を追加するには、.cshtml ファイルの名前に一致するコンパニオン .cshtml.css ファイルに CSS スタイルを配置します。 次の例では、Index.cshtml ページまたはビューにのみ適用される CSS スタイルが、Index.cshtml.css ファイルによって提供されます。

Pages/Index.cshtml.css (Razor Pages) または Views/Index.cshtml.css (MVC):

h1 {
    color: red;
}

CSS の分離は、ビルド時に発生します。 CSS セレクターは、アプリのページまたはビューによってレンダリングされたマックアップに一致するよう、フレームワークによって書き換えられます。 書き換えられた CSS スタイルは、静的なアセット {APP ASSEMBLY}.styles.css としてバンドルされ、生成されます。 プレースホルダー {APP ASSEMBLY} は、プロジェクトのアセンブリ名です。 バンドルされた CSS スタイルへのリンクは、アプリのレイアウトに配置されます。

アプリの Pages/Shared/_Layout.cshtml (Razor Pages) または Views/Shared/_Layout.cshtml (MVC) の <head> コンテンツでは、バンドルされた CSS スタイルへのリンクが存在することを確認します。

<link rel="stylesheet" href="~/{APP ASSEMBLY}.styles.css" />

次の例では、アプリのアセンブリ名は WebApp です。

<link rel="stylesheet" href="WebApp.styles.css" />

スコープ付き CSS ファイルで定義されているスタイルは、一致するファイルのレンダリング出力にのみ適用されます。 前の例では、アプリ内の他の場所で定義されている h1 CSS 宣言はすべて、Index の見出しスタイルと競合しません。 CSS スタイルのカスケードおよび継承の規則は、スコープ付き CSS ファイルに対して有効のままです。 たとえば、Index.cshtml ファイル内の <h1> 要素に直接適用されるスタイルは、Index.cshtml.css 内のスコープ付き CSS ファイルのスタイルをオーバーライドします。

Note

バンドルが発生したときに CSS スタイルの分離を保証するために、Razor コード ブロックでの CSS のインポートはサポートされていません。

CSS の分離は、HTML 要素にのみ適用されます。 CSS の分離は、タグ ヘルパーではサポートされていません。

バンドルされた CSS ファイル内では、各ページ、ビュー、または Razor コンポーネントが、b-{STRING} の形式のスコープ識別子に関連付けられています。ここで、{STRING} プレースホルダーは、フレームワークによって生成された 10 文字の文字列です。 次の例では、Razor Pages アプリの Index ページ内にある、前述の <h1> 要素のスタイルを示しています。

/* /Pages/Index.cshtml.rz.scp.css */
h1[b-3xxtam6d07] {
    color: red;
}

バンドルされているファイルから CSS スタイルが適用されている Index ページには、スコープ識別子が HTML 属性として追加されます。

<h1 b-3xxtam6d07>

識別子は、アプリに固有です。 ビルド時、規則 {STATIC WEB ASSETS BASE PATH}/Project.lib.scp.css でプロジェクト バンドルが作成されます。ここで、プレースホルダー {STATIC WEB ASSETS BASE PATH} は静的な Web アセットのベース パスです。

NuGet パッケージや Razor クラス ライブラリなどの他のプロジェクトを利用する場合、バンドル ファイルは次のようになります。

  • CSS インポートを使用してスタイルを参照する。
  • スタイルを使用するアプリの静的な Web アセットとして公開されない。

CSS プリプロセッサのサポート

CSS プリプロセッサは、変数、入れ子、モジュール、mixin、継承などの機能を利用することで CSS 開発を改善するのに役立ちます。 CSS の分離は、SASS や LESS などの CSS プリプロセッサをネイティブにサポートしませんが、ビルド プロセス中に、フレームワークにより CSS セレクターが書き換えられる前にプリプロセッサのコンパイルが行われる限り、CSS プリプロセッサの統合はシームレスです。 たとえば、Visual Studio を使用して、Visual Studio タスク ランナー エクスプローラーで既存のプリプロセッサ コンパイルをビルド前タスクとして構成します。

AspNetCore.SassCompiler などの多くのサードパーティ製 NuGet パッケージは、CSS の分離が発生する前に、ビルド プロセスの開始時に SASS または SCSS ファイルをコンパイルできます。追加の構成は必要ありません。

CSS の分離の構成

CSS 分離では、既存のツールやワークフローに依存関係がある場合など、いくつかの高度なシナリオでの構成が可能です。

スコープ識別子の形式をカスタマイズする

"このセクションの {Pages|Views} プレースホルダーは、Razor Pages アプリ用の Pages、または MVC アプリ用の Views のいずれかです。 "

既定では、スコープ識別子には、b-{STRING} の形式が使用されます。ここで、{STRING} プレースホルダーは、フレームワークによって生成される 10 文字の文字列です。 スコープ識別子の形式をカスタマイズするには、プロジェクト ファイルを目的のパターンに更新します。

<ItemGroup>
  <None Update="{Pages|Views}/Index.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>

前の例では、Index.cshtml.css 用に生成された CSS により、そのスコープ識別子は b-{STRING} から custom-scope-identifier に変更されます。

スコープ識別子を使用して、スコープ付き CSS ファイルでの継承を実現します。 次のプロジェクト ファイルの例では、BaseView.cshtml.css ファイルに、ビュー全体の共通スタイルが含まれています。 DerivedView.cshtml.css ファイルでは、これらのスタイルが継承されます。

<ItemGroup>
  <None Update="{Pages|Views}/BaseView.cshtml.css" CssScope="custom-scope-identifier" />
  <None Update="{Pages|Views}/DerivedView.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>

ワイルドカード (*) 演算子を使用して、複数のファイル間でスコープ識別子を共有します。

<ItemGroup>
  <None Update="{Pages|Views}/*.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>

静的な Web アセットのベース パスを変更する

スコープ付き CSS ファイルは、アプリのルートで生成されます。 プロジェクト ファイルでは、StaticWebAssetBasePath プロパティを使用して既定のパスを変更します。 次の例では、スコープ付き CSS ファイルとアプリの残りのアセットを _content パスに配置します。

<PropertyGroup>
  <StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
</PropertyGroup>

自動バンドルを無効にする

フレームワークでスコープ付きファイルを公開し、それを実行時に読み込む方法をオプトアウトするには、DisableScopedCssBundling プロパティを使用します。 このプロパティを使用する場合、obj ディレクトリからの CSS ファイルの分離、それらの公開、および実行時の読み込みを、他のツールまたはプロセスが担当することを意味します。

<PropertyGroup>
  <DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>

Razor クラス ライブラリ (RCL) のサポート

Razor クラス ライブラリ (RCL) により分離スタイルが提供される場合、<link> タグの href 属性は {STATIC WEB ASSET BASE PATH}/{PACKAGE ID}.bundle.scp.css を指します。ここで、プレースホルダーは次のとおりです。

  • {STATIC WEB ASSET BASE PATH}: 静的な Web 資産のベース パス。
  • {PACKAGE ID}: ライブラリのパッケージ識別子。 プロジェクト ファイルでパッケージ識別子が指定されていない場合、パッケージ識別子の既定値は、プロジェクトのアセンブリ名になります。

次に例を示します。

  • 静的な Web 資産のベース パスは _content/ClassLib です。
  • クラス ライブラリのアセンブリ名は ClassLib です。

Pages/Shared/_Layout.cshtml (Razor Pages) または Views/Shared/_Layout.cshtml (MVC):

<link href="_content/ClassLib/ClassLib.bundle.scp.css" rel="stylesheet">

RCL の詳細については、次の記事を参照してください。

Blazor の CSS の分離については、「ASP.NET Core Blazor の CSS の分離」をご覧ください。