Exibições no ASP.NET Core MVC

Por Steve Smith e Dave Brock

Este documento explica as exibições usadas em aplicativos do ASP.NET Core MVC. Para obter informações sobre páginas Razor, consulte Introdução às Páginas Razor no ASP.NET Core.

No padrão MVC (Modelo-Exibição-Controlador), a exibição cuida da apresentação de dados do aplicativo e da interação com o usuário. Uma exibição é um modelo HTML com marcação Razor inserida. A marcação Razor é um código que interage com a marcação HTML para produzir uma página da Web que é enviada ao cliente.

No MVC do ASP.NET Core, as exibições são arquivos .cshtml que usam a linguagem de programação C# na marcação Razor. Geralmente, arquivos de exibição são agrupados em pastas nomeadas para cada um dos controladores do aplicativo. As pastas são armazenadas em uma pasta Views na raiz do aplicativo:

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

O controlador Home é representado por uma pasta Home dentro da pasta Views. A pasta Home contém as exibições das páginas da Web About, Contacte Index (página inicial). Quando um usuário solicita uma dessas três páginas da Web, ações do controlador no controlador Home determinam qual das três exibições é usada para compilar e retornar uma página da Web para o usuário.

Use layouts para fornecer seções de páginas da Web consistentes e reduzir repetições de código. Layouts geralmente contêm o cabeçalho, elementos de navegação e menu e o rodapé. O cabeçalho e o rodapé geralmente contêm marcações repetitivas para muitos elementos de metadados, bem como links para ativos de script e estilo. Layouts ajudam a evitar essa marcação repetitiva em suas exibições.

Exibições parciais reduzem a duplicação de código gerenciando as partes reutilizáveis das exibições. Por exemplo, uma exibição parcial é útil para uma biografia do autor que aparece em várias exibições em um site de blog. Uma biografia do autor é um conteúdo de exibição comum e não requer que um código seja executado para produzi-lo para a página da Web. O conteúdo da biografia do autor é disponibilizado para a exibição usando somente o model binding, de modo que usar uma exibição parcial para esse tipo de conteúdo é ideal.

Componentes de exibição são semelhantes a exibições parciais no sentido em que permitem reduzir códigos repetitivos, mas são adequados para conteúdos de exibição que requerem que um código seja executado no servidor para renderizar a página da Web. Componentes de exibição são úteis quando o conteúdo renderizado requer uma interação com o banco de dados, como para o carrinho de compras de um site. Os componentes de exibição não ficam limitados ao model binding para produzir a saída da página da Web.

Benefícios do uso de exibições

As exibições ajudam a estabelecer uma separação de Interesses dentro de um aplicativo MVC, separando a marcação da interface do usuário de outras partes do aplicativo. Seguir um design de SoC faz com que seu aplicativo seja modular, o que fornece vários benefícios:

  • A manutenção do aplicativo é mais fácil, porque ele é melhor organizado. Geralmente, as exibições são agrupadas segundo os recursos do aplicativo. Isso facilita encontrar exibições relacionadas ao trabalhar em um recurso.
  • As partes do aplicativo ficam acopladas de forma flexível. Você pode compilar e atualizar as exibições do aplicativo separadamente da lógica de negócios e dos componentes de acesso a dados. É possível modificar os modos de exibição do aplicativo sem precisar necessariamente atualizar outras partes do aplicativo.
  • É mais fácil testar as partes da interface do usuário do aplicativo porque as exibições são unidades separadas.
  • Devido à melhor organização, é menos provável que você repita acidentalmente seções da interface do usuário.

Criando uma exibição

As exibições específicas a um controlador são criadas na pasta Views/[ControllerName]. As exibições que são compartilhadas entre controladores são colocadas na pasta Views/Shared. Para criar uma exibição, adicione um novo arquivo e dê a ele o mesmo nome que o da ação de seu controlador associado, com a extensão de arquivo .cshtml. Para criar uma exibição que corresponda à ação About no controlador Home, crie um arquivo About.cshtml na pasta Views/Home:

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

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

