оптимизировать обновления DataTable, привязанного к DataGridView - PullRequest
9 голосов
/ 20 марта 2009

В моем приложении есть форма, отображающая некоторые данные. Когда я впервые показываю форму, я загружаю некоторые данные в DataTable, а затем связываю DataTable с DataGridView. Я также запускаю асинхронный метод, который выполняет некоторые более медленные запросы к базе данных. Когда эти медленные запросы завершатся, мне нужно обновить несколько сотен строк в DataTable, заполнив значения, возвращаемые из более медленных запросов, например:

foreach (DataRow row in data.Rows)
{
    SlowLoadingData slow_stuff = slow_query_results[(int)row["id"]];

    row.BeginEdit();
    row[column_one] = slow_stuff.One;
    row[column_two] = slow_stuff.Two;
    row[column_three] = slow_stuff.Three;
    row.EndEdit();
}

Это очень медленно, зависание потока пользовательского интерфейса на минуту или более, предположительно потому, что каждая строка вызывает перерисовку.

После некоторых исследований я нашел способ сделать это быстро. Сначала свяжите DataGridView с BindingSource, который связан с DataTable, а не напрямую с DataTable. Затем выполните следующие действия при внесении изменений в таблицу данных:

binding_source.SuspendBinding();
binding_source.RaiseListChangedEvents = false;
// foreach (DataRow in Data.Rows) ... code above
binding_source.RaiseListChangedEvents = true;
binding_source.ResumeBinding();
grid.Refresh();

Тем не менее, есть проблема, и она обескураживает: приведенный выше код не позволяет DataGridView обнаруживать новые строки, добавленные в DataTable. Любые новые строки, добавленные в таблицу, не отображаются в сетке. Сетка также может выдавать исключения, если вы используете клавиши со стрелками для перемещения текущего выделения ячеек за нижний конец сетки, поскольку базовые данные В источнике больше строк, но сетка не создала строки сетки для их отображения.

Итак, два возможных решения, которые я вижу:

  1. Есть ли лучший способ подавления обновлений привязки при внесении изменений в базовую таблицу данных?

  2. Существует ли простой способ заставить DataGridView изящно обновить свою коллекцию строк сетки, чтобы она соответствовала количеству базовых строк DataTable? (Примечание: я пытался вызвать BindingSource.ResetBindings, но, похоже, он вызывает больше исключений, если у вас удалено строк из DataTable!)

Ответы [ 7 ]

6 голосов
/ 20 марта 2009

Вы можете попробовать использовать метод Merge в DataTable. Я постараюсь создать простое демо-приложение и опубликовать его здесь, но идея проста. Если вы хотите обновить таблицу, запросите результаты в новом DataTable, а затем объедините старую таблицу с новой таблицей. Поскольку обе таблицы имеют первичные ключи (вы можете создавать их в памяти, если они не возвращаются из БД), следует отслеживать изменения и без проблем обновлять DataGridView. Он также имеет то преимущество, что не теряет место пользователя в сетке.

ОК, вот пример. Я создаю форму с двумя кнопками и одним dataGridView. При нажатии кнопки 1 я заполняю основную таблицу некоторыми данными и привязываю к ней сетку. Затем, после второго нажатия, я создаю другую таблицу с той же схемой. Добавьте к нему данные (некоторые с одинаковым первичным ключом, а некоторые с новыми). Затем они объединяют их обратно в исходный стол. Обновляет сетку, как и ожидалось.

    public partial class Form1 : Form
    {
        private DataTable mainTable;
        public Form1()
        {
            InitializeComponent();
            this.mainTable = this.CreateTestTable();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            for (int i = 1; i <= 10; i++)
            {
                this.mainTable.Rows.Add(String.Format("Person{0}", i), i * i);
            }

            this.dataGridView1.DataSource = this.mainTable;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            DataTable newTable = this.CreateTestTable();
            for (int i = 1; i <= 15; i++)
            {
                newTable.Rows.Add(String.Format("Person{0}", i), i + i);
            }
            this.mainTable.Merge(newTable);
        }

        private DataTable CreateTestTable()
        {
            var result = new DataTable();
            result.Columns.Add("Name");
            result.Columns.Add("Age", typeof(int));
            result.PrimaryKey = new DataColumn[] { result.Columns["Name"] };

            return result;

        }
    }
4 голосов
/ 20 марта 2009

Рассматривали ли вы отключение dataGrid или bindingSource при заполнении таблицы и повторном подключении после этого? Это может выглядеть немного некрасиво, но должно быть намного быстрее.

3 голосов
/ 21 марта 2009

