«Потоковая передача» результатов в DataGridView из BackgroundWorker - PullRequest
2 голосов
/ 31 октября 2008

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

Как лучше всего это сделать?

Ответы [ 4 ]

1 голос
/ 09 января 2009

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

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

myBindingSource[n] = newItem;

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

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

1 голос
/ 03 ноября 2008

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

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

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

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

myDataGridView.Rows[rowNumber].SetValues(valuesFromNewPage);
1 голос
/ 03 ноября 2008

Вы могли бы:

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

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

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

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

0 голосов
/ 03 ноября 2008

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

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

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

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

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

...