Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / multimedia / GDI+

Scanning image dimensions using BackgroundWorker thread, ListView, and ProgressBar

4.29/5 (4 votes)
12 Oct 2009CPOL1 min read 34.1K   2.1K  
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.

screen.png

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.

C#
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;
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)