A marcação Razor começa com o símbolo @. Execute instruções em C# colocando o código C# dentro de blocos de código Razor entre chaves ({ ... }). Por exemplo, consulte a atribuição de "About" para ViewData["Title"] mostrado acima. É possível exibir valores em HTML simplesmente referenciando o valor com o símbolo @. Veja o conteúdo dos elementos <h2> e <h3> acima.

O conteúdo da exibição mostrado acima é apenas uma parte da página da Web inteira que é renderizada para o usuário. O restante do layout da página e outros aspectos comuns da exibição são especificados em outros arquivos de exibição. Para saber mais, consulte o tópico sobre Layout.

Como controladores especificam exibições

As exibições normalmente são retornadas de ações como ViewResult, que é um tipo de ActionResult. O método de ação pode criar e retornar um ViewResult diretamente, mas normalmente isso não é feito. Como a maioria dos controladores herda de Controller, basta usar o método auxiliar View para retornar o ViewResult:

HomeController.cs:

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

    return View();
}

Quando essa ação é retornada, a exibição About.cshtml mostrada na seção anterior é renderizada como a seguinte página da Web:

About page rendered in the Edge browser

O método auxiliar View tem várias sobrecargas. Opcionalmente, você pode especificar:

  • Uma exibição explícita a ser retornada:

    return View("Orders");
    
  • Um modelo a ser passado para a exibição:

    return View(Orders);
    
  • Um modo de exibição e um modelo:

    return View("Orders", Orders);
    

Descoberta de exibição

Quando uma ação retorna uma exibição, um processo chamado descoberta de exibição ocorre. Esse processo determina qual arquivo de exibição é usado com base no nome da exibição.

O comportamento padrão do método View (return View();) é retornar uma exibição com o mesmo nome que o método de ação do qual ela é chamada. Por exemplo, o About nome do método do controlador ActionResult é usado para pesquisar um arquivo de exibição chamado About.cshtml. Primeiro, o runtime procura a exibição na pasta Views/[ControllerName]. Se não encontrar uma exibição correspondente nela, ele procura pela exibição na pasta Shared.

Não importa se você retornar implicitamente o ViewResult com return View(); ou se passar explicitamente o nome de exibição para o método View com return View("<ViewName>");. Nos dois casos, a descoberta de exibição pesquisa por um arquivo de exibição correspondente nesta ordem:

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

Um caminho de arquivo de exibição pode ser fornecido em vez de um nome de exibição. Se um caminho absoluto que começa na raiz do aplicativo (ou é iniciado por "/" ou "~ /") estiver sendo usado, a extensão .cshtml deverá ser especificada:

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

Você também pode usar um caminho relativo para especificar exibições em diretórios diferentes sem a extensão .cshtml. Dentro do HomeController, você pode retornar a exibição Index de suas exibições Manage com um caminho relativo:

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

De forma semelhante, você pode indicar o atual diretório específico do controlador com o prefixo ". /":

return View("./About");

Exibições parciais e componentes de exibição usam mecanismos de descoberta semelhantes (mas não idênticos).

É possível personalizar a convenção padrão de como as exibições ficam localizadas dentro do aplicativo usando um IViewLocationExpander personalizado.

A descoberta de exibição depende da localização de arquivos de exibição pelo nome do arquivo. Se o sistema de arquivos subjacente diferenciar maiúsculas de minúsculas, os nomes de exibição provavelmente diferenciarão maiúsculas de minúsculas. Para fins de compatibilidade de sistemas operacionais, padronize as maiúsculas e minúsculas dos nomes de controladores e de ações e dos nomes de arquivos e pastas de exibição. Se encontrar um erro indicando que não é possível encontrar um arquivo de exibição ao trabalhar com um sistema de arquivos que diferencia maiúsculas de minúsculas, confirme que o uso de maiúsculas e minúsculas é correspondente entre o arquivo de exibição solicitado e o nome do arquivo de exibição real.

Siga a melhor prática de organizar a estrutura de arquivos de suas exibições de forma a refletir as relações entre controladores, ações e exibições para facilidade de manutenção e clareza.

