Asynchronous method calling using events and delegates






4.58/5 (8 votes)
This article describes how to call methods asynchronously using delegates and custom events.
Introduction
In this article, we’ll elaborate how to call methods asynchronously. As we all know, delegates are used to call methods asynchronously, but here we’ll combine asynchronous function calling and the event handling concept for achieving Asynchronous Processing. To avoid confusion, let’s define what we are going to do in this example shortly.
- We’ll develop a Task class that contains an
IterateFile()
method and three events –StatusEvent
,Complete
,ProgressChanged
.StatusEvent
will keep track of the file status during file processing. The Complete event is raised when file processing completes. TheProgressChanged
event is raised when the iteration completes. - We’ll also develop the
FileProcessing
class that contains theProcessing()
method andStatusEvent
.StatusEvent
is raised when file processing status changes. - We’ll have a Windows Form that displays all this information.
Objective
Achieving asynchronous behavior for our function calling using delegates and event handlers.
Using the code
We first develop the TaskToPerform
class with
the
IterateFile
method and events. So here is our class:
class TaskToPerform
{
public event StatusEventHandler StatusEvent
{
add
{
statusEvent += value;
}
remove
{
statusEvent -= value;
}
}
private StatusEventHandler statusEvent;
public event CompletedEventHandler Complete
{
add
{
complete += value;
}
remove
{
complete -= value;
}
}
private CompletedEventHandler complete;
public event ProgressValueChangedEventHandler ProgressValueChanged
{
add
{
progressValueChange += value;
}
remove
{
progressValueChange -= value;
}
}
private ProgressValueChangedEventHandler progressValueChange;
public void IterateFile(string path)
{
string[] files = System.IO.Directory.GetFiles(path, "*.*");
int tot = files.Length;
int cnt = 1;
var enumerator = files.GetEnumerator();
while (enumerator.MoveNext())
{
FileProcessing fp = new FileProcessing(enumerator.Current.ToString());
fp.StatusEvent+= new StatusEventHandler(This_StatusEvent);
fp.Processing();
if (progressValueChange != null)
progressValueChange(this,
new ProgressValueChangeEventArgs()
{ MaxValue = tot, MinValue = 0, Value = cnt++ });
}
if (complete != null)
complete(this);
}
public void This_StatusEvent(object sender, StatusEventArgs e)
{
if (statusEvent != null)
statusEvent(sender, e);
}
}
As we discussed we’ve a class with three events. One new thing that might surprise you is event declaration.
public event ProgressValueChangedEventHandler ProgressValueChanged
{
add
{
progressValueChange += value;
}
remove
{
progressValueChange -= value;
}
}
private ProgressValueChangedEventHandler progressValueChange;
Yes, we can define our events like properties also as shown above. This kind of declaration is known as custom event declaration.
Now we come to the main part of the discussion – the IterateFile
method.
public void IterateFile(string path)
{
string[] files = System.IO.Directory.GetFiles(path, "*.*");
int tot = files.Length;
int cnt = 1;
var enumerator = files.GetEnumerator();
while (enumerator.MoveNext())
{
FileProcessing fp = new FileProcessing(enumerator.Current.ToString());
fp.StatusEvent+= new StatusEventHandler(This_StatusEvent);
fp.Processing();
if (progressValueChange != null)
progressValueChange(this,
new ProgressValueChangeEventArgs() { MaxValue = tot, MinValue = 0, Value = cnt++ });
}
if (complete != null)
complete(this);
}
In this method, we fetch all files from a given path and iterate over that collection. Here we do something tricky that I define
the
FileProcessing
class object
and pass the file name as argument in the FileProcessing
class’ constructor. Then register its
StatusEvent
with a method defined in our task class.
That means we’ve event handler code for the FileProcessing
StatusEvent
in our
TaskToPerform
class. So whenever StatusEvent
is
invoked from the
FileProcessing
class it is handled in the TaskToPerform
class. The ProgressValueChange
event will fire after processing
is completed for each file so that from that event
we could get an idea about how many files completed processing. After all files complete their processing,
the Complete
event is fired that indicates completion of the task.
Let’s move to our second point of discussion that is the FileProcessing
class which is shown below:
class FileProcessing
{
private readonly string filename;
public event StatusEventHandler StatusEvent
{
add
{
statusEvent += value;
}
remove
{
statusEvent -= value;
}
}
private StatusEventHandler statusEvent;
public FileProcessing(string fileName) { this.filename = fileName; }
public void Processing()
{
System.Threading.Thread.Sleep(500);
if (statusEvent != null)
{
var args = new StatusEventArgs() { Name = filename, Status="Opened" };
statusEvent(this, args);
System.Threading.Thread.Sleep(500);
args.Status = "Processed";
statusEvent(this, args);
System.Threading.Thread.Sleep(1000);
args.Status = "Closed";
statusEvent(this, args);
System.Threading.Thread.Sleep(500);
}
}
}
As you can see, in this class, we’ve a Processing
method which fires the Status
event periodically. So ultimately we’ll have different states
for a single file periodically. I think there is no more description needed for this class. Finally we are moving to our main part that is on
the WinForm. On the WinForm I’ve put a progressbar and two labels. lblStatus
will show Filestatus
which
is updated during file processing. The progress bar works on the ProgressValueChanges
event. Let’s see the logic:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
void UpdateLabelStatus(Label lbl, string message)
{
if (lbl.InvokeRequired)
{
lbl.BeginInvoke(new ActionLabel, string>(UpdateLabelStatus), lbl, message);
}
else
{
lbl.Text = message;
lbl.Update();
lbl.Parent.Update();
}
}
void UpdateProgressBar(ProgressBar pBar, int maxValue, int value)
{
if (pBar.InvokeRequired)
{
pBar.BeginInvoke(new Action<ProgressBar, int, int>(
UpdateProgressBar), pBar, maxValue, value);
}
else
{
pBar.Maximum = maxValue;
pBar.Minimum = 0;
pBar.Value = value;
pBar.Update();
pBar.Parent.Update();
string pervalue = string.Format("{0} file(s) processed out of {1}", value, maxValue);
UpdateLabelStatus(lblProgress, pervalue.ToString());
}
}
void MyEventFunction(object sender, StatusEventArgs e)
{
UpdateLabelStatus(lblStatus, string.Format("File {0} {1} successfully", e.Name, e.Status));
}
void EventDemo_ProgressValueChanged(object sender, ProgressValueChangeEventArgs e)
{
UpdateProgressBar(pBar, e.MaxValue, e.Value);
}
void EventDemo_Complete(object sender)
{
UpdateLabelStatus(lblStatus, "NA");
UpdateLabelStatus(lblProgress, "NA");
}
private void Form1_Load(object sender, EventArgs e)
{ }
void InitiateProcess()
{
TaskToPerform ed = new TaskToPerform();
ed.StatusEvent += new StatusEventHandler(MyEventFunction);
ed.Complete += new CompletedEventHandler(EventDemo_Complete);
ed.ProgressValueChanged +=
new ProgressValueChangedEventHandler(EventDemo_ProgressValueChanged);
ed.IterateFile(@"C:\Windows");
}
private void btnStart_Click(object sender, EventArgs e)
{
System.Threading.Thread t = new System.Threading.Thread(InitiateProcess);
t.Start();
}
}
In this form, you’ll see that I used the Action<>
delegate to call the method asynchronously.
void UpdateLabelStatus(Label lbl, string message)
{
if (lbl.InvokeRequired)
{
lbl.BeginInvoke(new Action<Label, string>(UpdateLabelStatus), lbl, message);
}
else
{
lbl.Text = message;
lbl.Update();
lbl.Parent.Update();
}
}
In the UpdateLabelStatus
method, we pass the Label
object that is to be changed and the message string passed as arguments. We invoke this method asynchronously
using the Action<>
delegate. Thus we change the label value asynchronously and hence for the Progressbar also.
Conclusion
I hope this article has helped you understand asynchronous processing thoroughly, but still as a developer I advise you all to develop more examples to understand this concept thoroughly.