Radzen.DataGrid Virtualization issue

When using custom list virtualization not all records are shown when the total list length is between 11 and 20 list items. It only shows the first 10 rows and the resting rows are not loaded/shown.

It is a VS2019 .NET5.0 Blazor Server Web App. Using Radzen.Blazor package version 3.9.9.

Screenshot:

Razor page:

<RadzenDataGrid @ref="grid" Data="@virtualListItems" Count="@count" LoadData="@LoadData" AllowVirtualization="true"
                AllowSorting="true" AllowFiltering="true" FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive"
                TItem="LookupListItem" ColumnWidth="300px" Style="height:600px">
    <Columns>
        <RadzenDataGridColumn TItem="LookupListItem" Property="Id" Title="ID" Filterable="false" Sortable="false" Frozen="true" Width="50px" TextAlign="TextAlign.Center" />
        <RadzenDataGridColumn TItem="LookupListItem" Title="Action" Filterable="false" Sortable="false" TextAlign="TextAlign.Center" Width="70px">
            <Template Context="listItem">
                <RadzenButton Icon="edit" Size="ButtonSize.Small" Click="@(args => EditItem(listItem))" @onclick:stopPropagation="true" />
            </Template>
        </RadzenDataGridColumn>
        <RadzenDataGridColumn TItem="LookupListItem" Property="Code" Title="Code" Visible="@hasCode" Width="150px" />
        <RadzenDataGridColumn TItem="LookupListItem" Property="Name" Title="@columnName" />
        <RadzenDataGridColumn TItem="LookupListItem" Property="ParentName" Title="@parentName" Visible="@hasParent" />
        <RadzenDataGridColumn TItem="LookupListItem" Property="ParamList" Title="Research Domains" Filterable="false" Sortable="false" Visible="@hasResearchDomain" />
    </Columns>
</RadzenDataGrid>

Code:

        void LoadData(LoadDataArgs args)
        {
            var query = listItems.AsQueryable();

            if (!string.IsNullOrEmpty(args.Filter))
            {
                query = query.Where(args.Filter);
            }

            if (!string.IsNullOrEmpty(args.OrderBy))
            {
                query = query.OrderBy(args.OrderBy);
            }

            virtualListItems = query.Skip(args.Skip.Value).Take(args.Top.Value).ToList();

            count = query.Count();
        }

Many thanks for your feedback.
Grtz
Marc

Try setting the PageSize property of the grid to a higher value. It is 10 by default.

Hi Korchev,
Thanks for your reply. I've added PageSize="100" to the RadzenDataGrid component. Unfortunately, it does not solve the problem. When switching between lists with relatively small record sizes, it shows only the number of records from the previous list (e.g. 1st list contains 11 records, 2nd list contains 24 records, only first 11 records are shown for the 2nd list). When switching between lists with a larger number of records it seems to work fine.
Grtz
Marc

Can you reproduce this problem in our online demo? We don't know how to reproduce that issue.

Can you give me a hint how I can change something in the Online Demo?

The source is available here: https://github.com/radzenhq/radzen-blazor/tree/master/RadzenBlazorDemos

Hi Atanas,

I succeeded in reproducing the error, and the same time also have some remarks for the Virtualization demo with the LoadData event.

I've adapted the demo DataGridVirtualizationLoadData.razor page (embedded at the bottom of this message) to reflect the problem and also the solution I was struggling for with disconnected lists (using the LoadData event does not work for this).

Hope this is of interest for you, to remediate this strange behavior.

Many thanks.

Kind Regards
Marc

DataGridVirtualizationLoadData.razor page:

@page "/datagrid-virtualization-loaddata"
@using System.Linq.Dynamic.Core
@using RadzenBlazorDemos.Data
@using RadzenBlazorDemos.Models.Northwind
@using Microsoft.EntityFrameworkCore

@inject NorthwindContext dbContext

<h1>DataGrid <strong>Custom Virtualization</strong></h1>

<p>
    <h3>Test page for Virtualization with custom lists or disconnected lists.</h3>
    In contrast with the documentation in the Virtualization with LoadData page on the demo site, don't use the <code>LoadData</code> event, for this kind of custom lists!!<br />
    Instead make sure the list(s) are Queryable.<br />
    All functionality on Sorting and filtering works fine, except for small lists or results.<br />
    There is still a bug in the datagrid virtualization for this:
    <ul>
        <li>When the number of list items is between 10 and 20 it will show only the first 10 items</li>
        <li>Also when the result of a filtering is also in the range between 10 and 20, only the first 10 items are shown.</li>
    </ul>
    This example demonstrates it.  Select the different list sizes and count the result.  Try also filtering e.g. the 28 items list for Quantity >= 20 --> the result returns only 10 items while there should be 18 items.