Passar dados para exibições

Passe dados para exibições usando várias abordagens:

  • Dados fortemente tipados: viewmodel
  • Dados fracamente tipados
    • ViewData (ViewDataAttribute)
    • ViewBag

Dados fortemente tipados (viewmodel)

A abordagem mais robusta é especificar um tipo de modelo na exibição. Esse modelo é conhecido como viewmodel. Você passa uma instância do tipo viewmodel para a exibição da ação.

Usar um viewmodel para passar dados para uma exibição permite que a exibição tire proveito da verificação de tipo forte. Tipagem forte (ou fortemente tipado) significa que cada variável e constante têm um tipo definido explicitamente (por exemplo, string, int ou DateTime). A validade dos tipos usados em uma exibição é verificada em tempo de compilação.

O Visual Studio e o Visual Studio Code listam membros de classe fortemente tipados usando um recurso chamado IntelliSense. Quando quiser ver as propriedades de um viewmodel, digite o nome da variável para o viewmodel, seguido por um ponto final (.). Isso ajuda você a escrever código mais rapidamente e com menos erros.

Especifique um modelo usando a diretiva @model. Use o modelo com @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>

Para fornecer o modelo à exibição, o controlador o passa como um parâmetro:

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);
}

Não há restrições quanto aos tipos de modelo que você pode fornecer a uma exibição. Recomendamos o uso de viewmodels do tipo POCO (objeto CRL básico) com pouco ou nenhum comportamento (métodos) definido. Geralmente, classes de viewmodel são armazenadas na pasta Models ou em uma pasta ViewModels separada na raiz do aplicativo. O viewmodel Address usado no exemplo acima é um viewmodel POCO armazenado em um arquivo chamado Address.cs:

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; }
    }
}

Nada impede que você use as mesmas classes para seus tipos de viewmodel e seus tipos de modelo de negócios. No entanto, o uso de modelos separados permite que suas exibições variem independentemente das partes de lógica de negócios e de acesso a dados do aplicativo. A separação de modelos e viewmodels também oferece benefícios de segurança quando os modelos usam model binding e validação para dados enviados ao aplicativo pelo usuário.

Dados com tipo fraco (ViewData, atributo [ViewData] e ViewBag)

ViewBagnão está disponível por padrão para uso em PáginasRazorPageModel classes.

Além de exibições fortemente tipadas, as exibições têm acesso a uma coleção de dados fracamente tipados (também chamada de tipagem flexível). Diferente dos tipos fortes, ter tipos fracos (ou tipos flexíveis) significa que você não declara explicitamente o tipo dos dados que está usando. Você pode usar a coleção de dados fracamente tipados para transmitir pequenas quantidades de dados para dentro e para fora dos controladores e das exibições.

Passar dados entre... Exemplo
Um controlador e uma exibição Preencher uma lista suspensa com os dados.
Uma exibição e uma exibição de layout Definir o conteúdo do elemento <title> na exibição de layout de um arquivo de exibição.
Uma exibição parcial e uma exibição Um widget que exibe dados com base na página da Web que o usuário solicitou.

Essa coleção pode ser referenciada por meio das propriedades ViewData ou ViewBag em controladores e exibições. A propriedade ViewData é um dicionário de objetos fracamente tipados. A propriedade ViewBag é um wrapper em torno de ViewData que fornece propriedades dinâmicas à coleção de ViewData subjacente. Observação: as pesquisas de chave diferenciam maiúsculas de minúsculas tanto para ViewData quanto para ViewBag.

ViewData e ViewBag são resolvidos dinamicamente em runtime. Uma vez que não oferecem verificação de tipo em tempo de compilação, geralmente ambos são mais propensos a erros do que quando um viewmodel é usado. Por esse motivo, alguns desenvolvedores preferem nunca usar ViewData e ViewBag ou usá-los o mínimo possível.

ViewData

ViewData é um objeto ViewDataDictionary acessado por meio de chaves string. Dados de cadeias de caracteres podem ser armazenados e usados diretamente, sem a necessidade de conversão, mas você precisa converter os valores de outros objetos ViewData em tipos específicos quando extraí-los. Você pode usar ViewData para passar dados de controladores para exibições e dentro das exibições, incluindo exibições parciais e layouts.

