Scanning image dimensions using BackgroundWorker thread, ListView, and ProgressBar






4.29/5 (4 votes)
Scanning image dimensions can take a few milliseconds, so when scanning multiple images, a BackgroundWorker thread comes in handy. The progress is updated to a ListView and shown in a ProgressBar.
Introduction
This article is an extended version of another article that I found very interesting, named BackgroundWorker Threads and Supporting Cancel, written by Andrew D. Weiss.
This article explains how a BackgroundWorker
thread is run asynchronously to scan image dimensions. The user has the option to select a folder or select some specific images. After inserting the image names to a ListView
, a collection object is passed to the BackgroundWorker
that scans each image and reports the progress back and displays it on a ProgressBar
.
Using the code
When photos are added, they are stored in an object named PhotoCollection
, that inherits from List<Photo>
. Each photo has a FileInfo
instance and some added properties. The PhotoCollection
can be exported into an XML file, and imported back from an XML file, using the class XmlParser
.
This software can be extended further into a resizing program, renamer, organizer, or whatever you would want to do to a collection of photos. The BackgroundWorker
could also pre-generate thumbnails, and the ListView
could be replaced with a “Large Icon” view like you can see in Windows XP or later.
BackgroundWorker
The method UpdateListView_HeavyProcess()
starts the thread and feeds it its working argument. The method worker_DoWork()
will run the heavy process, and each time trd.ReportProgress()
is called inside worker_DoWork()
, the method worker_ProgressChanged()
is invoked which is allowed to update the GUI components. When the thread has completed its work, the method worker_RunWorkerCompleted()
is called which casts the results back to 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;
}
}