WPF DataGrid 加载时间不合理
我的 WPF DataGrids 加载时间总是很长,而且我在网上找不到任何类似的报告,所以我怀疑我做错了什么。现在我确信这一点,因为增加布局复杂性会大大减慢执行速度。在非常简单的布局中,DataGrid 会立即填充,而下面的代码大约需要 3 秒才能执行。
在以下代码中,即使每个单元格未绑定到任何属性并且 AutoGenerateColumns=False,加载 150 行和 11 列也需要约 3 秒的时间。 (我有一个两核 2.6GHz 处理器,有足够的 RAM)。
当 ItemsSource 属性在布局中设置如下时,就会出现瓶颈:
<Window x:Class="datagridtest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Border Background="LightSteelBlue" CornerRadius="10" Margin="10">
<ScrollViewer Margin="10" HorizontalScrollBarVisibility="Auto">
<Grid Margin="10,50,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Expander IsExpanded="True" Name="expander1" Grid.Row="0">
<Grid>
<DataGrid VirtualizingStackPanel.IsVirtualizing="True" AutoGenerateColumns="false" Name="dg" Height="auto" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserResizeRows="False" CanUserSortColumns="False">
<DataGrid.Columns>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Expander>
<Expander IsExpanded="true" Grid.Row="1">
<Grid>
<DataGrid AutoGenerateColumns="True" Height="auto" />
</Grid>
</Expander>
<Expander IsExpanded="true" Grid.Row="2">
<Grid>
<DataGrid AutoGenerateColumns="True" Height="auto" />
</Grid>
</Expander>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="121,-42,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click_2" />
</Grid>
</ScrollViewer>
</Border>
using System.Collections.ObjectModel;
namespace datagridtest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
class row
{
public string Name { get; set; }
public double Age { get; set; }
}
private void button1_Click_2(object sender, RoutedEventArgs e)
{
ObservableCollection<row> src = new ObservableCollection<row>();
for (int i = 0; i < 150; i++)
src.Add(new row { Name = i.ToString(), Age = i / 2 });
dg.ItemsSource = src;
}
}
}
I've always had long loading times with WPF DataGrids, and I cannot find any similar reports online, so I suspected that I was doing something wrong. Now I am sure of it, since adding layout complexity considerably slows down execution. In a very simple layout, the DataGrid populates instantaenously, whereas the code below takes around 3 seconds to execute.
In the following code, it takes ~3 seconds for 150 rows and 11 columns to load, even if each cell is not bound to any property and with AutoGenerateColumns=False. (I have a two core, 2.6GHz processor with plenty of RAM).
The bottle neck takes place when the ItemsSource property is set in a layout as the one below:
<Window x:Class="datagridtest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Border Background="LightSteelBlue" CornerRadius="10" Margin="10">
<ScrollViewer Margin="10" HorizontalScrollBarVisibility="Auto">
<Grid Margin="10,50,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Expander IsExpanded="True" Name="expander1" Grid.Row="0">
<Grid>
<DataGrid VirtualizingStackPanel.IsVirtualizing="True" AutoGenerateColumns="false" Name="dg" Height="auto" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserResizeRows="False" CanUserSortColumns="False">
<DataGrid.Columns>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn >
<DataGridTextColumn.Header >
<TextBlock Width="140" HorizontalAlignment="Center" TextAlignment="Center">untitled<LineBreak/>column</TextBlock>
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Expander>
<Expander IsExpanded="true" Grid.Row="1">
<Grid>
<DataGrid AutoGenerateColumns="True" Height="auto" />
</Grid>
</Expander>
<Expander IsExpanded="true" Grid.Row="2">
<Grid>
<DataGrid AutoGenerateColumns="True" Height="auto" />
</Grid>
</Expander>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="121,-42,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click_2" />
</Grid>
</ScrollViewer>
</Border>
using System.Collections.ObjectModel;
namespace datagridtest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
class row
{
public string Name { get; set; }
public double Age { get; set; }
}
private void button1_Click_2(object sender, RoutedEventArgs e)
{
ObservableCollection<row> src = new ObservableCollection<row>();
for (int i = 0; i < 150; i++)
src.Add(new row { Name = i.ToString(), Age = i / 2 });
dg.ItemsSource = src;
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
仅当 DataGrid 嵌入 ScrollViewer 中时才会出现此问题,如下所示:
这是有道理的,因为此配置会导致同时绘制整个 DataGrid(以便能够正确调整 ScrollViewer 的客户区域大小)。本质上,它覆盖了 DataGrid 的内置虚拟化行为,DataGrid 实现了自己的 ScrollBar,因此不必将其所有内容同时放置在布局中。
换句话说,很少需要在 ScrollViewer 中嵌入 DataGrid,因为 DataGrid 有自己的自动滚动功能。
The problem only occurs when the DataGrid is embedded inside a ScrollViewer like:
This makes sense because this configuration causes the whole DataGrid to be drawn at the same time (to be able to size the ScrollViewer's client area correctly). In essence, it overrides the built-in virtualization behavior of the DataGrid, which implements its own ScrollBars so that not all of its content has to be placed in the layout simultaneously.
In other words, embedding a DataGrid inside a ScrollViewer is rarely needed because the DataGrid has its own automatic scrolling.
我对包含 DataGrid 的 UserControl 也有类似的问题,有时当我将 UserControl 放在新表单或另一个 UserControl 上时,它会在重新绘制 DataGrid 时锁定界面(5 秒?)。与调整大小相同。
我追踪到
如果我将 UserControl 放入 StackPanel 中,也会发生相同的性能问题。当需要填充整个数据网格来计算封装容器的大小时,似乎与前面提到的调整大小错误有很大关系。
我刚刚发现根据之前的评论,为 ContentControl 设置 MaxHeight="[whatever]" 也有效。它可以比屏幕大。
I had a similar problem with a UserControl that contained a DataGrid, sometimes when I placed the UserControl on a new form or another UserControl it would lock up the interface (5 seconds?) while it redrew the DataGrid. Same with Resizing.
I tracked it down to
and the same performance issue also happened if I put the UserControl in a StackPanel. Seems very much to do with the previously mentioned resizing bug when the whole datagrid needs to be popluated to calculate the size of the encapsulating container.
I just discovered that setting MaxHeight="[whatever]" for the ContentControl also works, as per a previous comment. It can be larger than the screen.
您能看到是否所有行都是按布局生成的吗?通常虚拟化应该阻碍这一点并且只生成可见的行。 (使用其中一列中的模板对其进行测试,并在构造函数中对其进行计数)。如果 WPF 在尝试调整最大列的大小时无法确定 DataGrid 的正确宽度,则会出现错误 - 因此必须生成所有行来计算宽度最大的行。 (要测试最后一个 - 将其放在停靠面板中而不是网格中 - 停靠在左侧或右侧)
另外,请尝试 VirtualizingStackPanel.VirtualizationMode="Recycling" 以让它回收使用的模板。
Can you see if all rows are being generated layout-wise? Normally Virtualization should hinder that and only generate the visible rows. (Test it with a template in one of the columns and count it in the constructor). There is a bug if WPF cannot determine the correct width of the DataGrid as it tries to size to the largest column - thus having to generate all rows to calculate the one with the largest width. (To test the last one - place it in a dock-panel instead of a grid - docked left or right)
Also, try VirtualizingStackPanel.VirtualizationMode="Recycling" to let it recycle the templates used.
我对绑定数据网格有同样的问题,我注意到在第一次加载时速度很快,但在第二次和下一次加载时速度很慢。所以当我添加代码时:
然后
它变得非常快。
I have the same problem with bound Data grid, and I notice that in first load it is fast but on second and next it is slow. So when I add in code:
and then
it became very FAST.