Retrieving Contact Information In WP7.1 (Async/Sync Goodness)

30 October 2011

Over the last few days I've come up with another application that I'd love to make, and a small part of it involves retrieving contact details. As part of the Mango update, one of the new APIs provided by Microsoft is the contacts API, falling under the Microsoft.Phone.UserData namespace.

This is awesome, except that because the contact search method is only provided asynchronously, I was having a massive headache trying to write code that would return a random contact, wrapped in a DataObject, synchronously. In effect, I wanted something like this:

public Person GetRandomPerson(string s)
{
    Person myPerson = new Person();
    Person contacts = new Contacts();            
    contacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(contacts_SearchCompleted);
    contacts.SearchAsync(String.Empty, FilterKind.None, "All Contacts");            

    return Person;    //I'm not sure how this will ever work...
}

void contacts_SearchCompleted(object sender, ContactsSearchEventArgs e)
{
    //convert a Contact object to Person object here
}

After going around in circles with async and AsyncCallback and EAP and APM and a trillion google posts, it boiled down to a few things:

1) Passing the Person / DataObject through to the contact search callback as the third state/object parameter. - eg:

contacts.SearchAsync(String.Empty, FilterKind.None, myPerson);

2) using AutoResetEvent to control the thread execution, in order to prevent the GetRandomPerson() method from returning until we were sure the Contact Search had finished its thing - so creating an AutoResetEvent object, then calling are.WaitOne() after calling contacts.SearchAsync(), and in the callback, calling are.Set().

3) most importantly - calling all the code in a background thread. The UI thread doesn't like being asked to .WaitOne() and just dies, so I used ThreadPool.QueueUserWorkItem(). Of course, this means that my code now goes Async -> Sync -> Async (which is still async.)

My final code looked like this:

private void Button_Click(object sender, RoutedEventArgs e)
{
    //this should be done asynchronously - "request" a person

    List<Person> people = new List<Person>();
    PersonService ps = new PersonService();     
    ThreadPool.QueueUserWorkItem(func =>
    {
        for (int i = 0; i < 10; i++)
        {
                    people.Add(ps.GetRandomPerson()); //you need to call this on a separate thread or it will lock the ui thread.                       
        }                                   
        Dispatcher.BeginInvoke(() => { Status.Text = "done"; });    //your button click method is now also asynchronous
    });
}

/*** Helper class ***/      

public class PersonService
{
    AutoResetEvent sync = new AutoResetEvent(false);

    public Person GetRandomPerson()
    {
        Person person = new Person();
        Contacts contacts = new Contacts();            
        contacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(contacts_SearchCompleted);            
        contacts.SearchAsync(String.Empty, FilterKind.None, person);
        sync.WaitOne();
        return person;
    }

    void contacts_SearchCompleted(object sender, ContactsSearchEventArgs e)
    {
        Contact[] allContacts = e.Results.ToArray();
        Contact randomContact = allContacts[new Random().Next(allContacts.Length)];
        Person person = (Person)e.State;
        person.Name = randomContact.DisplayName;

        sync.Set();
    }
}

Note that the Windows Phone emulator comes loaded with some test contacts, but I'm not entirely sure they come with contact images so that's something you may need to double check if you're planning on using. If anyone's got a better way of solving the same issue, let me know!

Tags: async, contacts, Mango, wp7

Add a Comment

No Comments