Through a sample
application I will first demonstrate what a non-responsive UI is and how you get
one. Next I will demonstrate how to make the UI responsive through asynchronous
code. Finally I will demonstrate how to make the UI responsive through a much
simpler event-based asynchronous approach. I will also show how to keep the user
informed while the processing takes place with updates to Text Block's and a
Progress Bar.
What is a non responsive UI? Surely we've all witnessed a Windows Form or WPF
application that "locks up" from time to time. Have you ever thought of why this
happens? In a nutshell, it's typically because the application is running on a
single thread. Whether it's updating the UI or running some long process on the
back end such as a call to the database, everything must get into a single file
line and wait for the CPU to execute the command. So when we are making that
call to the database that takes a couple seconds to run the UI is left standing
in line waiting, unable to update itself and thus "locking up".
How can this unresponsive UI problem be resolved? Whether it's a Windows Form or
WPF application the UI updates on the main or primary thread. In order to keep
this thread free so the UI can remain responsive we need to create a new thread
to run any large tasks on the back-end. The classes used to accomplish this have
evolved over the different releases of the .NET Framework becoming easier and
richer in capabilities. This however can cause some confusion. If you do a
simple Google search on C# or VB and asynchronous or something similar you are
sure to get results showing many different ways of accomplishing asynchronous
processing. The answer to the question, which one do I use ?Of course depends on
what you're doing and what your goals are. Yes, I hate that answer also.
Since I cannot possibly cover every asynchronous scenario, what I would like to
focus on in this article is what I have found myself needing asynchronous
processing for a majority of the time. That would be keeping the UI of a WPF
application responsive while running a query on the database. Please note that
with some minor modifications the code in this article and in the downloadable
source code can be run for a Windows Form application also. In addition this
article is showing how to solve a specific problem with asynchronous
programming, by no means though is this the only problem asynchronous
programming is used for.
To help demonstrate synchronous, asynchronous and event-driven asynchronous
processing I will work through an application that transgresses through several
demos:
- Synchronous Demo (What not to do). Handle all
processing on a single thread and locking up the UI.
- Asynchronous Demo: Add a secondary thread to
free up that UI. I will also add some responsive text to the UI as a visual
indicator to let the user know where things are at.
- Asynchronous Event-Based Model Demo. With this I will also add a progress bar and some responsive text.
What Not To Do

Figure 1.
As I mentioned previously, what you do not want to do is run all you're
processing both back-end and UI on single thread. This will almost always lead
to a UI that locks up. Run the application, and click the Start button under
Synchronous Demo. As soon as you click the button try to drag the window around
your screen. You can't. If you try it several times the window may even turn
black, and you will get a "(Not Responding)" warning in the title bar. However,
after several seconds the window will unlock, the UI will update and you can
once again drag it around your screen freely.
Let's look at this code to see what's going on. If you look at the code for this
demo you will see the following:
First we have a delegate which is sort of like a function pointer but with more
functionality and provides type safety.
Delegate Function
SomeLongRunningMethodHandler(ByVal rowsToIterate As Integer) As String
We could easily not use the delegate in this sample and simply call the long
running method straight from the method handler. In fact, if I didn't already
know I was going to change its call to run asynchronously, I wouldn't use a
delegate. However, by using the delegate I can demonstrate how easy it is to go
from a synchronous call to an asynchronous call. In other words, let's say you
have a method that you may want to run asynchronously but you aren't sure. By
using a delegate you can make the call synchronously now, and later switch to an
asynchronous call with little effort.
I'm not going to go into too much more detail on delegates but the key to
remember is that the signature of the delegate must exactly match the signature
of the function (or Sub in VB) it will later reference. In this VB example the
delegate signature is for a Function that takes an Integer as a parameter and
returns a String.
Next we have the method handler for the click event of the button. After
resetting the Text Block to an empty String, the delegate is declared. Then the
delegate is instantiated (yes, a class is created when you create a delegate).
In this case a pointer to the function to be called by the delegate is passed as
a parameter to the constructor. What we now have is an instance of our delegate
(synchronousFunctionHandler) that points to the Function
SomeLongRunningSynchronousMethod. If you move down one more line you can see how
this method is called synchronously by the delegate. The delegate instance we
have is actually an instance of a class with several methods. One of those
methods is called Invoke. This is how we synchronously call the method attached
to the delegate. You may have also noticed the methods Begin Invoke and End
Invoke if you used intellisense.
Remember when I said that by using delegates we can easily move from synchronous
to asynchronous? You know have a clue as to how, and we will get into the
details of that soon.
Going back to our asynchronous example you can see the Invoke method is called
on the delegate instance. It is passed and integer as a parameter and returns a
string. That string is then assigned to a TextBlock to let the user know the
operation is complete.
Private Sub SynchronousStart_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)Handles synchronousStart.Click
Me.synchronousCount.Text
= ""
Dim synchronousFunctionHandler As SomeLongRunningMethodHandler
synchronousFunctionHandler = New SomeLongRunningMethodHandler(AddressOfMe.SomeLongRunningSynchronousMethod)
Dim returnValue As String =
synchronousFunctionHandler.Invoke(1000000000)
Me.synchronousCount.Text
= "Processing
completed. " & returnValue & "
rows processed."
End Sub
This is the function
that the delegate calls. As mentioned earlier it could have also been called
directly without any use of a delegate. It simply takes an integer and iterates
that many times returning the count as a string when completed. This method is
used to mimic any long running process you may have.
Private Function SomeLongRunningSynchronousMethod(ByVal rowsToIterate As Integer) As String
Dim cnt As Double =
0
For i As Long =
0 To rowsToIterate
cnt = cnt + 1
Next
Return cnt.ToString()
End Function
The bad news is that implementing this demo asynchronously causes an
unresponsive UI. The good news is that by using a delegate we have set ourselves
up to easily move to an asynchronous approach and a responsive UI.
A More Responsive Approach
Now run the downloaded demo again, but this time click the second Run button
(Synchronous Demo). Then try to drag the window around your screen. Notice
anything different? You can now click the button which calls the long running
method and drag the window around at the same time without anything locking up.
This is possible because the long running method is run on a secondary thread
freeing up the primary thread to handle all the UI requests.

