Необоснованное время загрузки WPF DataGrid

у меня всегда было много времени загрузки с Датагридами WPF, и я не могу найти подобных отчетов в интернете, поэтому я подозревал, что делаю что-то неправильно. Теперь я в этом уверен, так как добавление сложности макета значительно замедляет выполнение. В очень простой макет, в DataGrid заполняется instantaenously, в то время как ниже код занимает около 3 секунд, чтобы выполнить.

в следующем коде загрузка занимает ~3 секунды для 150 строк и 11 столбцов, даже если каждая ячейка не привязан к любому свойству и с AutoGenerateColumns=False. (У меня есть два ядра, процессор 2.6 GHz с большим количеством ОЗУ).

горлышко бутылки происходит, когда свойство 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;
    }
}
}

4 ответов


проблема возникает только тогда, когда DataGrid встроен в ScrollViewer, например:

<ScrollViewer>
    <Datagrid/>
</ScrollViewer>

это имеет смысл, потому что эта конфигурация заставляет весь DataGrid быть нарисованным одновременно (чтобы иметь возможность правильно определить размер клиентской области ScrollViewer). По сути, он переопределяет встроенное поведение виртуализации DataGrid, которое реализует свои собственные полосы прокрутки, так что не все его содержимое должно быть помещено в макет одновременно.

In другими словами, встраивание DataGrid в ScrollViewer редко требуется, потому что DataGrid имеет свою собственную автоматическую прокрутку.


У меня была аналогичная проблема с UserControl, который содержал DataGrid, иногда, когда я помещал UserControl в новую форму или другой UserControl, он блокировал интерфейс (5 секунд?) в то время как он перерисовал DataGrid. То же самое с изменением размера.

я отследил его до

Rowdefinition Height= "Auto"

и та же проблема производительности также произошла, если я поместил UserControl в StackPanel. Кажется, очень многое связано с ранее упоминалась ошибка изменения размера, когда весь datagrid должен быть заполнен для вычисления размера инкапсулирующего контейнера.

<UserControl x:Class="ExampleUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" d:DesignHeight="481" d:DesignWidth="773">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" /> <!-- 'AUTO' CAUSES EXTREMELY POOR PERFORMANCE -->
        </Grid.RowDefinitions>

        <Grid Grid.Row="0"> <!-- CHANGING TO STACKPANEL CAUSES EXTREMELY POOR PERFORMANCE -->
            <ContentControl Content="{Binding MyDataGridUserControl}" />
        </Grid>
    </Grid>

</UserControl>

Я только что обнаружил, что установка MaxHeight="[whatever] " для ContentControl также работает, согласно предыдущему комментарию. Он может быть больше экрана.


можете ли вы видеть, генерируются ли все строки по макету? Обычно виртуализация должна препятствовать этому и генерировать только видимые строки. (Проверьте его с помощью шаблона в одном из столбцов и подсчитайте его в конструкторе). Существует ошибка, если WPF не может определить правильную ширину DataGrid, поскольку он пытается определить размер до самого большого столбца - таким образом, необходимо сгенерировать все строки для вычисления с наибольшей шириной. (Чтобы проверить последний - поместите его в док-панель вместо сетки-закрепленной влево или вправо)

кроме того, попробуйте VirtualizingStackPanel.VirtualizationMode= "Recycling", чтобы позволить ему утилизировать используемые шаблоны.


у меня такая же проблема с связанной сеткой данных, и я замечаю, что при первой загрузке она быстрая, но на второй и следующей она медленная. Поэтому, когда я добавляю код:

DataGrid.ItemsSource = Nothing

а то

TableAdapter.Fill(Mydataset.MyStoredProcedure,....)
DataGrid.ItemsSource=Mydataset.MyStoredProcedure

это стало очень быстро.