When I wrote my first commercial application – a program that split XML files – I first created a class called SplitXML that split the file, but I needed to update a Progress Bar control with how much of the file had been split on the form that instantiated the SplitXML class. The following is a generic example of how you achieve this using delegates.
Note: This tutorial is available in both C# and Visual Basic
First create a DoWork Class
Visual Basic
Public Delegate Sub ProgressBarHandler(ByVal min As Integer, ByVal max As Integer, ByVal value As Integer)
Public Class DoWork
Public Event ReportProgress As ProgressBarHandler
Public Sub DoTheWork()
Dim min As Integer = 0
Dim max As Integer = 10
For value As Integer = 0 To max – 1
‘ Simulates a long running process like splitting a file,
‘ or getting data from a web service call, by stopping for
‘ 1 second (1000 milliseconds) in the loop
RaiseEvent ReportProgress(min, max, value)
System.Threading.Thread.Sleep(1000)
Next
End Sub
End Class
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ProgressBarDelegate
{
public delegate void ProgressBarHandler(int min, int max, int value);
public class DoWork
{
public event ProgressBarHandler ReportProgress;
public void DoTheWork()
{
int min = 0;
int max = 10;
for (int value = 0; value < max; value++)
{
// Simulates a long running process like splitting a file,
// or getting data from a web service call, by stopping for
// 1 second (1000 milliseconds) in the loop
ReportProgress(min, max, value);
System.Threading.Thread.Sleep(1000);
}
}
}
}
In the MainForm drag a button and a progress bar on the form
Click the button to add an event handler, and add the following code
Visual Basic
Public Class Form1
Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles button1.Click
Dim doWork As New DoWork()
AddHandler doWork.ReportProgress, AddressOf doWork_ProgressBarProgress
doWork.DoTheWork()
End Sub
Private Sub doWork_ProgressBarProgress(ByVal min As Integer, ByVal max As Integer, ByVal value As Integer)
progressBar1.Minimum = min
progressBar1.Maximum = max
progressBar1.Value = value + 1
End Sub
End Class
C#
using System;
using System.Windows.Forms;
namespace ProgressBarDelegate
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
DoWork doWork = new DoWork();
doWork.ReportProgress += new ProgressBarHandler(doWork_ProgressBarProgress);
doWork.DoTheWork();
}
void doWork_ProgressBarProgress(int min, int max, int value)
{
progressBar1.Minimum = min;
progressBar1.Maximum = max;
progressBar1.Value = value + 1;
}
}
}
This is quite a common requirement when programming i.e. letting an Form know the progress of a method in an instantiated class. Subsequent need of this functionality has resulted in my implementing this the correct "Windows" way:
Sometimes you need to pass more than one value or reference in the delegate. As a best practice the signature of a delegate is always;
Visual Basic
Public Delegate Sub SomeHandler(ByVal sender As Object, ByVal e As SomeEventArgs)
C#
public delegate void SomeHandler(object sender, SomeEventArgs e);
This means that if you need to pass more than one value (like the first example), this best practice is not adhered to. There is an eloquent way to do this though. I have two classes in the example.
First create a custom EventArgs class that takes two values (you can modify this to take any number of parameters your require)
Visual Basic
Public Class ProgressBarProgressEventArgs
Inherits System.EventArgs
Private m_maximum As Integer
Private m_stage As Integer
Public Property Maximum() As Integer
Get
Return m_maximum
End Get
Set(ByVal value As Integer)
m_maximum = value
End Set
End Property
Public Property Stage() As Integer
Get
Return m_stage
End Get
Set(ByVal value As Integer)
m_stage = value
End Set
End Property
End Class
C#
public class ProgressBarProgressEventArgs : System.EventArgs
{
private int maximum;
private int stage;
public int Maximum
{
get { return maximum; }
set { maximum = value; }
}
public int Stage
{
get { return stage; }
set { stage = value; }
}
}
Secondly create the DoWork class where the actual works takes place. This could be the splitting of an XML file, a production line in a factory or whatever.
Visual Basic
Public Delegate Sub ProgressBarHandler(ByVal sender As Object, ByVal e As ProgressBarProgressEventArgs)
Public Class DoWork
Public Event ReportProgress As ProgressBarHandler
Public Sub DoTheWork()
For stage As Integer = 1 To 5
OnStageCompleted(stage, 5)
System.Threading.Thread.Sleep(1000)
Next
End Sub
Private Sub OnStageCompleted(ByVal stage As Integer, ByVal max As Integer)
Dim args As New ProgressBarProgressEventArgs()
args.Stage = stage
args.Maximum = max
RaiseEvent ReportProgress(Me, args)
End Sub
End Class
C#
public delegate void ProgressBarHandler(object sender, ProgressBarProgressEventArgs e);
public class DoWork
{
public event ProgressBarHandler ReportProgress;
public void DoTheWork()
{
for (int stage = 1; stage < 6; stage++)
{
OnStageCompleted(stage, 5);
System.Threading.Thread.Sleep(1000);
}
}
private void OnStageCompleted(int stage, int max)
{
ProgressBarProgressEventArgs args = new ProgressBarProgressEventArgs();
args.Stage = stage;
args.Maximum = max;
ReportProgress(this, args);
}
}
Finally in the Mainform
Visual Basic
Public Class Form1
Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles button1.Click
Dim doWork As New DoWork()
AddHandler doWork.ReportProgress, AddressOf doWork_ReportProgress
doWork.DoTheWork()
End Sub
Private Sub doWork_ReportProgress(ByVal sender As Object, ByVal e As ProgressBarProgressEventArgs)
progressBar1.Maximum = e.Maximum
progressBar1.Value = e.Stage
End Sub
End Class
C#
using System;
using System.Windows.Forms;
namespace ProgressBarDelegate
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
DoWork doWork = new DoWork();
doWork.ReportProgress += new ProgressBarHandler(doWork_ReportProgress);
doWork.DoTheWork();
}
void doWork_ReportProgress(object sender, ProgressBarProgressEventArgs e)
{
progressBar1.Maximum = e.Maximum;
progressBar1.Value = e.Stage;
}
}
}
Try debugging this and you should see that this is much more "sensible", although the first example is much easier to understand. This is the preferred way of declaring delegates.
nice tutorial could maybe wrote one for updating control from classes
as example access form 2 to form 1 via a class
Do you have a sample project for Visual Basic by any chance? Part of the most vital parts of understanding delegates is to have a clear picture to work with. The way this tutorial is displayed add more complexity to an already complex piece of coding.
If your goal is to provide some understanding on how threads work, why make us piece together co-mingled code sources? If it’s not to much trouble, why not split the vb and c# code into an easy to use format where we can transfer your cool knowledge into a working sample?
I have been looking for this code for 2 days and I finally found it!. I now know how to use Delegate (which was an interview question that I got wrong!) Thank you, a very good snippet!!!!
Thank you for saying “thank you”.
Outstanding article, thanks m8!