Biribu
Biribu

Reputation: 3823

Wait until async operation ends Windows Phone

I am trying to parse some pois from a xml download from a server and I saw that it is done after the program continues in the main thread. I haven't found a way to solve it because I need it.

using System.Threading;

namespace XML_Parser
{
    class XMLParserPOI_Wiki
    {
        private static XMLParserPOI_Wiki objSingle = new XMLParserPOI_Wiki();
        public static XMLParserPOI_Wiki ObjSingle
        {
            get { return objSingle; }
            set { objSingle = value; }
        }
        private List<POI> places;
        public List<POI> Places
        {
            get { return places; }
        }
        private XMLParserPOI_Wiki()
        {

        }



        public void parseWikitude(string url)
        {
            places = new List<POI>();
            WebClient wc = new WebClient();
            wc.DownloadStringCompleted += HttpsCompleted;
            wc.DownloadStringAsync(new Uri(url));            
        }

        private void HttpsCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                XDocument xdoc = XDocument.Parse(e.Result, LoadOptions.None);
                XNamespace ns = "http://www.opengis.net/kml/2.2";
                XNamespace ns2 = "http://www.openarml.org/wikitude/1.0";
                var placemarkers = xdoc.Root.Descendants(ns + "Placemark");
                places =
                (from query in xdoc.Root.Descendants(ns + "Placemark")
                 select new POI
                 (
                   ...
                 )).ToList();
                System.Diagnostics.Debug.WriteLine("Lista");
                System.Diagnostics.Debug.WriteLine(places.Count);

            }
        }

    }
}

In my main class:

XMLParserPOI_Wiki parserXML = XMLParserPOI_Wiki.ObjSingle;

        parserXML.parseWikitude("http://myurl.php");
        System.Diagnostics.Debug.WriteLine("Lista de pois");
        System.Diagnostics.Debug.WriteLine(parserXML.Places.Count);
        for (int i = 0; i < parserXML.Places.Count; i++)
        {
            System.Diagnostics.Debug.WriteLine(parserXML.Places[i].getName());
        }

It prints Lista de POis and 0, before Lista and X (number of pois)

I guess I should freeze main thread but I tried a couple of times with some examples and they didn't work.

Can you point me to any tutorial about this? More than get an answer I want to understand how to deal with this kind of operations

Upvotes: 0

Views: 97

Answers (1)

yasen
yasen

Reputation: 3580

First of all, you don't want to block (freeze) the UI thread EVER!

This is called asynchronous programming. There are two things you can do to solve your problem (I recommend option 2!):

  1. Use the classic callback model. You basically call some long operation on a background thread and give a function to it, to execute when the long operation is done. Here's how to do it in your case.

    At the end of the HttpsCompleted method, invoke what you need on the UI Thread using:

    Deployment.Current.Dispatcher.BeginInvoke(delegate() {
        //The code here will be invoked on the UI thread
    });
    

    If you want to make the parseWikitude method reusable, you should pass an Action to it. This way you can call it from multiple places and tell it what to do on the UI thread when the parsing is done. Something like this:

    public void parseWikitude(string url, Action callback) {
        places = new List<POI>();
        WebClient wc = new WebClient();
        wc.DownloadStringCompleted += HttpsCompleted;
        wc.DownloadStringAsync(new Uri(url), callback);
    }
    
    private void HttpsCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        if (e.Error == null)
        {
            ...
    
            var callback = (Action)e.UserState;
            Deployment.Current.Dispatcher.BeginInvoke(callback);
        }
    }
    
    //And then when you use it, you do it like that
    parserXML.parseWikitude("http://myurl.php", delegate() {
       //The code here will be executed on the UI thread, after the parsing is done 
    });
    
  2. Use the (rather) new asnyc pattern in .NET. You should read about this, as it is one of the best features of .NET if you ask me. :) It basically does the callback thing automatically and makes the code a lot easier to read/maintain/work-with. Once you get used to it, that is.

    Here's an example:

    public Task<List<POI>> parseWikitude(string url) {
        TaskCompletionSource<List<POI>> resultTaskSource = new TaskCompletionSource<List<POI>>();
    
        WebClient wc = new WebClient();
        wc.DownloadStringCompleted += HttpsCompleted;
        wc.DownloadStringAsync(new Uri(url), resultTaskSource);
    
        return resultTaskSource.Task;
    }
    
    private void HttpsCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        if (e.Error == null)
        {
            //If needed, run the code here in a background thread
            //...
    
            var resultTaskSource = (TaskCompletionSource<List<POI>>)e.UserState;
            resultTaskSource.SetResult(places);
        }
    }
    
    //And when you need to use it, do it like that (note, this must be invoked in an async method!)
    var places = await parser.parseWikitude("http://myurl.php");
    //The code here will be executed on the same thread when the parsing is done, but the thread will not be blocked while the download is happening.
    

So, these are the two ways you can handle it. Option one is old-school, classic and easy. Option two is the new and cool way of doing async stuff. It really is a must-know. Simplifies a lot of things once you get used to it.

P.S. Sorry if I got carried away. :D

Upvotes: 2

Related Questions