A seguir, temos um exemplo que define valores para uma saudação e um endereço usando ViewData em uma ação:

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

    return View();
}

Trabalhar com os dados em uma exibição:

@{
    // 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>

Atributo [ViewData]

Outra abordagem que usa ViewDataDictionary é ViewDataAttribute. As propriedades nos controladores ou nos modelos da Página Razor marcadas com o atributo [ViewData] têm seus valores armazenados e carregados do dicionário.

No exemplo a seguir, o controlador Home contém uma propriedade Title marcada com [ViewData]. O método About define o título para a exibição 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();
    }
}

No layout, o título é lido a partir do dicionário ViewData:

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

ViewBag

ViewBagnão está disponível por padrão para uso em PáginasRazorPageModel classes.

ViewBag é um objeto Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.DynamicViewData que fornece acesso dinâmico aos objetos armazenados em ViewData. Pode ser mais conveniente trabalhar com ViewBag, pois ele não requer uma conversão. O exemplo a seguir mostra como usar ViewBag com o mesmo resultado que o uso de ViewData acima:

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>

Usando ViewData e ViewBag simultaneamente

ViewBagnão está disponível por padrão para uso em PáginasRazorPageModel classes.

Como ViewData e ViewBag fazem referência à mesma coleção ViewData subjacente, você pode usar ViewData e ViewBag, além de misturá-los e combiná-los ao ler e gravar valores.

Defina o título usando ViewBag e a descrição usando ViewData na parte superior de uma exibição About.cshtml:

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

Leia as propriedades, mas inverta o uso de ViewData e ViewBag. No arquivo _Layout.cshtml, obtenha o título usando ViewData e a descrição usando ViewBag:

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

Lembre-se de que cadeias de caracteres não exigem uma conversão para ViewData. Você pode usar @ViewData["Title"] sem converter.

Usar ViewData e ViewBag ao mesmo tempo funciona, assim como misturar e combinar e leitura e a gravação das propriedades. A seguinte marcação é renderizada:

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

Resumo das diferenças entre ViewData e ViewBag

ViewBagnão está disponível por padrão para uso em PáginasRazorPageModel classes.

  • ViewData
    • Deriva de ViewDataDictionary, de forma que tem propriedades de dicionário que podem ser úteis, como ContainsKey, Add, Remove e Clear.
    • Chaves no dicionário são cadeias de caracteres, de forma que espaços em branco são permitidos. Exemplo: ViewData["Some Key With Whitespace"]
    • Qualquer tipo diferente de string deve ser convertido na exibição para usar ViewData.
  • ViewBag
    • Deriva de Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.DynamicViewData, de forma que permite a criação de propriedades dinâmicas usando a notação de ponto (@ViewBag.SomeKey = <value or object>) e nenhuma conversão é necessária. A sintaxe de ViewBag torna mais rápido adicioná-lo a controladores e exibições.
    • Mais simples de verificar quanto à presença de valores nulos. Exemplo: @ViewBag.Person?.Name

Quando usar ViewData ou ViewBag

ViewData e ViewBag são abordagens igualmente válidas para passar pequenas quantidades de dados entre controladores e exibições. A escolha de qual delas usar é baseada na preferência. Você pode misturar e combinar objetos ViewData e ViewBag, mas é mais fácil ler e manter o código quando uma abordagem é usada de maneira consistente. Ambas as abordagens são resolvidas dinamicamente em runtime e, portanto, são propensas a causar erros de runtime. Algumas equipes de desenvolvimento as evitam.

Exibições dinâmicas

Exibições que não declaram um tipo de modelo usando @model, mas que têm uma instância de modelo passada a elas (por exemplo, return View(Address);) podem referenciar as propriedades da instância dinamicamente:

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

Esse recurso oferece flexibilidade, mas não oferece proteção de compilação ou IntelliSense. Se a propriedade não existir, a geração da página da Web falhará em runtime.

