Reputation: 143
I am trying to migrate from the xceed DataGrid
to the standard DataGrid
in the WPF controls.
The issue that I am encountering and is blocking me is the Column Virtualization.
More specifically, the initial creation of the columns of the DataGrid. Since my data source is dynamic, the number of columns has to be created at runtime and for a large number of columns, the application freezes - this does not happen with the xceed DataGrid.
I have tried different data sources: ListCollectionView
, CollectionViewSource
, DataTable
, but there is no difference.
This behavior of the DataGrid can be easily reproduced in a demo project. Creating 10k of columns will hang the UI for a few seconds, yet when you assign data to these columns it is instantaneous (as expected from the virtualization).
And without surprise, 10k of rows are created in an instant.
I have played with most of the options below
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsContainerVirtualizable="True"
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
VirtualizingPanel.VirtualizationMode="Standard"
EnableColumnVirtualization="True"
EnableRowVirtualization="True"
yet I cannot make the column creation to be virtualized.
Any suggestions?
Demo code below: MainWindow.xaml
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition />
</Grid.RowDefinitions>
<Button Content="Generate Columns" Click="Button_Click" Width="131" HorizontalAlignment="Left"/>
<Button Content="Assing data to columns" Click="Button_Click2" HorizontalAlignment="Left" Margin="161,0,0,0" Width="149" />
<Button Content="Assing data to rows" Click="Button_Click3" Width="149" HorizontalAlignment="Left" Margin="357,0,0,0"/>
<Button Content="Clear Grid" Click="Button_Click4" Width="149" HorizontalAlignment="Left" Margin="621,0,0,0"/>
<DataGrid x:Name="SeriesTableDataGrid"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsContainerVirtualizable="True"
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
VirtualizingPanel.VirtualizationMode="Standard"
EnableColumnVirtualization="True"
EnableRowVirtualization="True"
Grid.Row="1">
</DataGrid>
</Grid>
</Window>
MainWindow.xaml.cs
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
DataTable myDataTableColumnWeighted;
DataTable myDataTableRowWeighted;
public MainWindow()
{
InitializeComponent();
myDataTableColumnWeighted = new DataTable();
myDataTableRowWeighted = new DataTable();
var myItem = new List<string>();
for (int i = 0; i < 10000; i++)
{
myDataTableColumnWeighted.Columns.Add(new DataColumn("Column " + i));
myItem.Add("entry " + i);
var myOtherRow = myDataTableRowWeighted.NewRow();
myDataTableRowWeighted.Rows.Add(myOtherRow);
}
var myRow = myDataTableColumnWeighted.NewRow();
myRow.ItemArray = myItem.ToArray();
myDataTableColumnWeighted.Rows.Add(myRow);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
SeriesTableDataGrid.EnableColumnVirtualization = true;
foreach (DataColumn col in myDataTableColumnWeighted.Columns)
{
SeriesTableDataGrid.Columns.Add(new DataGridTextColumn { Header = col.ColumnName });
}
}
private void Button_Click2(object sender, RoutedEventArgs e)
{
var source = new CollectionViewSource();
source.Source = myDataTableColumnWeighted;
SeriesTableDataGrid.ItemsSource = myDataTableColumnWeighted.AsDataView();
}
private void Button_Click3(object sender, RoutedEventArgs e)
{
SeriesTableDataGrid.ItemsSource = myDataTableRowWeighted.AsDataView();
}
private void Button_Click4(object sender, RoutedEventArgs e)
{
SeriesTableDataGrid.Columns.Clear();
SeriesTableDataGrid.ItemsSource = null;
}
}
}
Upvotes: 1
Views: 465
Reputation: 29038
The lag you are experiencing is due to the loading/initialization time of the DataGrid
control and not rendering time. You are experiencing a data related performance issue. Your performance issue is not rendering related.
UI virtualization targets rendering performance and reduces the GUI memory footprint by managing the rendering of the data containers. In general, containers outside the viewport are not rendered (virtualized). Only visible containers are generated (realized).
The containers of a DataGrid
are the DataGridRow
and DataGridCell
classes. These two are the data containers that will be rendered. Those are the containers that are actually virtualized.
The columns on the other hand are just abstract layout elements.
A DataGrid
column on class level extends the DataGridColumn
class, for example the DataGridTextColumn
. You can see from the API reference that this class does not extend UIElement
(or Visual
). It is therefore not part of the visual tree (and therefore not a rendering candidate).
The DataGrid
will initialize all columns, but will only render those DataGridCells
that are visible (if DataGrid.EnableColumnVirtualization
is enabled).
The DataGrid
is not meant to represent a table of a relational database. It's meant to be a data table read by humans. For this reason, it doesn't make any sense to display 10k columns.
You definitely need to transform/partition the data into different views that actually make sense.
The Xceed DataGridControl
you are referring to is implemented from scratch. It only extends the WPF ItemsControl
.
I don't think it makes sense to display that many columns. So instead of worrying about how to initialize 10k columns, you should worry about how to split data into reasonable data tables. This will significantly improve the UX by a magnitude compared to fixing the 10k column issue.
Upvotes: 1