DataGrid LoadOnDemand blink on scroll (only with Firefox)

Hello.

--> I use .NET 7.0 and my application is in Blazor Server.

I am currently working with Radzen DataGrid and I try to load my data on the scroll (with the LoadData event).
I have a style of "cache" that store the data i have already fetch which is used when i scroll back.

My algorithm is :

  1. When the LoadData is fired
  2. If my page calculated with args.Skip.Value and args.Top.Value is not available in cache
    2.1. Then fetch the API and store the data in the cache
  3. Else
    3.1. Pick the data in cache

In fact that work but i got a graphic problem with Firefox, the grid blink when i scroll and that doesnt appear with other browsers.

You can see under two GIF of the behavior, the first one with Microsoft Edge and the second with Firefox.

#1 Microsoft Edge
RadzenEdge

#2 Mozilla Firefox
RadzenMozilla

Do you have any idea of what i did wrong ?
Thanks.

Best regards,
Yanis.

I also created a git issue at this url : DataGrid LoadOnDemand blink on scroll (only with Firefox) ยท Issue #1376 ยท radzenhq/radzen-blazor (github.com)

Hi @Yanis,

This seems as a browser issue I am afraid - probably a combination of custom styling and the virtualization. Does our online demo work without flashing for you? It seems fine when I view it with latest Firefox.

I try that and I contact you back.

For information I am testing multiple Blazor framework and i already faced to differences between Mozilla and chromium browsers (Edge, Opera..)

I have implemented the online demo, the Firefox Grid dont flash no more.

However, sometimes, the scroll is not smooth as Microsoft Edge, is it possible that the customisation i made amplify this at the point to flash ?

Best regards,
Yanis.

It could be related. You can try removing it to see if it makes any difference. Browser engines have different implementations and optimisations. Things that are perfectly valid CSS and HTML could cause flashes and other glitches. Fortunately this is a lot less common than it used to be 10 or 15 years ago.

Thank's for your reponses.

I still searching a solution for the problem and after some tests, it's appear that grid with a Count property set to a big value have some flashes or incoherent behaviors.

I have use the online demo without any customisation, I only add my load on demand algorithm.

