BackgroundWorker для заполнения DataGridView - PullRequest
0 голосов
/ 19 июня 2019

РЕДАКТИРОВАТЬ: Решено с помощью этого: http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/

В моем проекте (формы .net / windows) я заполняю DataGridView большим DataTable.Заполнение может занять до 20 секунд, поэтому мне нужно анимированное окно загрузки.Эта анимация останавливается, если поток занят, поэтому мне придется использовать новый поток либо для окна, либо для заполнения DataGridView.

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

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

Какой лучший способ показать мнеработа с анимированной формой при заполнении DataGridView?

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

Я запустил его без ошибок, но frmLoading по-прежнему не анимирован (хотя я поддерживаю его, пока рабочий поток не занят).

namespace a
{
    public partial class frmMain : DockContent, IPlugin
    {
        //...
        private delegate void SafeCallDelegate(DataTable dt);
        private Thread thread1 = null;
        private frmLoading frmLoading = new frmLoading();


        public frmMain()
        {
            //...
        }
        //...

        private void FillDataGrid(DataTable dt)
        {
            if(this.InvokeRequired)
            {
                var d = new SafeCallDelegate(FillDataGrid);
                Invoke(d, new object[] { dt });
            }
            else
            {
                //...
                DataGridFiller(dt);
            }
        }

        private void DataGridFiller(DataTable dt)
        {
            BindingSource dataSource = new BindingSource(dt, null);
            //...
            dgvData.DataSource = dataSource;
            //...
            frmLoading.Hide();
        }

        private void btnGetData_Click(object sender, EventArgs e)
        {
            DataTable dt = [...];

            // Wenn Daten vorhanden sind, dann anzeigen
            if (dt != null)
            {
                //...

                frmLoading.Show();
                thread1 = new Thread(() => FillDataGrid(dt));
                thread1.Start();
            }
        }
    }
}

Ответы [ 2 ]

2 голосов
/ 19 июня 2019

Второй подход верен: используйте BackgroundWorker для выполнения любой работы, которая замораживает пользовательский интерфейс, если выполняется в основном потоке. Об исключении, которое вы получаете, это потому, что выполняемый вами вызов не является потокобезопасным (потому что элемент управления был создан в другом потоке, который вызывает его методы). Пожалуйста, ознакомьтесь с этой ссылкой на MSDN , чтобы понять, как выполнять вызовы такого типа между потоками, используя управляемую событиями модель backgroundWorker.

Из ссылки:

Существует два способа безопасного вызова элемента управления Windows Forms из поток, который не создал этот элемент управления. Вы можете использовать System.Windows.Forms.Control.Invoke метод для вызова созданного делегата в основном потоке, который в свою очередь вызывает управление. Или вы можете реализовать System.ComponentModel.BackgroundWorker, который использует управляемая событиями модель для отделения работы, выполняемой в фоновом потоке, от отчетность по результатам.

Взгляните на второй пример, демонстрирующий эту технику с использованием backgroundWorker

РЕДАКТИРОВАТЬ

Из ваших комментариев я понял, что настоящая проблема здесь в размере. Проблема в том, что поток, которому принадлежит элемент управления DataGridView, является потоком, который его отображает, поэтому независимо от того, как, если вы загрузите все данные одновременно, он будет зависать на время, которое требуется этому потоку для отрисовки всех этих данных. на экране. К счастью, вы не первый, кто столкнулся с этой проблемой, и на этот раз вам помог Microsoft. Это, я думаю, отличный пример того, что проблема XY , настоящая проблема в том, что вы хотите загрузить огромный набор данных (X), и ответ о том, как это сделать, - здесь. , но вместо этого вы спросили, как отобразить значок загрузки при заполнении таблицы данных без зависания пользовательского интерфейса (Y), которое было вашей попыткой решения.

Это было с технической точки зрения, но с точки зрения UI / UX я бы хотел, чтобы вы подумали об использовании этой формы на самом деле. Вам действительно нужно загружать все эти данные каждый раз, когда вы посещаете этот раздел? Если ответ «нет», возможно, вы могли бы подумать о реализации нумерации страниц (например, здесь ). Также 200 столбцов означают горизонтальную прокрутку даже для сверхширокого монитора. Я не могу понять, в каком пользовательском случае вам нужна вся эта информация сразу в виде списка / таблицы. Я думаю, что, возможно, реализация интерфейса Master-Detail может быть более полезной.

РЕДАКТИРОВАТЬ 2,0

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

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

0 голосов
/ 19 июня 2019

Используйте асинхронную задачу, которая отображает всплывающее окно загрузки, пока DataGridView не заполнится.

Вот ссылка на статью, созданную Microsoft для асинхронного программирования: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

...