</p>
<RadzenRadioButtonList @bind-Value=@radioValue TValue="int" Orientation="Orientation.Vertical" Change=@((args) => OnChange(args, "RadioButtonList with vertical orientation"))>
    <Items>
        <RadzenRadioButtonListItem Text="Empty list" Value="0" />
        <RadzenRadioButtonListItem Text="All Orders" Value="1" />
        <RadzenRadioButtonListItem Text="Top 13 Orders" Value="2" />
        <RadzenRadioButtonListItem Text="Top 28 Orders" Value="3" />
    </Items>
</RadzenRadioButtonList>

<RadzenExample Name="DataGridVirtualizationLoadData" Documentation="false" Heading="false">
    <RadzenDataGrid Data="@orderDetails" TItem="OrderDetail" AllowVirtualization="true" Style="height:400px"
                    AllowFiltering="true" FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive" LogicalFilterOperator="LogicalFilterOperator.Or"
                    AllowSorting="true" >
        <Columns>
            <RadzenDataGridColumn TItem="OrderDetail" Property="OrderID" Title="OrderID" />
            <RadzenDataGridColumn TItem="OrderDetail" Property="ProductID" Title="ProductID" />
            <RadzenDataGridColumn TItem="OrderDetail" Property="UnitPrice" Title="Unit Price">
                <Template Context="detail">
                    @String.Format(new System.Globalization.CultureInfo("en-US"), "{0:C}", detail.UnitPrice)
                </Template>
            </RadzenDataGridColumn>
            <RadzenDataGridColumn TItem="OrderDetail" Property="Quantity" Title="Quantity" />
            <RadzenDataGridColumn TItem="OrderDetail" Property="Discount" Title="Discount">
                <Template Context="detail">
                    @String.Format("{0}%", detail.Discount * 100)
                </Template>
            </RadzenDataGridColumn>
        </Columns>
    </RadzenDataGrid>
</RadzenExample>
@code {
    int count;
    IEnumerable<OrderDetail> orderDetails;

    List<OrderDetail> fullList;
    List<OrderDetail> smallList;
    List<OrderDetail> largerList;

    int radioValue = 0;

    protected override void OnInitialized()
    {
        fullList = dbContext.OrderDetails.ToList();
        smallList = dbContext.OrderDetails.Take(13).ToList();
        largerList = dbContext.OrderDetails.Take(28).ToList();

        orderDetails = new List<OrderDetail>();
    }

    void OnChange(object value, string name)
    {
        switch (radioValue)
        {
            case 0:
                orderDetails = new List<OrderDetail>(); // empty list
                break;
            case 1:
                orderDetails = fullList.AsQueryable();
                break;
            case 2:
                orderDetails =smallList.AsQueryable();
                break;
            case 3:
                orderDetails = largerList.AsQueryable();
                break;
        }

        StateHasChanged();
    }
}

Does setting the Count property of the DataGrid resolve the case? It is required when using the LoadData event. It should be set to the total number of records. For the sake of this demo it should probably be set to @orderDetails.Count

I tried this before I found my solution with disconnected custom lists.

This is an example that isn't working:

@page "/datagrid-virtualization-loaddata"
@using System.Linq.Dynamic.Core
@using RadzenBlazorDemos.Data
@using RadzenBlazorDemos.Models.Northwind
@using Microsoft.EntityFrameworkCore

@inject NorthwindContext dbContext

<h1>DataGrid <strong>Custom Virtualization</strong></h1>

<p>
    <h3>Test page for Virtualization with custom lists or disconnected lists.</h3>
    In contrast with the documentation in the Virtualization with LoadData page on the demo site, don't use the <code>LoadData</code> event, for this kind of custom lists!!<br />
    Instead make sure the list(s) are Queryable.<br />
    All functionality on Sorting and filtering works fine, except for small lists or results.<br />
    There is still a bug in the datagrid virtualization for this:
    <ul>
        <li>When the number of list items is between 10 and 20 it will show only the first 10 items</li>
        <li>Also when the result of a filtering is also in the range between 10 and 20, only the first 10 items are shown.</li>
    </ul>
    This example demonstrates it.  Select the different list sizes and count the result.  Try also filtering e.g. the 28 items list for Quantity >= 20 --> the result returns only 10 items while there should be 18 items.
