Как правильно обновить DataGridView, чтобы графический интерфейс не зависал - PullRequest
3 голосов
/ 18 апреля 2011

У меня есть фоновый работник, который выполняет некоторые вычисления и сообщает о ходе выполнения в виде строки.Эта строка должна быть вставлена ​​в dataGridView.Но при вставке значений GUI зависает.

private void bckgrSorter_DoWork(object sender, DoWorkEventArgs e)
{
    for (int i=0; i<1000; i++)
    {
        // doing some calculations
        bckgrSorter.ReportProgress(i, someString);
    }
}    

 private void bckgrSorter_ProgressChanged(object sender, ProgressChangedEventArgs e)
 {
     string results = (string)e.UserState;
     dataGridView1.Rows.Add(results);
 }

Так что, хотя я выполняю все тяжелые вычисления в фоновом потоке, GUI по-прежнему зависает из-за DataGridView.

Редактировать длякод:

private void bckgrSorter_DoWork(object sender, DoWorkEventArgs e)
    {
        string[] folders = // some folders to get File List from

        bckgrFileScanner.RunWorkerAsync(folders);            
    }

private void bckgrFileScanner_DoWork(object sender, DoWorkEventArgs e)
    {
        string[] folders = (string[])e.Argument;
        foreach (string f in folders)
        {
            GetFileList(ref scannedFiles, f, bckgrFileScanner);
            bckgrFileScanner.ReportProgress(1);
        }
    }

public void GetFileList(ref List<FileInfo> fList, string fPath, BackgroundWorker scanner)
        {
            DirectoryInfo di = new DirectoryInfo(fPath);
            FileInfo[] fi = di.GetFiles();

            foreach (FileInfo fiTemp in fi)
            {
                //ar ~$ saakas nevajadzīgie temp faili, tos izlaižam
                if (fiTemp.Name.StartsWith("~$") == false)
                {
                    fList.Add(fiTemp);
                    scanner.ReportProgress(0, fiTemp.Name);
                }
            }
            DirectoryInfo[] dFolders = di.GetDirectories();

            //katrai apakšmapei rekursīvi izsaucam šo funkciju
            foreach (DirectoryInfo d in dFolders)
            {
                GetFileList(ref fList, d.FullName, scanner);
            }
        }

private void bckgrFileScanner_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        if (e.ProgressPercentage == 0)
        {
            filesDataGrid.Rows.Add(e.UserState);
        }

        else progressBar1.PerformStep();
    }

Ответы [ 3 ]

1 голос
/ 18 апреля 2011

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

Бабар прав.Что-то вроде этого, вероятно, будет делать то, что вы хотите (я не пытался скомпилировать):

    public void GetFileList(ref List<FileInfo> fList, string fPath, BackgroundWorker scanner)
    {
        DirectoryInfo di = new DirectoryInfo(fPath);
        FileInfo[] fi = di.GetFiles();

        List<string> progressData = new List<string>();

        foreach (FileInfo fiTemp in fi)
        {
            //ar ~$ saakas nevajadzigie temp faili, tos izlaižam
            if (fiTemp.Name.StartsWith("~$") == false)
            {
                fList.Add(fiTemp);
                progressData.Add(fiTemp.Name);
                if (progressData.Count > 50){
                    scanner.ReportProgress(0, progressData.ToArray());
                    progressData.Clear();//You've just copied the data to an array and sent it to the GUI, clear the list and start counting up again
                }
            }
        }

        if (progressData.Count > 0){
            scanner.ReportProgress(0, progressData.ToArray());
        }

        DirectoryInfo[] dFolders = di.GetDirectories();

        //katrai apakšmapei rekursivi izsaucam šo funkciju
        foreach (DirectoryInfo d in dFolders)
        {
            GetFileList(ref fList, d.FullName, scanner);
        }
    }

Это не очень хорошее решение, но это начало ...

Вынужно будет также привести e.UserState к строковому массиву в вашем обратном вызове ...

1 голос
/ 18 апреля 2011

Вы добавляете имена файлов в сетку, как только они обнаруживаются. В директории разумного размера это легко перегрузит поток пользовательского интерфейса запросами на добавление строк в сетку. В результате ваш пользовательский интерфейс зависает.

Вот как вы должны обновить код. Вместо того, чтобы вызывать ReportProgress () для отдельного файла, код должен поддерживать список обнаруженных файлов. Как только список достигнет порогового размера, скажем, 100, вы должны вызвать ReportProgress () и передать этот список как UserState.

Это значительно сократит количество вызовов в потоке пользовательского интерфейса, что сделает ваше приложение отзывчивым.

0 голосов
/ 18 апреля 2011

Нет ли свойства элемента управления, которое временно останавливает обновление элемента управления? К сожалению, я не могу вспомнить имя.

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

Другой идеей было бы скрыть это во время обновления.

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

...