Если вы используете BindingSource для сложной привязки данных, важно понимать, что SuspendBinding и ResumeBinding только приостанавливают и возобновляют привязку для текущего элемента. Это позволяет отключить привязку для текущего элемента и измените кучу его свойств без каких-либо отдельных изменений в свойстве, передаваемом в связанный элемент управления. (Это не объяснено в документации для BindingSource, где это было бы полезно, о нет: это в документации для CurrencyManager.)

Любые изменения, которые вы вносите в другие элементы списка, т. Е. Все, кроме текущего элемента, вызывают событие ListChanged. Если вы отключите эти события, BindingSource перестанет сообщать привязанному элементу управления об изменениях в списке, пока вы не включите их снова. Это дает результат, который вы видели: вы добавляете все свои строки в базовый DataTable, но, поскольку вы отключили ListChanged события, BindingSource не сообщает DataGridView о них, и поэтому DataGridView остается пустым.

Правильным решением является вызов ResetBindings, который заставляет BindingSource обновить все связанные с ним элементы управления, используя текущие значения в его списке привязок.

Какие исключения вы получаете после звонка ResetBindings? Потому что он прекрасно работает, добавляю ли я, редактирую, удаляю или удаляю строки из базового DataTable.

2 голосов
/ 26 августа 2010

Я столкнулся с подобной проблемой. Вот решение, которое еще проще (хотя и менее элегантно).

Я обнаружил, что это:

dataGridView.DataSource = null;
dataTable.BeginLoadData();
foreach(DataRow row in dataTable.Rows){
    //modify row
}
dataTable.EndLoadData();
dataGridView.DataSource = dataTable;

... намного быстрее, чем это:

dataTable.BeginLoadData();
foreach(DataRow row in dataTable.Rows){
    //modify row
}
dataTable.EndLoadData();

Cheers - DC

1 голос
/ 14 июля 2015

Просто опубликуйте это как решение: добавьте некоторые примечания к комментариям и постам уже. Метод Merge Table, упомянутый BFree, является очень хорошим методом для использования, и я думаю, что это правильный подход, не говоря уже о том, что он очень простой и элегантный. Вот мои записи о том, почему, и я не уверен, что кто-то из них поймал хиты на сервере для запроса. Оператор заявил в своих комментариях к BFree, что ему нужно будет скопировать таблицу, чтобы сделать то, что ему нужно, конечно, какую именно таблицу я не уверен, потому что его код:

foreach (DataRow row in data.Rows)

Эти строки взяты из его таблицы, называемой данными, где требуется копия для этого - у него уже есть.

Тогда вот что-то, что попирает прямо на КАЖДУЮ итерацию этого цикла:

SlowLoadingData slow_stuff = slow_query_results[(int)row["id"]];

Действительно ли ОП запрашивает базу данных каждую итерацию этих строк (что, если это большая таблица, мы говорим 100 000 строк +). Подумайте о нагрузке на сервер (его приложение также должно генерировать этот запрос!), А также об объеме трафика, который он размещает в сети для этого! ЕСЛИ это единственное приложение, может быть, все в порядке, но даже при этом это не то, что я бы предпочел сделать, если бы я хотел быть эффективным.

Если сбор данных из базы данных в одном запросе кажется многим - тогда, возможно, лучшим способом было бы скомпоновать его данные и выполнить слияние.

SlowLoadingData Page1_SlowLoadingData = slow_query_results[Page1] as DataTable;
data.Merge(Page1_SlowLoadingData);

SlowLoadingData Page2_SlowLoadingData = slow_query_results[Page2] as DataTable;
data.Merge(Page2_SlowLoadingData);
0 голосов
/ 08 марта 2015

Я считаю, что решение от Ravi LVS для codeproject работает хорошо:

BindingSource bs = new BindingSource();
DataTable dt = new DataTable();

bs.DataSource = dt;
bs.SuspendBinding();
bs.RaiseListChangedEvents = false; 
bs.Filter = "1=0"; 
dt.BeginLoadData(); 

//== some modification on data table

dt.EndLoadData();
bs.RaiseListChangedEvents = true;
bs.Filter = "";

Ссылка на исходную страницу: http://www.codeproject.com/Tips/55730/Achieve-performance-while-updating-a-datatable-bou

0 голосов
/ 18 июля 2013

Я обнаружил, что использование resetBindings, кажется, перемещает полосу прокрутки, и пользователь остается думать, что я сделал? Я обнаружил, что с помощью bindingList в качестве источника данных, с объектом, который использует INotifyPropertyChanged, а затем, когда я редактировал строку, (привязанный к объекту). строка не обновлялась до изменения щелчка или выбора в форме.

но вызов dgv.Refresh (), похоже, решил проблему без изменения прокрутки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...