使用后台线程、ListView 和 ProgressBar 扫描图像尺寸






4.29/5 (4投票s)
扫描图像尺寸可能需要几毫秒,因此在扫描多张图像时,后台线程会很有用。进度会更新到 ListView 并显示在 ProgressBar 中。
引言
这篇文章是另一篇文章的扩展版本,我发现这篇文章非常有趣,名为 后台线程和支持取消,作者是 Andrew D. Weiss。
本文解释了如何异步运行 BackgroundWorker
线程以扫描图像尺寸。用户可以选择一个文件夹或选择特定的图像。在将图像名称插入 ListView
后,将集合对象传递给 BackgroundWorker
,该线程扫描每张图像并报告进度,并在 ProgressBar
上显示。
使用代码
当添加照片时,它们存储在一个名为 PhotoCollection
的对象中,该对象继承自 List<Photo>
。每张照片都有一个 FileInfo
实例和一些添加的属性。PhotoCollection
可以导出到 XML 文件,并使用 XmlParser
类从 XML 文件导入回来。
此软件可以进一步扩展为调整大小程序、重命名器、组织器,或者您想对照片集合执行的任何操作。BackgroundWorker
还可以预生成缩略图,并且 ListView
可以替换为类似于 Windows XP 或更高版本中看到的“大图标”视图。
BackgroundWorker
方法 UpdateListView_HeavyProcess()
启动线程并为其提供工作参数。方法 worker_DoWork()
将运行繁重的工作,并且每次在 worker_DoWork()
内部调用 trd.ReportProgress()
时,都会调用方法 worker_ProgressChanged()
,该方法允许更新 GUI 组件。当线程完成其工作后,将调用方法 worker_RunWorkerCompleted()
,该方法将结果转换为 PhotoCollection
。
private void UpdateListView_HeavyProcess()
{
// Do not try to execute the background worker if
// he is already busy with work
if (!worker.IsBusy)
{
// Prepare the progress var
prbProgress.Minimum = 0;
prbProgress.Maximum = photos.Count;
prbProgress.Value = 0;
prbProgress.Step = 1;
prbProgress.Enabled = true;
// Run the worker thread, taking photos as argument
worker.RunWorkerAsync(photos);
}
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// Create an instance that represents the running thread
BackgroundWorker trd = sender as BackgroundWorker;
// Cast the argument object to PhotoCollection
PhotoCollection col = (PhotoCollection)e.Argument;
Image img;
// Go through each photo using indices
for (int i = 0; i < col.Count; i++)
{
// If the photo HAS dimention - cancel the operation because
// it is VERY unlikely that the photo will be changed when
// already added to this list
if (col[i].PixelHeight == 0 && col[i].PixelWidth == 0)
{
img = Image.FromFile(col[i].FullName);
col[i].PixelWidth = (int)img.PhysicalDimension.Width;
col[i].PixelHeight = (int)img.PhysicalDimension.Height;
}
// Report back the photo number and scanned dimention
trd.ReportProgress(i, col[i].Dimention);
// If the thread MUST stop running!
if (trd.CancellationPending)
{
// Set the e.Cancel flag so that the WorkerCompleted event
// knows that the process was canceled.
e.Cancel = true;
// Save the changed object to result
e.Result = col;
// Report back negative value, indicating that the execution is done
trd.ReportProgress(-1);
// Stop running the working method
return;
}
}
// Save the changed object to result
e.Result = col;
// Report back negative value, indicating that the execution is done
trd.ReportProgress(-1);
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Cast the results to the PhotoCollection object
// owned by this thread
photos = (PhotoCollection)e.Result;
// Now update the whole list again!
this.UpdateListView(true);
// Reset the progress bar
prbProgress.Minimum = 0;
prbProgress.Value = 0;
prbProgress.Enabled = false;
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
try
{
// Update the title bar at top that the program is working on some photos
this.Text = "<" + photos[e.ProgressPercentage].Name +
"> " + ProgramName;
// Perform a progress step
prbProgress.PerformStep();
// Try to update the ListView by using the progress percentage
// as an index in the ListView items
lsvFiles.Items[e.ProgressPercentage].SubItems[2].Text = e.UserState.ToString();
}
catch
{
this.Text = ProgramName;
}
}