The grid with 500 k elements is working on Microsoft Edge but not on Mozilla, you can switch (click multiple time or refresh the page if it doesn't work) to small one grid (1500 elements) and the grid will fork fine also with Mozilla.

I have join my code, do you see any error in my code that can be the cause ?

Thank you very much :grinning:

Best regards,
Yanis.

@page "/demo-radzen"

<RadzenButton Click="SwitchMode">Switch of mode</RadzenButton>
@if (FlashMob)
{
    <p> In the current configuration (500000 OrderDetails) the grid will flash (on Mozilla Firefox). </p>
}
else
{
    <p> In the current configuration (1500 OrderDetails) the grid will work fine.</p>
}

<RadzenDataGrid Data="@_orderDetailsDisplayed"
                LoadData="LoadData"
                Count="_allOrderDetails.Count()"
                TItem="OrderDetail"
                AllowVirtualization="true"
                Style="height:400px"
                AllowFiltering="true"
                FilterPopupRenderMode="PopupRenderMode.OnDemand"
                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>

@code {
    private IEnumerable<OrderDetail> _allOrderDetails = GetAListOfOrderDetail(500000); // See this as a database

    private Dictionary<int, List<OrderDetail>> _cachedOrderDetails = new Dictionary<int, List<OrderDetail>>(); // The data already fetch

    private IEnumerable<OrderDetail> _orderDetailsDisplayed;

    public bool IsBusy = false;

    public bool FlashMob = true;

    public int PageSize = 100;

    protected override void OnInitialized()
    {
        _orderDetailsDisplayed = GetOrderDetailsOnDemand(0, PageSize);

        base.OnInitialized();
    }
        
    public void SwitchMode()
    {
        FlashMob = !FlashMob;
        _allOrderDetails = GetAListOfOrderDetail(FlashMob ? 500000 : 1500);
        _cachedOrderDetails = new Dictionary<int, List<OrderDetail>>();
        _orderDetailsDisplayed = GetOrderDetailsOnDemand(0, PageSize);
        StateHasChanged();
    }

    private async void LoadData(LoadDataArgs args)
    {
        _orderDetailsDisplayed = GetOrderDetailsOnDemand(args.Skip.Value, args.Top.Value);

        if(_orderDetailsDisplayed.Count() > 0)
            Console.WriteLine($"Display from ID {_orderDetailsDisplayed.Min(order => order.OrderID)} to {_orderDetailsDisplayed.Max(order => order.OrderID)}");

        StateHasChanged();
    }

    public List<OrderDetail> GetOrderDetailsOnDemand(int startPos, int elementToTake)
    {
        var fetchOrderDetails = new List<OrderDetail>();

        int startPage = GetStartPage(startPos);
        int endPage = GetEndPage(startPos, elementToTake);

        for (int pageToExplore = startPage; pageToExplore <= endPage; pageToExplore++)
        {
            if (_cachedOrderDetails.ContainsKey(pageToExplore))
            {
                fetchOrderDetails.AddRange(_cachedOrderDetails[pageToExplore]);
            }
            else
            {
                if (IsBusy) continue;
                IsBusy = true;

                List<OrderDetail> result = FetchData(pageToExplore, PageSize);

                fetchOrderDetails.AddRange(result);
                _cachedOrderDetails.Add(pageToExplore, result);

                IsBusy = false;
            }
        }
        return fetchOrderDetails.Skip(startPos - (startPage - 1) * PageSize).Take(elementToTake).ToList();
    }

    public List<OrderDetail> FetchData(int pageNumber, int pageSize)
    {
        //Replace by database fetching
        return _allOrderDetails.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToList();
    }

    private int GetStartPage(int startPos)
    {
        int startPage = (int)Math.Ceiling((decimal)startPos / PageSize);

        if (Math.DivRem(startPos, PageSize).Remainder.Equals(0))
        {
            startPage++;
        }

        if (startPos.Equals(0))
        {
            startPage = 1;
        }

        return startPage;
    }

    private int GetEndPage(int startPos, int elementToTake)
    {
        int endPage = (int)Math.Ceiling((decimal)(startPos + elementToTake - 1) / PageSize);

        if (Math.DivRem(startPos + elementToTake - 1, PageSize).Remainder.Equals(0))
        {
            endPage++;
        }

        return endPage;
    }

    #region Generate a list
    public static List<OrderDetail> GetAListOfOrderDetail(int limit)
    {

        var orderDetailsGenerated = new List<OrderDetail>();

        for (int i = 0; i < limit; i++)
        {
            orderDetailsGenerated.Add(GenerateOrderDetail(i));
        }
        return orderDetailsGenerated;
    }

    public static OrderDetail GenerateOrderDetail(int id)
    {
        Random random = new Random();

        int productId = random.Next(1, 51);

        int quantity = random.Next(10, 71);

        double unitPrice = random.NextDouble() * 50 + 1;

        double discount = random.NextDouble();

        return new OrderDetail(id, productId, unitPrice, quantity, discount);
    }

    public class OrderDetail
    {
        public int OrderID { get; set; }
        public int ProductID { get; set; }
        public double UnitPrice { get; set; }
        public int Quantity { get; set; }
        public double Discount { get; set; }

        public OrderDetail()
        {
        }

        public OrderDetail(int orderId, int productId, double unitPrice, int quantity, double discount)
        {
            OrderID = orderId;
            ProductID = productId;
            UnitPrice = unitPrice;
            Quantity = quantity;
            Discount = discount;
        }
    }
    #endregion
}



The DataGrid (and other Radzen Blazor components) virtualization relies completely on default Microsoft Virtualize component which works differently in different browsers - we cannot change or fix that in any ways.

1 Like