</p>
<RadzenRadioButtonList @bind-Value=@radioValue TValue="int" Orientation="Orientation.Vertical" Change=@((args) => OnChange(args, "RadioButtonList with vertical orientation"))>
    <Items>
        <RadzenRadioButtonListItem Text="Empty list" Value="0" />
        <RadzenRadioButtonListItem Text="All Orders" Value="1" />
        <RadzenRadioButtonListItem Text="Top 13 Orders" Value="2" />
        <RadzenRadioButtonListItem Text="Top 28 Orders" Value="3" />
    </Items>
</RadzenRadioButtonList>

<RadzenExample Name="DataGridVirtualizationLoadData" Documentation="false" Heading="false">
    <RadzenDataGrid Data="@orderDetails" TItem="OrderDetail" LoadData="@LoadData" Count="@orderDetails.Count()" AllowVirtualization="true" Style="height:400px"
                    AllowFiltering="true" FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive" LogicalFilterOperator="LogicalFilterOperator.Or"
                    AllowSorting="true" >
        <Columns>
            <RadzenDataGridColumn TItem="OrderDetail" Property="OrderID" Title="OrderID" />
            <RadzenDataGridColumn TItem="OrderDetail" Property="ProductID" Title="ProductID" />
            <RadzenDataGridColumn TItem="OrderDetail" Property="UnitPrice" Title="Unit Price">
                <Template Context="detail">
                    @String.Format(new System.Globalization.CultureInfo("en-US"), "{0:C}", detail.UnitPrice)
                </Template>
            </RadzenDataGridColumn>
            <RadzenDataGridColumn TItem="OrderDetail" Property="Quantity" Title="Quantity" />
            <RadzenDataGridColumn TItem="OrderDetail" Property="Discount" Title="Discount">
                <Template Context="detail">
                    @String.Format("{0}%", detail.Discount * 100)
                </Template>
            </RadzenDataGridColumn>
        </Columns>
    </RadzenDataGrid>
</RadzenExample>
@code {
    IEnumerable<OrderDetail> orderDetails;

    List<OrderDetail> fullList;
    List<OrderDetail> smallList;
    List<OrderDetail> largerList;

    int radioValue = 0;

    protected override void OnInitialized()
    {
        fullList = dbContext.OrderDetails.ToList();
        smallList = dbContext.OrderDetails.Take(13).ToList();
        largerList = dbContext.OrderDetails.Take(28).ToList();

        orderDetails = new List<OrderDetail>();
    }

    void OnChange(object value, string name)
    {
        switch (radioValue)
        {
            case 0:
                orderDetails = new List<OrderDetail>(); // empty list
                break;
            case 1:
                orderDetails = fullList;
                break;
            case 2:
                orderDetails =smallList;
                break;
            case 3:
                orderDetails = largerList;
                break;
        }

        StateHasChanged();
    }

    void LoadData(LoadDataArgs args)
    {
        var query = orderDetails.AsQueryable();

        if (!string.IsNullOrEmpty(args.Filter))
        {
            query = query.Where(args.Filter);
        }

        if (!string.IsNullOrEmpty(args.OrderBy))
        {
            query = query.OrderBy(args.OrderBy);
        }

        orderDetails = query.Skip(args.Skip.Value).Take(args.Top.Value).ToList();
    }
}

Similar issue with LoadEvent and pulling data through an API. When filtering down to 13 items (using the default page size of 10), the last 3 items do not get loaded. Only seeing a skip=0 and take=10.When filtering to 22 items, everything gets loaded while scrolling (a skip-0 with take=10, followed by skip=1 (or skip=3) and take=19).

A related question: does virtualization require a fixed height? The virtualization does not seem to work when using flex-1 to fill the height of the screen.

Have you resolved your issue? I'm having the same issue. On page load, the DataLoad event fires multiple times with skip=0. When I scroll down, it fires sometimes with skip=0, sometimes with skip=1. If I keep scrolling, it eventually uses a more relevant skip value but it's still not right.

To answer your question, indeed, the grid must have a fixed height for virtualization to work.

I accepted that virtualization only works with fixed height which is usually not great when you design for different screen sizes. I used other solutions like paging when a fixed height was not acceptable.