DoEvents() in WPF?

2082
6
04-03-2018 01:34 PM
AlexZlotin1
New Contributor III

I am using a locator to geocode addresses and then add the locations to the map as graphics (geocode, add graphic to the map, repeat). As the code runs in a loop, the map freezes and becomes responsive again after the last address has been geocoded and added to the map. If there are 30 addresses, the map is frozen for 8 seconds and then all the graphics get added at once. I would like the map to refresh after each location graphic has been added, and be responsive at all times.

As I understand, this happens because the code runs in the default UI thread. One option to solve this is to use the WPF version of the Windows Forms DoEvents() statement:

Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { }));

I have not been able to make this work, as I am not sure where this line of code should go. 

A cleaner way perhaps is to use a background thread (BackgroundWorker) to do the geocoding. With this approach, however, I am running into problems accessing objects in the UI thread.

I am using ArcGIS Runtime 100.1.

Thanks for any suggestions!

Alex

0 Kudos
6 Replies
dotMorten_esri
Esri Notable Contributor

As I understand, this happens because the code runs in the default UI thread

I'm curious what you do for 30 graphics that takes 8 seconds? There's nothing specifically in the runtime rendering that blocks the UI thread, apart from a brief moment of presenting the updated map 60 times a second (but that's a couple milliseconds at most). Everything else, including the rendering is done on background threads.

Are you ensuring your geocoding is done on a background thread and performing in a non-blocking way?

I have written code that is capable of updating 1000s of graphics a second, while maintaining a response map.

0 Kudos
AlexZlotin1
New Contributor III

Thanks Morten. Yes, the problem is not with rendering of the graphics, but with geocoding. I do need to run geocoding in a background thread so the UI thread is not blocked. Is there a way to run geocoding on a background thread synchronously and return a value? Is BackgroundWorker the only way to run code on a background thread?

Here what I want to achieve:

foreach (string address in addresses)

{

    - Instantiate a background worker (?), geocode the address and return X/Y coordinates

    - Create a point graphic and add it to the map  

0 Kudos
dotMorten_esri
Esri Notable Contributor

How are you doing the geocoding? The call should be asynchronous. (I assume you're not using .Wait(), but using 'await' right ?)

You could also try pushing it to another thread. For example:

var graphics = await Task.Run<List<Graphic>>(() =>
{
     var result = new List<Graphic>();
     for (int i = 0; i < 10; i++)
     {
          result.Add(new Graphic()); //Replace with geocode operation
     }
     return result;
});

0 Kudos
AlexZlotin1
New Contributor III

Thanks again, Morten. I got this working by using a BackgroundWorker to do the geocoding on another thread. Appreciate the help!

0 Kudos
dotMorten_esri
Esri Notable Contributor

A background worker definitely shouldn't be necessary. I'd still like to understand what you were doing that caused the UI thread to block like that, in case you hit a bug. Almost all our long-running tasks (meaning anything that can potentially exceed 50ms) are already asynchronous and runs on background threads. Could you share a snippet of the code that was doing this?

0 Kudos
dotMorten_esri
Esri Notable Contributor

Just to follow up and complete this thread after seeing some of the sample code.

The issue here is that the GeocodeAsync method returns a Task object. The .Result property was used on that Task, which will block until it completes. This is similar to using .Wait() and both should be avoided, as not only do they block, but they can cause dead-locks in a multi-threaded application.

Instead using the async/await keywords would prevent the geocodes (and any other Task returning method) from blocking.

 

If you’re new to async/await, I’d highly recommend watching these short videos that gives a great overview how to use it: https://channel9.msdn.com/Series/Three-Essential-Tips-for-Async

0 Kudos