我正在尝试运行一个从指定文件夹获取文件列表并在DataGridView中表示它的方法。该方法在BackgroundWorker中运行,因此我希望GUI保持活动状态。但它仍然冻结。这是一个例子:
private void startScan_Click(object sender, EventArgs e)
{
bckgrFileScanner.RunWorkerAsync();
}
private void bckgrFileScanner_DoWork(object sender, DoWorkEventArgs e)
{
//for each folder in list perform this function, which scans folder and gets all files
for (int i = 0; i < folderList.Items.Count; i++)
{
GetFileList(ref scannedFiles, folderList.Items[i].ToString(), bckgrFileScanner);
}
}
public static void GetFileList(ref List<FileInfo> fList, string fPath, BackgroundWorker scanner)
{
DirectoryInfo di = new DirectoryInfo(fPath);
FileInfo[] fi = di.GetFiles();
foreach (FileInfo fiTemp in fi)
{
if (fiTemp.Name.StartsWith("~$") == false)
{
//adds items to list of all scanned files
fList.Add(fiTemp);
//reports file name to ProgressChanged method
scanner.ReportProgress(0, fiTemp);
}
}
DirectoryInfo[] dFolders = di.GetDirectories();
//use recursion for all subfolders
foreach (DirectoryInfo d in dFolders)
{
GetFileList(ref fList, d.FullName, scanner);
}
}
private void bckgrFileScanner_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//userstate is filename, so add it to table
filesDataGrid.Rows.Add(e.UserState.ToString());
}
答案 0 :(得分:3)
不要在循环的每一行更新reportprogress事件。 相反,每2次或更多次迭代调用ReportProgress(最好的方法是计算步骤,如果你有200000则不在每行更新) 并在整个过程完成后(在已完成的事件中)填充网格,以更新所有行。我认为进度只是为了更新进度,而不是将一堆数据填充到控件中但我可能错了: 来自here:
提示您可能比您对BackgroundWorker的了解更多 类。 BackgroundWorker的名称可能表明它更多 比实际情况复杂。有关线程的更多细节 并且中止调用,但是一旦你理解了BackgroundWorker就是这样 Windows窗体中的线程的结构“叠加”,它是相当的 直观。以下是步骤:
首先,使用参数调用RunWorkerAsync。你可以传递任何参数 在BackgroundWorker上使用此方法,包括null。它必须 从对象继承,一切都是。
其次,运行自定义处理。您执行昂贵的代码 DoWork方法。在程序执行时在此处插入暂停 计算
第三,它结束了。处理完成后,RunWorkerCompleted 叫做。在此方法中,您将收到结果。这样,你的 BackgroundWorker对象修改另一个线程上的对象 完成后接收它。
我认为当需要在短时间内进行太多更新时,它会挂起,甚至调用Apllication.DoEvents()也不会一直有效。 我希望它会有所帮助,我知道的有点晚了,但它总比没有好;)
答案 1 :(得分:1)
这个(简化的)示例适用于我,并且具有可爱的响应式UI:
BackgroundWorker m_objWorker = new BackgroundWorker();
public FormBackgroundWorkerExample()
{
InitializeComponent();
m_objWorker.WorkerReportsProgress = true;
m_objWorker.DoWork += new DoWorkEventHandler(m_objWorker_DoWork);
m_objWorker.ProgressChanged += new ProgressChangedEventHandler(m_objWorker_ProgressChanged);
}
private void btnStart_Click(object sender, EventArgs e)
{
m_objWorker.RunWorkerAsync();
}
private void m_objWorker_DoWork(object sender, DoWorkEventArgs e)
{
//for each folder in list perform this function, which scans folder and gets all files
for (int i = 0; i <= 100; i++)
{
m_objWorker.ReportProgress(i, "FooBar");
Thread.Sleep(1000);
}
}
private void m_objWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
dataGridView1.Rows.Add(e.UserState.ToString());
}
也许这会让你知道你的错误在哪里?
编辑:你可能想在没有数据网格的情况下尝试它来尝试找出问题。
答案 2 :(得分:1)
可能是因为你正在锁定背景工作者。
在您的DoWork方法中
GetFileList(ref scannedFiles, folderList.Items[i].ToString(), bckgrFileScanner);
Normaly你应该可以从你的DoWork方法访问bckgrFileScanner,并直接将其称为bckgrFileScanner.ReportProgress(.... ....)
当像你一样传递它时,它现在将报告后台工作线程的进度,后者现在与UI线程不同。 (谁拥有bckgrFileScanner)
修改澄清:
答案 3 :(得分:0)
您正在扫描仪上呼叫ReportProgress
。不应该是bckgrFileScanner
?
修改强>
scanFiles列表是否有可能数据绑定到UI?如果是这样,对列表的更改会导致UI更新。
答案 4 :(得分:0)
可能的原因:
我想你只想报告文件名:
//reports file name to ProgressChanged method
scanner.ReportProgress(0, fiTemp.Name);
或者DoWork中的folderList是一个UI控件:
for (int i = 0; i < folderList.Items.Count; i++)
{
GetFileList(ref scannedFiles, folderList.Items[i].ToString(), bckgrFileScanner);
}
为什么不将文件夹列表传递给RunWorkerAsync方法。