如何正确更新DataGridView,因此GUI不会冻结

时间:2011-04-18 11:21:47

标签: c# .net datagridview freeze

我有一个背景工作者,它会进行一些计算并将进度结果报告为字符串。需要将此字符串插入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);
 }

因此即使我在后台线程上进行了所有繁重的计算,由于DataGridView,GUI仍然会冻结。

编辑代码:

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 个答案:

答案 0 :(得分:1)

在我看来,您需要批处理工作线程的结果。也许你可以让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 :(得分:1)

一旦发现文件名,就会将文件名添加到网格中。在一个合理大小的目录上,这将很容易地重载UI线程与在网格中添加行的请求。结果你的用户界面冻结了。

以下是更新代码的方法。代码应该维护到目前为止发现的文件列表,而不是为单个文件调用ReportProgress()。一旦列表达到阈值大小100,那么你应该调用ReportProgress()并将该列表作为UserState传递。

这将显着减少对UI线程的调用次数,使您的应用程序响应。

答案 2 :(得分:0)

是否存在暂时停止控件更新的控件属性?可悲的是,我不记得这个名字。

您还可以尝试在后台创建控件,并在完成后将其添加到表单中。但要小心避免线程问题。

另一个想法是在更新期间隐藏它。

一个绝对“有效”的丑陋解决方案是让你的后台线程一次添加一个项目,等待几毫秒(在此期间GUI线程可以实际执行更改),并添加下一个项目。这将花费很长时间,但GUI在更新期间不会冻结。