views:

775

answers:

5

Hi, I´m programming a .NET Compact Framework application which shows maps on a PDA.

I´ve created an ad hoc component that paints it´s own piece of the whole map, using several of this components the big picture is composed. I did it this way to avoid the latency of painting the whole map in a single step.

What I would like to do know is to paint this pieces in their own thread, so the map appears to grow as a single entity and (also, and more important) to avoid freezing the rest of the user interface.

Right know each piece of the map is painted in its onPaint method. My idea is to, somehow, tell the system "execute this code in a thread please".

Something like:

protected override void OnPaint(PaintEventArgs e)
    {
       // <code to be executed in a thread>
       e.Graphics.paintTHis();
       e.Graphics.paintThat();
       whateverItTakesToPaintThisPieceOfTheMap();
       // </code to be executed in a thread>
    }

Do you know how to do this? Or is my approach simply wrong?

Thanks for your time!

+2  A: 

The approach is wrong. Code that updates the ui has to run on the ui thread. You'll get an exception if you update the ui from another thread.

Mendelt
A: 

I would suggest that you have some kind of messaging system from the underlying threads to the main UI thread.

This way the main UI thread makes all the changes and is triggered from the underlying threads. Also make sure you can send some data with those messages, in case you want to send some complex information back to the main UI thread.

Gustavo Carreno
+1  A: 

In order to call a function that updates the UI from another thread use the Invoke function of the Form.

Here is a good reference

http://weblogs.asp.net/justin_rogers/articles/126345.aspx

Edit: as pointed out in comments BeginInvoke would be better if you want the calling code not to wait for the UI thread.

Lou Franco
Invoke places a call on the original thread - which defeats the purpose.
ima
No, Invoke makes the UI call on the UI thread, but it does block. BeginInvoke would be better, though
Lou Franco
+1  A: 

Draw map in memory in a background thread, then render (in the UI thread) that raster image to screen when ready. Use BufferedGraphics if possible, GDI otherwise.

ima
+1  A: 

If rendering the map is time consuming and you don't want to freeze the GUI thread (make you app unresponsive) you could divide the screen into cells. Use a background thread to render a cell as a bitmap, use Invoke to tell the GUI thread to draw the finished cell, when invoke returns, let the thread continue on with the next cell.

You will need to draw direcly onto the control (not inside Paint()) or you will need to call a invalidateRect and have some logic to make sure your calculated image matches what the system wants you to draw.

This will make your image appear gradually, and your UI will be responsive. If the user makes some kind of action that makes it unnecessary to continue drawing, just abort the thread.

mliesen