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

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

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

Узкое место возникает, когда свойство 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;
    }
}
}

person Eugenio De Hoyos    schedule 26.07.2010    source источник
comment
Не могли бы вы опубликовать пример кода, который показывает это? Я не думаю, что WPF имеет встроенную сетку данных.   -  person Alan Jackson    schedule 26.07.2010
comment
Спасибо, Алан. Думаю, я использую DataGrid из Toolkit. Я добавил код и новые комментарии в исходный вопрос.   -  person Eugenio De Hoyos    schedule 26.07.2010
comment
@Alan: это происходит в версии 4.0 .Net framework.   -  person Goblin    schedule 26.07.2010


Ответы (4)


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

<ScrollViewer>
    <Datagrid/>
</ScrollViewer>

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

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

person Eugenio De Hoyos    schedule 26.07.2010
comment
Кроме того, никогда не позволяйте DataGrid автоматически изменять размер в любом измерении. Если вы планируете размещать их внутри других элементов управления, рекомендуется установить для свойства MaxHeight значение, меньшее размера экрана, поскольку DataGrid не очень эффективен при большом макете. - person Eugenio De Hoyos; 26.07.2010
comment
+1 спасатель! Я временно установил ScrollViewer и забыл об этом ... был шокирован, когда увидел, что сетка ползет при обновлении. - person Gishu; 11.12.2012

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

Я отследил это до

Высота определения строки = "Авто"

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

<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 также работает, как в предыдущем комментарии. Он может быть больше экрана.

person TripleAntigen    schedule 14.09.2011
comment
Спасибо, что указали на тот факт, что проблема возникает, когда для параметра «Высота» установлено значение «Авто». В своем ответе я этого явно не подчеркивал. Однако я бы не сказал, что это ошибка, просто проблема дизайна. Когда сетка данных обрабатывает свои собственные полосы прокрутки и виртуальную клиентскую область, возможны многие оптимизации, но мы не можем ожидать, что все эти оптимизации будут действовать, когда виртуальная область обрабатывается родительским элементом управления — или мы можем? В любом случае, в настоящее время сетка данных, похоже, предназначена для очень специфического использования :( Это отстой, потому что в некоторых пользовательских интерфейсах у нас есть полосы прокрутки внутри полос прокрутки. - person Eugenio De Hoyos; 14.09.2011
comment
Такого рода ограничение WPF, которого мы не ожидали, есть. - person Eugenio De Hoyos; 14.09.2011
comment
Спасибо за этот ответ - это действительно была проблема, с которой я столкнулся. После удаления строки «Высота = Авто» для строки сетки данных у меня резко увеличилась производительность. - person Don Gossett; 24.06.2016

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

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

person Goblin    schedule 26.07.2010
comment
Спасибо Гоблин! Вы были на правильном пути, поскольку мой DataGrid пытался сгенерировать все строки одновременно. Но это произошло из-за встраивания DataGrid внутрь ScrollView. Виртуализация не была проблемой (поскольку размер DataGrid не ограничивался размерами экрана). - person Eugenio De Hoyos; 26.07.2010

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

DataGrid.ItemsSource = Nothing

а потом

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

стало очень БЫСТРО.

person SLAVICA    schedule 16.04.2015