оптимизация обновлений DataTable, привязанных к DataGridView
у меня есть форма в моем приложение, которое отображает некоторые данные. Когда я впервые показываю форму, я загружаю некоторые данные в 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. Затем выполните следующие действия при внесении изменений в DataTable:
binding_source.SuspendBinding();
binding_source.RaiseListChangedEvents = false;
// foreach (DataRow in Data.Rows) ... code above
binding_source.RaiseListChangedEvents = true;
binding_source.ResumeBinding();
grid.Refresh();
есть проблема, хотя, и это doozy: код выше предотвращает DataGridView от обнаружения новых строк, добавленных в DataTable. любые новые строки, добавленные в таблица не отображается в сетке. сетка может также создавать исключения, если вы используете клавиши со стрелками для перемещения текущего выбора ячейки с нижнего конца сетки, потому что базовый источник данных имеет больше строк, но сетка не создала строки сетки для их отображения.
Итак, два возможных решения, которые я вижу:
есть ли лучший способ подавить обновления привязки при внесении изменений в базовый Объект DataTable?
есть ли простой способ сказать DataGridView изящно обновить свою коллекцию строк сетки, чтобы соответствовать количеству базовых строк DataTable? (Примечание: Я попытался вызвать BindingSource.ResetBindings, но, похоже, вызывает больше исключений, если у вас есть удалены строки из DataTable!)
7 ответов
рассматривали ли вы возможность отключения dataGrid или bindingSource при заполнении таблицы и повторном подключении после этого? Это может выглядеть немного уродливо, но это должно быть намного быстрее.
вы можете попробовать использовать merge метод на DataTable. Я попытаюсь создать простое демо-приложение и опубликовать его здесь, но идея проста. Если вы хотите обновить сетку, запросите результаты в новую таблицу DataTable, а затем объедините старую таблицу с новой таблицей. Пока обе таблицы имеют первичные ключи (вы можете создать их im-память, если они не возвращаются из БД), он должен отслеживать изменения и легко обновлять DataGridView. Он также имеет преимущество не потеря места пользователей в сетке.
хорошо, вот образец. Я создаю форму с двумя кнопками и одним dataGridView. При нажатии кнопки button1 я заполняю основную таблицу некоторыми данными и привязываю к ней сетку. Затем, при втором щелчке, я создаю другую таблицу с той же схемой. Добавьте к нему данные (некоторые из них имеют тот же первичный ключ, а некоторые-новые). Затем они объединяют их обратно в исходную таблицу. Он обновляет сетку, как ожидалось.
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;
}
}
если вы используете BindingSource
для сложной привязки данных важно понимать, что SuspendBinding
и ResumeBinding
только приостановить и возобновить обязательную силу для текущего элемента. это позволяет отключить привязку для текущего элемента и изменить кучу его свойств без каких-либо отдельных изменений свойства выталкивается в связанный элемент управления. (Это не объясняется в документации для BindingSource
, где это было бы полезно, О нет: это в документации для CurrencyManager
.)
любые изменения, внесенные вами в другие элементы списка, т. е. все, кроме текущего элемента, поднимите ListChanged
событие. Если эти события отключены, BindingSource перестает сообщать связанному элементу управления об изменениях в списке до их повторного включения. Это имеет результат, который вы видели: вы добавляете все свои строки в базовый DataTable
, но так как ты превратила ListChanged
событий от,BindingSource
Не скажу DataGridView
о них и так DataGridView
остается пустым.
правильное решение-вызвать ResetBindings
, который принуждает BindingSource
чтобы обновить все связанные с ним элементы управления, используя текущие значения в связанном списке.
какие исключения вы получаете после вызова ResetBindings
? Потому что он отлично работает для меня, добавляю ли я, редактирую, удаляю или удаляю строки из базового DataTable
.
я столкнулся с подобной проблемой. Вот решение, которое еще проще (хотя и менее элегантно).
Я нашел, что это:
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();
Ура -- DC
просто опубликуйте это как решение: сделайте некоторые заметки в комментариях и сообщениях уже. Метод таблицы слияния, упомянутый BFree, - очень хороший метод для использования, и я думаю, что правильный подход не слишком упоминает очень простой и элегантный. Вот мои заметки, почему и большой, я не уверен, что кто-то поймал хиты на сервере для запроса. ОП заявил в своих комментариях к BFree, что ему нужно будет скопировать таблицу, чтобы сделать то, что ему нужно, конечно, какая таблица, которую я не конечно, потому что его код:
foreach (DataRow row in data.Rows)
эти строки приходят из его таблицы, называемой data, где требуется копия - у него уже есть.
тогда вот что просто поражает прямо на каждой итерации этого цикла:
SlowLoadingData slow_stuff = slow_query_results[(int)row["id"]];
действительно ли OP запрашивает базу данных каждую итерацию этих строк (что, если это большая таблица, мы говорим о 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);
Я обнаружил, что использование resetBindings, похоже, перемещает полосу прокрутки, и пользователь остается думать, что я сделал? Я обнаружил, что использование bindingList в качестве источника данных с объектом, который использует INotifyPropertyChanged, а затем при редактировании строки (привязанной к объекту). строка не обновлялась до щелчка или изменения выбора в форме.
но вызов dgv.Refresh (), казалось, решил проблему, без изменения прокрутки.
Я нахожу решение 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