Mais recursos das exibições

Auxiliares de Marca facilitam a adição do comportamento do lado do servidor às marcações HTML existentes. O uso de Auxiliares de marca evita a necessidade de escrever código personalizado ou auxiliares em suas exibições. Auxiliares de marca são aplicados como atributos a elementos HTML e são ignorados por editores que não podem processá-los. Isso permite editar e renderizar a marcação da exibição em várias ferramentas.

É possível gerar uma marcação HTML personalizada com muitos auxiliares HTML internos. Uma lógica de interface do usuário mais complexa pode ser tratada por Componentes de exibição. Componentes de exibição fornecem o mesmo SoC oferecido por controladores e exibições. Eles podem eliminar a necessidade de ações e exibições que lidam com os dados usados por elementos comuns da interface do usuário.

Como muitos outros aspectos do ASP.NET Core, as exibições dão suporte à injeção de dependência, permitindo que serviços sejam injetados em exibições.

Isolamento de CSS

Isole estilos CSS em páginas, exibições e componentes individuais para reduzir ou evitar:

  • Dependências de estilos globais que podem ser desafiadoras de manter.
  • Conflitos de estilo em conteúdo aninhado.

Para adicionar um arquivo CSS com escopo para uma página ou exibição, coloque os estilos CSS em um arquivo .cshtml.css complementar correspondente ao nome do arquivo .cshtml. No exemplo a seguir, um arquivo Index.cshtml.css fornece estilos CSS aplicados somente à página ou exibição Index.cshtml.

Pages/Index.cshtml.css (Razor Pages) ou Views/Index.cshtml.css (MVC):

h1 {
    color: red;
}

O isolamento de CSS ocorre no momento do build. A estrutura reescreve seletores de CSS para fazer a correspondência da marcação renderizada pelas páginas ou exibições do aplicativo. Os estilos CSS reescritos são empacotados e produzidos como um ativo estático, {APP ASSEMBLY}.styles.css. O espaço reservado {APP ASSEMBLY} é o nome do assembly do projeto. Um link para os estilos CSS empacotados é colocado no layout do aplicativo.

No conteúdo <head> do aplicativo Pages/Shared/_Layout.cshtml (Razor Pages) ou Views/Shared/_Layout.cshtml (MVC), adicione ou confirme a presença do link para os estilos CSS empacotados:

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

No seguinte exemplo, o nome do assembly do aplicativo é WebApp:

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

Os estilos definidos em um arquivo CSS com escopo são aplicados apenas à saída renderizada do arquivo correspondente. No exemplo anterior, as declarações CSS h1 definidas em outro lugar no aplicativo não entram em conflito com o estilo de título de Index. O estilo CSS em cascata e as regras de herança permanecem em vigor para arquivos CSS com escopo. Por exemplo, estilos aplicados diretamente a um elemento <h1> no arquivo Index.cshtml substituem os estilos do arquivo CSS com escopo em Index.cshtml.css.

Observação

Para garantir o isolamento do estilo CSS quando o agrupamento ocorrer, não há suporte para a importação de CSS em blocos de código de Razor.

O isolamento de CSS se aplica apenas a elementos HTML. Não há suporte para isolamento de CSS para Auxiliares de Marcação.

Dentro do arquivo CSS empacotado, cada página, exibição ou componente Razor é associado a um identificador de escopo no formato b-{STRING}, em que o espaço reservado {STRING} é uma cadeia de caracteres de dez caracteres gerada pela estrutura. O seguinte exemplo fornece o estilo do elemento <h1> anterior na página Index de um aplicativo Razor Pages:

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

Na página Index em que o estilo CSS é aplicada do arquivo empacotado, o identificador de escopo é acrescentado como um atributo HTML:

<h1 b-3xxtam6d07>

O identificador é exclusivo para um aplicativo. No momento da compilação, um pacote de projeto é criado com a convenção {STATIC WEB ASSETS BASE PATH}/Project.lib.scp.css, em que o espaço reservado {STATIC WEB ASSETS BASE PATH} é o caminho base dos ativos da Web estáticos.

