Why Naive WPF Grids Freeze with Large Datasets
A typical WPF grid implementation often looks like this:
- Bind directly to a List o ObservableCollection containing all rows
- Let WPF create a visual element for each row
- Rely on default virtualization settings (or none at all)
This works fine for a few thousand rows. But once you push into hundreds of thousands or millions of items:
- UI thread blocks while the grid creates and measures visual elements
- Scrolling becomes jittery because too many containers are being recycled or recreated
- Memory usage spikes because the grid tries to keep too many visuals alive
The result: the grid feels slow, even if your data access is fast.
How Xceed DataGrid for WPF Handles 1 Million Rows
Xceed DataGrid para WPF is designed for large, real-world datasets. The key difference is how it handles virtualization and async data loading:
- Data virtualization: Only a small window of rows is realized at any time, even if the source contains 1M+ items
- UI virtualization: Visual containers are reused aggressively, minimizing layout and rendering cost
- Async loading: Data is fetched and prepared on background threads, keeping the UI responsive
Practically, this means:
- Initial grid load stays fast, even with huge datasets
- Scrolling remains smooth because you are not paying the cost of 1M visuals
- Memory footprint stays predictable and manageable
Before/After: Naive WPF Grid vs Xceed DataGrid
Let’s compare a simplified scenario.
Naive WPF Grid (Conceptual)
- Dataset: 1,000,000 rows in a List
- Rejilla: Standard ItemsControl-style grid with minimal virtualization
Observed behavior in a typical setup:
- Initial load: several seconds before the UI becomes responsive
- Scrolling: noticeable stutter when paging through large jumps
- Memory: high usage due to many realized containers
Xceed DataGrid for WPF with Async Virtualization
- Same dataset: 1,000,000 rows, but exposed through a virtualized source
- Rejilla: Xceed DataGrid configured for data + UI virtualization
Observed behavior in a typical setup:
- Initial load: UI becomes usable much faster because only a small subset is realized
- Scrolling: smooth, even when jumping far in the dataset
- Memory: significantly lower because only visible rows are realized
The rest of this article shows how to get there in your own project.
Setting Up Xceed DataGrid for WPF
First, install the DataGrid package via NuGet and reference it in your XAML.
XAML: Basic Grid Setup with Virtualization
<Window x:Class="MillionRowsDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid"
Title="1M Rows Demo" Height="600" Width="1000">
<Grid>
<xcdg:DataGridControl
x:Name="dataGrid"
ItemsSource="{Binding Rows}"
AutoCreateColumns="True"
EnableColumnVirtualization="True"
EnableRowVirtualization="True"
VirtualizingPanel.ScrollUnit="Pixel"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
UseLayoutRounding="True" />
</Grid>
</Window>
Key points:
- EnableRowVirtualization y EnableColumnVirtualization keep the number of realized containers low
- VirtualizingPanel settings ensure virtualization stays active even when grouping or scrolling heavily
Generating 1 Million Rows in the ViewModel
For demonstration purposes, we can simulate a 1M-row dataset. In a real app, you might be streaming from a database or service.
public class MainViewModel
{
public IList<RowViewModel> Rows { get; }
public MainViewModel()
{
const int rowCount = 1_000_000;
var rows = new List<RowViewModel>(rowCount);
for (int i = 0; i < rowCount; i++)
{
rows.Add(new RowViewModel
{
Id = i,
Name = $"Item {i}",
Value = i % 1000,
Timestamp = DateTime.UtcNow.AddSeconds(-i)
});
}
Rows = rows;
}
}
public class RowViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public int Value { get; set; }
public DateTime Timestamp { get; set; }
}
Even with a simple in-memory list, Xceed DataGrid’s virtualization will prevent the UI from trying to render all 1M rows at once.
Introducing Async Data Virtualization
For truly large or remote datasets, you don’t want to materialize all rows in memory. Instead, you can use a virtualized collection that loads pages of data on demand.
Below is a conceptual example of a paged, async collection pattern:
public class AsyncVirtualizedCollection<T> : IList<T>
{
// Implementation omitted for brevity.
// The important idea: items are loaded in pages on demand,
// often using async calls to a repository or service.
}
public class MillionRowsViewModel
{
public IList<RowViewModel> Rows { get; }
public MillionRowsViewModel(IDataSource dataSource)
{
Rows = new AsyncVirtualizedCollection<RowViewModel>(
pageIndex => dataSource.GetPageAsync(pageIndex),
pageSize: 200,
itemCount: 1_000_000);
}
}
With this pattern:
- The grid only requests the pages it needs for the current viewport
- Data is fetched asynchronously, keeping the UI thread responsive
- You avoid loading all 1M rows into memory at once
Xceed DataGrid works very well with this style of virtualized source because it is built for data virtualization scenarios.
Simple Benchmark: Load Time, Memory, Scroll Smoothness
Every application is different, but you can run a simple benchmark comparing your current grid to Xceed DataGrid.
1. Measure Initial Load Time
- Start a stopwatch when you set ItemsSource
- Stop it when the grid first becomes interactive
var stopwatch = Stopwatch.StartNew();
dataGrid.ItemsSource = viewModel.Rows;
stopwatch.Stop();
Debug.WriteLine($"Initial grid load: {stopwatch.ElapsedMilliseconds} ms");
Compare:
- Naive grid: Often multiple seconds with 1M rows
- Xceed DataGrid: Typically much faster because it only realizes visible rows
2. Monitor Memory Usage
Use your usual profiling tools (Visual Studio Diagnostic Tools, dotMemory, etc.) and compare:
- Baseline memory before binding the grid
- Memory after scrolling through a large portion of the dataset
With Xceed DataGrid and proper virtualization, you should see:
- Lower peak memory
- More stable memory usage over time
3. Evaluate Scroll Smoothness
This is subjective but critical for user experience:
- Scroll slowly and quickly through the dataset
- Jump using PageUp/PageDown and scrollbar dragging
With Xceed DataGrid configured for virtualization, scrolling should remain smooth and responsive, even when the underlying dataset is very large.
Best Practices for Large Datasets in WPF with Xceed DataGrid
To get the best performance when rendering 1M+ rows:
- Use data virtualization for remote or very large datasets
- Keep row templates lightweight—avoid heavy visuals or complex layouts in each cell
- Enable row and column virtualization explicitly
- Avoid unnecessary converters in hot paths; precompute values where possible
- Profile early with realistic data volumes, not just sample data
These practices, combined with Xceed DataGrid’s virtualization engine, let you scale far beyond what a naive WPF grid can handle.
Try This with Your Own Dataset
The best benchmark is your real application, with your real data and constraints.
Try Xceed DataGrid for WPF with your own dataset – start a 45-day trial:https://xceed.com/trial/
If you want help reproducing these results or mapping this approach to your architecture, we’re happy to collaborate.
Need help reproducing these results? Reach out:https://xceed.com/support/
FAQ
Q: Do I really need data virtualization if I already use UI virtualization?A: UI virtualization helps, but it only controls how many visual elements are created. With 1M+ rows, you also need data virtualization so you are not loading or holding all items in memory at once.
Q: Will grouping, sorting, and filtering still perform well with 1M rows?A: Yes, but you should be intentional. Xceed DataGrid is optimized for these operations, and you can combine them with server-side or paged operations when working with very large datasets.
Q: Can I start with an in-memory list and move to virtualization later?A: Absolutely. Many teams begin with an in-memory list for simplicity, then introduce a virtualized collection once they understand their real data volumes and performance requirements.
Q: What if my existing grid is already in production and struggling?A: You can introduce Xceed DataGrid incrementally—start with a single screen or module, benchmark it against your current grid, and then roll out the change once you’re confident in the performance gains.