2008-10-31 6 views
2

Есть ли способ «потокового» набора результатов (например, DataTable) из BackgroundWorker в DataGridView. Я хочу сделать запрос данных и заполнить результаты в DataGridView по мере их поступления (например, результаты сетки запросов в SQL Server Management Studio). Моя первая мысль заключалась в том, чтобы использовать BackgroundWorker (чтобы избежать эффекта замораживания UI), но все равно будет ощутимое «отставание», поскольку BackgroundWorker загружает результаты.«Потоковые» результаты в DataGridView из BackgroundWorker

Что было бы лучшим способом сделать это?

ответ

1

Вы могли:

Свяжите DataGridView с первоначально пустой DataTable.

Затем в рабочем потоке используйте поточно-безопасную коллекцию (например, синхронизированную очередь) и вызовы Control.BeginInvoke для передачи информации о записи в поток пользовательского интерфейса.

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

Однако, используя многопоточность, вы сразу же можете сделать вашу программу гораздо более вероятной. Я не пробовал эту конкретную схему и не знаю, будет ли рендеринг GridView непригодным для использования, пока элементы добавляются к нему. Я заполнил treeview, используя многопоточность, и это действительно классный эффект. Тем не менее, я в конечном итоге отключил функциональность, поскольку она ввела ошибки из-за того, что не была полностью правильной реализацией, связанной со всеми возможными взаимодействиями с пользователем.

1

I есть попытался и сделал это. Приложение и архитектура были выполнены до моего прихода в компанию, и то, что я сделал, было сделано так, что оно было «paged» и асинхронно.

У них была хранимая процедура, в которой уже был пейджинг ... так что я сделал по первому запросу, отправил обратно «размер» итоговых результатов (вместе с данными страницы1).

На стороне клиента я добавил пустые строки в DataTable ... (blank, за исключением для поля «RowNumber»).

На последующих страницах данных (которые были получены async), я бы получил строки [X], установил его «ItemArray» в новый массив и обновил мою сетку. Пример:

myDataGridView.Rows[rowNumber].SetValues(valuesFromNewPage); 
0

Если процесс занимает 2 секунды или меньше, то я бы показать «занято» курсор и сделать инлайн обновления. Есть 2 причины для этого:

  • Кто-то, кто начинает операцию, которая только собирается занять всего пару секунд все еще будет находиться в «сфокусированного» способа мышления к тому времени, операция завершена. Подсознательно он все еще ждет результатов своего действия и еще не переключил свой сознательный мозг из этого особого внимания (пока приложение показывает «занятый» сигнал).

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

Если процесс занимает до 5 секунд, то сначала я сосредоточусь на сокращении его до 2 секунд или меньше.Опять же, гораздо проще сосредоточиться на повышении производительности (например, подкачки в SQL), чем на внедрение многопоточности. Даже с типом BackgroundWorker несекретные взаимодействия, связанные с многопоточными процессами, по-прежнему трудно анализировать и делать безопасными.

Если вы обнаружите, что нет возможности уменьшить задержку до 2 секунд или меньше, вы можете использовать что-то вроде техники, рекомендованной confuzatron. Но даже тогда, возможно, все еще хочет, чтобы показать прогресс диалогового окна для конечного пользователя, потому что он это будет иметь переключение контекста после более чем 2-х секунд ожидания. Диалог прогресса дает ему возможность легко усваивать информацию о том, когда он может вернуться к фокусировке на вашем контексте.

1

Это работает намного лучше, если у вас есть только один поток, который может изменить базовую коллекцию. У меня есть DGV только для чтения, так что единственный способ добавления/удаления строк - манипулировать базовым BindingSource. У меня есть поток синхронизации, который регулярно добавляет/удаляет элементы из BindingSource.

я должен сделать одну «хитрая» вещь - если элемент Обновляется выбранный элемент, вы не можете просто сказать

myBindingSource[n] = newItem; 

, а вы должны глубоко скопировать значения из новый элемент в существующий. В противном случае вы вызовете «измененное» событие, которое будет перерисовывать все, что привязано к вашему DataSource. У меня получилось заметное мерцание, когда я сделал справочную копию, и переключился на глубокую копию (только для текущего элемента).

Конечно, если вы позволяете пользователю возиться с данными непосредственно из формы, а не использовать его как просмотр только для чтения, вы открываете совершенно новую (уродливую!) Банку червей.

Смежные вопросы