New to Telerik UI for Blazor? Download free 30-day trial

Pin rows on top of the Grid

Environment

Product Grid for Blazor

Description

I would like to pin one or more rows on top of the Grid so that they are always visible to the users.

Solution

To achieve the desired behavior you can:

  • Use the OnRead event to place the row/rows on top of the data collection for the Grid.
  • Use the OnRowRender event to add a custom CSS class to the rows you want to pin.
  • Dynamically calculate the top CSS rule with JavaScript.
  • Register the JavaScript file.
@* Pin rows to the Grid *@

@using Telerik.DataSource
@using Telerik.DataSource.Extensions

@inject IJSRuntime js

<style>
    .k-grid-row-sticky {
        top: 0;
        z-index: auto;
        border: 0;
        position: static;
    }

        .k-grid-row-sticky td {
            border-bottom-width: 1px;
            border-top-width: 1px;
            position: sticky;
            top: inherit;
            bottom: inherit;
            z-index: 2;
            background-color: #f6f6f6;
            border-top-color: rgba(0, 0, 0, 0.3);
            border-bottom-color: rgba(0, 0, 0, 0.3);
        }
</style>

<TelerikGrid TItem="@Employee"
             OnRead="@ReadItems"
             Sortable="true"
             ScrollMode="@GridScrollMode.Scrollable"
             Height="600px"
             RowHeight="40"
             OnRowRender="@RowRenderHandler">
    <GridColumns>
        <GridColumn Field=@nameof(Employee.ID) />
        <GridColumn Field=@nameof(Employee.Name) Title="Name" />
        <GridColumn Field=@nameof(Employee.HireDate) Title="Hire Date" />
    </GridColumns>
</TelerikGrid>

@code {
    private void RowRenderHandler(GridRowRenderEventArgs args)
    {
        var gridRow = args.Item as Employee;

        foreach (var pinnedRowIndex in pinnedGridRows)
        {
            if (gridRow.ID == pinnedRowIndex)
            {
                args.Class = "k-grid-row-sticky";
            }
        }
    }

    public List<int> pinnedGridRows { get; set; } = new List<int>()
    {
        5, 12, 21
    };

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await js.InvokeVoidAsync("pinElements", ".k-grid-row-sticky", 40);
    }

    public int pinnedRowIndex { get; set; } = 20;

    public List<Employee> SourceData { get; set; }

    private List<Employee> PinnedTopRow(DataSourceResult result)
    {
        List<Employee> _data = result.Data.Cast<Employee>().ToList();
        var indexToInsert = 0;

        foreach (var pinnedRow in pinnedGridRows)
        {
            var rowToBePinned = _data.FirstOrDefault(x => x.ID == pinnedRow);
            _data.Remove(rowToBePinned);

            _data.Insert(indexToInsert, rowToBePinned);
            indexToInsert++;
        }

        return _data;
    }

    protected async Task ReadItems(GridReadEventArgs args)
    {
        await Task.Delay(1000);

        var datasourceResult = SourceData.ToDataSourceResult(args.Request);

        args.Data = PinnedTopRow(datasourceResult);
        args.Total = datasourceResult.Total;
    }

    protected override void OnInitialized()
    {
        SourceData = GenerateData();
    }

    private List<Employee> GenerateData()
    {
        var result = new List<Employee>();
        var rand = new Random();
        for (int i = 0; i < 100; i++)
        {
            result.Add(new Employee()
            {
                ID = i,
                Name = "Name " + i,
                HireDate = DateTime.Now.Date.AddDays(rand.Next(-20, 20))
            });
        }

        return result;
    }

    public class Employee
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public DateTime HireDate { get; set; }
    }
}
@* Calculate the top CSS rule *@

function pinElements(pinCSSClass, rowHeight) {
    let elements = document.querySelectorAll(pinCSSClass);

    elements.forEach((element, index) => {
        element.style.top = index * rowHeight + "px"
    });
}
In this article