Загрузка DataTable замедляется при привязке к DataGridView.Datasource - PullRequest
3 голосов
/ 18 июля 2009

Я обыскал все, и я не могу понять это. Я работаю над пользовательским интерфейсом Winforms, который тянет большие объемы строк, которые мне нужно отобразить в DataGridView. Я уже прочитал все об ограничении числа строк и разбивке на страницы, и для меня нет абсолютно никакого хорошего способа сделать это. По сути, я работаю над элементом управления TargetDataViewer диспетчера расширенных событий для SQL Server 2008, который я написал в Codeplex.

http://extendedeventmanager.codeplex.com/

Я ограничен тем, что я могу сделать, основываясь на конкретной цели и как она представляет данные. Я пытаюсь выполнить потоковую передачу данных, которые были прочитаны из целевого объекта, в DataGridView, подобно тому, как Profiler или SQL Server Management Studio отображают данные во время их потоковой передачи. Я переписал много кода, и BackgroundWorker извлекает данные и обработка его в DataTable. Если я не установлю DataGridView.DataSource = DataTable, я могу загрузить 300K + строк данных в DataTable за несколько минут, это действительно быстро. Как только я добавляю DataTable в DataSource, он замедляется почти до полной остановки (вместо нескольких минут те же строки 300K могут занять 1/2 часа).

Я знаю, что проблема не в моем коде обработки, а в том, что он привязан к DataGridView.DataSource, и у меня есть временный код, чтобы доказать это. Я не могу понять, как обойти это. Что касается производительности, я могу позднее связать элемент управления с таблицей данных после загрузки данных, но это действительно дурацкий пользовательский опыт. Я вижу, что многие люди жалуются на влияние производительности DataGridView при загрузке данных, так что это может быть просто ограничением? Есть идеи?

1 Ответ

12 голосов
/ 18 июля 2009

Подумайте о том, что происходит, когда вы заполняете несвязанный DataTable одной строкой из DataReader: создается DataRow, заполняется из DataReader и добавляется в коллекцию Rows. Затем при создании привязки DataGridView извлекает данные из таблицы и создает представление на экране.

Что происходит, когда вы заполняете DataTable, чья привязка к DataGridView включена? Целый беспорядок обработки событий. Каждый раз, когда вы изменяете связанное свойство, возникает событие с измененным свойством, и связанный с ним элемент управления обрабатывает его. Это не происходит 300 000 раз, это происходит 300 000 раз для каждого столбца .

Что, если вы отключите это, и обновите связанный элемент управления только изредка? Посмотрите на этот метод:

private void PopulateDataTable()
{
    int rowCount = 10000;

    bindingSource1.RaiseListChangedEvents = false;
    for (int i = 0; i < rowCount; i++)
    {
        DataRow r = DT.NewRow();
        for (int j = 0; j < ColumnCount; j++)
        {
            r[j] = "Column" + (j + 1);
        }
        DT.Rows.Add(r);

        if (i % 500 == 0)
        {
            bindingSource1.RaiseListChangedEvents = true;
            bindingSource1.ResetBindings(false);
            Application.DoEvents();
            bindingSource1.RaiseListChangedEvents = false;
        }
    }
    bindingSource1.RaiseListChangedEvents = true
}

Вы должны вызвать ResetBindings, чтобы принудительно обновить связанный элемент управления. Это требует времени, потому что вы не можете обойтись без затрат на строительство DataGridViewRow объектов, но вывоз событий - значительное улучшение. На моей машине, если я заполняю 10-столбец, 10000 строк DataTable, который связан с DataGridView, это займет 2900 миллисекунд. Если я оставляю привязку данных отключенной все время, это занимает 155 миллисекунд. Если я сбрасываю привязки каждые 500 строк, это займет 840 миллисекунд.

Конечно, если бы я заполнял таблицу из 300 000 строк, я бы не сбрасывал привязки каждые 500 строк; Я, вероятно, сделаю это один раз на отметке 500 строк, а затем выключу, пока операция не завершится. Но даже если вы сделаете это, вам нужно будет вызывать Application.DoEvents каждый раз, чтобы пользовательский интерфейс мог реагировать на события.

Редактировать

Не берите в голову этот бит о Application.DoEvents; вам не нужно этого делать, если вы заполняете таблицу в фоновом режиме.

Но вам нужно убедиться, что вы сбрасываете привязки в ProgressChanged обработчике событий BackgroundWorker, а не в методе DoWork. И вы столкнетесь с целым миром боли, если на самом деле позволите пользователю редактировать данные в ограниченном DataGridView при заполнении его источника данных в другом потоке.

...