Easy UI threading with Background Worker
Written by Mike James   
Friday, 01 October 2010
Article Index
Easy UI threading with Background Worker
Invoke and BackgroundWorker patterns
A bouncing ball example
Using BackgroundWorker
Going further

Banner

Bounce a ball

It is always difficult to find a good simple example and in this case you would certainly do things differently if you really wanted to implement a bouncing ball graphic. 

However it is a good example, because I've seen beginners try to transfer this simple idea from say Windows Forms where it more or less works, with the help of DoEvents, to WPF/Silverlight where it simply doesn't. 

So let's code a bounding rectangle the dumb way first. The form has a Canvas, a Button and the Canvas has a single Rectangle to display:

<Grid x:Name="LayoutRoot" Background="White">
 <Button Content="Button"
Height="23"
Name="button1"
Width="75"
Click="button1_Click" />
 <Canvas Height="250"
Name="canvas1"
Width="250">
<Rectangle
Canvas.Left="50"
Canvas.Top="100"
Height="15"
Name="rectangle1"
Stroke="Black"
StrokeThickness="1"
Width="15" />
 </Canvas>
</Grid>

The button's click event handler simply calls a method to do the work which in turn calls an Update routine to change the Rectangles position by adding a "velocity" term to its position:

int vx = 1;
int vy = 1;
private void button1_Click(object sender,
RoutedEventArgs e)
{
Work(); 
}

void Work()
{
do
{
 update();
 } while (true);
}

Notice that the infinite loop a sure sign that something isn't going to work on a single threaded UI - but with the addition of a DoEvents it does work in Windows Forms.

Think of the Work method in general as doing something complicated like solving a differential equation that takes a long time and then calling update to display the result.

In this case the update method is simply:

void update()
{
Canvas.SetLeft(rectangle1,
Canvas.GetLeft(rectangle1) + vx);
 Canvas.SetTop(rectangle1,
Canvas.GetTop(rectangle1) + vy);
 if(Canvas.GetLeft(rectangle1)<=0 || 
(Canvas.GetLeft(rectangle1)+
rectangle1.Width)>=
(canvas1.Width-1)) vx=-vx;
 if(Canvas.GetTop(rectangle1)<=0 ||
(Canvas.GetTop(rectangle1)+
rectangle1.Height)>=
(canvas1.Height-1)) vy=-vy;
}

You can see that the first two instructions move the position of the Rectangle and the two if statements check to see if it has reached the edge of the canvas. If it has the if statements simply reverse the appropriate velocity.

If you don't like the example consider the Work method doing some complex number crunching and then calling Update to display the intermediate result in an effort to show the user how things are developing. 

Fixing it

Of course if you run this example nothing will happen.

Once you click the button the infinite loop starts to run and updates the UI objects but as the UI thread never gets a chance to do anything else the application locks.

It is worth emphasising here that if you do put a DoEvents in the loop everything works. 

Many novice WPF programmers when they find themselves in this situation add something like:

Thread.Sleep(100);

in the hope that pausing the thread might give the UI time to update - again this doesn't work because the UI thread is the on that is put to sleep.

One solution that does work is to use a timer to run the  the Update method. As long as the total time that the Update method takes to run is less than the timer tick rate you can use this to share the UI thread between computation and the UI. Sometimes this is a natural approach sometimes it isn't.

Banner

<ASIN:143022455X>

<ASIN:0596800959>

<ASIN:159059990X>

<ASIN:143022519X>

<ASIN:1430272910>

 



Last Updated ( Friday, 01 October 2010 )