Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF Progress Bar using Threads

0.00/5 (No votes)
9 Oct 2011 3  
WPF Progress Bar using Threads

Introduction

The following article describes a method to create a WPF project that provides a progress bar on a thread separate to your worker thread. The worker thread will be completing a task that will take 5 seconds, therefore you do not want your app to become unresponsive.

The code makes use of WPF windows, C# events, C# delegates. It would be useful if you are familiar with these types but it is not essential as I will explain what is needed.

In order to keep this tutorial as simple as possible, I will not make use of any WPF functionality to improve the appearance of the windows.

Application Design

Start Screen

The project contains two WPF windows and one class:

  • The first window named MainWindow will display information for the user. The window contains one button titled 'Begin Processing' and one label that will display a message to the user from your worker method.
  • The second window will host your progress bar. This window will be displayed while your 5 second operation is invoking.
  • The class named MyClass.cs will contain a method that will take approximately 5 seconds to complete, I will simulate a method taking 5 seconds by asking the thread to sleep for five seconds using the method Thread.Sleep(). The button in your Main Window will invoke this method.

The workflow of this application is as follows:

  • App starts (MainWindow class in invoked)
  • User clicks Button (Named Begin Processing)
  • A second thread is created that invokes MyClass
  • The Progress Window is displayed
  • MyClass completes and it invokes a method in MainWindow to close the Progress Window.

Based on the description of the workflow, we need the MainClass to include two methods:

  • The first method will carry out the required logic to start the worker thread and display the progress bar, it is called OnWorkerMethodStart().
  • The second method will be invoked when the worker thread has completed, it will close the progress bar and collect a string from the worker thread, it is called OnWorkerMethodComplete and accepts a string as a parameter.

The signature/name of the methods in the MainWindow are given below. We will fill in the logic in these methods later.

private void button1_Click(object sender, RoutedEventArgs e)
{
    OnWorkerMethodStart();
}
private void OnWorkerMethodStart()
{
    //Enter Method Logic here
}
private void OnWorkerMethodComplete(string message)
{
    //Enter Method Logic here
}

Worker Class (MyClass.cs)

App Running

Now that we know what we want to happen, we set about creating MyClass.

The first type we must create is a delegate. You can think of the delegate as a middle man to get to the method 'private void OnWorkerMethodComplete(string message)' in the MainWindow class.

To create the delegate, you can copy the name of the target method from MainWindow.cs private void OnWorkerMethodComplete(string message) and replace 'private' with 'public delegate' and rename as required.

public delegate void OnWorkerMethodCompleteDelegate(string message);

We will create the link between the middleman and the MainWindow later. For now, just be aware that this is what he does. On top of creating our middleman (delegate), we need to create an event to invoke the middleman into action.

The event declaration involves three pieces of data:

  • We tell .NET we need an event 'public event'
  • We tell .NET that it should call our middleman (delegate) 'public event OnWorkerMethodCompleteDelegate'
  • And finally, we give it a name 'public event OnWorkerMethodCompleteDelegate OnWorkerComplete;

So we are basically saying this event should call our middleman, which will call the method in MainWindow.cs:

private void OnWorkerMethodComplete(string message)

Finally we create our WorkerMethod, we simulate code that would take five seconds to complete by adding Thread.Sleep(5000) and we call the event providing a message saying "The processing is complete". The event will give it to the middleman and he will give the message to the method in MainWindow.cs:

private void OnWorkerMethodComplete(string message)
class MyClass
{
    public delegate void OnWorkerMethodCompleteDelegate(string message);
    public event OnWorkerMethodCompleteDelegate OnWorkerComplete;

    public void WorkerMethod()
    {
        Thread.Sleep(5000);
        OnWorkerComplete("The processing is complete");
    }
}

MainWindow.cs

Now we return to the MainWindow class. We begin by creating the ProgressBarWindow object private ProgressBarWindow pbw = null;. This is the Window we will invoke that contains the progress bar.

In the OnWorkerMethodStart() method, we create an instance of MyClass and then we hook up the event in myclass to our method OnWorkerMethodComplete.

myC.OnWorkerComplete +=
	new MyClass.OnWorkerMethodCompleteDelegate(OnWorkerMethodComplete);

We are telling .NET that when we invoke the OnWorkerComplete event, we want the delegate in myclass MyClass.OnWorkerMethodCompleteDelegate to invoke the method in MainWindow (OnWorkerMethodComplete).

We then create a thread and set the method in WorkerMethod from MyClass as the method to invoke on another thread. While this second thread begins, we create ProgressBarWindow and call the show method.

ThreadStart tStart = new ThreadStart(myC.WorkerMethod);
            Thread t = new Thread(tStart);
            t.Start();

As soon as the 5 seconds in the workerMethod completes, the event is called and it now knows to call the delegate which also knows to call the method private void OnWorkerMethodComplete(string message). Finally within this method, we use the .Dispatcher.Invoke methods on the instance of ProgressBarWindow and the Label to close the progress bar window and set the content of the label with the string value passed from the worker method.

App Complete

public partial class MainWindow : Window
{
    private ProgressBarWindow pbw = null;

    public MainWindow()
    {
        InitializeComponent();
    }
    private void button1_Click(object sender, RoutedEventArgs e)
    {
        OnWorkerMethodStart();
    }

    private void OnWorkerMethodStart()
    {
        MyClass myC = new MyClass();
        myC.OnWorkerComplete +=
        new MyClass.OnWorkerMethodCompleteDelegate(OnWorkerMethodComplete);
        ThreadStart tStart = new ThreadStart(myC.WorkerMethod);
        Thread t = new Thread(tStart);
        t.Start();

        pbw = new ProgressBarWindow();
        pbw.Owner = this;
        pbw.ShowDialog();
    }

    private void OnWorkerMethodComplete(string message)
    {
        pbw.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
        new Action(
        delegate()
        {
            pbw.Close();
        }
        ));

        myLabel.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
        new Action(
        delegate()
        {
            myLabel.Content = message;
        }
        ));
     }
}

Thanks for reading my article. All feedback is very much welcome!!

History

  • 9th October, 2011: Revision 1: In the method OnWorkerMethodStart(), the line pbw.Show(); was replaced with pbw.ShowDialog();
    This ensures that the progress bar window is a modal window, i.e. a child of the MainWindow. You then cannot invoke the progress window multiple times. The attached source code has also been updated to reflect this change.
  • 8th October, 2011: Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here