Se outros projetos forem utilizados, como pacotes NuGet ou bibliotecas de classes Razor, o arquivo empacotado:

  • Fará referência aos estilos que usam importações de CSS.
  • Não será publicado como um ativo da Web estático do aplicativo que consome os estilos.

Suporte para pré-processador de CSS

Pré-processadores de CSS são úteis para aprimorar o desenvolvimento de CSS utilizando recursos como variáveis, aninhamento, módulos, mixins e herança. Embora o isolamento de CSS não dê suporte nativo a pré-processadores CSS, como Sass ou Less, a integração de pré-processadores de CSS é contínua desde que a compilação do pré-processador ocorra antes que a estrutura reescreva os seletores de CSS durante o processo de build. Usando o Visual Studio, por exemplo, configure a compilação de pré-processador existente como uma tarefa Before Build no Gerenciador do Executor de Tarefas do Visual Studio.

Muitos pacotes NuGet de terceiros, como AspNetCore.SassCompiler, podem compilar arquivos SASS/SCSS no início do processo de build antes que o isolamento de CSS ocorra e nenhuma configuração adicional seja necessária.

Configuração do isolamento de CSS

O isolamento de CSS permite a configuração de alguns cenários avançados, como quando há dependências em ferramentas ou fluxos de trabalho existentes.

Personalizar o formato de identificador de escopo

Nesta seção, o espaço reservado {Pages|Views} é Pages para aplicativos Razor Pages ou Views para aplicativos MVC.

Por padrão, os identificadores de escopo usam o formato b-{STRING}, em que o espaço reservado {STRING} é uma cadeia de caracteres de dez caracteres gerada pela estrutura. Para personalizar o formato do identificador de escopo, atualize o arquivo de projeto para um padrão desejado:

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

No exemplo anterior, o CSS gerado para Index.cshtml.css altera o identificador de escopo de b-{STRING} para custom-scope-identifier.

Use identificadores de escopo para obter a herança com arquivos CSS com escopo. No exemplo de arquivo de projeto a seguir, um arquivo BaseView.cshtml.css contém estilos comuns entre exibições. Um arquivo DerivedView.cshtml.css herda esses estilos.

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

Use o operador curinga (*) para compartilhar identificadores de escopo em vários arquivos:

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

Alterar o caminho base para ativos da Web estáticos

O arquivo CSS com escopo é gerado na raiz do aplicativo. No arquivo de projeto, use a propriedade StaticWebAssetBasePath para alterar o caminho padrão. O seguinte exemplo coloca o arquivo CSS com escopo, e o restante dos ativos do aplicativo, no caminho _content:

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

Desabilitar o agrupamento automático

Para recusar como a estrutura publica e carrega os arquivos com escopo no runtime, use a propriedade DisableScopedCssBundling. Ao usar essa propriedade, outras ferramentas ou processos são responsáveis por tirar os arquivos CSS isolados do diretório obj e publicá-los e carregá-los em runtime:

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

Suporte à RCL (biblioteca de classes) Razor

Quando uma RCL (biblioteca de classes) Razor fornece estilos isolados, o atributo <link> da marca href aponta para {STATIC WEB ASSET BASE PATH}/{PACKAGE ID}.bundle.scp.css, onde estão os espaços reservados:

  • {STATIC WEB ASSET BASE PATH}: o caminho de base do ativo da Web estático.
  • {PACKAGE ID}: o identificador de pacote da biblioteca. O identificador de pacote usará como padrão o nome do assembly do projeto se não for especificado no arquivo de projeto.

No exemplo a seguir:

  • O caminho de base do ativo da Web estático é _content/ClassLib.
  • O nome do assembly da biblioteca de classes é ClassLib.

Pages/Shared/_Layout.cshtml (Razor Pages) ou Views/Shared/_Layout.cshtml (MVC):

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

Para obter mais informações sobre RCLs, consulte os seguintes artigos:

Para obter informações sobre o isolamento de CSS de Blazor, consulte Isolamento de CSS do Blazor do ASP.NET Core.