Blazor - Usando o recurso da Virtualização (.NET 5)

Hoje veremos como usar o recurso da virtualização do Blazor no ambiente do .NET 5.

Se você esta chegando agora e não sabe o que é o Blazor leia o artigo ASP .NET Core - Iniciando com o Blazor - Macoratti; se você já conhece e quer saber mais pode fazer o curso de Blazor Essencial.  

Blazor - Virtualização

Com o .NET 5.0 uma das novidades do Blazor é a virtualização.

A ideia da virtualização no Blazor é renderizar apenas o html que a aplicação esta apresentando na tela ao usuário.

Para fazer isso a virtualização limita a renderização da interface do usuário apenas às partes que estão visíveis ao usuário no momento

Com a virtualização se temos uma longa lista de muitos itens, apenas um subconjunto dos dados será renderizado, aqueles que serão apresentados na interface.

Assim, de forma dinâmica a medida que o usuário se move na listagem para cima ou para baixo as linhas vão sendo renderizadas de forma que no documento HTML teremos apenas as linhas que estamos exibindo na tela.

Para aplicar este recurso o Blazor oferece o componente Virtualize que adicionar a virtualização aos componentes do aplicativo.

Usando Virtualize

Uma lista típica ou um componente baseado em tabela pode usar um loop foreach da linguagem C# para renderizar cada item na lista ou cada linha na tabela, assim:

@foreach (var cliente in clientes)
{
<tr>
    <td>@cliente.Nome</td>
    <td>@cliente.Email</td>
    <td>@cliente.Telefone</td>
</tr>
}

Conforme o tamanho da lista aumenta, renderizar todas as linhas da tabela dessa forma pode demorar um pouco, resultando em um atraso perceptível na interface do usuário.

Em vez disso, você pode substituir o loop foreach pelo componente Virtualize, que apenas renderiza as linhas que estão visíveis no momento.

<Virtualize Items="clientes" ItemSize="40" Context="cliente">
   <tr>
    <td>@cliente.Nome</td>
    <td>@cliente.Email</td>
    <td>@cliente.Telefone</td>
</tr>
</Virtualize>

O componente Virtualize calcula quantos itens renderizar com base na altura do contêiner e no tamanho dos itens renderizados em pixels. Você especifica como renderizar cada item usando o modelo ItemContent ou com conteúdo filho. Se os itens renderizados ficarem ligeiramente fora do tamanho especificado, o componente Virtualize ajusta o número de itens renderizados com base na saída renderizada anteriormente.

Se você não quiser carregar todos os itens na memória, você pode especificar um ItemsProvider, como mostrado abaixo:

<Virtualize ItemsProvider="CarregaClientes" ItemSize="40" Context="cliente">
   <tr>
    <td>@cliente.Nome</td>
    <td>@cliente.Email</td>
    <td>@cliente.Telefone</td>
</tr>
</Virtualize>

Um provedor de itens é um método delegado que recupera de maneira assíncrona os itens solicitados sob demanda.

O provedor de itens recebe um ItemsProviderRequest, que especifica o número necessário de itens começando em um índice inicial específico e então recupera os itens solicitados de um banco de dados ou outro serviço e os retorna como um ItemsProviderResult <TItem> junto com uma contagem do número total de itens disponíveis.

Vejamos um exemplo básico de utilização deste recurso no Blazor onde vamos exibir uma lista de tarefas usando a abordagem padrão com laço foreach e a seguir usando o componente Virtualize.

Recursos usados:

Criando o projeto no VS Community 2019

Abra o VS 2019 Community (versão mínima 16.6) e selecione a opção Create a New Project;

A seguir selecione a opção Blazor app e clique em next;

Informe o nome do projeto :  Blazor_Virtualizacao1 a localização e clique em Create;

Selecione a opção - Blazor WebAssembly App e clique no botão Create para criar o projeto.

Com o projeto criado vamos limpar o projeto excluindo os arquivos abaixo e suas referências:

Criando o componente sem virtualização

Neste projeto vamos criar a pasta Models e nesta pasta crie a classe Tarefa:

    public class Tarefa
    {
        public int Id { get; set; }
        public string Nome { get; set; }
        public DateTime Inicio { get; set; }
    }

Na pasta Pages vamos criar um componente razor chamado ExemploSemVirtualizacao.razor com o código abaixo:

@page "/exemplo1"
<h3>Virtualização</h3>
Quantidade de Tarefas:
<input type="number" @bind="quantidadeTarefas" />
<button type="button" @onclick="GerarTarefasClick">Gerar Tarefas</button>
<div>
    <table class="table table-striped">
        <thead>
            <tr>
                <th>Id </th>
                <th>Tarefa </th>
                <th>Inicio </th>
                <th>Prazo(Em dias)</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var tarefa in Tarefas)
            {
            <tr>
                <td>@tarefa.Id </td>
                <td>@tarefa.Nome </td>
                <td><input type="date" @bind="tarefa.Inicio" /> </td>
                <td>@(DateTime.Today.Subtract(tarefa.Inicio).TotalDays.ToString())</td>
            </tr>
            }
        </tbody>
    </table>
