-
Notifications
You must be signed in to change notification settings - Fork 446
Description
🐛 Bug Report: Sticky Header Stops Working in FluentDataGrid with Virtualize="true"
💻 Repro or Code Sample
@page "/issue"
<h3>Items Übersicht</h3>
<FluentDataGrid Items="_filteredItems.AsQueryable()"
ItemSize="48"
Virtualize="true"
AutoFit="true"
DisplayMode="DataGridDisplayMode.Table"
ResizableColumns="true"
GenerateHeader="GenerateHeaderOption.Sticky">
<PropertyColumn Width="150px" Property="@(c => c.Id)" Title="ID" Sortable="true" Filtered="_filter.Id != string.Empty">
<ColumnOptions>
<div class="search-box">
<FluentSearch type="search" Autofocus="true"
Immediate="true"
@bind-Value="_filter.Id"
Placeholder="ID ..." />
</div>
</ColumnOptions>
</PropertyColumn>
<PropertyColumn Width="200px" Property="@(c => c.Title)" Title="Title" Sortable="true" />
<PropertyColumn Width="100px" Property="@(c => c.Value)" Title="Value" Sortable="true" />
</FluentDataGrid>
@code {
private List<Item> _allItems = new();
private List<Item> _filteredItems = new();
private FilterModel _filter = new();
protected override void OnInitialized()
{
for (int i = 1; i <= 100; i++)
{
_allItems.Add(new Item
{
Id = $"ID-{i:D3}",
Title = $"Title {i}",
Value = i * 10
});
}
_filteredItems = _allItems.ToList();
}
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
_filter.PropertyChanged += (_, __) => ApplyFilter();
}
}
private void ApplyFilter()
{
_filteredItems = _allItems
.Where(item => string.IsNullOrEmpty(_filter.Id) || item.Id.Contains(_filter.Id, StringComparison.OrdinalIgnoreCase))
.ToList();
StateHasChanged();
}
public class Item
{
public string Id { get; set; }
public string Title { get; set; }
public int Value { get; set; }
}
public class FilterModel : System.ComponentModel.INotifyPropertyChanged
{
private string _id = string.Empty;
public string Id
{
get => _id;
set
{
if (_id != value)
{
_id = value;
PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(nameof(Id)));
}
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
}
}
Steps to reproduce:
- Populate the grid with enough items to allow scrolling at least twice the height of the grid.
- Scroll down the size of the grid
- Paste a string into the search input field.
🤔 Expected Behavior
The sticky header should remain visible at the top of the grid while scrolling, and the grid content should remain visible below it.
😯 Current Behavior
When typing or pasting in the filter, the sticky header scrolls with the content instead of staying fixed. Depending on the scroll position, neither the header nor the grid items are visible.
💁 Possible Solution
Investigate how sticky headers interact with virtualized rows when StateHasChanged is triggered from filter input changes.
Adjust header positioning after virtualization re-renders.
🔦 Context
The issue prevents users from seeing the column header and grid content when filtering with large datasets.
Removing Virtualize="true" and adding @bind-Value:after="@(async () => await InvokeAsync(StateHasChanged))" to the Search Field fixes the sticky header but introduces performance issues. Removing Immediate="true" also fixes the issue.

🌍 Your Environment
OS & Device: Windows, PC
Browser: Chrome, Firefox, Edge
.NET Version: 9
Microsoft.FluentUI.AspNetCore.Components Version: 4.12.1
Blazor: Interactive SSR
I’m happy to help investigate or contribute a fix if needed.