Click here to Skip to main content
15,512,632 members
Articles / Programming Languages / C#
Article
Posted 14 Mar 2008

Stats

130.8K views
4.8K downloads
88 bookmarked

Multiple Thread Progress Bar Control

Rate me:
Please Sign up or sign in to vote.
3.97/5 (15 votes)
14 Mar 2008CPOL4 min read
Control that shows multiple threads and events for each thread
MultiThreadProgressBar2.JPG

MultiThreadProgressBar1.JPG

Introduction

This article presents a control that creates threads and displays the progress and any messages sent from the thread.

Background

Running multiple threads and seeing the status of where they are has been a tricky subject for quite a while. The main thing being that you could not access anything that is being controlled by the main (UI) thread. This includes all parts of the form you are displaying and the properties and variables on it.

The .NET Framework 2.0 has helped clear up some of that with the introduction of the BackgroundWorker class. This class allows you to run some code on its own thread. This allows your UI to remain responsive while the code runs.

What It Does

This control can be easily modified to be used to monitor/control batch operations that can be done simultaneously. Some examples:

  • File Transfers
  • Image Processing
  • Directory Processing
  • Screen scraping

Each thread can send its own messages back to the container. The container shows those messages in a grid as the threads are running.

The thread container actually does throttling as well. Before you start the threads, you can set the number of threads you want to run concurrently. I hope to eventually make this property dynamic so that it can be changed on the fly.

What It Does Not Do

This control is designed for items that do not update the UI other than what is already displayed. If you need to render objects and update the screen, you will need to modify the way this works. That may be another article.

How It Works

Each thread is represented in a ThreadView which inherits from UserControl. This ThreadView contains a BackgroundWorker. When the ThreadContainer.Start method is called, it creates as many threads as it can (either Concurrent or TotalThreads if it is less than Concurrent). The ThreadViews are docked to the top so that they are all displayed nicely and expand to the width of the ThreadContainer. After creating the new ThreadView and display it and start it. As it works, it raises its JobEvent event. This passes the message to the container and the ThreadContainer stores it in its DataTable.

Each ThreadView contains a JobCompleted event that notifies the ThreadContainer that it is done.

How to Use the Code

I have divided up the code for the control and the test app into two projects. So, if you usually like to look at the code and the forms before running it, then THE FIRST THING YOU DO AFTER OPENING THE PROJECT IS BUILD IT OR YOU WILL GET ERRORS because I have stripped out the information to make the download smaller.

The ThreadView Class

Currently the code sleeps for a random number of milliseconds 100 times. Not terribly useful. Go to the ThreadView class and the worker_DoWork method. You can change this code to do whatever you would like it to do.

Here is the code. I have omitted some of the comments for brevity.

C#
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
    for (int i= 0; i < 100; i++) 
    {
        //this checks to see if the user pressed the cancel button
        if(worker.CancellationPending)
            break;

        worker.ReportProgress(i, "Progress:" + i.ToString());

        try
        {
            Thread.Sleep(r.Next(0, _maxSleep));
        }
        catch 
        {
            // You really want to do a better job than I did with error handling    
        }
        OnJobEvent(new JobEventEventArgs(ThreadName, "Progress: " + i.ToString()));
    }
}

There are two function calls that you need to worry about:

  • C#
    worker.ReportProgress(i, "Progress: " + i.ToString()); 

    This line sets the progress bars position as well as the text above the progress bar.

  • C#
    OnJobEvent(new JobEventEventArgs(ThreadName, "Progress: " + i.ToString())); 

    This sends any messages you want to post back to the ThreadContainer.

That’s it! Change the code to do what you want it to do and add the progress bars and the messages to your messages and you are done!

Properties and Constructors

Currently I pass the maximum time I would like to the thread to sleep in via a property, but you can define whatever properties you would like, or change the constructor and pass them in as parameters. What you have to do is make sure you pass it into the ThreadContainer as well as the ThreadView classes. Or you can pass a value to the ThreadView (i.e. a DirectoryInfo) and it calculates the values for you (DirectoryInfo.GetFiles().GetUpperBound(0)).

Controlling the ThreadView Progress Bar Maximum

Currently I have set the bar maximum at 100, but if the number of objects you are working with is not 100, you have 3 options:

  • Take the number of iterations you are doing and do an integer divide on the current index.
  • Pass the object you are going to work with (i.e. DirectoryInfo) into the ThreadView constructor and calculate the maximum. (DirectoryInfo.GetFiles().GetUpperBound(0)).
  • Pass the maximum into the thread as a property or a constructor parameter.

History

  • 14th March, 2008: Initial post

License

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


Written By
Software Developer (Senior) MPF Federal
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionCan anyone point me to a TPL Dataflow version of this? Pin
ancich6-Dec-19 10:06
ancich6-Dec-19 10:06 
GeneralMy vote of 5 Pin
Bernard Chayer9-Jul-12 5:03
professionalBernard Chayer9-Jul-12 5:03 
Questionhow does it work? Pin
auldh26-Jan-10 8:58
auldh26-Jan-10 8:58 
this article covers what i'm looking for but i'm at a lost on how it works. i'm trying to understand what is actually updating the progressbar.

i need to call 3 progressbars in my c# project. i did not build a "dll" for the threading. my application calls multiple functions in a linear fashion. i want to give a status to specific progressbars as the function is called and processes it request.

my head is spinning with this threading (calling the backgroundworker).

in your project you call the "OnJobEvent(new JobEventEventArgs(ThreadName, "Progress: " + i.ToString()));" to call your thread class. can you help me understand how it is possible to mimic what you are doing?