This demo uses the same SomeLongRunningSynchronousMethod as the previous
example. It will also begin by declaring and then instantiating a delegate that
will eventually reference the long running method. In addition, you will see a
second delegate created with the name UpdateUIHandler which we will discuss
later. Here are the delegates and event handler for the button click of the
second demo.
Delegate Function AsyncMethodHandler(ByVal rowsToIterate As Integer) As String
Delegate Sub UpdateUIHandler(ByVal rowsupdated As String)
Private Sub AsynchronousStart_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Me.asynchronousCount.Text
= ""
Me.visualIndicator.Text
= "Processing,
Please Wait...."
Me.visualIndicator.Visibility
= Windows.Visibility.Visible
Dim caller As AsyncMethodHandler
caller = New AsyncMethodHandler(AddressOf Me.SomeLongRunningSynchronousMethod)
caller.Begin(Invoke(1000000000, AddressOf CallbackMethod, Nothing))
End Sub
Notice the event method starts
out similar to the previous example. We setup some UI controls, then we declare
and instantiate the first delegate. After that, things get a little different.
Notice the call from the delegate instance "caller" to Begin Invoke. Begin
Invoke is an asynchronous call and replaces the call to Invoke seen in the
previous example. When calling invoke we passed the parameter that both the
delegate and delegate method had in their signature. We do the same with Begin
Invoke; however there are two additional parameters passed which are not seen in
the delegate or delegate method signature. The two additional parameters are
Delegate Callback of type AsyncCallback and DelegateAsyncState of type Object.
Again you do not add these two additional parameters to your delegate
declaration or the method the delegate instance points to, however you must
address them both in the Begin Invoke call.
Essentially there are multiple ways to handle asynchronous execution using Begin
Invoke. The values passed for these parameters depend on which technique is
used. Some of these techniques include:
- Call Begin Invoke, do some processing, call
End Invoke.
- Using a Wait Handle of type IAsyncResult
returned by Begin Invoke.
- Polling using the Is Completed property of
the IAsyncResult returned by Begin Invoke.
- Executing a callback method when the asynchronous call completes.
We will use the last
technique, executing a callback method when the asynchronous call completes. We
can use this method because the primary thread which initiates the asynchronous
call does not need to process the results of that call. Essentially what this
enables us to do is call Begin Invoke to fire off the long running method on a
new thread. Begin Invoke returns immediately to the caller, the primary thread
in our case so UI processing can continue without locking up. Once the long
running method has completed, the callback method will be called and passed the
results of the long running method as type IAsyncResult. We could end everything
here, however in our demo we want to take the results passed into the callback
method and update the UI with them.
You can see our call to Begin Invoke passes an integer which is required by the
delegate and delegate method as the first parameter. The second parameter is a
pointer to the callback method. The final value passed is "Nothing" because we
do not need to use the DelegateAsyncState in our approach. Also notice we are
setting the text and visibility property of the visual Indicator Text Block
here. We can access this control because this method is called on the primary
thread which is also where these controls were created.
Protected Sub CallbackMethod(ByVal ar As IAsyncResult)
Try
Dim result As AsyncResult
= CType(ar,
AsyncResult)
Dim caller As AsyncMethodHandler
= CType(result.AsyncDelegate,
AsyncMethodHandler)
Dim returnValue As String =
caller.EndInvoke(ar)
UpdateUI(returnValue)
Catch ex As Exception
Dim exMessage As String
exMessage = "Error:
" & ex.Message
UpdateUI(exMessage)
End Try
End Sub

Join the conversation! Your thoughts help the community grow.