DataGridView настройка источника данных происходит слишком медленно
Привет,
У меня сейчас действительно странная проблема. Я создал пользовательский элемент управления с 2 gridviews и 2 пользовательскими полосами прокрутки на нем, gridHeader сверху, gridData снизу. Первый gridview (gridHeader) используется для ввода пользователем поискового запроса в каждом отдельном столбце. Второй gridview (gridData) используется для просмотра результирующих данных. Для каждого столбца в gridHeader я также создаю элемент управления фильтром, который будет помещен поверх columnheader, с помощью этого элемента управления фильтром пользователь также может фильтровать данные, проверяя включение/выключение уникальных значений в списке столбца(нажмите на маленькую кнопку в столбце, и вы увидите уникальные значения).
Управление работает и зарекомендовало себя, но оно слишком медленное. Загрузка некоторых источников данных может занять до 1000 мс. После проверки кода путем добавления таймеров я понял, что проблема заключается не в тяжелой фильтрации, которую я делаю, а в том, что она вызвана gridviews при назначении datatable, а также при добавлении элементов управления фильтра в заголовки столбцов.
Чтобы как-то решить вторую проблему, я начал с того, что не удалил FilterColumnClass, когда он не был обнаружен, и оставил их в памяти, так что вторая загрузка будет быстрее. Но если я назначаю источник данных заголовка-gridview, заполненный только 1 строкой, это занимает около 180 МС, в то время как когда я назначаю источник данных data-gridview, заполненный 100 строками, это занимает всего около 10 мс.
Когда заголовок-сетка назначается, 1 событие получает triggerd, который ничего не делает из-за флага "_StopEventHandling", который установлен в true.
Я поместил тайминги в качестве комментария в код, 1-е означает; в первый раз, когда вы назначаете datatable элементу управления, 2-е означает; во второй раз, когда вы назначили datatable. Время в МС, которое вы видите,-это время, которое потребовалось compaired до начала функции или compaired до последнего снимка времени.
Вот часть кода о котором идет речь:
public class SomeCustomUserControl { private DataTable _AllData = null; private DataTable _ViewData = null; private List<FilterColumnClass> _FilterColumns = new List<FilterColumnClass>(); private bool _StopEventHandling = false; private bool _DataBound = false; private bool _DataBoundToSendSelected = false; private int _LastSortColumnIndex = -1; private bool _LastSortColumnDir = false; public void SetDataSource(DataTable value) { // Set all filtercontrols to invisible foreach (FilterColumnClass filter in _FilterColumns) { filter.FilterCheckControl.Visible = false; } // Assigning the received datatable to a local variable _AllData = value; // Do the databinding DataBind(); } private void DataBind() { if (_AllData != null) { // Letting the control know we've started databinding the data. _DataBound = true; // Letting the control know it should NOT handle any events from the grids _StopEventHandling = true; // Included in example: scroll down! PrepareGridViews(); // 1st: 0ms 2nd: 0ms // Included in example: scroll down! PrepareFilterColumns(); // 1st: 472ms 2nd: 252ms // ================ !!! HERE !!! ==================== // Creates a new datatable according the filters _ViewData = FilterRows(_AllData, _FilterColumns); gridData.DataSource = _ViewData; // ================ !!! HERE !!! ==================== // THIS IS REALLY FAST: 1st: 12ms 2nd: 10ms // The _ViewData datatable contains around 100 records // Sorts the new datasource according the last sort if (_LastSortColumnIndex != -1) { if (gridHeader.Columns.Count > _LastSortColumnIndex) { try { if (_LastSortColumnDir) gridData.Sort(gridData.Columns[gridHeader.Columns[_LastSortColumnIndex].Name], ListSortDirection.Ascending); else gridData.Sort(gridData.Columns[gridHeader.Columns[_LastSortColumnIndex].Name], ListSortDirection.Descending); } catch { } } } // 1st: 0ms 2nd: 0ms // Sets the columns to the users preferences, like column-name, column-width and column-order if (gridData.Columns.Count > 0) Personalize_Columns(); // 1st: 194ms 2nd: 187ms // Sets the custom scrollbars min, max and value settings PrepareScrollBars(); // 1st: 0ms 2nd: 1ms // Resets the vertical position of the custom scrollbars SetVerticalPosition(vScrollBar1.Value); // 1st: 0ms 2nd: 0ms // Resets the horizontal position of the custom scrollbars foreach (DataGridViewColumn col in gridHeader.Columns) { if (col.DisplayIndex == 0) { _LastScrollingColumnIndex = -1; SetHorizontalPosition(col.Index); } } // Checks if the parent-control wants to mark some items in the grid AskMarkedItems(); // 1st: 196ms 2nd: 36ms // Letting the control know we're done databinding the data _DataBoundToSendSelected = true; // Letting the control know it should continue handling the events from the grids _StopEventHandling = false; } else { // Reset the grids gridHeader.DataSource = null; gridData.DataSource = null; _ViewData = null; } // TOTAL 1st: 874ms 2nd: 486ms } private void PrepareGridViews() { // Speeds up the gridviews SetDoubleBuffered(gridData, true); SetDoubleBuffered(gridHeader, true); // Templating gridHeader.RowTemplate.Height = PublicFunctions.UniversalHeight - 6; gridData.RowTemplate.Height = PublicFunctions.UniversalHeight - 6; // No columnheader because we use the one from gridHeader gridData.ColumnHeadersVisible = false; } private void PrepareFilterColumns() { // Index counter for the column order int index = 0; // Suspend the layout to speed up the adding of the filter controls this.SuspendLayout(); foreach (DataColumn col in _AllData.Columns) { // First try to find the the filter column class, if it is already added FilterColumnClass filter = _FilterColumns.Find(delegate(FilterColumnClass item) { return item.ColumnName == col.ColumnName; }); if (filter != null) { // filter column class found, reset the index to be sure filter.Index = index; filter.FilterCheckControl.Visible = true; } else { // filter column not found, create one and add it to this control FilterColumnClass new_filter = new FilterColumnClass(); new_filter.ColumnName = col.ColumnName; new_filter.Index = index; new_filter.FilterCheckControl.Visible = true; new_filter.FilterCheckControl.eventAskCurrentDataTable += new AskDataTableByInt(FilterCheckControl_eventAskCurrentDataTable); new_filter.FilterCheckControl.eventAskSourceDataTable += new AskDataTableByInt(FilterCheckControl_eventAskSourceDataTable); new_filter.FilterCheckControl.eventApplyFilter += new EventHandler(FilterCheckControl_eventApplyFilter); _FilterColumns.Add(new_filter); this.Controls.Add(new_filter.FilterCheckControl); } index++; } // Adding of filter controls complete resume the layout this.ResumeLayout(); // ================ !!! HERE !!! ==================== // 1st: 256ms 2nd: 46ms // ================ !!! HERE !!! ==================== // Set the filter columns that are not found to invisible foreach (FilterColumnClass filter in _FilterColumns) { bool found = false; foreach (DataColumn col in _AllData.Columns) { if (filter.ColumnName == col.ColumnName) { found = true; break; } } if (!found) { filter.FilterCheckControl.Visible = false; } } // 1st: 0ms 2nd: 0ms // Create a datatable for the header with empty strings DataTable header = new DataTable("FilterRow"); // Add all columns of the datasource to it foreach (DataColumn col in _AllData.Columns) { header.Columns.Add(new DataColumn(col.ColumnName, typeof(string))); } // 1st: 0ms 2nd: 0ms // Create a empty row for it DataRow filterrow = header.NewRow(); foreach (FilterColumnClass filter in _FilterColumns) { // If the filter control is visible we have to take over the old value of that filter. if (filter.FilterCheckControl.Visible) { try { filterrow.ItemArray[filter.Index] = filter.FilterTextString; } catch (Exception ex) { PublicFunctions.ReportBug(ex, _Shared); } } } // Add the filter row to the header datatable header.Rows.Add(filterrow); // 1st: 0ms 2nd: 0ms // ================ !!! HERE !!! ==================== // Assign the header datatable to the grid gridHeader.DataSource = header; // ================ !!! HERE !!! ==================== // THIS IS REALLY SLOW 1st: 162ms 2nd: 171ms // The header datatable contains 1 row // TOTAL 1st: 418ms 2nd: 217ms } private void SetDoubleBuffered(DataGridView grid, bool setting) { Type dgvType = grid.GetType(); PropertyInfo pi = dgvType.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic); pi.SetValue(grid, setting, null); } }
Я надеюсь, что вы, ребята, можете видеть сквозь большое количество кода.
BobJanova
Насколько велик _AllData? Фильтрация-это не очень умный процесс (он получает каждую строку, передает ее в ваш код и спрашивает, Хотите ли вы ее получить), а на умеренных наборах данных (порядка 1000) он, вероятно, будет слишком медленным.
Ответ может заключаться в том, чтобы фильтровать данные с помощью запроса, а не в коде.
willempipi
Спасибо, но, как я уже сказал, процесс фильтрации(выполняемый функцией "FilterRows(_AllData, _FilterColumns);") не является проблемой, он занимает всего около 10 мс. Проблема заключается в присвоении datatable сетке заголовка(содержащей только 1 строку), которая занимает около 180 МС, и добавлении filtercontrols к usercontrol (это.Управления.Добавить(new_filter.FilterCheckControl);) который занимает около 250 мс.
Фильтрация, которую я делаю, далеко не универсальна и сложна для выполнения в запросе, и я использую свою фильтрацию для данных, которые уже отфильтрованы. _AllData будет содержать не более 1000 строк.