WPF标准DataGrid动态创建大量列时UI卡顿问题求助
WPF标准DataGrid大量列创建卡顿问题解决方案
问题背景
从Xceed DataGrid迁移至WPF标准DataGrid时,遇到列虚拟化相关的UI卡顿问题:动态创建10000列时应用冻结数秒,但分配数据或创建10000行均无延迟。已配置所有虚拟化相关属性(EnableColumnVirtualization="True"、VirtualizingPanel系列属性等),并尝试过ListCollectionView、CollectionViewSource、DataTable等数据源,问题仍存在。
核心原因
WPF标准DataGrid的EnableColumnVirtualization是列显示阶段的虚拟化,仅控制可见区域外的列不渲染;而手动循环添加10000个DataGridColumn对象到Columns集合时,每个列的初始化、集合变更通知都会触发UI线程的同步操作,大量累积导致UI冻结。Xceed DataGrid在列创建的底层机制上做了批量优化或延迟初始化,因此无此问题。
解决方案
关键优化:利用自动列生成替代手动添加
通过AutoGenerateColumns="True"让DataGrid自动从数据源生成列,此方式会结合EnableColumnVirtualization的机制,内部优化列的创建与加载流程,避免手动添加带来的UI线程阻塞。
具体修改步骤
- 在XAML中为DataGrid开启自动列生成:
<DataGrid x:Name="SeriesTableDataGrid" AutoGenerateColumns="True" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.IsContainerVirtualizable="True" VirtualizingPanel.IsVirtualizingWhenGrouping="True" VirtualizingPanel.VirtualizationMode="Standard" EnableColumnVirtualization="True" EnableRowVirtualization="True" Grid.Row="1"> </DataGrid> - 移除手动添加列的逻辑,直接绑定数据源触发自动列生成:
修改Button_Click事件,无需逐个创建列,直接设置ItemsSource即可自动生成对应列:private void Button_Click(object sender, RoutedEventArgs e) { SeriesTableDataGrid.EnableColumnVirtualization = true; // 直接绑定数据源,自动生成列 SeriesTableDataGrid.ItemsSource = myDataTableColumnWeighted.AsDataView(); } - (可选)自定义列模板
如果需要自定义列的样式或类型,可通过AutoGeneratingColumn事件拦截并修改列的配置,避免手动创建所有列:
同时在XAML中绑定该事件:private void SeriesTableDataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) { // 自定义列头 e.Column.Header = $"Custom {e.PropertyName}"; // 强制设置为文本列(示例) if (e.Column is DataGridTextColumn textColumn) { textColumn.FontSize = 12; } }<DataGrid x:Name="SeriesTableDataGrid" AutoGenerateColumns="True" AutoGeneratingColumn="SeriesTableDataGrid_AutoGeneratingColumn" ...> </DataGrid>
修改后的完整代码
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 (Auto)" 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" AutoGenerateColumns="True" AutoGeneratingColumn="SeriesTableDataGrid_AutoGeneratingColumn" 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 { 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; // 自动生成列,无需手动添加 SeriesTableDataGrid.ItemsSource = myDataTableColumnWeighted.AsDataView(); } private void Button_Click2(object sender, RoutedEventArgs e) { 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; } private void SeriesTableDataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) { // 自定义列示例:修改列头 e.Column.Header = $"Col {e.PropertyName.Split(' ')[1]}"; // 可根据需要设置列的其他属性,如宽度、样式等 e.Column.Width = DataGridLength.Auto; } } }
内容的提问来源于stack exchange,提问作者Madenis
相关产品推荐
相关产品推荐