//sample of code...
private void ListLDN_Start(string[] filenames, BackgroundWorker
worker, DoWorkEventArgs e)
{
//starting conerting the listldn into a comma delimited file
:
:
//start the body of the work
lcount = build_master_gspfile(filenames);
build_master_gsparray(lcount, worker, e);
compare_files(lcount, filenames);
//finish the body of the work
}

private int build_master_gspfile(string[] filenames)
{
int lcount = 0; // file list incrementer to walk through the
file list
string data = null; //ra dta string read from input file then
stored in ouput file for later
string temp1 = null, temp2 = string.Empty, site_number = null;
int comma = 0, quote = 0, a = 0;
bool peg = false; //use this to gauge if a record was matched

lcount = 0;

//master file stored in program dir.
StreamWriter sw = new StreamWriter(master_file);
try
{
for (lcount = 0; lcount < filenames.Length; lcount++)
{
:
:
worker.ReportProgress(iSite / 16);
:
:
}//while (sinput.Peek() >= 0)
//should peg not set to turn there was an error in the
gsp file with
//its own site number
if (peg == false)
{
MessageBox.Show("Process stopped because local site
number (" + temp1 +
") is missing in its own file: " +
filenames[lcount] +
". Program error, system Abort.");
Application.Exit();
}
}//for(lcount = 0; lcount < filenames.Length; lcount++)
}
catch (Exception err)
{
ErrorFile ef = new ErrorFile();
:
:
}
sw.Close();
return lcount;
}//private void build_master_gspfile()

private void build_master_gsparray(int lcount, BackgroundWorker
worker, DoWorkEventArgs e)
{
//open the gsp master file and load the master gsp array
int iSite = 0, iElements = 0, iTGSP = 0, iComma = 0;
StreamReader mgsp = new StreamReader(master_file);
mgsp_count = 0; //initialize the elemenst count of number of
gsps stored in
//master gsp array.

//the mater_file is comma delimited and quote defined text format.
//need to read each line and extract the comma elements and
populate
//the master_array(#sites, #elements).
while (mgsp.Peek() >= 0)
{
:
:
if (iElements == 98 && rBngsp.Checked == true)
{
iTGSP = Convert.ToInt16(master_gsp[iSite, 1]);
tgsp_array[iTGSP] = "1";
worker.ReportProgress(iSite / 16);
iSite++;
i = 0;
iElements = 0;
iPlace = 0;
}
if (iElements >= 96 && rBListLDN.Checked == true)
{
if (iElements == 96)
{
master_gsp[iSite, 96] = "";
master_gsp[iSite, 97] = "";
}
if (iElements == 97)
master_gsp[iSite, 97] = "";
iTGSP = Convert.ToInt16(master_gsp[iSite, 1]);
tgsp_array[iTGSP] = "1";
worker.ReportProgress(iSite / 16);
iSite++;
i = 0;
iElements = 0;
iPlace = 0;
}
}//for (int i = 0; iSite < lcount; ++i)
if (iSite == lcount)
break;
}//while (mgsp.Peek() >= 0)
mgsp.Close();
mgsp_count = iSite;
}//private void build_master_gsparray(int lcount)
GeneralNice Pin
MaxGuernsey17-Mar-08 19:39
MaxGuernsey17-Mar-08 19:39 
GeneralRe: Nice Pin
R2B218-Mar-08 5:58
R2B218-Mar-08 5:58 
GeneralRe: Nice Pin
MaxGuernsey18-Mar-08 8:17
MaxGuernsey18-Mar-08 8:17 
GeneralRe: Nice Pin
R2B218-Mar-08 13:02
R2B218-Mar-08 13:02 
GeneralRe: Nice Pin
MaxGuernsey18-Mar-08 15:29
MaxGuernsey18-Mar-08 15:29 
GeneralRe: Nice Pin
R2B219-Mar-08 6:55
R2B219-Mar-08 6:55 
GeneralRe: Nice Pin
MaxGuernsey19-Mar-08 7:41
MaxGuernsey19-Mar-08 7:41 
GeneralRe: Nice Pin
R2B219-Mar-08 8:35
R2B219-Mar-08 8:35 
GeneralRe: Nice Pin
MaxGuernsey19-Mar-08 9:10
MaxGuernsey19-Mar-08 9:10 
GeneralRe: Nice Pin
R2B219-Mar-08 10:10
R2B219-Mar-08 10:10 
GeneralRe: Nice Pin
MaxGuernsey19-Mar-08 11:14
MaxGuernsey19-Mar-08 11:14 
GeneralRe: Nice Pin
Stuart Roberts1-Apr-08 14:52
Stuart Roberts1-Apr-08 14:52 
GeneralRe: Nice Pin
MaxGuernsey1-Apr-08 15:05
MaxGuernsey1-Apr-08 15:05 
GeneralRe: Nice Pin
R2B22-Apr-08 5:28
R2B22-Apr-08 5:28 
GeneralBuild problem... Pin
thompsons15-Mar-08 9:10
thompsons15-Mar-08 9:10 
GeneralRe: Build problem... Pin
Rickybibi17-Mar-08 1:54
Rickybibi17-Mar-08 1:54 
GeneralRe: Build problem... Pin
R2B217-Mar-08 5:37
R2B217-Mar-08 5:37 
GeneralRe: Build problem... Pin
Ciupaz4-Sep-08 22:20
Ciupaz4-Sep-08 22:20 
GeneralRe: Build problem... Pin
R2B25-Sep-08 4:09
R2B25-Sep-08 4:09 
GeneralRe: Build problem... Pin
Ciupaz5-Sep-08 5:48
Ciupaz5-Sep-08 5:48 
GeneralRe: Build problem... Pin
thompsons17-Mar-08 9:13
thompsons17-Mar-08 9:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.