</div>
@code
    {
    private List<Tarefa> Tarefas = new List<Tarefa>();
    private int quantidadeTarefas = 50;
    protected override void OnInitialized()
    {
        Tarefas = ObterTarefas(quantidadeTarefas);
    }
    private void GerarTarefasClick()
    {
        Tarefas = ObterTarefas(quantidadeTarefas);
    }
    public List<Tarefa> ObterTarefas(int quantidade)
    {
        var lista = new List<Tarefa>();
        for(int i=0; i < quantidade; i++ )
        {
            lista.Add(new Tarefa()
            {
                Id = i,
                Nome = $"Tarefa {i}",
                Inicio = DateTime.Today.AddDays(-i)
            });
        }
        return lista;
    }
}

Este código faz o seguinte:

Gera uma quantidade de tarefas com nome e data de início de forma automática e exibe os dados em uma tabela HTML onde o prazo da tarefa esta sendo calculado de forma dinâmica quando o usuário edita o valor da data de início da tarefa.

Aqui estamos usando a abordagem tradicional e percorrendo os dados usando um laço foreach.

Nesta abordagem todos os dados serão renderizados na memória para exibição mesmo que somente uma parte deles seja visível na tela ao usuário.

Criando o componente sem virtualização

Vamos criar agora na pasta Pages o componente ExemploComVirtualizacao.razor com o código abaixo:

@page "/exemplo2"
<h3>Virtualizar</h3>
Quantidade de Tarefas:
<input type="number" @bind="quantidadeTarefas" />
<button type="button" @onclick="GerarTarefasClick">Gerar Tarefas</button>
<div>
    <table class="table table-striped">
        <thead>
            <tr>
                <th>Id </th>
                <th>Tarefa </th>
                <th>Inicio </th>
                <th>Prazo(Em dias)</th>
            </tr>
        </thead>
        <tbody>
            <Virtualize Items="Tarefas" Context="tarefa" ItemSize="40">
                <tr>
                    <td>@tarefa.Id </td>
                    <td>@tarefa.Nome </td>
                    <td><input type="date" @bind="tarefa.Inicio" /> </td>
                    <td>@(DateTime.Today.Subtract(tarefa.Inicio).TotalDays.ToString())</td>
                </tr>
            </Virtualize>
        </tbody>
    </table>
</div>
@code
    {
    private List<Tarefa> Tarefas = new List<Tarefa>();
    private int quantidadeTarefas = 50;
    protected override void OnInitialized()
    {
        Tarefas = ObterTarefas(quantidadeTarefas);
    }
    private void GerarTarefasClick()
    {
        Tarefas = ObterTarefas(quantidadeTarefas);
    }
    public List<Tarefa> ObterTarefas(int quantidade)
    {
        var lista = new List<Tarefa>();
        for (int i = 0; i < quantidade; i++)
        {
            lista.Add(new Tarefa()
            {
                Id = i,
                Nome = $"Tarefa {i}",
                Inicio = DateTime.Today.AddDays(-i)
            });
        }
        return lista;
    }
}

Este código faz exatamente a mesma coisa que o componente anterior. A diferença é que estamos usando o componente Virtualize onde substituímos o trecho de código a seguir:

           @foreach (var tarefa in Tarefas)
            {
           
<tr>
                <td>@tarefa.Id </td>
                <td>@tarefa.Nome </td>
                <td><input type="date" @bind="tarefa.Inicio" /> </td>
                <td>@(DateTime.Today.Subtract(tarefa.Inicio).TotalDays.ToString())</td>
            </tr>
            }

Pelo seguinte código, substituindo o foreach por Virtualize e definindo as propriedades Items, Context e ItemSize do componente:

             <Virtualize Items="Tarefas" Context="tarefa" ItemSize="40">
              
 <tr>
                    <td>@tarefa.Id </td>
                    <td>@tarefa.Nome </td>
                    <td><input type="date" @bind="tarefa.Inicio" /> </td>
                    <td>@(DateTime.Today.Subtract(tarefa.Inicio).TotalDays.ToString())</td>
                </tr>
            </Virtualize>

No arquivo NavMenu.razor da pasta Shared vamos alterar o código para criar opções no menu para chamar cada componente criado acima:

...
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
   <ul class="nav flex-column">
       <li class="nav-item px-3">
           <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
             <span class="oi oi-home" aria-hidden="true"></span> Home
          </NavLink>
       </li>
       <li class="nav-item px-3">
            <NavLink class="nav-link" href="exemplo1">
               <span class="oi oi-plus" aria-hidden="true"></span> Sem Virtualização
            </NavLink>
        </li>
        <li class="nav-item px-3">
              <NavLink class="nav-link" href="exemplo2">
                 <span class="oi oi-plus" aria-hidden="true"></span> Com Virtualização
              </NavLink>
         </li>
</ul>
</div>
...

Pronto ! já podemos testar o projeto e ver a diferença entre usar foreach e usar Virtualize:

Executando o projeto teremos o resultado a seguir:

Notamos que com o uso do componente Virtualize temos um ganho de desempenho e isso será mais sensível quanto maior for a quantidade de itens na lista a serem exibidos.

Na próxima parte do artigo vamos continuar a mostrar outros recursos do componente Virtualize.

Pegue o projeto aqui:  Blazor_Virtualizacao1.zip (sem as referências)

"Dando graças ao Pai que nos fez idôneos para participar da herança dos santos na luz;
O qual nos tirou da potestade das trevas, e nos transportou para o reino do Filho do seu amor;"
Colossenses 1:12,13

 


Referências:


José Carlos Macoratti